1
2
3
4
5
6
7
8
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
24
25#define REPLAY_VERSION 0xe02007
26
27#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
28
29ReplayMode replay_mode = REPLAY_MODE_NONE;
30char *replay_snapshot;
31
32
33static char *replay_filename;
34ReplayState replay_state;
35static GSList *replay_blockers;
36
37bool replay_next_event_is(int event)
38{
39 bool res = false;
40
41
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
59 return res;
60 }
61 }
62 return res;
63}
64
65uint64_t replay_get_current_step(void)
66{
67 return cpu_get_icount_raw();
68}
69
70int 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
81void 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
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
98
99
100 qemu_notify_event();
101 }
102 }
103 }
104}
105
106bool 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
126bool 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
138bool 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
157bool 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
168void 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
176bool 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
188
189
190
191
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
208
209
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
215
216
217 if (checkpoint != CHECKPOINT_CLOCK_WARP_START
218
219
220
221
222
223
224 && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
225 replay_save_events(checkpoint);
226 }
227 res = true;
228 }
229out:
230 in_checkpoint = false;
231 return res;
232}
233
234bool replay_has_checkpoint(void)
235{
236 bool res = false;
237 if (replay_mode == REPLAY_MODE_PLAY) {
238 g_assert(replay_mutex_locked());
239 replay_account_executed_instructions();
240 res = EVENT_CHECKPOINT <= replay_state.data_kind
241 && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
242 }
243 return res;
244}
245
246static void replay_enable(const char *fname, int mode)
247{
248 const char *fmode = NULL;
249 assert(!replay_file);
250
251 switch (mode) {
252 case REPLAY_MODE_RECORD:
253 fmode = "wb";
254 break;
255 case REPLAY_MODE_PLAY:
256 fmode = "rb";
257 break;
258 default:
259 fprintf(stderr, "Replay: internal error: invalid replay mode\n");
260 exit(1);
261 }
262
263 atexit(replay_finish);
264
265 replay_file = fopen(fname, fmode);
266 if (replay_file == NULL) {
267 fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
268 exit(1);
269 }
270
271 replay_filename = g_strdup(fname);
272 replay_mode = mode;
273 replay_mutex_init();
274
275 replay_state.data_kind = -1;
276 replay_state.instructions_count = 0;
277 replay_state.current_step = 0;
278 replay_state.has_unread_data = 0;
279
280
281 if (replay_mode == REPLAY_MODE_RECORD) {
282 fseek(replay_file, HEADER_SIZE, SEEK_SET);
283 } else if (replay_mode == REPLAY_MODE_PLAY) {
284 unsigned int version = replay_get_dword();
285 if (version != REPLAY_VERSION) {
286 fprintf(stderr, "Replay: invalid input log file version\n");
287 exit(1);
288 }
289
290 fseek(replay_file, HEADER_SIZE, SEEK_SET);
291 replay_fetch_data_kind();
292 }
293
294 replay_init_events();
295}
296
297void replay_configure(QemuOpts *opts)
298{
299 const char *fname;
300 const char *rr;
301 ReplayMode mode = REPLAY_MODE_NONE;
302 Location loc;
303
304 if (!opts) {
305 return;
306 }
307
308 loc_push_none(&loc);
309 qemu_opts_loc_restore(opts);
310
311 rr = qemu_opt_get(opts, "rr");
312 if (!rr) {
313
314 goto out;
315 } else if (!strcmp(rr, "record")) {
316 mode = REPLAY_MODE_RECORD;
317 } else if (!strcmp(rr, "replay")) {
318 mode = REPLAY_MODE_PLAY;
319 } else {
320 error_report("Invalid icount rr option: %s", rr);
321 exit(1);
322 }
323
324 fname = qemu_opt_get(opts, "rrfile");
325 if (!fname) {
326 error_report("File name not specified for replay");
327 exit(1);
328 }
329
330 replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
331 replay_vmstate_register();
332 replay_enable(fname, mode);
333
334out:
335 loc_pop(&loc);
336}
337
338void replay_start(void)
339{
340 if (replay_mode == REPLAY_MODE_NONE) {
341 return;
342 }
343
344 if (replay_blockers) {
345 error_reportf_err(replay_blockers->data, "Record/replay: ");
346 exit(1);
347 }
348 if (!use_icount) {
349 error_report("Please enable icount to use record/replay");
350 exit(1);
351 }
352
353
354
355 replay_enable_events();
356}
357
358void replay_finish(void)
359{
360 if (replay_mode == REPLAY_MODE_NONE) {
361 return;
362 }
363
364 replay_save_instructions();
365
366
367 if (replay_file) {
368 if (replay_mode == REPLAY_MODE_RECORD) {
369
370 replay_put_event(EVENT_END);
371
372
373 fseek(replay_file, 0, SEEK_SET);
374 replay_put_dword(REPLAY_VERSION);
375 }
376
377 fclose(replay_file);
378 replay_file = NULL;
379 }
380 if (replay_filename) {
381 g_free(replay_filename);
382 replay_filename = NULL;
383 }
384
385 g_free(replay_snapshot);
386 replay_snapshot = NULL;
387
388 replay_finish_events();
389}
390
391void replay_add_blocker(Error *reason)
392{
393 replay_blockers = g_slist_prepend(replay_blockers, reason);
394}
395