xref: /openbmc/qemu/replay/replay.c (revision 6f03770d)
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         /* If we are already in checkpoint, then there is no need
184            for additional synchronization.
185            Recursion occurs when HW event modifies timers.
186            Timer modification may invoke the checkpoint and
187            proceed to recursion. */
188         return true;
189     }
190     in_checkpoint = true;
191 
192     replay_save_instructions();
193 
194     if (replay_mode == REPLAY_MODE_PLAY) {
195         g_assert(replay_mutex_locked());
196         if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
197             replay_finish_event();
198         } else if (replay_state.data_kind != EVENT_ASYNC) {
199             res = false;
200             goto out;
201         }
202         replay_read_events(checkpoint);
203         /* replay_read_events may leave some unread events.
204            Return false if not all of the events associated with
205            checkpoint were processed */
206         res = replay_state.data_kind != EVENT_ASYNC;
207     } else if (replay_mode == REPLAY_MODE_RECORD) {
208         g_assert(replay_mutex_locked());
209         replay_put_event(EVENT_CHECKPOINT + checkpoint);
210         /* This checkpoint belongs to several threads.
211            Processing events from different threads is
212            non-deterministic */
213         if (checkpoint != CHECKPOINT_CLOCK_WARP_START
214             /* FIXME: this is temporary fix, other checkpoints
215                       may also be invoked from the different threads someday.
216                       Asynchronous event processing should be refactored
217                       to create additional replay event kind which is
218                       nailed to the one of the threads and which processes
219                       the event queue. */
220             && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
221             replay_save_events(checkpoint);
222         }
223         res = true;
224     }
225 out:
226     in_checkpoint = false;
227     return res;
228 }
229 
230 bool replay_has_checkpoint(void)
231 {
232     bool res = false;
233     if (replay_mode == REPLAY_MODE_PLAY) {
234         g_assert(replay_mutex_locked());
235         replay_account_executed_instructions();
236         res = EVENT_CHECKPOINT <= replay_state.data_kind
237               && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
238     }
239     return res;
240 }
241 
242 static void replay_enable(const char *fname, int mode)
243 {
244     const char *fmode = NULL;
245     assert(!replay_file);
246 
247     switch (mode) {
248     case REPLAY_MODE_RECORD:
249         fmode = "wb";
250         break;
251     case REPLAY_MODE_PLAY:
252         fmode = "rb";
253         break;
254     default:
255         fprintf(stderr, "Replay: internal error: invalid replay mode\n");
256         exit(1);
257     }
258 
259     atexit(replay_finish);
260 
261     replay_file = fopen(fname, fmode);
262     if (replay_file == NULL) {
263         fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
264         exit(1);
265     }
266 
267     replay_filename = g_strdup(fname);
268     replay_mode = mode;
269     replay_mutex_init();
270 
271     replay_state.data_kind = -1;
272     replay_state.instruction_count = 0;
273     replay_state.current_icount = 0;
274     replay_state.has_unread_data = 0;
275 
276     /* skip file header for RECORD and check it for PLAY */
277     if (replay_mode == REPLAY_MODE_RECORD) {
278         fseek(replay_file, HEADER_SIZE, SEEK_SET);
279     } else if (replay_mode == REPLAY_MODE_PLAY) {
280         unsigned int version = replay_get_dword();
281         if (version != REPLAY_VERSION) {
282             fprintf(stderr, "Replay: invalid input log file version\n");
283             exit(1);
284         }
285         /* go to the beginning */
286         fseek(replay_file, HEADER_SIZE, SEEK_SET);
287         replay_fetch_data_kind();
288     }
289 
290     replay_init_events();
291 }
292 
293 void replay_configure(QemuOpts *opts)
294 {
295     const char *fname;
296     const char *rr;
297     ReplayMode mode = REPLAY_MODE_NONE;
298     Location loc;
299 
300     if (!opts) {
301         return;
302     }
303 
304     loc_push_none(&loc);
305     qemu_opts_loc_restore(opts);
306 
307     rr = qemu_opt_get(opts, "rr");
308     if (!rr) {
309         /* Just enabling icount */
310         goto out;
311     } else if (!strcmp(rr, "record")) {
312         mode = REPLAY_MODE_RECORD;
313     } else if (!strcmp(rr, "replay")) {
314         mode = REPLAY_MODE_PLAY;
315     } else {
316         error_report("Invalid icount rr option: %s", rr);
317         exit(1);
318     }
319 
320     fname = qemu_opt_get(opts, "rrfile");
321     if (!fname) {
322         error_report("File name not specified for replay");
323         exit(1);
324     }
325 
326     replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
327     replay_vmstate_register();
328     replay_enable(fname, mode);
329 
330 out:
331     loc_pop(&loc);
332 }
333 
334 void replay_start(void)
335 {
336     if (replay_mode == REPLAY_MODE_NONE) {
337         return;
338     }
339 
340     if (replay_blockers) {
341         error_reportf_err(replay_blockers->data, "Record/replay: ");
342         exit(1);
343     }
344     if (!icount_enabled()) {
345         error_report("Please enable icount to use record/replay");
346         exit(1);
347     }
348 
349     /* Timer for snapshotting will be set up here. */
350 
351     replay_enable_events();
352 }
353 
354 void replay_finish(void)
355 {
356     if (replay_mode == REPLAY_MODE_NONE) {
357         return;
358     }
359 
360     replay_save_instructions();
361 
362     /* finalize the file */
363     if (replay_file) {
364         if (replay_mode == REPLAY_MODE_RECORD) {
365             /*
366              * Can't do it in the signal handler, therefore
367              * add shutdown event here for the case of Ctrl-C.
368              */
369             replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
370             /* write end event */
371             replay_put_event(EVENT_END);
372 
373             /* write header */
374             fseek(replay_file, 0, SEEK_SET);
375             replay_put_dword(REPLAY_VERSION);
376         }
377 
378         fclose(replay_file);
379         replay_file = NULL;
380     }
381     if (replay_filename) {
382         g_free(replay_filename);
383         replay_filename = NULL;
384     }
385 
386     g_free(replay_snapshot);
387     replay_snapshot = NULL;
388 
389     replay_mode = REPLAY_MODE_NONE;
390 
391     replay_finish_events();
392 }
393 
394 void replay_add_blocker(Error *reason)
395 {
396     replay_blockers = g_slist_prepend(replay_blockers, reason);
397 }
398 
399 const char *replay_get_filename(void)
400 {
401     return replay_filename;
402 }
403