Merge remote-tracking branch 'remotes/nvme/tags/nvme-fixes-20210407-pull-request...
[qemu/ar7.git] / replay / replay.c
blob6df2abc18c7ff3e0a757ac0e5c01a6bb3ec32f6a
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  */
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"
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))
29 ReplayMode replay_mode = REPLAY_MODE_NONE;
30 char *replay_snapshot;
32 /* Name of replay file  */
33 static char *replay_filename;
34 ReplayState replay_state;
35 static GSList *replay_blockers;
37 /* Replay breakpoints */
38 uint64_t replay_break_icount = -1ULL;
39 QEMUTimer *replay_break_timer;
41 bool replay_next_event_is(int event)
43     bool res = false;
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     }
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;
69 uint64_t replay_get_current_icount(void)
71     return icount_get_raw();
74 int replay_get_instructions(void)
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;
92 void replay_account_executed_instructions(void)
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     }
102 bool replay_exception(void)
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     }
119     return true;
122 bool replay_has_exception(void)
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     }
131     return res;
134 bool replay_interrupt(void)
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     }
150     return true;
153 bool replay_has_interrupt(void)
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;
164 void replay_shutdown_request(ShutdownCause cause)
166     if (replay_mode == REPLAY_MODE_RECORD) {
167         g_assert(replay_mutex_locked());
168         replay_put_event(EVENT_SHUTDOWN + cause);
169     }
172 bool replay_checkpoint(ReplayCheckpoint checkpoint)
174     bool res = false;
175     static bool in_checkpoint;
176     assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
178     if (!replay_file) {
179         return true;
180     }
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;
193     replay_save_instructions();
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;
231 bool replay_has_checkpoint(void)
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;
243 static void replay_enable(const char *fname, int mode)
245     const char *fmode = NULL;
246     assert(!replay_file);
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     }
260     atexit(replay_finish);
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     }
268     replay_filename = g_strdup(fname);
269     replay_mode = mode;
270     replay_mutex_init();
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;
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     }
291     replay_init_events();
294 void replay_configure(QemuOpts *opts)
296     const char *fname;
297     const char *rr;
298     ReplayMode mode = REPLAY_MODE_NONE;
299     Location loc;
301     if (!opts) {
302         return;
303     }
305     loc_push_none(&loc);
306     qemu_opts_loc_restore(opts);
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     }
321     fname = qemu_opt_get(opts, "rrfile");
322     if (!fname) {
323         error_report("File name not specified for replay");
324         exit(1);
325     }
327     replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
328     replay_vmstate_register();
329     replay_enable(fname, mode);
331 out:
332     loc_pop(&loc);
335 void replay_start(void)
337     if (replay_mode == REPLAY_MODE_NONE) {
338         return;
339     }
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     }
350     /* Timer for snapshotting will be set up here. */
352     replay_enable_events();
355 void replay_finish(void)
357     if (replay_mode == REPLAY_MODE_NONE) {
358         return;
359     }
361     replay_save_instructions();
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);
374             /* write header */
375             fseek(replay_file, 0, SEEK_SET);
376             replay_put_dword(REPLAY_VERSION);
377         }
379         fclose(replay_file);
380         replay_file = NULL;
381     }
382     if (replay_filename) {
383         g_free(replay_filename);
384         replay_filename = NULL;
385     }
387     g_free(replay_snapshot);
388     replay_snapshot = NULL;
390     replay_mode = REPLAY_MODE_NONE;
392     replay_finish_events();
395 void replay_add_blocker(Error *reason)
397     replay_blockers = g_slist_prepend(replay_blockers, reason);
400 const char *replay_get_filename(void)
402     return replay_filename;