1
2
3
4
5
6
7
8
9
10
11
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"
22
23
24
25#define REPLAY_VERSION 0xe0200a
26
27#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
28
29ReplayMode replay_mode = REPLAY_MODE_NONE;
30char *replay_snapshot;
31
32
33static char *replay_filename;
34ReplayState replay_state;
35static GSList *replay_blockers;
36
37
38uint64_t replay_break_icount = -1ULL;
39QEMUTimer *replay_break_timer;
40
41bool replay_next_event_is(int event)
42{
43 bool res = false;
44
45
46 if (replay_state.instruction_count != 0) {
47 assert(replay_state.data_kind == EVENT_INSTRUCTION);
48 return event == EVENT_INSTRUCTION;
49 }
50
51 while (true) {
52 unsigned int data_kind = replay_state.data_kind;
53 if (event == data_kind) {
54 res = true;
55 }
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
63 return res;
64 }
65 }
66 return res;
67}
68
69uint64_t replay_get_current_icount(void)
70{
71 return icount_get_raw();
72}
73
74int replay_get_instructions(void)
75{
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;
85 }
86 }
87 }
88 replay_mutex_unlock();
89 return res;
90}
91
92void replay_account_executed_instructions(void)
93{
94 if (replay_mode == REPLAY_MODE_PLAY) {
95 g_assert(replay_mutex_locked());
96 if (replay_state.instruction_count > 0) {
97 int count = (int)(replay_get_current_icount()
98 - replay_state.current_icount);
99
100
101 assert(count >= 0);
102
103 replay_state.instruction_count -= count;
104 replay_state.current_icount += count;
105 if (replay_state.instruction_count == 0) {
106 assert(replay_state.data_kind == EVENT_INSTRUCTION);
107 replay_finish_event();
108
109
110
111 qemu_notify_event();
112 }
113
114 if (replay_break_icount == replay_state.current_icount) {
115
116 timer_mod_ns(replay_break_timer,
117 qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
118 }
119 }
120 }
121}
122
123bool replay_exception(void)
124{
125
126 if (replay_mode == REPLAY_MODE_RECORD) {
127 g_assert(replay_mutex_locked());
128 replay_save_instructions();
129 replay_put_event(EVENT_EXCEPTION);
130 return true;
131 } else if (replay_mode == REPLAY_MODE_PLAY) {
132 g_assert(replay_mutex_locked());
133 bool res = replay_has_exception();
134 if (res) {
135 replay_finish_event();
136 }
137 return res;
138 }
139
140 return true;
141}
142
143bool replay_has_exception(void)
144{
145 bool res = false;
146 if (replay_mode == REPLAY_MODE_PLAY) {
147 g_assert(replay_mutex_locked());
148 replay_account_executed_instructions();
149 res = replay_next_event_is(EVENT_EXCEPTION);
150 }
151
152 return res;
153}
154
155bool replay_interrupt(void)
156{
157 if (replay_mode == REPLAY_MODE_RECORD) {
158 g_assert(replay_mutex_locked());
159 replay_save_instructions();
160 replay_put_event(EVENT_INTERRUPT);
161 return true;
162 } else if (replay_mode == REPLAY_MODE_PLAY) {
163 g_assert(replay_mutex_locked());
164 bool res = replay_has_interrupt();
165 if (res) {
166 replay_finish_event();
167 }
168 return res;
169 }
170
171 return true;
172}
173
174bool replay_has_interrupt(void)
175{
176 bool res = false;
177 if (replay_mode == REPLAY_MODE_PLAY) {
178 g_assert(replay_mutex_locked());
179 replay_account_executed_instructions();
180 res = replay_next_event_is(EVENT_INTERRUPT);
181 }
182 return res;
183}
184
185void replay_shutdown_request(ShutdownCause cause)
186{
187 if (replay_mode == REPLAY_MODE_RECORD) {
188 g_assert(replay_mutex_locked());
189 replay_put_event(EVENT_SHUTDOWN + cause);
190 }
191}
192
193bool replay_checkpoint(ReplayCheckpoint checkpoint)
194{
195 bool res = false;
196 static bool in_checkpoint;
197 assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
198
199 if (!replay_file) {
200 return true;
201 }
202
203 if (in_checkpoint) {
204
205
206
207
208
209 return true;
210 }
211 in_checkpoint = true;
212
213 replay_save_instructions();
214
215 if (replay_mode == REPLAY_MODE_PLAY) {
216 g_assert(replay_mutex_locked());
217 if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
218 replay_finish_event();
219 } else if (replay_state.data_kind != EVENT_ASYNC) {
220 res = false;
221 goto out;
222 }
223 replay_read_events(checkpoint);
224
225
226
227 res = replay_state.data_kind != EVENT_ASYNC;
228 } else if (replay_mode == REPLAY_MODE_RECORD) {
229 g_assert(replay_mutex_locked());
230 replay_put_event(EVENT_CHECKPOINT + checkpoint);
231
232
233
234 if (checkpoint != CHECKPOINT_CLOCK_WARP_START
235
236
237
238
239
240
241 && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
242 replay_save_events(checkpoint);
243 }
244 res = true;
245 }
246out:
247 in_checkpoint = false;
248 return res;
249}
250
251bool replay_has_checkpoint(void)
252{
253 bool res = false;
254 if (replay_mode == REPLAY_MODE_PLAY) {
255 g_assert(replay_mutex_locked());
256 replay_account_executed_instructions();
257 res = EVENT_CHECKPOINT <= replay_state.data_kind
258 && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
259 }
260 return res;
261}
262
263static void replay_enable(const char *fname, int mode)
264{
265 const char *fmode = NULL;
266 assert(!replay_file);
267
268 switch (mode) {
269 case REPLAY_MODE_RECORD:
270 fmode = "wb";
271 break;
272 case REPLAY_MODE_PLAY:
273 fmode = "rb";
274 break;
275 default:
276 fprintf(stderr, "Replay: internal error: invalid replay mode\n");
277 exit(1);
278 }
279
280 atexit(replay_finish);
281
282 replay_file = fopen(fname, fmode);
283 if (replay_file == NULL) {
284 fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
285 exit(1);
286 }
287
288 replay_filename = g_strdup(fname);
289 replay_mode = mode;
290 replay_mutex_init();
291
292 replay_state.data_kind = -1;
293 replay_state.instruction_count = 0;
294 replay_state.current_icount = 0;
295 replay_state.has_unread_data = 0;
296
297
298 if (replay_mode == REPLAY_MODE_RECORD) {
299 fseek(replay_file, HEADER_SIZE, SEEK_SET);
300 } else if (replay_mode == REPLAY_MODE_PLAY) {
301 unsigned int version = replay_get_dword();
302 if (version != REPLAY_VERSION) {
303 fprintf(stderr, "Replay: invalid input log file version\n");
304 exit(1);
305 }
306
307 fseek(replay_file, HEADER_SIZE, SEEK_SET);
308 replay_fetch_data_kind();
309 }
310
311 replay_init_events();
312}
313
314void replay_configure(QemuOpts *opts)
315{
316 const char *fname;
317 const char *rr;
318 ReplayMode mode = REPLAY_MODE_NONE;
319 Location loc;
320
321 if (!opts) {
322 return;
323 }
324
325 loc_push_none(&loc);
326 qemu_opts_loc_restore(opts);
327
328 rr = qemu_opt_get(opts, "rr");
329 if (!rr) {
330
331 goto out;
332 } else if (!strcmp(rr, "record")) {
333 mode = REPLAY_MODE_RECORD;
334 } else if (!strcmp(rr, "replay")) {
335 mode = REPLAY_MODE_PLAY;
336 } else {
337 error_report("Invalid icount rr option: %s", rr);
338 exit(1);
339 }
340
341 fname = qemu_opt_get(opts, "rrfile");
342 if (!fname) {
343 error_report("File name not specified for replay");
344 exit(1);
345 }
346
347 replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
348 replay_vmstate_register();
349 replay_enable(fname, mode);
350
351out:
352 loc_pop(&loc);
353}
354
355void replay_start(void)
356{
357 if (replay_mode == REPLAY_MODE_NONE) {
358 return;
359 }
360
361 if (replay_blockers) {
362 error_reportf_err(replay_blockers->data, "Record/replay: ");
363 exit(1);
364 }
365 if (!icount_enabled()) {
366 error_report("Please enable icount to use record/replay");
367 exit(1);
368 }
369
370
371
372 replay_enable_events();
373}
374
375void replay_finish(void)
376{
377 if (replay_mode == REPLAY_MODE_NONE) {
378 return;
379 }
380
381 replay_save_instructions();
382
383
384 if (replay_file) {
385 if (replay_mode == REPLAY_MODE_RECORD) {
386
387
388
389
390 replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
391
392 replay_put_event(EVENT_END);
393
394
395 fseek(replay_file, 0, SEEK_SET);
396 replay_put_dword(REPLAY_VERSION);
397 }
398
399 fclose(replay_file);
400 replay_file = NULL;
401 }
402 if (replay_filename) {
403 g_free(replay_filename);
404 replay_filename = NULL;
405 }
406
407 g_free(replay_snapshot);
408 replay_snapshot = NULL;
409
410 replay_mode = REPLAY_MODE_NONE;
411
412 replay_finish_events();
413}
414
415void replay_add_blocker(Error *reason)
416{
417 replay_blockers = g_slist_prepend(replay_blockers, reason);
418}
419
420const char *replay_get_filename(void)
421{
422 return replay_filename;
423}
424