]> Git Repo - qemu.git/blob - replay/replay.c
Merge remote-tracking branch 'remotes/cody/tags/block-pull-request' into staging
[qemu.git] / replay / replay.c
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 static void replay_enable(const char *fname, int mode)
228 {
229     const char *fmode = NULL;
230     assert(!replay_file);
231
232     switch (mode) {
233     case REPLAY_MODE_RECORD:
234         fmode = "wb";
235         break;
236     case REPLAY_MODE_PLAY:
237         fmode = "rb";
238         break;
239     default:
240         fprintf(stderr, "Replay: internal error: invalid replay mode\n");
241         exit(1);
242     }
243
244     atexit(replay_finish);
245
246     replay_file = fopen(fname, fmode);
247     if (replay_file == NULL) {
248         fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
249         exit(1);
250     }
251
252     replay_filename = g_strdup(fname);
253     replay_mode = mode;
254     replay_mutex_init();
255
256     replay_state.data_kind = -1;
257     replay_state.instructions_count = 0;
258     replay_state.current_step = 0;
259     replay_state.has_unread_data = 0;
260
261     /* skip file header for RECORD and check it for PLAY */
262     if (replay_mode == REPLAY_MODE_RECORD) {
263         fseek(replay_file, HEADER_SIZE, SEEK_SET);
264     } else if (replay_mode == REPLAY_MODE_PLAY) {
265         unsigned int version = replay_get_dword();
266         if (version != REPLAY_VERSION) {
267             fprintf(stderr, "Replay: invalid input log file version\n");
268             exit(1);
269         }
270         /* go to the beginning */
271         fseek(replay_file, HEADER_SIZE, SEEK_SET);
272         replay_fetch_data_kind();
273     }
274
275     replay_init_events();
276 }
277
278 void replay_configure(QemuOpts *opts)
279 {
280     const char *fname;
281     const char *rr;
282     ReplayMode mode = REPLAY_MODE_NONE;
283     Location loc;
284
285     if (!opts) {
286         return;
287     }
288
289     loc_push_none(&loc);
290     qemu_opts_loc_restore(opts);
291
292     rr = qemu_opt_get(opts, "rr");
293     if (!rr) {
294         /* Just enabling icount */
295         goto out;
296     } else if (!strcmp(rr, "record")) {
297         mode = REPLAY_MODE_RECORD;
298     } else if (!strcmp(rr, "replay")) {
299         mode = REPLAY_MODE_PLAY;
300     } else {
301         error_report("Invalid icount rr option: %s", rr);
302         exit(1);
303     }
304
305     fname = qemu_opt_get(opts, "rrfile");
306     if (!fname) {
307         error_report("File name not specified for replay");
308         exit(1);
309     }
310
311     replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
312     replay_vmstate_register();
313     replay_enable(fname, mode);
314
315 out:
316     loc_pop(&loc);
317 }
318
319 void replay_start(void)
320 {
321     if (replay_mode == REPLAY_MODE_NONE) {
322         return;
323     }
324
325     if (replay_blockers) {
326         error_reportf_err(replay_blockers->data, "Record/replay: ");
327         exit(1);
328     }
329     if (!use_icount) {
330         error_report("Please enable icount to use record/replay");
331         exit(1);
332     }
333
334     /* Timer for snapshotting will be set up here. */
335
336     replay_enable_events();
337 }
338
339 void replay_finish(void)
340 {
341     if (replay_mode == REPLAY_MODE_NONE) {
342         return;
343     }
344
345     replay_save_instructions();
346
347     /* finalize the file */
348     if (replay_file) {
349         if (replay_mode == REPLAY_MODE_RECORD) {
350             /* write end event */
351             replay_put_event(EVENT_END);
352
353             /* write header */
354             fseek(replay_file, 0, SEEK_SET);
355             replay_put_dword(REPLAY_VERSION);
356         }
357
358         fclose(replay_file);
359         replay_file = NULL;
360     }
361     if (replay_filename) {
362         g_free(replay_filename);
363         replay_filename = NULL;
364     }
365
366     g_free(replay_snapshot);
367     replay_snapshot = NULL;
368
369     replay_finish_events();
370 }
371
372 void replay_add_blocker(Error *reason)
373 {
374     replay_blockers = g_slist_prepend(replay_blockers, reason);
375 }
This page took 0.048193 seconds and 4 git commands to generate.