]> git.ipfire.org Git - thirdparty/qemu.git/blame - replay/replay.c
replay: wake up vCPU when replaying
[thirdparty/qemu.git] / replay / replay.c
CommitLineData
d73abd6d
PD
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
d38ea87a 12#include "qemu/osdep.h"
da34e65c 13#include "qapi/error.h"
d73abd6d 14#include "sysemu/replay.h"
26bc60ac
PD
15#include "replay-internal.h"
16#include "qemu/timer.h"
8b427044 17#include "qemu/main-loop.h"
922a01a0 18#include "qemu/option.h"
d2528bdc 19#include "sysemu/cpus.h"
b60c48a7 20#include "sysemu/sysemu.h"
7615936e
PD
21#include "qemu/error-report.h"
22
23/* Current version of the replay mechanism.
24 Increase it when file format changes. */
80be169c 25#define REPLAY_VERSION 0xe02007
7615936e
PD
26/* Size of replay log header */
27#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
d73abd6d
PD
28
29ReplayMode replay_mode = REPLAY_MODE_NONE;
9c2037d0 30char *replay_snapshot;
26bc60ac 31
7615936e
PD
32/* Name of replay file */
33static char *replay_filename;
26bc60ac 34ReplayState replay_state;
0194749a 35static GSList *replay_blockers;
26bc60ac
PD
36
37bool replay_next_event_is(int event)
38{
39 bool res = false;
40
41 /* nothing to skip - not all instructions used */
42 if (replay_state.instructions_count != 0) {
f186d64d 43 assert(replay_state.data_kind == EVENT_INSTRUCTION);
26bc60ac
PD
44 return event == EVENT_INSTRUCTION;
45 }
46
47 while (true) {
f186d64d 48 if (event == replay_state.data_kind) {
26bc60ac
PD
49 res = true;
50 }
f186d64d 51 switch (replay_state.data_kind) {
802f045a 52 case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST:
b60c48a7 53 replay_finish_event();
cf83f140
EB
54 qemu_system_shutdown_request(replay_state.data_kind -
55 EVENT_SHUTDOWN);
b60c48a7 56 break;
26bc60ac
PD
57 default:
58 /* clock, time_t, checkpoint and other events */
59 return res;
60 }
61 }
62 return res;
63}
64
65uint64_t replay_get_current_step(void)
66{
67 return cpu_get_icount_raw();
68}
8b427044
PD
69
70int replay_get_instructions(void)
71{
72 int res = 0;
73 replay_mutex_lock();
74 if (replay_next_event_is(EVENT_INSTRUCTION)) {
75 res = replay_state.instructions_count;
76 }
77 replay_mutex_unlock();
78 return res;
79}
80
81void replay_account_executed_instructions(void)
82{
83 if (replay_mode == REPLAY_MODE_PLAY) {
d759c951 84 g_assert(replay_mutex_locked());
8b427044
PD
85 if (replay_state.instructions_count > 0) {
86 int count = (int)(replay_get_current_step()
87 - replay_state.current_step);
982263ce
AB
88
89 /* Time can only go forward */
90 assert(count >= 0);
91
8b427044
PD
92 replay_state.instructions_count -= count;
93 replay_state.current_step += count;
94 if (replay_state.instructions_count == 0) {
f186d64d 95 assert(replay_state.data_kind == EVENT_INSTRUCTION);
8b427044
PD
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();
101 }
102 }
8b427044
PD
103 }
104}
6f060969
PD
105
106bool replay_exception(void)
107{
d759c951 108
6f060969 109 if (replay_mode == REPLAY_MODE_RECORD) {
d759c951 110 g_assert(replay_mutex_locked());
6f060969 111 replay_save_instructions();
6f060969 112 replay_put_event(EVENT_EXCEPTION);
6f060969
PD
113 return true;
114 } else if (replay_mode == REPLAY_MODE_PLAY) {
d759c951 115 g_assert(replay_mutex_locked());
6f060969
PD
116 bool res = replay_has_exception();
117 if (res) {
6f060969 118 replay_finish_event();
6f060969
PD
119 }
120 return res;
121 }
122
123 return true;
124}
125
126bool replay_has_exception(void)
127{
128 bool res = false;
129 if (replay_mode == REPLAY_MODE_PLAY) {
d759c951 130 g_assert(replay_mutex_locked());
6f060969 131 replay_account_executed_instructions();
6f060969 132 res = replay_next_event_is(EVENT_EXCEPTION);
6f060969
PD
133 }
134
135 return res;
136}
137
138bool replay_interrupt(void)
139{
140 if (replay_mode == REPLAY_MODE_RECORD) {
d759c951 141 g_assert(replay_mutex_locked());
6f060969 142 replay_save_instructions();
6f060969 143 replay_put_event(EVENT_INTERRUPT);
6f060969
PD
144 return true;
145 } else if (replay_mode == REPLAY_MODE_PLAY) {
d759c951 146 g_assert(replay_mutex_locked());
6f060969
PD
147 bool res = replay_has_interrupt();
148 if (res) {
6f060969 149 replay_finish_event();
6f060969
PD
150 }
151 return res;
152 }
153
154 return true;
155}
156
157bool replay_has_interrupt(void)
158{
159 bool res = false;
160 if (replay_mode == REPLAY_MODE_PLAY) {
d759c951 161 g_assert(replay_mutex_locked());
6f060969 162 replay_account_executed_instructions();
6f060969 163 res = replay_next_event_is(EVENT_INTERRUPT);
6f060969
PD
164 }
165 return res;
166}
b60c48a7 167
802f045a 168void replay_shutdown_request(ShutdownCause cause)
b60c48a7
PD
169{
170 if (replay_mode == REPLAY_MODE_RECORD) {
d759c951 171 g_assert(replay_mutex_locked());
802f045a 172 replay_put_event(EVENT_SHUTDOWN + cause);
b60c48a7
PD
173 }
174}
8bd7f71d
PD
175
176bool replay_checkpoint(ReplayCheckpoint checkpoint)
177{
178 bool res = false;
66eb7825 179 static bool in_checkpoint;
8bd7f71d 180 assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
8bd7f71d
PD
181
182 if (!replay_file) {
183 return true;
184 }
185
66eb7825
PD
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;
193 }
194 in_checkpoint = true;
195
196 replay_save_instructions();
8bd7f71d
PD
197
198 if (replay_mode == REPLAY_MODE_PLAY) {
d759c951 199 g_assert(replay_mutex_locked());
8bd7f71d
PD
200 if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
201 replay_finish_event();
f186d64d 202 } else if (replay_state.data_kind != EVENT_ASYNC) {
8bd7f71d
PD
203 res = false;
204 goto out;
205 }
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 */
f186d64d 210 res = replay_state.data_kind != EVENT_ASYNC;
8bd7f71d 211 } else if (replay_mode == REPLAY_MODE_RECORD) {
d759c951 212 g_assert(replay_mutex_locked());
8bd7f71d 213 replay_put_event(EVENT_CHECKPOINT + checkpoint);
89e46eb4
PD
214 /* This checkpoint belongs to several threads.
215 Processing events from different threads is
216 non-deterministic */
217 if (checkpoint != CHECKPOINT_CLOCK_WARP_START) {
218 replay_save_events(checkpoint);
219 }
8bd7f71d
PD
220 res = true;
221 }
222out:
66eb7825 223 in_checkpoint = false;
8bd7f71d
PD
224 return res;
225}
7615936e 226
0c08185f
PD
227bool replay_has_checkpoint(void)
228{
229 bool res = false;
230 if (replay_mode == REPLAY_MODE_PLAY) {
231 g_assert(replay_mutex_locked());
232 replay_account_executed_instructions();
233 res = EVENT_CHECKPOINT <= replay_state.data_kind
234 && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
235 }
236 return res;
237}
238
7615936e
PD
239static void replay_enable(const char *fname, int mode)
240{
241 const char *fmode = NULL;
242 assert(!replay_file);
243
244 switch (mode) {
245 case REPLAY_MODE_RECORD:
246 fmode = "wb";
247 break;
248 case REPLAY_MODE_PLAY:
249 fmode = "rb";
250 break;
251 default:
252 fprintf(stderr, "Replay: internal error: invalid replay mode\n");
253 exit(1);
254 }
255
256 atexit(replay_finish);
257
7615936e
PD
258 replay_file = fopen(fname, fmode);
259 if (replay_file == NULL) {
260 fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
261 exit(1);
262 }
263
264 replay_filename = g_strdup(fname);
7615936e 265 replay_mode = mode;
d759c951
AB
266 replay_mutex_init();
267
f186d64d 268 replay_state.data_kind = -1;
7615936e
PD
269 replay_state.instructions_count = 0;
270 replay_state.current_step = 0;
f186d64d 271 replay_state.has_unread_data = 0;
7615936e
PD
272
273 /* skip file header for RECORD and check it for PLAY */
274 if (replay_mode == REPLAY_MODE_RECORD) {
275 fseek(replay_file, HEADER_SIZE, SEEK_SET);
276 } else if (replay_mode == REPLAY_MODE_PLAY) {
277 unsigned int version = replay_get_dword();
278 if (version != REPLAY_VERSION) {
279 fprintf(stderr, "Replay: invalid input log file version\n");
280 exit(1);
281 }
282 /* go to the beginning */
283 fseek(replay_file, HEADER_SIZE, SEEK_SET);
284 replay_fetch_data_kind();
285 }
286
287 replay_init_events();
288}
289
290void replay_configure(QemuOpts *opts)
291{
292 const char *fname;
293 const char *rr;
294 ReplayMode mode = REPLAY_MODE_NONE;
890ad550
EH
295 Location loc;
296
297 if (!opts) {
298 return;
299 }
300
301 loc_push_none(&loc);
302 qemu_opts_loc_restore(opts);
7615936e
PD
303
304 rr = qemu_opt_get(opts, "rr");
305 if (!rr) {
306 /* Just enabling icount */
d9d3aaea 307 goto out;
7615936e
PD
308 } else if (!strcmp(rr, "record")) {
309 mode = REPLAY_MODE_RECORD;
310 } else if (!strcmp(rr, "replay")) {
311 mode = REPLAY_MODE_PLAY;
312 } else {
313 error_report("Invalid icount rr option: %s", rr);
314 exit(1);
315 }
316
317 fname = qemu_opt_get(opts, "rrfile");
318 if (!fname) {
319 error_report("File name not specified for replay");
320 exit(1);
321 }
322
9c2037d0 323 replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
306e196f 324 replay_vmstate_register();
7615936e 325 replay_enable(fname, mode);
890ad550 326
d9d3aaea 327out:
890ad550 328 loc_pop(&loc);
7615936e
PD
329}
330
331void replay_start(void)
332{
333 if (replay_mode == REPLAY_MODE_NONE) {
334 return;
335 }
336
0194749a 337 if (replay_blockers) {
c29b77f9 338 error_reportf_err(replay_blockers->data, "Record/replay: ");
0194749a
PD
339 exit(1);
340 }
4c27b859
PD
341 if (!use_icount) {
342 error_report("Please enable icount to use record/replay");
343 exit(1);
344 }
0194749a 345
7615936e
PD
346 /* Timer for snapshotting will be set up here. */
347
348 replay_enable_events();
349}
350
351void replay_finish(void)
352{
353 if (replay_mode == REPLAY_MODE_NONE) {
354 return;
355 }
356
357 replay_save_instructions();
358
359 /* finalize the file */
360 if (replay_file) {
361 if (replay_mode == REPLAY_MODE_RECORD) {
362 /* write end event */
363 replay_put_event(EVENT_END);
364
365 /* write header */
366 fseek(replay_file, 0, SEEK_SET);
367 replay_put_dword(REPLAY_VERSION);
368 }
369
370 fclose(replay_file);
371 replay_file = NULL;
372 }
373 if (replay_filename) {
374 g_free(replay_filename);
375 replay_filename = NULL;
376 }
377
9c2037d0
PD
378 g_free(replay_snapshot);
379 replay_snapshot = NULL;
380
7615936e 381 replay_finish_events();
7615936e 382}
0194749a
PD
383
384void replay_add_blocker(Error *reason)
385{
386 replay_blockers = g_slist_prepend(replay_blockers, reason);
387}