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
)
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
;
52 unsigned int data_kind
= replay_state
.data_kind
;
53 if (event
== data_kind
) {
57 case EVENT_SHUTDOWN
... EVENT_SHUTDOWN_LAST
:
58 replay_finish_event();
59 qemu_system_shutdown_request(data_kind
- EVENT_SHUTDOWN
);
62 /* clock, time_t, checkpoint and other events */
69 uint64_t replay_get_current_icount(void)
71 return icount_get_raw();
74 int replay_get_instructions(void)
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();
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
);
110 } else if (replay_mode
== REPLAY_MODE_PLAY
) {
111 g_assert(replay_mutex_locked());
112 bool res
= replay_has_exception();
114 replay_finish_event();
122 bool replay_has_exception(void)
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
);
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
);
141 } else if (replay_mode
== REPLAY_MODE_PLAY
) {
142 g_assert(replay_mutex_locked());
143 bool res
= replay_has_interrupt();
145 replay_finish_event();
153 bool replay_has_interrupt(void)
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
);
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
)
175 static bool in_checkpoint
;
176 assert(EVENT_CHECKPOINT
+ checkpoint
<= EVENT_CHECKPOINT_LAST
);
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. */
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
) {
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
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
220 && checkpoint
!= CHECKPOINT_CLOCK_VIRTUAL
) {
221 replay_save_events(checkpoint
);
226 in_checkpoint
= false;
230 bool replay_has_checkpoint(void)
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
;
242 static void replay_enable(const char *fname
, int mode
)
244 const char *fmode
= NULL
;
245 assert(!replay_file
);
248 case REPLAY_MODE_RECORD
:
251 case REPLAY_MODE_PLAY
:
255 fprintf(stderr
, "Replay: internal error: invalid replay mode\n");
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
));
267 replay_filename
= g_strdup(fname
);
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");
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
)
297 ReplayMode mode
= REPLAY_MODE_NONE
;
305 qemu_opts_loc_restore(opts
);
307 rr
= qemu_opt_get(opts
, "rr");
309 /* Just enabling icount */
311 } else if (!strcmp(rr
, "record")) {
312 mode
= REPLAY_MODE_RECORD
;
313 } else if (!strcmp(rr
, "replay")) {
314 mode
= REPLAY_MODE_PLAY
;
316 error_report("Invalid icount rr option: %s", rr
);
320 fname
= qemu_opt_get(opts
, "rrfile");
322 error_report("File name not specified for replay");
326 replay_snapshot
= g_strdup(qemu_opt_get(opts
, "rrsnapshot"));
327 replay_vmstate_register();
328 replay_enable(fname
, mode
);
334 void replay_start(void)
336 if (replay_mode
== REPLAY_MODE_NONE
) {
340 if (replay_blockers
) {
341 error_reportf_err(replay_blockers
->data
, "Record/replay: ");
344 if (!icount_enabled()) {
345 error_report("Please enable icount to use record/replay");
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
) {
360 replay_save_instructions();
362 /* finalize the 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
);
374 fseek(replay_file
, 0, SEEK_SET
);
375 replay_put_dword(REPLAY_VERSION
);
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
;