blob: 83ed9e0e24ae1dfe447fdb3cd1861ae471d399c7 [file] [log] [blame]
Pavel Dovgalyukd73abd62015-09-17 19:23:37 +03001/*
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
Peter Maydelld38ea872016-01-29 17:50:05 +000012#include "qemu/osdep.h"
Markus Armbrusterda34e652016-03-14 09:01:28 +010013#include "qapi/error.h"
Pavel Dovgalyukd73abd62015-09-17 19:23:37 +030014#include "sysemu/replay.h"
Markus Armbruster54d31232019-08-12 07:23:59 +020015#include "sysemu/runstate.h"
Pavel Dovgalyuk26bc60a2015-09-17 19:23:54 +030016#include "replay-internal.h"
17#include "qemu/timer.h"
Pavel Dovgalyuk8b427042015-09-17 19:24:05 +030018#include "qemu/main-loop.h"
Markus Armbruster922a01a2018-02-01 12:18:46 +010019#include "qemu/option.h"
Paolo Bonzinid2528bd2017-03-03 12:01:16 +010020#include "sysemu/cpus.h"
Pavel Dovgalyuk76159362015-09-17 19:25:07 +030021#include "qemu/error-report.h"
22
23/* Current version of the replay mechanism.
24 Increase it when file format changes. */
Pavel Dovgalyuk677a3ba2020-05-22 09:35:27 +030025#define REPLAY_VERSION 0xe0200a
Pavel Dovgalyuk76159362015-09-17 19:25:07 +030026/* Size of replay log header */
27#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
Pavel Dovgalyukd73abd62015-09-17 19:23:37 +030028
29ReplayMode replay_mode = REPLAY_MODE_NONE;
Pavel Dovgalyuk9c2037d2017-01-24 10:17:47 +030030char *replay_snapshot;
Pavel Dovgalyuk26bc60a2015-09-17 19:23:54 +030031
Pavel Dovgalyuk76159362015-09-17 19:25:07 +030032/* Name of replay file */
33static char *replay_filename;
Pavel Dovgalyuk26bc60a2015-09-17 19:23:54 +030034ReplayState replay_state;
Pavel Dovgalyuk01947492015-09-17 19:25:13 +030035static GSList *replay_blockers;
Pavel Dovgalyuk26bc60a2015-09-17 19:23:54 +030036
37bool replay_next_event_is(int event)
38{
39 bool res = false;
40
41 /* nothing to skip - not all instructions used */
Pavel Dovgalyuk13f26712019-07-25 11:44:43 +030042 if (replay_state.instruction_count != 0) {
Pavel Dovgalyukf186d642016-09-26 11:08:04 +030043 assert(replay_state.data_kind == EVENT_INSTRUCTION);
Pavel Dovgalyuk26bc60a2015-09-17 19:23:54 +030044 return event == EVENT_INSTRUCTION;
45 }
46
47 while (true) {
Pavel Dovgalyuke957ad82019-07-25 11:44:32 +030048 unsigned int data_kind = replay_state.data_kind;
49 if (event == data_kind) {
Pavel Dovgalyuk26bc60a2015-09-17 19:23:54 +030050 res = true;
51 }
Pavel Dovgalyuke957ad82019-07-25 11:44:32 +030052 switch (data_kind) {
Eric Blake802f0452017-05-15 16:41:12 -050053 case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST:
Pavel Dovgalyukb60c48a2015-09-17 19:24:33 +030054 replay_finish_event();
Pavel Dovgalyuke957ad82019-07-25 11:44:32 +030055 qemu_system_shutdown_request(data_kind - EVENT_SHUTDOWN);
Pavel Dovgalyukb60c48a2015-09-17 19:24:33 +030056 break;
Pavel Dovgalyuk26bc60a2015-09-17 19:23:54 +030057 default:
58 /* clock, time_t, checkpoint and other events */
59 return res;
60 }
61 }
62 return res;
63}
64
Pavel Dovgalyuk13f26712019-07-25 11:44:43 +030065uint64_t replay_get_current_icount(void)
Pavel Dovgalyuk26bc60a2015-09-17 19:23:54 +030066{
67 return cpu_get_icount_raw();
68}
Pavel Dovgalyuk8b427042015-09-17 19:24:05 +030069
70int replay_get_instructions(void)
71{
72 int res = 0;
73 replay_mutex_lock();
74 if (replay_next_event_is(EVENT_INSTRUCTION)) {
Pavel Dovgalyuk13f26712019-07-25 11:44:43 +030075 res = replay_state.instruction_count;
Pavel Dovgalyuk8b427042015-09-17 19:24:05 +030076 }
77 replay_mutex_unlock();
78 return res;
79}
80
81void replay_account_executed_instructions(void)
82{
83 if (replay_mode == REPLAY_MODE_PLAY) {
Alex Bennéed759c952018-02-27 12:52:48 +030084 g_assert(replay_mutex_locked());
Pavel Dovgalyuk13f26712019-07-25 11:44:43 +030085 if (replay_state.instruction_count > 0) {
86 int count = (int)(replay_get_current_icount()
87 - replay_state.current_icount);
Alex Bennée982263c2017-04-05 11:05:28 +010088
89 /* Time can only go forward */
90 assert(count >= 0);
91
Pavel Dovgalyuk13f26712019-07-25 11:44:43 +030092 replay_state.instruction_count -= count;
93 replay_state.current_icount += count;
94 if (replay_state.instruction_count == 0) {
Pavel Dovgalyukf186d642016-09-26 11:08:04 +030095 assert(replay_state.data_kind == EVENT_INSTRUCTION);
Pavel Dovgalyuk8b427042015-09-17 19:24:05 +030096 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 }
Pavel Dovgalyuk8b427042015-09-17 19:24:05 +0300103 }
104}
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300105
106bool replay_exception(void)
107{
Alex Bennéed759c952018-02-27 12:52:48 +0300108
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300109 if (replay_mode == REPLAY_MODE_RECORD) {
Alex Bennéed759c952018-02-27 12:52:48 +0300110 g_assert(replay_mutex_locked());
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300111 replay_save_instructions();
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300112 replay_put_event(EVENT_EXCEPTION);
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300113 return true;
114 } else if (replay_mode == REPLAY_MODE_PLAY) {
Alex Bennéed759c952018-02-27 12:52:48 +0300115 g_assert(replay_mutex_locked());
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300116 bool res = replay_has_exception();
117 if (res) {
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300118 replay_finish_event();
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300119 }
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) {
Alex Bennéed759c952018-02-27 12:52:48 +0300130 g_assert(replay_mutex_locked());
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300131 replay_account_executed_instructions();
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300132 res = replay_next_event_is(EVENT_EXCEPTION);
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300133 }
134
135 return res;
136}
137
138bool replay_interrupt(void)
139{
140 if (replay_mode == REPLAY_MODE_RECORD) {
Alex Bennéed759c952018-02-27 12:52:48 +0300141 g_assert(replay_mutex_locked());
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300142 replay_save_instructions();
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300143 replay_put_event(EVENT_INTERRUPT);
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300144 return true;
145 } else if (replay_mode == REPLAY_MODE_PLAY) {
Alex Bennéed759c952018-02-27 12:52:48 +0300146 g_assert(replay_mutex_locked());
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300147 bool res = replay_has_interrupt();
148 if (res) {
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300149 replay_finish_event();
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300150 }
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) {
Alex Bennéed759c952018-02-27 12:52:48 +0300161 g_assert(replay_mutex_locked());
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300162 replay_account_executed_instructions();
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300163 res = replay_next_event_is(EVENT_INTERRUPT);
Pavel Dovgalyuk6f060962015-09-17 19:24:16 +0300164 }
165 return res;
166}
Pavel Dovgalyukb60c48a2015-09-17 19:24:33 +0300167
Eric Blake802f0452017-05-15 16:41:12 -0500168void replay_shutdown_request(ShutdownCause cause)
Pavel Dovgalyukb60c48a2015-09-17 19:24:33 +0300169{
170 if (replay_mode == REPLAY_MODE_RECORD) {
Alex Bennéed759c952018-02-27 12:52:48 +0300171 g_assert(replay_mutex_locked());
Eric Blake802f0452017-05-15 16:41:12 -0500172 replay_put_event(EVENT_SHUTDOWN + cause);
Pavel Dovgalyukb60c48a2015-09-17 19:24:33 +0300173 }
174}
Pavel Dovgalyuk8bd7f712015-09-17 19:24:44 +0300175
176bool replay_checkpoint(ReplayCheckpoint checkpoint)
177{
178 bool res = false;
Pavel Dovgalyuk66eb7822018-02-27 12:53:05 +0300179 static bool in_checkpoint;
Pavel Dovgalyuk8bd7f712015-09-17 19:24:44 +0300180 assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
Pavel Dovgalyuk8bd7f712015-09-17 19:24:44 +0300181
182 if (!replay_file) {
183 return true;
184 }
185
Pavel Dovgalyuk66eb7822018-02-27 12:53:05 +0300186 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();
Pavel Dovgalyuk8bd7f712015-09-17 19:24:44 +0300197
198 if (replay_mode == REPLAY_MODE_PLAY) {
Alex Bennéed759c952018-02-27 12:52:48 +0300199 g_assert(replay_mutex_locked());
Pavel Dovgalyuk8bd7f712015-09-17 19:24:44 +0300200 if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
201 replay_finish_event();
Pavel Dovgalyukf186d642016-09-26 11:08:04 +0300202 } else if (replay_state.data_kind != EVENT_ASYNC) {
Pavel Dovgalyuk8bd7f712015-09-17 19:24:44 +0300203 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 */
Pavel Dovgalyukf186d642016-09-26 11:08:04 +0300210 res = replay_state.data_kind != EVENT_ASYNC;
Pavel Dovgalyuk8bd7f712015-09-17 19:24:44 +0300211 } else if (replay_mode == REPLAY_MODE_RECORD) {
Alex Bennéed759c952018-02-27 12:52:48 +0300212 g_assert(replay_mutex_locked());
Pavel Dovgalyuk8bd7f712015-09-17 19:24:44 +0300213 replay_put_event(EVENT_CHECKPOINT + checkpoint);
Pavel Dovgalyuk89e46eb2018-02-27 12:53:16 +0300214 /* This checkpoint belongs to several threads.
215 Processing events from different threads is
216 non-deterministic */
Pavel Dovgalyukca9759c2018-10-18 09:33:45 +0300217 if (checkpoint != CHECKPOINT_CLOCK_WARP_START
218 /* FIXME: this is temporary fix, other checkpoints
219 may also be invoked from the different threads someday.
220 Asynchronous event processing should be refactored
221 to create additional replay event kind which is
222 nailed to the one of the threads and which processes
223 the event queue. */
224 && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
Pavel Dovgalyuk89e46eb2018-02-27 12:53:16 +0300225 replay_save_events(checkpoint);
226 }
Pavel Dovgalyuk8bd7f712015-09-17 19:24:44 +0300227 res = true;
228 }
229out:
Pavel Dovgalyuk66eb7822018-02-27 12:53:05 +0300230 in_checkpoint = false;
Pavel Dovgalyuk8bd7f712015-09-17 19:24:44 +0300231 return res;
232}
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300233
Pavel Dovgalyuk0c081852018-09-12 11:19:45 +0300234bool 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
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300246static 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
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300265 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);
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300272 replay_mode = mode;
Alex Bennéed759c952018-02-27 12:52:48 +0300273 replay_mutex_init();
274
Pavel Dovgalyukf186d642016-09-26 11:08:04 +0300275 replay_state.data_kind = -1;
Pavel Dovgalyuk13f26712019-07-25 11:44:43 +0300276 replay_state.instruction_count = 0;
277 replay_state.current_icount = 0;
Pavel Dovgalyukf186d642016-09-26 11:08:04 +0300278 replay_state.has_unread_data = 0;
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300279
280 /* skip file header for RECORD and check it for PLAY */
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 /* go to the beginning */
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;
Eduardo Habkost890ad552016-02-12 17:02:26 -0200302 Location loc;
303
304 if (!opts) {
305 return;
306 }
307
308 loc_push_none(&loc);
309 qemu_opts_loc_restore(opts);
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300310
311 rr = qemu_opt_get(opts, "rr");
312 if (!rr) {
313 /* Just enabling icount */
Markus Armbrusterd9d3aae2016-04-27 16:29:08 +0200314 goto out;
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300315 } 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
Pavel Dovgalyuk9c2037d2017-01-24 10:17:47 +0300330 replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
Pavel Dovgalyuk306e1962016-09-26 11:08:10 +0300331 replay_vmstate_register();
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300332 replay_enable(fname, mode);
Eduardo Habkost890ad552016-02-12 17:02:26 -0200333
Markus Armbrusterd9d3aae2016-04-27 16:29:08 +0200334out:
Eduardo Habkost890ad552016-02-12 17:02:26 -0200335 loc_pop(&loc);
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300336}
337
338void replay_start(void)
339{
340 if (replay_mode == REPLAY_MODE_NONE) {
341 return;
342 }
343
Pavel Dovgalyuk01947492015-09-17 19:25:13 +0300344 if (replay_blockers) {
Markus Armbrusterc29b77f2015-12-18 16:35:14 +0100345 error_reportf_err(replay_blockers->data, "Record/replay: ");
Pavel Dovgalyuk01947492015-09-17 19:25:13 +0300346 exit(1);
347 }
Pavel Dovgalyuk4c27b852015-09-17 19:25:18 +0300348 if (!use_icount) {
349 error_report("Please enable icount to use record/replay");
350 exit(1);
351 }
Pavel Dovgalyuk01947492015-09-17 19:25:13 +0300352
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300353 /* Timer for snapshotting will be set up here. */
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 /* finalize the file */
367 if (replay_file) {
368 if (replay_mode == REPLAY_MODE_RECORD) {
Pavel Dovgalyuked5d7ff2020-05-22 09:45:54 +0300369 /*
370 * Can't do it in the signal handler, therefore
371 * add shutdown event here for the case of Ctrl-C.
372 */
373 replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300374 /* write end event */
375 replay_put_event(EVENT_END);
376
377 /* write header */
378 fseek(replay_file, 0, SEEK_SET);
379 replay_put_dword(REPLAY_VERSION);
380 }
381
382 fclose(replay_file);
383 replay_file = NULL;
384 }
385 if (replay_filename) {
386 g_free(replay_filename);
387 replay_filename = NULL;
388 }
389
Pavel Dovgalyuk9c2037d2017-01-24 10:17:47 +0300390 g_free(replay_snapshot);
391 replay_snapshot = NULL;
392
Pavel Dovgalyukae25dcc2019-09-17 14:58:13 +0300393 replay_mode = REPLAY_MODE_NONE;
394
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300395 replay_finish_events();
Pavel Dovgalyuk76159362015-09-17 19:25:07 +0300396}
Pavel Dovgalyuk01947492015-09-17 19:25:13 +0300397
398void replay_add_blocker(Error *reason)
399{
400 replay_blockers = g_slist_prepend(replay_blockers, reason);
401}