xref: /openbmc/qemu/replay/replay.c (revision 966f2ec3)
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 "replay-internal.h"
16 #include "qemu/timer.h"
17 #include "qemu/main-loop.h"
18 #include "qemu/option.h"
19 #include "sysemu/cpus.h"
20 #include "sysemu/sysemu.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              0xe02007
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.instructions_count != 0) {
43         assert(replay_state.data_kind == EVENT_INSTRUCTION);
44         return event == EVENT_INSTRUCTION;
45     }
46 
47     while (true) {
48         if (event == replay_state.data_kind) {
49             res = true;
50         }
51         switch (replay_state.data_kind) {
52         case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST:
53             replay_finish_event();
54             qemu_system_shutdown_request(replay_state.data_kind -
55                                          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_step(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.instructions_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.instructions_count > 0) {
86             int count = (int)(replay_get_current_step()
87                               - replay_state.current_step);
88 
89             /* Time can only go forward */
90             assert(count >= 0);
91 
92             replay_state.instructions_count -= count;
93             replay_state.current_step += count;
94             if (replay_state.instructions_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             replay_save_events(checkpoint);
219         }
220         res = true;
221     }
222 out:
223     in_checkpoint = false;
224     return res;
225 }
226 
227 bool replay_has_checkpoint(void)
228 {
229     bool res = false;
230     if (replay_mode == REPLAY_MODE_PLAY) {
231         g_assert(replay_mutex_locked());
232         replay_account_executed_instructions();
233         res = EVENT_CHECKPOINT <= replay_state.data_kind
234               && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
235     }
236     return res;
237 }
238 
239 static void replay_enable(const char *fname, int mode)
240 {
241     const char *fmode = NULL;
242     assert(!replay_file);
243 
244     switch (mode) {
245     case REPLAY_MODE_RECORD:
246         fmode = "wb";
247         break;
248     case REPLAY_MODE_PLAY:
249         fmode = "rb";
250         break;
251     default:
252         fprintf(stderr, "Replay: internal error: invalid replay mode\n");
253         exit(1);
254     }
255 
256     atexit(replay_finish);
257 
258     replay_file = fopen(fname, fmode);
259     if (replay_file == NULL) {
260         fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
261         exit(1);
262     }
263 
264     replay_filename = g_strdup(fname);
265     replay_mode = mode;
266     replay_mutex_init();
267 
268     replay_state.data_kind = -1;
269     replay_state.instructions_count = 0;
270     replay_state.current_step = 0;
271     replay_state.has_unread_data = 0;
272 
273     /* skip file header for RECORD and check it for PLAY */
274     if (replay_mode == REPLAY_MODE_RECORD) {
275         fseek(replay_file, HEADER_SIZE, SEEK_SET);
276     } else if (replay_mode == REPLAY_MODE_PLAY) {
277         unsigned int version = replay_get_dword();
278         if (version != REPLAY_VERSION) {
279             fprintf(stderr, "Replay: invalid input log file version\n");
280             exit(1);
281         }
282         /* go to the beginning */
283         fseek(replay_file, HEADER_SIZE, SEEK_SET);
284         replay_fetch_data_kind();
285     }
286 
287     replay_init_events();
288 }
289 
290 void replay_configure(QemuOpts *opts)
291 {
292     const char *fname;
293     const char *rr;
294     ReplayMode mode = REPLAY_MODE_NONE;
295     Location loc;
296 
297     if (!opts) {
298         return;
299     }
300 
301     loc_push_none(&loc);
302     qemu_opts_loc_restore(opts);
303 
304     rr = qemu_opt_get(opts, "rr");
305     if (!rr) {
306         /* Just enabling icount */
307         goto out;
308     } else if (!strcmp(rr, "record")) {
309         mode = REPLAY_MODE_RECORD;
310     } else if (!strcmp(rr, "replay")) {
311         mode = REPLAY_MODE_PLAY;
312     } else {
313         error_report("Invalid icount rr option: %s", rr);
314         exit(1);
315     }
316 
317     fname = qemu_opt_get(opts, "rrfile");
318     if (!fname) {
319         error_report("File name not specified for replay");
320         exit(1);
321     }
322 
323     replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
324     replay_vmstate_register();
325     replay_enable(fname, mode);
326 
327 out:
328     loc_pop(&loc);
329 }
330 
331 void replay_start(void)
332 {
333     if (replay_mode == REPLAY_MODE_NONE) {
334         return;
335     }
336 
337     if (replay_blockers) {
338         error_reportf_err(replay_blockers->data, "Record/replay: ");
339         exit(1);
340     }
341     if (!use_icount) {
342         error_report("Please enable icount to use record/replay");
343         exit(1);
344     }
345 
346     /* Timer for snapshotting will be set up here. */
347 
348     replay_enable_events();
349 }
350 
351 void replay_finish(void)
352 {
353     if (replay_mode == REPLAY_MODE_NONE) {
354         return;
355     }
356 
357     replay_save_instructions();
358 
359     /* finalize the file */
360     if (replay_file) {
361         if (replay_mode == REPLAY_MODE_RECORD) {
362             /* write end event */
363             replay_put_event(EVENT_END);
364 
365             /* write header */
366             fseek(replay_file, 0, SEEK_SET);
367             replay_put_dword(REPLAY_VERSION);
368         }
369 
370         fclose(replay_file);
371         replay_file = NULL;
372     }
373     if (replay_filename) {
374         g_free(replay_filename);
375         replay_filename = NULL;
376     }
377 
378     g_free(replay_snapshot);
379     replay_snapshot = NULL;
380 
381     replay_finish_events();
382 }
383 
384 void replay_add_blocker(Error *reason)
385 {
386     replay_blockers = g_slist_prepend(replay_blockers, reason);
387 }
388