replay: avoid recursive call of checkpoints
[qemu/ar7.git] / replay / replay.c
blobeae8daf18ab8e9c2eb10b82441f27b5aedd58e69
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/replay.h"
15 #include "replay-internal.h"
16 #include "qemu/timer.h"
17 #include "qemu/main-loop.h"
18 #include "qemu/option.h"
19 #include "sysemu/cpus.h"
20 #include "sysemu/sysemu.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 0xe02007
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 bool replay_next_event_is(int event)
39 bool res = false;
41 /* nothing to skip - not all instructions used */
42 if (replay_state.instructions_count != 0) {
43 assert(replay_state.data_kind == EVENT_INSTRUCTION);
44 return event == EVENT_INSTRUCTION;
47 while (true) {
48 if (event == replay_state.data_kind) {
49 res = true;
51 switch (replay_state.data_kind) {
52 case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST:
53 replay_finish_event();
54 qemu_system_shutdown_request(replay_state.data_kind -
55 EVENT_SHUTDOWN);
56 break;
57 default:
58 /* clock, time_t, checkpoint and other events */
59 return res;
62 return res;
65 uint64_t replay_get_current_step(void)
67 return cpu_get_icount_raw();
70 int replay_get_instructions(void)
72 int res = 0;
73 replay_mutex_lock();
74 if (replay_next_event_is(EVENT_INSTRUCTION)) {
75 res = replay_state.instructions_count;
77 replay_mutex_unlock();
78 return res;
81 void replay_account_executed_instructions(void)
83 if (replay_mode == REPLAY_MODE_PLAY) {
84 g_assert(replay_mutex_locked());
85 if (replay_state.instructions_count > 0) {
86 int count = (int)(replay_get_current_step()
87 - replay_state.current_step);
89 /* Time can only go forward */
90 assert(count >= 0);
92 replay_state.instructions_count -= count;
93 replay_state.current_step += count;
94 if (replay_state.instructions_count == 0) {
95 assert(replay_state.data_kind == EVENT_INSTRUCTION);
96 replay_finish_event();
97 /* Wake up iothread. This is required because
98 timers will not expire until clock counters
99 will be read from the log. */
100 qemu_notify_event();
106 bool replay_exception(void)
109 if (replay_mode == REPLAY_MODE_RECORD) {
110 g_assert(replay_mutex_locked());
111 replay_save_instructions();
112 replay_put_event(EVENT_EXCEPTION);
113 return true;
114 } else if (replay_mode == REPLAY_MODE_PLAY) {
115 g_assert(replay_mutex_locked());
116 bool res = replay_has_exception();
117 if (res) {
118 replay_finish_event();
120 return res;
123 return true;
126 bool replay_has_exception(void)
128 bool res = false;
129 if (replay_mode == REPLAY_MODE_PLAY) {
130 g_assert(replay_mutex_locked());
131 replay_account_executed_instructions();
132 res = replay_next_event_is(EVENT_EXCEPTION);
135 return res;
138 bool replay_interrupt(void)
140 if (replay_mode == REPLAY_MODE_RECORD) {
141 g_assert(replay_mutex_locked());
142 replay_save_instructions();
143 replay_put_event(EVENT_INTERRUPT);
144 return true;
145 } else if (replay_mode == REPLAY_MODE_PLAY) {
146 g_assert(replay_mutex_locked());
147 bool res = replay_has_interrupt();
148 if (res) {
149 replay_finish_event();
151 return res;
154 return true;
157 bool replay_has_interrupt(void)
159 bool res = false;
160 if (replay_mode == REPLAY_MODE_PLAY) {
161 g_assert(replay_mutex_locked());
162 replay_account_executed_instructions();
163 res = replay_next_event_is(EVENT_INTERRUPT);
165 return res;
168 void replay_shutdown_request(ShutdownCause cause)
170 if (replay_mode == REPLAY_MODE_RECORD) {
171 g_assert(replay_mutex_locked());
172 replay_put_event(EVENT_SHUTDOWN + cause);
176 bool replay_checkpoint(ReplayCheckpoint checkpoint)
178 bool res = false;
179 static bool in_checkpoint;
180 assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
182 if (!replay_file) {
183 return true;
186 if (in_checkpoint) {
187 /* If we are already in checkpoint, then there is no need
188 for additional synchronization.
189 Recursion occurs when HW event modifies timers.
190 Timer modification may invoke the checkpoint and
191 proceed to recursion. */
192 return true;
194 in_checkpoint = true;
196 replay_save_instructions();
198 if (replay_mode == REPLAY_MODE_PLAY) {
199 g_assert(replay_mutex_locked());
200 if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
201 replay_finish_event();
202 } else if (replay_state.data_kind != EVENT_ASYNC) {
203 res = false;
204 goto out;
206 replay_read_events(checkpoint);
207 /* replay_read_events may leave some unread events.
208 Return false if not all of the events associated with
209 checkpoint were processed */
210 res = replay_state.data_kind != EVENT_ASYNC;
211 } else if (replay_mode == REPLAY_MODE_RECORD) {
212 g_assert(replay_mutex_locked());
213 replay_put_event(EVENT_CHECKPOINT + checkpoint);
214 replay_save_events(checkpoint);
215 res = true;
217 out:
218 in_checkpoint = false;
219 return res;
222 static void replay_enable(const char *fname, int mode)
224 const char *fmode = NULL;
225 assert(!replay_file);
227 switch (mode) {
228 case REPLAY_MODE_RECORD:
229 fmode = "wb";
230 break;
231 case REPLAY_MODE_PLAY:
232 fmode = "rb";
233 break;
234 default:
235 fprintf(stderr, "Replay: internal error: invalid replay mode\n");
236 exit(1);
239 atexit(replay_finish);
241 replay_file = fopen(fname, fmode);
242 if (replay_file == NULL) {
243 fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
244 exit(1);
247 replay_filename = g_strdup(fname);
248 replay_mode = mode;
249 replay_mutex_init();
251 replay_state.data_kind = -1;
252 replay_state.instructions_count = 0;
253 replay_state.current_step = 0;
254 replay_state.has_unread_data = 0;
256 /* skip file header for RECORD and check it for PLAY */
257 if (replay_mode == REPLAY_MODE_RECORD) {
258 fseek(replay_file, HEADER_SIZE, SEEK_SET);
259 } else if (replay_mode == REPLAY_MODE_PLAY) {
260 unsigned int version = replay_get_dword();
261 if (version != REPLAY_VERSION) {
262 fprintf(stderr, "Replay: invalid input log file version\n");
263 exit(1);
265 /* go to the beginning */
266 fseek(replay_file, HEADER_SIZE, SEEK_SET);
267 replay_fetch_data_kind();
270 replay_init_events();
273 void replay_configure(QemuOpts *opts)
275 const char *fname;
276 const char *rr;
277 ReplayMode mode = REPLAY_MODE_NONE;
278 Location loc;
280 if (!opts) {
281 return;
284 loc_push_none(&loc);
285 qemu_opts_loc_restore(opts);
287 rr = qemu_opt_get(opts, "rr");
288 if (!rr) {
289 /* Just enabling icount */
290 goto out;
291 } else if (!strcmp(rr, "record")) {
292 mode = REPLAY_MODE_RECORD;
293 } else if (!strcmp(rr, "replay")) {
294 mode = REPLAY_MODE_PLAY;
295 } else {
296 error_report("Invalid icount rr option: %s", rr);
297 exit(1);
300 fname = qemu_opt_get(opts, "rrfile");
301 if (!fname) {
302 error_report("File name not specified for replay");
303 exit(1);
306 replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
307 replay_vmstate_register();
308 replay_enable(fname, mode);
310 out:
311 loc_pop(&loc);
314 void replay_start(void)
316 if (replay_mode == REPLAY_MODE_NONE) {
317 return;
320 if (replay_blockers) {
321 error_reportf_err(replay_blockers->data, "Record/replay: ");
322 exit(1);
324 if (!use_icount) {
325 error_report("Please enable icount to use record/replay");
326 exit(1);
329 /* Timer for snapshotting will be set up here. */
331 replay_enable_events();
334 void replay_finish(void)
336 if (replay_mode == REPLAY_MODE_NONE) {
337 return;
340 replay_save_instructions();
342 /* finalize the file */
343 if (replay_file) {
344 if (replay_mode == REPLAY_MODE_RECORD) {
345 /* write end event */
346 replay_put_event(EVENT_END);
348 /* write header */
349 fseek(replay_file, 0, SEEK_SET);
350 replay_put_dword(REPLAY_VERSION);
353 fclose(replay_file);
354 replay_file = NULL;
356 if (replay_filename) {
357 g_free(replay_filename);
358 replay_filename = NULL;
361 g_free(replay_snapshot);
362 replay_snapshot = NULL;
364 replay_finish_events();
367 void replay_add_blocker(Error *reason)
369 replay_blockers = g_slist_prepend(replay_blockers, reason);