]> git.proxmox.com Git - mirror_qemu.git/blame - replay/replay.c
cpu-timers, icount: new modules
[mirror_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"
740b1759 14#include "sysemu/cpu-timers.h"
d73abd6d 15#include "sysemu/replay.h"
54d31236 16#include "sysemu/runstate.h"
26bc60ac 17#include "replay-internal.h"
8b427044 18#include "qemu/main-loop.h"
922a01a0 19#include "qemu/option.h"
d2528bdc 20#include "sysemu/cpus.h"
7615936e
PD
21#include "qemu/error-report.h"
22
23/* Current version of the replay mechanism.
24 Increase it when file format changes. */
677a3bab 25#define REPLAY_VERSION 0xe0200a
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 */
13f26713 42 if (replay_state.instruction_count != 0) {
f186d64d 43 assert(replay_state.data_kind == EVENT_INSTRUCTION);
26bc60ac
PD
44 return event == EVENT_INSTRUCTION;
45 }
46
47 while (true) {
e957ad8a
PD
48 unsigned int data_kind = replay_state.data_kind;
49 if (event == data_kind) {
26bc60ac
PD
50 res = true;
51 }
e957ad8a 52 switch (data_kind) {
802f045a 53 case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST:
b60c48a7 54 replay_finish_event();
e957ad8a 55 qemu_system_shutdown_request(data_kind - 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
13f26713 65uint64_t replay_get_current_icount(void)
26bc60ac
PD
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)) {
13f26713 75 res = replay_state.instruction_count;
8b427044
PD
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());
13f26713
PD
85 if (replay_state.instruction_count > 0) {
86 int count = (int)(replay_get_current_icount()
87 - replay_state.current_icount);
982263ce
AB
88
89 /* Time can only go forward */
90 assert(count >= 0);
91
13f26713
PD
92 replay_state.instruction_count -= count;
93 replay_state.current_icount += count;
94 if (replay_state.instruction_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 */
ca9759c2
PD
217 if (checkpoint != CHECKPOINT_CLOCK_WARP_START
218 /* FIXME: this is temporary fix, other checkpoints
219 may also be invoked from the different threads someday.
220 Asynchronous event processing should be refactored
221 to create additional replay event kind which is
222 nailed to the one of the threads and which processes
223 the event queue. */
224 && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
89e46eb4
PD
225 replay_save_events(checkpoint);
226 }
8bd7f71d
PD
227 res = true;
228 }
229out:
66eb7825 230 in_checkpoint = false;
8bd7f71d
PD
231 return res;
232}
7615936e 233
0c08185f
PD
234bool replay_has_checkpoint(void)
235{
236 bool res = false;
237 if (replay_mode == REPLAY_MODE_PLAY) {
238 g_assert(replay_mutex_locked());
239 replay_account_executed_instructions();
240 res = EVENT_CHECKPOINT <= replay_state.data_kind
241 && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
242 }
243 return res;
244}
245
7615936e
PD
246static void replay_enable(const char *fname, int mode)
247{
248 const char *fmode = NULL;
249 assert(!replay_file);
250
251 switch (mode) {
252 case REPLAY_MODE_RECORD:
253 fmode = "wb";
254 break;
255 case REPLAY_MODE_PLAY:
256 fmode = "rb";
257 break;
258 default:
259 fprintf(stderr, "Replay: internal error: invalid replay mode\n");
260 exit(1);
261 }
262
263 atexit(replay_finish);
264
7615936e
PD
265 replay_file = fopen(fname, fmode);
266 if (replay_file == NULL) {
267 fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
268 exit(1);
269 }
270
271 replay_filename = g_strdup(fname);
7615936e 272 replay_mode = mode;
d759c951
AB
273 replay_mutex_init();
274
f186d64d 275 replay_state.data_kind = -1;
13f26713
PD
276 replay_state.instruction_count = 0;
277 replay_state.current_icount = 0;
f186d64d 278 replay_state.has_unread_data = 0;
7615936e
PD
279
280 /* skip file header for RECORD and check it for PLAY */
281 if (replay_mode == REPLAY_MODE_RECORD) {
282 fseek(replay_file, HEADER_SIZE, SEEK_SET);
283 } else if (replay_mode == REPLAY_MODE_PLAY) {
284 unsigned int version = replay_get_dword();
285 if (version != REPLAY_VERSION) {
286 fprintf(stderr, "Replay: invalid input log file version\n");
287 exit(1);
288 }
289 /* go to the beginning */
290 fseek(replay_file, HEADER_SIZE, SEEK_SET);
291 replay_fetch_data_kind();
292 }
293
294 replay_init_events();
295}
296
297void replay_configure(QemuOpts *opts)
298{
299 const char *fname;
300 const char *rr;
301 ReplayMode mode = REPLAY_MODE_NONE;
890ad550
EH
302 Location loc;
303
304 if (!opts) {
305 return;
306 }
307
308 loc_push_none(&loc);
309 qemu_opts_loc_restore(opts);
7615936e
PD
310
311 rr = qemu_opt_get(opts, "rr");
312 if (!rr) {
313 /* Just enabling icount */
d9d3aaea 314 goto out;
7615936e
PD
315 } else if (!strcmp(rr, "record")) {
316 mode = REPLAY_MODE_RECORD;
317 } else if (!strcmp(rr, "replay")) {
318 mode = REPLAY_MODE_PLAY;
319 } else {
320 error_report("Invalid icount rr option: %s", rr);
321 exit(1);
322 }
323
324 fname = qemu_opt_get(opts, "rrfile");
325 if (!fname) {
326 error_report("File name not specified for replay");
327 exit(1);
328 }
329
9c2037d0 330 replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
306e196f 331 replay_vmstate_register();
7615936e 332 replay_enable(fname, mode);
890ad550 333
d9d3aaea 334out:
890ad550 335 loc_pop(&loc);
7615936e
PD
336}
337
338void replay_start(void)
339{
340 if (replay_mode == REPLAY_MODE_NONE) {
341 return;
342 }
343
0194749a 344 if (replay_blockers) {
c29b77f9 345 error_reportf_err(replay_blockers->data, "Record/replay: ");
0194749a
PD
346 exit(1);
347 }
740b1759 348 if (!icount_enabled()) {
4c27b859
PD
349 error_report("Please enable icount to use record/replay");
350 exit(1);
351 }
0194749a 352
7615936e
PD
353 /* Timer for snapshotting will be set up here. */
354
355 replay_enable_events();
356}
357
358void replay_finish(void)
359{
360 if (replay_mode == REPLAY_MODE_NONE) {
361 return;
362 }
363
364 replay_save_instructions();
365
366 /* finalize the file */
367 if (replay_file) {
368 if (replay_mode == REPLAY_MODE_RECORD) {
ed5d7ff3
PD
369 /*
370 * Can't do it in the signal handler, therefore
371 * add shutdown event here for the case of Ctrl-C.
372 */
373 replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
7615936e
PD
374 /* write end event */
375 replay_put_event(EVENT_END);
376
377 /* write header */
378 fseek(replay_file, 0, SEEK_SET);
379 replay_put_dword(REPLAY_VERSION);
380 }
381
382 fclose(replay_file);
383 replay_file = NULL;
384 }
385 if (replay_filename) {
386 g_free(replay_filename);
387 replay_filename = NULL;
388 }
389
9c2037d0
PD
390 g_free(replay_snapshot);
391 replay_snapshot = NULL;
392
ae25dccb
PD
393 replay_mode = REPLAY_MODE_NONE;
394
7615936e 395 replay_finish_events();
7615936e 396}
0194749a
PD
397
398void replay_add_blocker(Error *reason)
399{
400 replay_blockers = g_slist_prepend(replay_blockers, reason);
401}