]> git.proxmox.com Git - mirror_qemu.git/blob - replay/replay.c
Use g_new() & friends where that makes obvious sense
[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/cpu-timers.h"
15 #include "sysemu/replay.h"
16 #include "sysemu/runstate.h"
17 #include "replay-internal.h"
18 #include "qemu/main-loop.h"
19 #include "qemu/option.h"
20 #include "sysemu/cpus.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 0xe0200a
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 /* Replay breakpoints */
38 uint64_t replay_break_icount = -1ULL;
39 QEMUTimer *replay_break_timer;
40
41 bool replay_next_event_is(int event)
42 {
43 bool res = false;
44
45 /* nothing to skip - not all instructions used */
46 if (replay_state.instruction_count != 0) {
47 assert(replay_state.data_kind == EVENT_INSTRUCTION);
48 return event == EVENT_INSTRUCTION;
49 }
50
51 while (true) {
52 unsigned int data_kind = replay_state.data_kind;
53 if (event == data_kind) {
54 res = true;
55 }
56 switch (data_kind) {
57 case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST:
58 replay_finish_event();
59 qemu_system_shutdown_request(data_kind - EVENT_SHUTDOWN);
60 break;
61 default:
62 /* clock, time_t, checkpoint and other events */
63 return res;
64 }
65 }
66 return res;
67 }
68
69 uint64_t replay_get_current_icount(void)
70 {
71 return icount_get_raw();
72 }
73
74 int replay_get_instructions(void)
75 {
76 int res = 0;
77 replay_mutex_lock();
78 if (replay_next_event_is(EVENT_INSTRUCTION)) {
79 res = replay_state.instruction_count;
80 if (replay_break_icount != -1LL) {
81 uint64_t current = replay_get_current_icount();
82 assert(replay_break_icount >= current);
83 if (current + res > replay_break_icount) {
84 res = replay_break_icount - current;
85 }
86 }
87 }
88 replay_mutex_unlock();
89 return res;
90 }
91
92 void replay_account_executed_instructions(void)
93 {
94 if (replay_mode == REPLAY_MODE_PLAY) {
95 g_assert(replay_mutex_locked());
96 if (replay_state.instruction_count > 0) {
97 replay_advance_current_icount(replay_get_current_icount());
98 }
99 }
100 }
101
102 bool replay_exception(void)
103 {
104
105 if (replay_mode == REPLAY_MODE_RECORD) {
106 g_assert(replay_mutex_locked());
107 replay_save_instructions();
108 replay_put_event(EVENT_EXCEPTION);
109 return true;
110 } else if (replay_mode == REPLAY_MODE_PLAY) {
111 g_assert(replay_mutex_locked());
112 bool res = replay_has_exception();
113 if (res) {
114 replay_finish_event();
115 }
116 return res;
117 }
118
119 return true;
120 }
121
122 bool replay_has_exception(void)
123 {
124 bool res = false;
125 if (replay_mode == REPLAY_MODE_PLAY) {
126 g_assert(replay_mutex_locked());
127 replay_account_executed_instructions();
128 res = replay_next_event_is(EVENT_EXCEPTION);
129 }
130
131 return res;
132 }
133
134 bool replay_interrupt(void)
135 {
136 if (replay_mode == REPLAY_MODE_RECORD) {
137 g_assert(replay_mutex_locked());
138 replay_save_instructions();
139 replay_put_event(EVENT_INTERRUPT);
140 return true;
141 } else if (replay_mode == REPLAY_MODE_PLAY) {
142 g_assert(replay_mutex_locked());
143 bool res = replay_has_interrupt();
144 if (res) {
145 replay_finish_event();
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 g_assert(replay_mutex_locked());
158 replay_account_executed_instructions();
159 res = replay_next_event_is(EVENT_INTERRUPT);
160 }
161 return res;
162 }
163
164 void replay_shutdown_request(ShutdownCause cause)
165 {
166 if (replay_mode == REPLAY_MODE_RECORD) {
167 g_assert(replay_mutex_locked());
168 replay_put_event(EVENT_SHUTDOWN + cause);
169 }
170 }
171
172 bool replay_checkpoint(ReplayCheckpoint checkpoint)
173 {
174 bool res = false;
175 static bool in_checkpoint;
176 assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
177
178 if (!replay_file) {
179 return true;
180 }
181
182 if (in_checkpoint) {
183 /*
184 Recursion occurs when HW event modifies timers.
185 Prevent performing icount warp in this case and
186 wait for another invocation of the checkpoint.
187 */
188 g_assert(replay_mode == REPLAY_MODE_PLAY);
189 return false;
190 }
191 in_checkpoint = true;
192
193 replay_save_instructions();
194
195 if (replay_mode == REPLAY_MODE_PLAY) {
196 g_assert(replay_mutex_locked());
197 if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
198 replay_finish_event();
199 } else if (replay_state.data_kind != EVENT_ASYNC) {
200 res = false;
201 goto out;
202 }
203 replay_read_events(checkpoint);
204 /* replay_read_events may leave some unread events.
205 Return false if not all of the events associated with
206 checkpoint were processed */
207 res = replay_state.data_kind != EVENT_ASYNC;
208 } else if (replay_mode == REPLAY_MODE_RECORD) {
209 g_assert(replay_mutex_locked());
210 replay_put_event(EVENT_CHECKPOINT + checkpoint);
211 /* This checkpoint belongs to several threads.
212 Processing events from different threads is
213 non-deterministic */
214 if (checkpoint != CHECKPOINT_CLOCK_WARP_START
215 /* FIXME: this is temporary fix, other checkpoints
216 may also be invoked from the different threads someday.
217 Asynchronous event processing should be refactored
218 to create additional replay event kind which is
219 nailed to the one of the threads and which processes
220 the event queue. */
221 && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
222 replay_save_events(checkpoint);
223 }
224 res = true;
225 }
226 out:
227 in_checkpoint = false;
228 return res;
229 }
230
231 bool replay_has_checkpoint(void)
232 {
233 bool res = false;
234 if (replay_mode == REPLAY_MODE_PLAY) {
235 g_assert(replay_mutex_locked());
236 replay_account_executed_instructions();
237 res = EVENT_CHECKPOINT <= replay_state.data_kind
238 && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
239 }
240 return res;
241 }
242
243 static void replay_enable(const char *fname, int mode)
244 {
245 const char *fmode = NULL;
246 assert(!replay_file);
247
248 switch (mode) {
249 case REPLAY_MODE_RECORD:
250 fmode = "wb";
251 break;
252 case REPLAY_MODE_PLAY:
253 fmode = "rb";
254 break;
255 default:
256 fprintf(stderr, "Replay: internal error: invalid replay mode\n");
257 exit(1);
258 }
259
260 atexit(replay_finish);
261
262 replay_file = fopen(fname, fmode);
263 if (replay_file == NULL) {
264 fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
265 exit(1);
266 }
267
268 replay_filename = g_strdup(fname);
269 replay_mode = mode;
270 replay_mutex_init();
271
272 replay_state.data_kind = -1;
273 replay_state.instruction_count = 0;
274 replay_state.current_icount = 0;
275 replay_state.has_unread_data = 0;
276
277 /* skip file header for RECORD and check it for PLAY */
278 if (replay_mode == REPLAY_MODE_RECORD) {
279 fseek(replay_file, HEADER_SIZE, SEEK_SET);
280 } else if (replay_mode == REPLAY_MODE_PLAY) {
281 unsigned int version = replay_get_dword();
282 if (version != REPLAY_VERSION) {
283 fprintf(stderr, "Replay: invalid input log file version\n");
284 exit(1);
285 }
286 /* go to the beginning */
287 fseek(replay_file, HEADER_SIZE, SEEK_SET);
288 replay_fetch_data_kind();
289 }
290
291 replay_init_events();
292 }
293
294 void replay_configure(QemuOpts *opts)
295 {
296 const char *fname;
297 const char *rr;
298 ReplayMode mode = REPLAY_MODE_NONE;
299 Location loc;
300
301 if (!opts) {
302 return;
303 }
304
305 loc_push_none(&loc);
306 qemu_opts_loc_restore(opts);
307
308 rr = qemu_opt_get(opts, "rr");
309 if (!rr) {
310 /* Just enabling icount */
311 goto out;
312 } else if (!strcmp(rr, "record")) {
313 mode = REPLAY_MODE_RECORD;
314 } else if (!strcmp(rr, "replay")) {
315 mode = REPLAY_MODE_PLAY;
316 } else {
317 error_report("Invalid icount rr option: %s", rr);
318 exit(1);
319 }
320
321 fname = qemu_opt_get(opts, "rrfile");
322 if (!fname) {
323 error_report("File name not specified for replay");
324 exit(1);
325 }
326
327 replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
328 replay_vmstate_register();
329 replay_enable(fname, mode);
330
331 out:
332 loc_pop(&loc);
333 }
334
335 void replay_start(void)
336 {
337 if (replay_mode == REPLAY_MODE_NONE) {
338 return;
339 }
340
341 if (replay_blockers) {
342 error_reportf_err(replay_blockers->data, "Record/replay: ");
343 exit(1);
344 }
345 if (!icount_enabled()) {
346 error_report("Please enable icount to use record/replay");
347 exit(1);
348 }
349
350 /* Timer for snapshotting will be set up here. */
351
352 replay_enable_events();
353 }
354
355 void replay_finish(void)
356 {
357 if (replay_mode == REPLAY_MODE_NONE) {
358 return;
359 }
360
361 replay_save_instructions();
362
363 /* finalize the file */
364 if (replay_file) {
365 if (replay_mode == REPLAY_MODE_RECORD) {
366 /*
367 * Can't do it in the signal handler, therefore
368 * add shutdown event here for the case of Ctrl-C.
369 */
370 replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
371 /* write end event */
372 replay_put_event(EVENT_END);
373
374 /* write header */
375 fseek(replay_file, 0, SEEK_SET);
376 replay_put_dword(REPLAY_VERSION);
377 }
378
379 fclose(replay_file);
380 replay_file = NULL;
381 }
382 if (replay_filename) {
383 g_free(replay_filename);
384 replay_filename = NULL;
385 }
386
387 g_free(replay_snapshot);
388 replay_snapshot = NULL;
389
390 replay_mode = REPLAY_MODE_NONE;
391
392 replay_finish_events();
393 }
394
395 void replay_add_blocker(Error *reason)
396 {
397 replay_blockers = g_slist_prepend(replay_blockers, reason);
398 }
399
400 const char *replay_get_filename(void)
401 {
402 return replay_filename;
403 }