xref: /openbmc/qemu/replay/replay.c (revision de15df5e)
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/replay.h"
15 #include "sysemu/runstate.h"
16 #include "replay-internal.h"
17 #include "qemu/timer.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 bool replay_next_event_is(int event)
38 {
39     bool res = false;
40 
41     /* nothing to skip - not all instructions used */
42     if (replay_state.instruction_count != 0) {
43         assert(replay_state.data_kind == EVENT_INSTRUCTION);
44         return event == EVENT_INSTRUCTION;
45     }
46 
47     while (true) {
48         unsigned int data_kind = replay_state.data_kind;
49         if (event == data_kind) {
50             res = true;
51         }
52         switch (data_kind) {
53         case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST:
54             replay_finish_event();
55             qemu_system_shutdown_request(data_kind - EVENT_SHUTDOWN);
56             break;
57         default:
58             /* clock, time_t, checkpoint and other events */
59             return res;
60         }
61     }
62     return res;
63 }
64 
65 uint64_t replay_get_current_icount(void)
66 {
67     return cpu_get_icount_raw();
68 }
69 
70 int 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.instruction_count;
76     }
77     replay_mutex_unlock();
78     return res;
79 }
80 
81 void replay_account_executed_instructions(void)
82 {
83     if (replay_mode == REPLAY_MODE_PLAY) {
84         g_assert(replay_mutex_locked());
85         if (replay_state.instruction_count > 0) {
86             int count = (int)(replay_get_current_icount()
87                               - replay_state.current_icount);
88 
89             /* Time can only go forward */
90             assert(count >= 0);
91 
92             replay_state.instruction_count -= count;
93             replay_state.current_icount += count;
94             if (replay_state.instruction_count == 0) {
95                 assert(replay_state.data_kind == EVENT_INSTRUCTION);
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         }
103     }
104 }
105 
106 bool replay_exception(void)
107 {
108 
109     if (replay_mode == REPLAY_MODE_RECORD) {
110         g_assert(replay_mutex_locked());
111         replay_save_instructions();
112         replay_put_event(EVENT_EXCEPTION);
113         return true;
114     } else if (replay_mode == REPLAY_MODE_PLAY) {
115         g_assert(replay_mutex_locked());
116         bool res = replay_has_exception();
117         if (res) {
118             replay_finish_event();
119         }
120         return res;
121     }
122 
123     return true;
124 }
125 
126 bool replay_has_exception(void)
127 {
128     bool res = false;
129     if (replay_mode == REPLAY_MODE_PLAY) {
130         g_assert(replay_mutex_locked());
131         replay_account_executed_instructions();
132         res = replay_next_event_is(EVENT_EXCEPTION);
133     }
134 
135     return res;
136 }
137 
138 bool replay_interrupt(void)
139 {
140     if (replay_mode == REPLAY_MODE_RECORD) {
141         g_assert(replay_mutex_locked());
142         replay_save_instructions();
143         replay_put_event(EVENT_INTERRUPT);
144         return true;
145     } else if (replay_mode == REPLAY_MODE_PLAY) {
146         g_assert(replay_mutex_locked());
147         bool res = replay_has_interrupt();
148         if (res) {
149             replay_finish_event();
150         }
151         return res;
152     }
153 
154     return true;
155 }
156 
157 bool replay_has_interrupt(void)
158 {
159     bool res = false;
160     if (replay_mode == REPLAY_MODE_PLAY) {
161         g_assert(replay_mutex_locked());
162         replay_account_executed_instructions();
163         res = replay_next_event_is(EVENT_INTERRUPT);
164     }
165     return res;
166 }
167 
168 void replay_shutdown_request(ShutdownCause cause)
169 {
170     if (replay_mode == REPLAY_MODE_RECORD) {
171         g_assert(replay_mutex_locked());
172         replay_put_event(EVENT_SHUTDOWN + cause);
173     }
174 }
175 
176 bool replay_checkpoint(ReplayCheckpoint checkpoint)
177 {
178     bool res = false;
179     static bool in_checkpoint;
180     assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
181 
182     if (!replay_file) {
183         return true;
184     }
185 
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();
197 
198     if (replay_mode == REPLAY_MODE_PLAY) {
199         g_assert(replay_mutex_locked());
200         if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
201             replay_finish_event();
202         } else if (replay_state.data_kind != EVENT_ASYNC) {
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 */
210         res = replay_state.data_kind != EVENT_ASYNC;
211     } else if (replay_mode == REPLAY_MODE_RECORD) {
212         g_assert(replay_mutex_locked());
213         replay_put_event(EVENT_CHECKPOINT + checkpoint);
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             /* 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) {
225             replay_save_events(checkpoint);
226         }
227         res = true;
228     }
229 out:
230     in_checkpoint = false;
231     return res;
232 }
233 
234 bool 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 
246 static 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 
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);
272     replay_mode = mode;
273     replay_mutex_init();
274 
275     replay_state.data_kind = -1;
276     replay_state.instruction_count = 0;
277     replay_state.current_icount = 0;
278     replay_state.has_unread_data = 0;
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 
297 void replay_configure(QemuOpts *opts)
298 {
299     const char *fname;
300     const char *rr;
301     ReplayMode mode = REPLAY_MODE_NONE;
302     Location loc;
303 
304     if (!opts) {
305         return;
306     }
307 
308     loc_push_none(&loc);
309     qemu_opts_loc_restore(opts);
310 
311     rr = qemu_opt_get(opts, "rr");
312     if (!rr) {
313         /* Just enabling icount */
314         goto out;
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 
330     replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
331     replay_vmstate_register();
332     replay_enable(fname, mode);
333 
334 out:
335     loc_pop(&loc);
336 }
337 
338 void replay_start(void)
339 {
340     if (replay_mode == REPLAY_MODE_NONE) {
341         return;
342     }
343 
344     if (replay_blockers) {
345         error_reportf_err(replay_blockers->data, "Record/replay: ");
346         exit(1);
347     }
348     if (!use_icount) {
349         error_report("Please enable icount to use record/replay");
350         exit(1);
351     }
352 
353     /* Timer for snapshotting will be set up here. */
354 
355     replay_enable_events();
356 }
357 
358 void 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) {
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);
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 
390     g_free(replay_snapshot);
391     replay_snapshot = NULL;
392 
393     replay_mode = REPLAY_MODE_NONE;
394 
395     replay_finish_events();
396 }
397 
398 void replay_add_blocker(Error *reason)
399 {
400     replay_blockers = g_slist_prepend(replay_blockers, reason);
401 }
402