docs: update to show preferred boolean syntax for -vnc
[qemu/ar7.git] / replay / replay.c
blobc806fec69ac7e2d9bb2ec4341806ed5e8ec105ac
1 /*
2 * replay.c
4 * Copyright (c) 2010-2015 Institute for System Programming
5 * of the Russian Academy of Sciences.
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.
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"
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))
29 ReplayMode replay_mode = REPLAY_MODE_NONE;
30 char *replay_snapshot;
32 /* Name of replay file */
33 static char *replay_filename;
34 ReplayState replay_state;
35 static GSList *replay_blockers;
37 /* Replay breakpoints */
38 uint64_t replay_break_icount = -1ULL;
39 QEMUTimer *replay_break_timer;
41 bool replay_next_event_is(int event)
43 bool res = false;
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;
51 while (true) {
52 unsigned int data_kind = replay_state.data_kind;
53 if (event == data_kind) {
54 res = true;
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;
66 return res;
69 uint64_t replay_get_current_icount(void)
71 return icount_get_raw();
74 int replay_get_instructions(void)
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;
88 replay_mutex_unlock();
89 return res;
92 void replay_account_executed_instructions(void)
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());
102 bool replay_exception(void)
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();
116 return res;
119 return true;
122 bool replay_has_exception(void)
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);
131 return res;
134 bool replay_interrupt(void)
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();
147 return res;
150 return true;
153 bool replay_has_interrupt(void)
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);
161 return res;
164 void replay_shutdown_request(ShutdownCause cause)
166 if (replay_mode == REPLAY_MODE_RECORD) {
167 g_assert(replay_mutex_locked());
168 replay_put_event(EVENT_SHUTDOWN + cause);
172 bool replay_checkpoint(ReplayCheckpoint checkpoint)
174 bool res = false;
175 static bool in_checkpoint;
176 assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
178 if (!replay_file) {
179 return true;
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;
190 in_checkpoint = true;
192 replay_save_instructions();
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;
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);
223 res = true;
225 out:
226 in_checkpoint = false;
227 return res;
230 bool replay_has_checkpoint(void)
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;
239 return res;
242 static void replay_enable(const char *fname, int mode)
244 const char *fmode = NULL;
245 assert(!replay_file);
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);
259 atexit(replay_finish);
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);
267 replay_filename = g_strdup(fname);
268 replay_mode = mode;
269 replay_mutex_init();
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;
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);
285 /* go to the beginning */
286 fseek(replay_file, HEADER_SIZE, SEEK_SET);
287 replay_fetch_data_kind();
290 replay_init_events();
293 void replay_configure(QemuOpts *opts)
295 const char *fname;
296 const char *rr;
297 ReplayMode mode = REPLAY_MODE_NONE;
298 Location loc;
300 if (!opts) {
301 return;
304 loc_push_none(&loc);
305 qemu_opts_loc_restore(opts);
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);
320 fname = qemu_opt_get(opts, "rrfile");
321 if (!fname) {
322 error_report("File name not specified for replay");
323 exit(1);
326 replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
327 replay_vmstate_register();
328 replay_enable(fname, mode);
330 out:
331 loc_pop(&loc);
334 void replay_start(void)
336 if (replay_mode == REPLAY_MODE_NONE) {
337 return;
340 if (replay_blockers) {
341 error_reportf_err(replay_blockers->data, "Record/replay: ");
342 exit(1);
344 if (!icount_enabled()) {
345 error_report("Please enable icount to use record/replay");
346 exit(1);
349 /* Timer for snapshotting will be set up here. */
351 replay_enable_events();
354 void replay_finish(void)
356 if (replay_mode == REPLAY_MODE_NONE) {
357 return;
360 replay_save_instructions();
362 /* finalize the file */
363 if (replay_file) {
364 if (replay_mode == REPLAY_MODE_RECORD) {
366 * Can't do it in the signal handler, therefore
367 * add shutdown event here for the case of Ctrl-C.
369 replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
370 /* write end event */
371 replay_put_event(EVENT_END);
373 /* write header */
374 fseek(replay_file, 0, SEEK_SET);
375 replay_put_dword(REPLAY_VERSION);
378 fclose(replay_file);
379 replay_file = NULL;
381 if (replay_filename) {
382 g_free(replay_filename);
383 replay_filename = NULL;
386 g_free(replay_snapshot);
387 replay_snapshot = NULL;
389 replay_mode = REPLAY_MODE_NONE;
391 replay_finish_events();
394 void replay_add_blocker(Error *reason)
396 replay_blockers = g_slist_prepend(replay_blockers, reason);
399 const char *replay_get_filename(void)
401 return replay_filename;