xref: /openbmc/qemu/replay/replay.c (revision 19f4ed36)
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 
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 /* 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))
28 
29 ReplayMode replay_mode = REPLAY_MODE_NONE;
30 char *replay_snapshot;
31 
32 /* Name of replay file  */
33 static char *replay_filename;
34 ReplayState replay_state;
35 static GSList *replay_blockers;
36 
37 /* Replay breakpoints */
38 uint64_t replay_break_icount = -1ULL;
39 QEMUTimer *replay_break_timer;
40 
41 bool replay_next_event_is(int event)
42 {
43     bool res = false;
44 
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;
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             /* clock, time_t, checkpoint and other events */
63             return res;
64         }
65     }
66     return res;
67 }
68 
69 uint64_t replay_get_current_icount(void)
70 {
71     return icount_get_raw();
72 }
73 
74 int 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 
92 void 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             replay_advance_current_icount(replay_get_current_icount());
98         }
99     }
100 }
101 
102 bool replay_exception(void)
103 {
104 
105     if (replay_mode == REPLAY_MODE_RECORD) {
106         g_assert(replay_mutex_locked());
107         replay_save_instructions();
108         replay_put_event(EVENT_EXCEPTION);
109         return true;
110     } else if (replay_mode == REPLAY_MODE_PLAY) {
111         g_assert(replay_mutex_locked());
112         bool res = replay_has_exception();
113         if (res) {
114             replay_finish_event();
115         }
116         return res;
117     }
118 
119     return true;
120 }
121 
122 bool replay_has_exception(void)
123 {
124     bool res = false;
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);
129     }
130 
131     return res;
132 }
133 
134 bool replay_interrupt(void)
135 {
136     if (replay_mode == REPLAY_MODE_RECORD) {
137         g_assert(replay_mutex_locked());
138         replay_save_instructions();
139         replay_put_event(EVENT_INTERRUPT);
140         return true;
141     } else if (replay_mode == REPLAY_MODE_PLAY) {
142         g_assert(replay_mutex_locked());
143         bool res = replay_has_interrupt();
144         if (res) {
145             replay_finish_event();
146         }
147         return res;
148     }
149 
150     return true;
151 }
152 
153 bool replay_has_interrupt(void)
154 {
155     bool res = false;
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);
160     }
161     return res;
162 }
163 
164 void replay_shutdown_request(ShutdownCause cause)
165 {
166     if (replay_mode == REPLAY_MODE_RECORD) {
167         g_assert(replay_mutex_locked());
168         replay_put_event(EVENT_SHUTDOWN + cause);
169     }
170 }
171 
172 bool replay_checkpoint(ReplayCheckpoint checkpoint)
173 {
174     bool res = false;
175     static bool in_checkpoint;
176     assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
177 
178     if (!replay_file) {
179         return true;
180     }
181 
182     if (in_checkpoint) {
183         /*
184            Recursion occurs when HW event modifies timers.
185            Prevent performing icount warp in this case and
186            wait for another invocation of the checkpoint.
187         */
188         g_assert(replay_mode == REPLAY_MODE_PLAY);
189         return false;
190     }
191     in_checkpoint = true;
192 
193     replay_save_instructions();
194 
195     if (replay_mode == REPLAY_MODE_PLAY) {
196         g_assert(replay_mutex_locked());
197         if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
198             replay_finish_event();
199         } else if (replay_state.data_kind != EVENT_ASYNC) {
200             res = false;
201             goto out;
202         }
203         replay_read_events(checkpoint);
204         /* replay_read_events may leave some unread events.
205            Return false if not all of the events associated with
206            checkpoint were processed */
207         res = replay_state.data_kind != EVENT_ASYNC;
208     } else if (replay_mode == REPLAY_MODE_RECORD) {
209         g_assert(replay_mutex_locked());
210         replay_put_event(EVENT_CHECKPOINT + checkpoint);
211         /* This checkpoint belongs to several threads.
212            Processing events from different threads is
213            non-deterministic */
214         if (checkpoint != CHECKPOINT_CLOCK_WARP_START
215             /* FIXME: this is temporary fix, other checkpoints
216                       may also be invoked from the different threads someday.
217                       Asynchronous event processing should be refactored
218                       to create additional replay event kind which is
219                       nailed to the one of the threads and which processes
220                       the event queue. */
221             && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
222             replay_save_events(checkpoint);
223         }
224         res = true;
225     }
226 out:
227     in_checkpoint = false;
228     return res;
229 }
230 
231 bool replay_has_checkpoint(void)
232 {
233     bool res = false;
234     if (replay_mode == REPLAY_MODE_PLAY) {
235         g_assert(replay_mutex_locked());
236         replay_account_executed_instructions();
237         res = EVENT_CHECKPOINT <= replay_state.data_kind
238               && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
239     }
240     return res;
241 }
242 
243 static void replay_enable(const char *fname, int mode)
244 {
245     const char *fmode = NULL;
246     assert(!replay_file);
247 
248     switch (mode) {
249     case REPLAY_MODE_RECORD:
250         fmode = "wb";
251         break;
252     case REPLAY_MODE_PLAY:
253         fmode = "rb";
254         break;
255     default:
256         fprintf(stderr, "Replay: internal error: invalid replay mode\n");
257         exit(1);
258     }
259 
260     atexit(replay_finish);
261 
262     replay_file = fopen(fname, fmode);
263     if (replay_file == NULL) {
264         fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
265         exit(1);
266     }
267 
268     replay_filename = g_strdup(fname);
269     replay_mode = mode;
270     replay_mutex_init();
271 
272     replay_state.data_kind = -1;
273     replay_state.instruction_count = 0;
274     replay_state.current_icount = 0;
275     replay_state.has_unread_data = 0;
276 
277     /* skip file header for RECORD and check it for PLAY */
278     if (replay_mode == REPLAY_MODE_RECORD) {
279         fseek(replay_file, HEADER_SIZE, SEEK_SET);
280     } else if (replay_mode == REPLAY_MODE_PLAY) {
281         unsigned int version = replay_get_dword();
282         if (version != REPLAY_VERSION) {
283             fprintf(stderr, "Replay: invalid input log file version\n");
284             exit(1);
285         }
286         /* go to the beginning */
287         fseek(replay_file, HEADER_SIZE, SEEK_SET);
288         replay_fetch_data_kind();
289     }
290 
291     replay_init_events();
292 }
293 
294 void replay_configure(QemuOpts *opts)
295 {
296     const char *fname;
297     const char *rr;
298     ReplayMode mode = REPLAY_MODE_NONE;
299     Location loc;
300 
301     if (!opts) {
302         return;
303     }
304 
305     loc_push_none(&loc);
306     qemu_opts_loc_restore(opts);
307 
308     rr = qemu_opt_get(opts, "rr");
309     if (!rr) {
310         /* Just enabling icount */
311         goto out;
312     } else if (!strcmp(rr, "record")) {
313         mode = REPLAY_MODE_RECORD;
314     } else if (!strcmp(rr, "replay")) {
315         mode = REPLAY_MODE_PLAY;
316     } else {
317         error_report("Invalid icount rr option: %s", rr);
318         exit(1);
319     }
320 
321     fname = qemu_opt_get(opts, "rrfile");
322     if (!fname) {
323         error_report("File name not specified for replay");
324         exit(1);
325     }
326 
327     replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
328     replay_vmstate_register();
329     replay_enable(fname, mode);
330 
331 out:
332     loc_pop(&loc);
333 }
334 
335 void replay_start(void)
336 {
337     if (replay_mode == REPLAY_MODE_NONE) {
338         return;
339     }
340 
341     if (replay_blockers) {
342         error_reportf_err(replay_blockers->data, "Record/replay: ");
343         exit(1);
344     }
345     if (!icount_enabled()) {
346         error_report("Please enable icount to use record/replay");
347         exit(1);
348     }
349 
350     /* Timer for snapshotting will be set up here. */
351 
352     replay_enable_events();
353 }
354 
355 void replay_finish(void)
356 {
357     if (replay_mode == REPLAY_MODE_NONE) {
358         return;
359     }
360 
361     replay_save_instructions();
362 
363     /* finalize the file */
364     if (replay_file) {
365         if (replay_mode == REPLAY_MODE_RECORD) {
366             /*
367              * Can't do it in the signal handler, therefore
368              * add shutdown event here for the case of Ctrl-C.
369              */
370             replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
371             /* write end event */
372             replay_put_event(EVENT_END);
373 
374             /* write header */
375             fseek(replay_file, 0, SEEK_SET);
376             replay_put_dword(REPLAY_VERSION);
377         }
378 
379         fclose(replay_file);
380         replay_file = NULL;
381     }
382     if (replay_filename) {
383         g_free(replay_filename);
384         replay_filename = NULL;
385     }
386 
387     g_free(replay_snapshot);
388     replay_snapshot = NULL;
389 
390     replay_mode = REPLAY_MODE_NONE;
391 
392     replay_finish_events();
393 }
394 
395 void replay_add_blocker(Error *reason)
396 {
397     replay_blockers = g_slist_prepend(replay_blockers, reason);
398 }
399 
400 const char *replay_get_filename(void)
401 {
402     return replay_filename;
403 }
404