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