]> git.proxmox.com Git - mirror_qemu.git/blob - replay/replay.c
Merge remote-tracking branch 'remotes/bonzini-gitlab/tags/for-upstream' into staging
[mirror_qemu.git] / replay / replay.c
1 /*
2 * replay.c
3 *
4 * Copyright (c) 2010-2015 Institute for System Programming
5 * of the Russian Academy of Sciences.
6 *
7 * This work is licensed under the terms of the GNU GPL, version 2 or later.
8 * See the COPYING file in the top-level directory.
9 *
10 */
11
12 #include "qemu/osdep.h"
13 #include "qapi/error.h"
14 #include "sysemu/cpu-timers.h"
15 #include "sysemu/replay.h"
16 #include "sysemu/runstate.h"
17 #include "replay-internal.h"
18 #include "qemu/main-loop.h"
19 #include "qemu/option.h"
20 #include "sysemu/cpus.h"
21 #include "qemu/error-report.h"
22
23 /* Current version of the replay mechanism.
24 Increase it when file format changes. */
25 #define REPLAY_VERSION 0xe0200a
26 /* Size of replay log header */
27 #define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
28
29 ReplayMode replay_mode = REPLAY_MODE_NONE;
30 char *replay_snapshot;
31
32 /* Name of replay file */
33 static char *replay_filename;
34 ReplayState replay_state;
35 static GSList *replay_blockers;
36
37 /* Replay breakpoints */
38 uint64_t replay_break_icount = -1ULL;
39 QEMUTimer *replay_break_timer;
40
41 bool replay_next_event_is(int event)
42 {
43 bool res = false;
44
45 /* nothing to skip - not all instructions used */
46 if (replay_state.instruction_count != 0) {
47 assert(replay_state.data_kind == EVENT_INSTRUCTION);
48 return event == EVENT_INSTRUCTION;
49 }
50
51 while (true) {
52 unsigned int data_kind = replay_state.data_kind;
53 if (event == data_kind) {
54 res = true;
55 }
56 switch (data_kind) {
57 case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST:
58 replay_finish_event();
59 qemu_system_shutdown_request(data_kind - EVENT_SHUTDOWN);
60 break;
61 default:
62 /* clock, time_t, checkpoint and other events */
63 return res;
64 }
65 }
66 return res;
67 }
68
69 uint64_t replay_get_current_icount(void)
70 {
71 return icount_get_raw();
72 }
73
74 int replay_get_instructions(void)
75 {
76 int res = 0;
77 replay_mutex_lock();
78 if (replay_next_event_is(EVENT_INSTRUCTION)) {
79 res = replay_state.instruction_count;
80 if (replay_break_icount != -1LL) {
81 uint64_t current = replay_get_current_icount();
82 assert(replay_break_icount >= current);
83 if (current + res > replay_break_icount) {
84 res = replay_break_icount - current;
85 }
86 }
87 }
88 replay_mutex_unlock();
89 return res;
90 }
91
92 void replay_account_executed_instructions(void)
93 {
94 if (replay_mode == REPLAY_MODE_PLAY) {
95 g_assert(replay_mutex_locked());
96 if (replay_state.instruction_count > 0) {
97 replay_advance_current_icount(replay_get_current_icount());
98 }
99 }
100 }
101
102 bool replay_exception(void)
103 {
104
105 if (replay_mode == REPLAY_MODE_RECORD) {
106 g_assert(replay_mutex_locked());
107 replay_save_instructions();
108 replay_put_event(EVENT_EXCEPTION);
109 return true;
110 } else if (replay_mode == REPLAY_MODE_PLAY) {
111 g_assert(replay_mutex_locked());
112 bool res = replay_has_exception();
113 if (res) {
114 replay_finish_event();
115 }
116 return res;
117 }
118
119 return true;
120 }
121
122 bool replay_has_exception(void)
123 {
124 bool res = false;
125 if (replay_mode == REPLAY_MODE_PLAY) {
126 g_assert(replay_mutex_locked());
127 replay_account_executed_instructions();
128 res = replay_next_event_is(EVENT_EXCEPTION);
129 }
130
131 return res;
132 }
133
134 bool replay_interrupt(void)
135 {
136 if (replay_mode == REPLAY_MODE_RECORD) {
137 g_assert(replay_mutex_locked());
138 replay_save_instructions();
139 replay_put_event(EVENT_INTERRUPT);
140 return true;
141 } else if (replay_mode == REPLAY_MODE_PLAY) {
142 g_assert(replay_mutex_locked());
143 bool res = replay_has_interrupt();
144 if (res) {
145 replay_finish_event();
146 }
147 return res;
148 }
149
150 return true;
151 }
152
153 bool replay_has_interrupt(void)
154 {
155 bool res = false;
156 if (replay_mode == REPLAY_MODE_PLAY) {
157 g_assert(replay_mutex_locked());
158 replay_account_executed_instructions();
159 res = replay_next_event_is(EVENT_INTERRUPT);
160 }
161 return res;
162 }
163
164 void replay_shutdown_request(ShutdownCause cause)
165 {
166 if (replay_mode == REPLAY_MODE_RECORD) {
167 g_assert(replay_mutex_locked());
168 replay_put_event(EVENT_SHUTDOWN + cause);
169 }
170 }
171
172 bool replay_checkpoint(ReplayCheckpoint checkpoint)
173 {
174 bool res = false;
175 static bool in_checkpoint;
176 assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
177
178 if (!replay_file) {
179 return true;
180 }
181
182 if (in_checkpoint) {
183 /* If we are already in checkpoint, then there is no need
184 for additional synchronization.
185 Recursion occurs when HW event modifies timers.
186 Timer modification may invoke the checkpoint and
187 proceed to recursion. */
188 return true;
189 }
190 in_checkpoint = true;
191
192 replay_save_instructions();
193
194 if (replay_mode == REPLAY_MODE_PLAY) {
195 g_assert(replay_mutex_locked());
196 if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
197 replay_finish_event();
198 } else if (replay_state.data_kind != EVENT_ASYNC) {
199 res = false;
200 goto out;
201 }
202 replay_read_events(checkpoint);
203 /* replay_read_events may leave some unread events.
204 Return false if not all of the events associated with
205 checkpoint were processed */
206 res = replay_state.data_kind != EVENT_ASYNC;
207 } else if (replay_mode == REPLAY_MODE_RECORD) {
208 g_assert(replay_mutex_locked());
209 replay_put_event(EVENT_CHECKPOINT + checkpoint);
210 /* This checkpoint belongs to several threads.
211 Processing events from different threads is
212 non-deterministic */
213 if (checkpoint != CHECKPOINT_CLOCK_WARP_START
214 /* FIXME: this is temporary fix, other checkpoints
215 may also be invoked from the different threads someday.
216 Asynchronous event processing should be refactored
217 to create additional replay event kind which is
218 nailed to the one of the threads and which processes
219 the event queue. */
220 && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
221 replay_save_events(checkpoint);
222 }
223 res = true;
224 }
225 out:
226 in_checkpoint = false;
227 return res;
228 }
229
230 bool replay_has_checkpoint(void)
231 {
232 bool res = false;
233 if (replay_mode == REPLAY_MODE_PLAY) {
234 g_assert(replay_mutex_locked());
235 replay_account_executed_instructions();
236 res = EVENT_CHECKPOINT <= replay_state.data_kind
237 && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
238 }
239 return res;
240 }
241
242 static void replay_enable(const char *fname, int mode)
243 {
244 const char *fmode = NULL;
245 assert(!replay_file);
246
247 switch (mode) {
248 case REPLAY_MODE_RECORD:
249 fmode = "wb";
250 break;
251 case REPLAY_MODE_PLAY:
252 fmode = "rb";
253 break;
254 default:
255 fprintf(stderr, "Replay: internal error: invalid replay mode\n");
256 exit(1);
257 }
258
259 atexit(replay_finish);
260
261 replay_file = fopen(fname, fmode);
262 if (replay_file == NULL) {
263 fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
264 exit(1);
265 }
266
267 replay_filename = g_strdup(fname);
268 replay_mode = mode;
269 replay_mutex_init();
270
271 replay_state.data_kind = -1;
272 replay_state.instruction_count = 0;
273 replay_state.current_icount = 0;
274 replay_state.has_unread_data = 0;
275
276 /* skip file header for RECORD and check it for PLAY */
277 if (replay_mode == REPLAY_MODE_RECORD) {
278 fseek(replay_file, HEADER_SIZE, SEEK_SET);
279 } else if (replay_mode == REPLAY_MODE_PLAY) {
280 unsigned int version = replay_get_dword();
281 if (version != REPLAY_VERSION) {
282 fprintf(stderr, "Replay: invalid input log file version\n");
283 exit(1);
284 }
285 /* go to the beginning */
286 fseek(replay_file, HEADER_SIZE, SEEK_SET);
287 replay_fetch_data_kind();
288 }
289
290 replay_init_events();
291 }
292
293 void replay_configure(QemuOpts *opts)
294 {
295 const char *fname;
296 const char *rr;
297 ReplayMode mode = REPLAY_MODE_NONE;
298 Location loc;
299
300 if (!opts) {
301 return;
302 }
303
304 loc_push_none(&loc);
305 qemu_opts_loc_restore(opts);
306
307 rr = qemu_opt_get(opts, "rr");
308 if (!rr) {
309 /* Just enabling icount */
310 goto out;
311 } else if (!strcmp(rr, "record")) {
312 mode = REPLAY_MODE_RECORD;
313 } else if (!strcmp(rr, "replay")) {
314 mode = REPLAY_MODE_PLAY;
315 } else {
316 error_report("Invalid icount rr option: %s", rr);
317 exit(1);
318 }
319
320 fname = qemu_opt_get(opts, "rrfile");
321 if (!fname) {
322 error_report("File name not specified for replay");
323 exit(1);
324 }
325
326 replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
327 replay_vmstate_register();
328 replay_enable(fname, mode);
329
330 out:
331 loc_pop(&loc);
332 }
333
334 void replay_start(void)
335 {
336 if (replay_mode == REPLAY_MODE_NONE) {
337 return;
338 }
339
340 if (replay_blockers) {
341 error_reportf_err(replay_blockers->data, "Record/replay: ");
342 exit(1);
343 }
344 if (!icount_enabled()) {
345 error_report("Please enable icount to use record/replay");
346 exit(1);
347 }
348
349 /* Timer for snapshotting will be set up here. */
350
351 replay_enable_events();
352 }
353
354 void replay_finish(void)
355 {
356 if (replay_mode == REPLAY_MODE_NONE) {
357 return;
358 }
359
360 replay_save_instructions();
361
362 /* finalize the file */
363 if (replay_file) {
364 if (replay_mode == REPLAY_MODE_RECORD) {
365 /*
366 * Can't do it in the signal handler, therefore
367 * add shutdown event here for the case of Ctrl-C.
368 */
369 replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
370 /* write end event */
371 replay_put_event(EVENT_END);
372
373 /* write header */
374 fseek(replay_file, 0, SEEK_SET);
375 replay_put_dword(REPLAY_VERSION);
376 }
377
378 fclose(replay_file);
379 replay_file = NULL;
380 }
381 if (replay_filename) {
382 g_free(replay_filename);
383 replay_filename = NULL;
384 }
385
386 g_free(replay_snapshot);
387 replay_snapshot = NULL;
388
389 replay_mode = REPLAY_MODE_NONE;
390
391 replay_finish_events();
392 }
393
394 void replay_add_blocker(Error *reason)
395 {
396 replay_blockers = g_slist_prepend(replay_blockers, reason);
397 }
398
399 const char *replay_get_filename(void)
400 {
401 return replay_filename;
402 }