xref: /openbmc/qemu/replay/replay.c (revision 08da3180)
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              0xe02005
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_state.data_kind == EVENT_INSTRUCTION);
42         return event == EVENT_INSTRUCTION;
43     }
44 
45     while (true) {
46         if (event == replay_state.data_kind) {
47             res = true;
48         }
49         switch (replay_state.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_state.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_state.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_state.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_state.data_kind = -1;
241     replay_state.instructions_count = 0;
242     replay_state.current_step = 0;
243     replay_state.has_unread_data = 0;
244 
245     /* skip file header for RECORD and check it for PLAY */
246     if (replay_mode == REPLAY_MODE_RECORD) {
247         fseek(replay_file, HEADER_SIZE, SEEK_SET);
248     } else if (replay_mode == REPLAY_MODE_PLAY) {
249         unsigned int version = replay_get_dword();
250         if (version != REPLAY_VERSION) {
251             fprintf(stderr, "Replay: invalid input log file version\n");
252             exit(1);
253         }
254         /* go to the beginning */
255         fseek(replay_file, HEADER_SIZE, SEEK_SET);
256         replay_fetch_data_kind();
257     }
258 
259     replay_init_events();
260 }
261 
262 void replay_configure(QemuOpts *opts)
263 {
264     const char *fname;
265     const char *rr;
266     ReplayMode mode = REPLAY_MODE_NONE;
267     Location loc;
268 
269     if (!opts) {
270         return;
271     }
272 
273     loc_push_none(&loc);
274     qemu_opts_loc_restore(opts);
275 
276     rr = qemu_opt_get(opts, "rr");
277     if (!rr) {
278         /* Just enabling icount */
279         goto out;
280     } else if (!strcmp(rr, "record")) {
281         mode = REPLAY_MODE_RECORD;
282     } else if (!strcmp(rr, "replay")) {
283         mode = REPLAY_MODE_PLAY;
284     } else {
285         error_report("Invalid icount rr option: %s", rr);
286         exit(1);
287     }
288 
289     fname = qemu_opt_get(opts, "rrfile");
290     if (!fname) {
291         error_report("File name not specified for replay");
292         exit(1);
293     }
294 
295     replay_vmstate_register();
296     replay_enable(fname, mode);
297 
298 out:
299     loc_pop(&loc);
300 }
301 
302 void replay_start(void)
303 {
304     if (replay_mode == REPLAY_MODE_NONE) {
305         return;
306     }
307 
308     if (replay_blockers) {
309         error_reportf_err(replay_blockers->data, "Record/replay: ");
310         exit(1);
311     }
312     if (!use_icount) {
313         error_report("Please enable icount to use record/replay");
314         exit(1);
315     }
316 
317     /* Timer for snapshotting will be set up here. */
318 
319     replay_enable_events();
320 }
321 
322 void replay_finish(void)
323 {
324     if (replay_mode == REPLAY_MODE_NONE) {
325         return;
326     }
327 
328     replay_save_instructions();
329 
330     /* finalize the file */
331     if (replay_file) {
332         if (replay_mode == REPLAY_MODE_RECORD) {
333             /* write end event */
334             replay_put_event(EVENT_END);
335 
336             /* write header */
337             fseek(replay_file, 0, SEEK_SET);
338             replay_put_dword(REPLAY_VERSION);
339         }
340 
341         fclose(replay_file);
342         replay_file = NULL;
343     }
344     if (replay_filename) {
345         g_free(replay_filename);
346         replay_filename = NULL;
347     }
348 
349     replay_finish_events();
350     replay_mutex_destroy();
351 }
352 
353 void replay_add_blocker(Error *reason)
354 {
355     replay_blockers = g_slist_prepend(replay_blockers, reason);
356 }
357