xref: /openbmc/qemu/replay/replay.c (revision 1f7685fa)
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 "qemu-common.h"
15 #include "sysemu/replay.h"
16 #include "replay-internal.h"
17 #include "qemu/timer.h"
18 #include "qemu/main-loop.h"
19 #include "sysemu/sysemu.h"
20 #include "qemu/error-report.h"
21 
22 /* Current version of the replay mechanism.
23    Increase it when file format changes. */
24 #define REPLAY_VERSION              0xe02004
25 /* Size of replay log header */
26 #define HEADER_SIZE                 (sizeof(uint32_t) + sizeof(uint64_t))
27 
28 ReplayMode replay_mode = REPLAY_MODE_NONE;
29 
30 /* Name of replay file  */
31 static char *replay_filename;
32 ReplayState replay_state;
33 static GSList *replay_blockers;
34 
35 bool replay_next_event_is(int event)
36 {
37     bool res = false;
38 
39     /* nothing to skip - not all instructions used */
40     if (replay_state.instructions_count != 0) {
41         assert(replay_data_kind == EVENT_INSTRUCTION);
42         return event == EVENT_INSTRUCTION;
43     }
44 
45     while (true) {
46         if (event == replay_data_kind) {
47             res = true;
48         }
49         switch (replay_data_kind) {
50         case EVENT_SHUTDOWN:
51             replay_finish_event();
52             qemu_system_shutdown_request();
53             break;
54         default:
55             /* clock, time_t, checkpoint and other events */
56             return res;
57         }
58     }
59     return res;
60 }
61 
62 uint64_t replay_get_current_step(void)
63 {
64     return cpu_get_icount_raw();
65 }
66 
67 int replay_get_instructions(void)
68 {
69     int res = 0;
70     replay_mutex_lock();
71     if (replay_next_event_is(EVENT_INSTRUCTION)) {
72         res = replay_state.instructions_count;
73     }
74     replay_mutex_unlock();
75     return res;
76 }
77 
78 void replay_account_executed_instructions(void)
79 {
80     if (replay_mode == REPLAY_MODE_PLAY) {
81         replay_mutex_lock();
82         if (replay_state.instructions_count > 0) {
83             int count = (int)(replay_get_current_step()
84                               - replay_state.current_step);
85             replay_state.instructions_count -= count;
86             replay_state.current_step += count;
87             if (replay_state.instructions_count == 0) {
88                 assert(replay_data_kind == EVENT_INSTRUCTION);
89                 replay_finish_event();
90                 /* Wake up iothread. This is required because
91                    timers will not expire until clock counters
92                    will be read from the log. */
93                 qemu_notify_event();
94             }
95         }
96         replay_mutex_unlock();
97     }
98 }
99 
100 bool replay_exception(void)
101 {
102     if (replay_mode == REPLAY_MODE_RECORD) {
103         replay_save_instructions();
104         replay_mutex_lock();
105         replay_put_event(EVENT_EXCEPTION);
106         replay_mutex_unlock();
107         return true;
108     } else if (replay_mode == REPLAY_MODE_PLAY) {
109         bool res = replay_has_exception();
110         if (res) {
111             replay_mutex_lock();
112             replay_finish_event();
113             replay_mutex_unlock();
114         }
115         return res;
116     }
117 
118     return true;
119 }
120 
121 bool replay_has_exception(void)
122 {
123     bool res = false;
124     if (replay_mode == REPLAY_MODE_PLAY) {
125         replay_account_executed_instructions();
126         replay_mutex_lock();
127         res = replay_next_event_is(EVENT_EXCEPTION);
128         replay_mutex_unlock();
129     }
130 
131     return res;
132 }
133 
134 bool replay_interrupt(void)
135 {
136     if (replay_mode == REPLAY_MODE_RECORD) {
137         replay_save_instructions();
138         replay_mutex_lock();
139         replay_put_event(EVENT_INTERRUPT);
140         replay_mutex_unlock();
141         return true;
142     } else if (replay_mode == REPLAY_MODE_PLAY) {
143         bool res = replay_has_interrupt();
144         if (res) {
145             replay_mutex_lock();
146             replay_finish_event();
147             replay_mutex_unlock();
148         }
149         return res;
150     }
151 
152     return true;
153 }
154 
155 bool replay_has_interrupt(void)
156 {
157     bool res = false;
158     if (replay_mode == REPLAY_MODE_PLAY) {
159         replay_account_executed_instructions();
160         replay_mutex_lock();
161         res = replay_next_event_is(EVENT_INTERRUPT);
162         replay_mutex_unlock();
163     }
164     return res;
165 }
166 
167 void replay_shutdown_request(void)
168 {
169     if (replay_mode == REPLAY_MODE_RECORD) {
170         replay_mutex_lock();
171         replay_put_event(EVENT_SHUTDOWN);
172         replay_mutex_unlock();
173     }
174 }
175 
176 bool replay_checkpoint(ReplayCheckpoint checkpoint)
177 {
178     bool res = false;
179     assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
180     replay_save_instructions();
181 
182     if (!replay_file) {
183         return true;
184     }
185 
186     replay_mutex_lock();
187 
188     if (replay_mode == REPLAY_MODE_PLAY) {
189         if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
190             replay_finish_event();
191         } else if (replay_data_kind != EVENT_ASYNC) {
192             res = false;
193             goto out;
194         }
195         replay_read_events(checkpoint);
196         /* replay_read_events may leave some unread events.
197            Return false if not all of the events associated with
198            checkpoint were processed */
199         res = replay_data_kind != EVENT_ASYNC;
200     } else if (replay_mode == REPLAY_MODE_RECORD) {
201         replay_put_event(EVENT_CHECKPOINT + checkpoint);
202         replay_save_events(checkpoint);
203         res = true;
204     }
205 out:
206     replay_mutex_unlock();
207     return res;
208 }
209 
210 static void replay_enable(const char *fname, int mode)
211 {
212     const char *fmode = NULL;
213     assert(!replay_file);
214 
215     switch (mode) {
216     case REPLAY_MODE_RECORD:
217         fmode = "wb";
218         break;
219     case REPLAY_MODE_PLAY:
220         fmode = "rb";
221         break;
222     default:
223         fprintf(stderr, "Replay: internal error: invalid replay mode\n");
224         exit(1);
225     }
226 
227     atexit(replay_finish);
228 
229     replay_mutex_init();
230 
231     replay_file = fopen(fname, fmode);
232     if (replay_file == NULL) {
233         fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
234         exit(1);
235     }
236 
237     replay_filename = g_strdup(fname);
238 
239     replay_mode = mode;
240     replay_data_kind = -1;
241     replay_state.instructions_count = 0;
242     replay_state.current_step = 0;
243 
244     /* skip file header for RECORD and check it for PLAY */
245     if (replay_mode == REPLAY_MODE_RECORD) {
246         fseek(replay_file, HEADER_SIZE, SEEK_SET);
247     } else if (replay_mode == REPLAY_MODE_PLAY) {
248         unsigned int version = replay_get_dword();
249         if (version != REPLAY_VERSION) {
250             fprintf(stderr, "Replay: invalid input log file version\n");
251             exit(1);
252         }
253         /* go to the beginning */
254         fseek(replay_file, HEADER_SIZE, SEEK_SET);
255         replay_fetch_data_kind();
256     }
257 
258     replay_init_events();
259 }
260 
261 void replay_configure(QemuOpts *opts)
262 {
263     const char *fname;
264     const char *rr;
265     ReplayMode mode = REPLAY_MODE_NONE;
266     Location loc;
267 
268     if (!opts) {
269         return;
270     }
271 
272     loc_push_none(&loc);
273     qemu_opts_loc_restore(opts);
274 
275     rr = qemu_opt_get(opts, "rr");
276     if (!rr) {
277         /* Just enabling icount */
278         return;
279     } else if (!strcmp(rr, "record")) {
280         mode = REPLAY_MODE_RECORD;
281     } else if (!strcmp(rr, "replay")) {
282         mode = REPLAY_MODE_PLAY;
283     } else {
284         error_report("Invalid icount rr option: %s", rr);
285         exit(1);
286     }
287 
288     fname = qemu_opt_get(opts, "rrfile");
289     if (!fname) {
290         error_report("File name not specified for replay");
291         exit(1);
292     }
293 
294     replay_enable(fname, mode);
295 
296     loc_pop(&loc);
297 }
298 
299 void replay_start(void)
300 {
301     if (replay_mode == REPLAY_MODE_NONE) {
302         return;
303     }
304 
305     if (replay_blockers) {
306         error_reportf_err(replay_blockers->data, "Record/replay: ");
307         exit(1);
308     }
309     if (!use_icount) {
310         error_report("Please enable icount to use record/replay");
311         exit(1);
312     }
313 
314     /* Timer for snapshotting will be set up here. */
315 
316     replay_enable_events();
317 }
318 
319 void replay_finish(void)
320 {
321     if (replay_mode == REPLAY_MODE_NONE) {
322         return;
323     }
324 
325     replay_save_instructions();
326 
327     /* finalize the file */
328     if (replay_file) {
329         if (replay_mode == REPLAY_MODE_RECORD) {
330             /* write end event */
331             replay_put_event(EVENT_END);
332 
333             /* write header */
334             fseek(replay_file, 0, SEEK_SET);
335             replay_put_dword(REPLAY_VERSION);
336         }
337 
338         fclose(replay_file);
339         replay_file = NULL;
340     }
341     if (replay_filename) {
342         g_free(replay_filename);
343         replay_filename = NULL;
344     }
345 
346     replay_finish_events();
347     replay_mutex_destroy();
348 }
349 
350 void replay_add_blocker(Error *reason)
351 {
352     replay_blockers = g_slist_prepend(replay_blockers, reason);
353 }
354