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