xref: /openbmc/qemu/replay/replay.c (revision da33fc09)
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             int count = (int)(replay_get_current_icount()
98                               - replay_state.current_icount);
99 
100             /* Time can only go forward */
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                 /* Wake up iothread. This is required because
109                    timers will not expire until clock counters
110                    will be read from the log. */
111                 qemu_notify_event();
112             }
113             /* Execution reached the break step */
114             if (replay_break_icount == replay_state.current_icount) {
115                 /* Cannot make callback directly from the vCPU thread */
116                 timer_mod_ns(replay_break_timer,
117                     qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
118             }
119         }
120     }
121 }
122 
123 bool 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 
143 bool 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 
155 bool 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 
174 bool 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 
185 void 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 
193 bool 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         /* If we are already in checkpoint, then there is no need
205            for additional synchronization.
206            Recursion occurs when HW event modifies timers.
207            Timer modification may invoke the checkpoint and
208            proceed to recursion. */
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         /* replay_read_events may leave some unread events.
225            Return false if not all of the events associated with
226            checkpoint were processed */
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         /* This checkpoint belongs to several threads.
232            Processing events from different threads is
233            non-deterministic */
234         if (checkpoint != CHECKPOINT_CLOCK_WARP_START
235             /* FIXME: this is temporary fix, other checkpoints
236                       may also be invoked from the different threads someday.
237                       Asynchronous event processing should be refactored
238                       to create additional replay event kind which is
239                       nailed to the one of the threads and which processes
240                       the event queue. */
241             && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
242             replay_save_events(checkpoint);
243         }
244         res = true;
245     }
246 out:
247     in_checkpoint = false;
248     return res;
249 }
250 
251 bool 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 
263 static 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     /* skip file header for RECORD and check it for PLAY */
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         /* go to the beginning */
307         fseek(replay_file, HEADER_SIZE, SEEK_SET);
308         replay_fetch_data_kind();
309     }
310 
311     replay_init_events();
312 }
313 
314 void 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         /* Just enabling icount */
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 
351 out:
352     loc_pop(&loc);
353 }
354 
355 void 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     /* Timer for snapshotting will be set up here. */
371 
372     replay_enable_events();
373 }
374 
375 void replay_finish(void)
376 {
377     if (replay_mode == REPLAY_MODE_NONE) {
378         return;
379     }
380 
381     replay_save_instructions();
382 
383     /* finalize the file */
384     if (replay_file) {
385         if (replay_mode == REPLAY_MODE_RECORD) {
386             /*
387              * Can't do it in the signal handler, therefore
388              * add shutdown event here for the case of Ctrl-C.
389              */
390             replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
391             /* write end event */
392             replay_put_event(EVENT_END);
393 
394             /* write header */
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 
415 void replay_add_blocker(Error *reason)
416 {
417     replay_blockers = g_slist_prepend(replay_blockers, reason);
418 }
419 
420 const char *replay_get_filename(void)
421 {
422     return replay_filename;
423 }
424