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