1d73abd6dSPavel Dovgalyuk /* 2d73abd6dSPavel Dovgalyuk * replay.c 3d73abd6dSPavel Dovgalyuk * 4d73abd6dSPavel Dovgalyuk * Copyright (c) 2010-2015 Institute for System Programming 5d73abd6dSPavel Dovgalyuk * of the Russian Academy of Sciences. 6d73abd6dSPavel Dovgalyuk * 7d73abd6dSPavel Dovgalyuk * This work is licensed under the terms of the GNU GPL, version 2 or later. 8d73abd6dSPavel Dovgalyuk * See the COPYING file in the top-level directory. 9d73abd6dSPavel Dovgalyuk * 10d73abd6dSPavel Dovgalyuk */ 11d73abd6dSPavel Dovgalyuk 12d38ea87aSPeter Maydell #include "qemu/osdep.h" 13da34e65cSMarkus Armbruster #include "qapi/error.h" 14d73abd6dSPavel Dovgalyuk #include "sysemu/replay.h" 1554d31236SMarkus Armbruster #include "sysemu/runstate.h" 1626bc60acSPavel Dovgalyuk #include "replay-internal.h" 1726bc60acSPavel Dovgalyuk #include "qemu/timer.h" 188b427044SPavel Dovgalyuk #include "qemu/main-loop.h" 19922a01a0SMarkus Armbruster #include "qemu/option.h" 20d2528bdcSPaolo Bonzini #include "sysemu/cpus.h" 217615936eSPavel Dovgalyuk #include "qemu/error-report.h" 227615936eSPavel Dovgalyuk 237615936eSPavel Dovgalyuk /* Current version of the replay mechanism. 247615936eSPavel Dovgalyuk Increase it when file format changes. */ 25*677a3babSPavel Dovgalyuk #define REPLAY_VERSION 0xe0200a 267615936eSPavel Dovgalyuk /* Size of replay log header */ 277615936eSPavel Dovgalyuk #define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t)) 28d73abd6dSPavel Dovgalyuk 29d73abd6dSPavel Dovgalyuk ReplayMode replay_mode = REPLAY_MODE_NONE; 309c2037d0SPavel Dovgalyuk char *replay_snapshot; 3126bc60acSPavel Dovgalyuk 327615936eSPavel Dovgalyuk /* Name of replay file */ 337615936eSPavel Dovgalyuk static char *replay_filename; 3426bc60acSPavel Dovgalyuk ReplayState replay_state; 350194749aSPavel Dovgalyuk static GSList *replay_blockers; 3626bc60acSPavel Dovgalyuk 3726bc60acSPavel Dovgalyuk bool replay_next_event_is(int event) 3826bc60acSPavel Dovgalyuk { 3926bc60acSPavel Dovgalyuk bool res = false; 4026bc60acSPavel Dovgalyuk 4126bc60acSPavel Dovgalyuk /* nothing to skip - not all instructions used */ 4213f26713SPavel Dovgalyuk if (replay_state.instruction_count != 0) { 43f186d64dSPavel Dovgalyuk assert(replay_state.data_kind == EVENT_INSTRUCTION); 4426bc60acSPavel Dovgalyuk return event == EVENT_INSTRUCTION; 4526bc60acSPavel Dovgalyuk } 4626bc60acSPavel Dovgalyuk 4726bc60acSPavel Dovgalyuk while (true) { 48e957ad8aSPavel Dovgalyuk unsigned int data_kind = replay_state.data_kind; 49e957ad8aSPavel Dovgalyuk if (event == data_kind) { 5026bc60acSPavel Dovgalyuk res = true; 5126bc60acSPavel Dovgalyuk } 52e957ad8aSPavel Dovgalyuk switch (data_kind) { 53802f045aSEric Blake case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST: 54b60c48a7SPavel Dovgalyuk replay_finish_event(); 55e957ad8aSPavel Dovgalyuk qemu_system_shutdown_request(data_kind - EVENT_SHUTDOWN); 56b60c48a7SPavel Dovgalyuk break; 5726bc60acSPavel Dovgalyuk default: 5826bc60acSPavel Dovgalyuk /* clock, time_t, checkpoint and other events */ 5926bc60acSPavel Dovgalyuk return res; 6026bc60acSPavel Dovgalyuk } 6126bc60acSPavel Dovgalyuk } 6226bc60acSPavel Dovgalyuk return res; 6326bc60acSPavel Dovgalyuk } 6426bc60acSPavel Dovgalyuk 6513f26713SPavel Dovgalyuk uint64_t replay_get_current_icount(void) 6626bc60acSPavel Dovgalyuk { 6726bc60acSPavel Dovgalyuk return cpu_get_icount_raw(); 6826bc60acSPavel Dovgalyuk } 698b427044SPavel Dovgalyuk 708b427044SPavel Dovgalyuk int replay_get_instructions(void) 718b427044SPavel Dovgalyuk { 728b427044SPavel Dovgalyuk int res = 0; 738b427044SPavel Dovgalyuk replay_mutex_lock(); 748b427044SPavel Dovgalyuk if (replay_next_event_is(EVENT_INSTRUCTION)) { 7513f26713SPavel Dovgalyuk res = replay_state.instruction_count; 768b427044SPavel Dovgalyuk } 778b427044SPavel Dovgalyuk replay_mutex_unlock(); 788b427044SPavel Dovgalyuk return res; 798b427044SPavel Dovgalyuk } 808b427044SPavel Dovgalyuk 818b427044SPavel Dovgalyuk void replay_account_executed_instructions(void) 828b427044SPavel Dovgalyuk { 838b427044SPavel Dovgalyuk if (replay_mode == REPLAY_MODE_PLAY) { 84d759c951SAlex Bennée g_assert(replay_mutex_locked()); 8513f26713SPavel Dovgalyuk if (replay_state.instruction_count > 0) { 8613f26713SPavel Dovgalyuk int count = (int)(replay_get_current_icount() 8713f26713SPavel Dovgalyuk - replay_state.current_icount); 88982263ceSAlex Bennée 89982263ceSAlex Bennée /* Time can only go forward */ 90982263ceSAlex Bennée assert(count >= 0); 91982263ceSAlex Bennée 9213f26713SPavel Dovgalyuk replay_state.instruction_count -= count; 9313f26713SPavel Dovgalyuk replay_state.current_icount += count; 9413f26713SPavel Dovgalyuk if (replay_state.instruction_count == 0) { 95f186d64dSPavel Dovgalyuk assert(replay_state.data_kind == EVENT_INSTRUCTION); 968b427044SPavel Dovgalyuk replay_finish_event(); 978b427044SPavel Dovgalyuk /* Wake up iothread. This is required because 988b427044SPavel Dovgalyuk timers will not expire until clock counters 998b427044SPavel Dovgalyuk will be read from the log. */ 1008b427044SPavel Dovgalyuk qemu_notify_event(); 1018b427044SPavel Dovgalyuk } 1028b427044SPavel Dovgalyuk } 1038b427044SPavel Dovgalyuk } 1048b427044SPavel Dovgalyuk } 1056f060969SPavel Dovgalyuk 1066f060969SPavel Dovgalyuk bool replay_exception(void) 1076f060969SPavel Dovgalyuk { 108d759c951SAlex Bennée 1096f060969SPavel Dovgalyuk if (replay_mode == REPLAY_MODE_RECORD) { 110d759c951SAlex Bennée g_assert(replay_mutex_locked()); 1116f060969SPavel Dovgalyuk replay_save_instructions(); 1126f060969SPavel Dovgalyuk replay_put_event(EVENT_EXCEPTION); 1136f060969SPavel Dovgalyuk return true; 1146f060969SPavel Dovgalyuk } else if (replay_mode == REPLAY_MODE_PLAY) { 115d759c951SAlex Bennée g_assert(replay_mutex_locked()); 1166f060969SPavel Dovgalyuk bool res = replay_has_exception(); 1176f060969SPavel Dovgalyuk if (res) { 1186f060969SPavel Dovgalyuk replay_finish_event(); 1196f060969SPavel Dovgalyuk } 1206f060969SPavel Dovgalyuk return res; 1216f060969SPavel Dovgalyuk } 1226f060969SPavel Dovgalyuk 1236f060969SPavel Dovgalyuk return true; 1246f060969SPavel Dovgalyuk } 1256f060969SPavel Dovgalyuk 1266f060969SPavel Dovgalyuk bool replay_has_exception(void) 1276f060969SPavel Dovgalyuk { 1286f060969SPavel Dovgalyuk bool res = false; 1296f060969SPavel Dovgalyuk if (replay_mode == REPLAY_MODE_PLAY) { 130d759c951SAlex Bennée g_assert(replay_mutex_locked()); 1316f060969SPavel Dovgalyuk replay_account_executed_instructions(); 1326f060969SPavel Dovgalyuk res = replay_next_event_is(EVENT_EXCEPTION); 1336f060969SPavel Dovgalyuk } 1346f060969SPavel Dovgalyuk 1356f060969SPavel Dovgalyuk return res; 1366f060969SPavel Dovgalyuk } 1376f060969SPavel Dovgalyuk 1386f060969SPavel Dovgalyuk bool replay_interrupt(void) 1396f060969SPavel Dovgalyuk { 1406f060969SPavel Dovgalyuk if (replay_mode == REPLAY_MODE_RECORD) { 141d759c951SAlex Bennée g_assert(replay_mutex_locked()); 1426f060969SPavel Dovgalyuk replay_save_instructions(); 1436f060969SPavel Dovgalyuk replay_put_event(EVENT_INTERRUPT); 1446f060969SPavel Dovgalyuk return true; 1456f060969SPavel Dovgalyuk } else if (replay_mode == REPLAY_MODE_PLAY) { 146d759c951SAlex Bennée g_assert(replay_mutex_locked()); 1476f060969SPavel Dovgalyuk bool res = replay_has_interrupt(); 1486f060969SPavel Dovgalyuk if (res) { 1496f060969SPavel Dovgalyuk replay_finish_event(); 1506f060969SPavel Dovgalyuk } 1516f060969SPavel Dovgalyuk return res; 1526f060969SPavel Dovgalyuk } 1536f060969SPavel Dovgalyuk 1546f060969SPavel Dovgalyuk return true; 1556f060969SPavel Dovgalyuk } 1566f060969SPavel Dovgalyuk 1576f060969SPavel Dovgalyuk bool replay_has_interrupt(void) 1586f060969SPavel Dovgalyuk { 1596f060969SPavel Dovgalyuk bool res = false; 1606f060969SPavel Dovgalyuk if (replay_mode == REPLAY_MODE_PLAY) { 161d759c951SAlex Bennée g_assert(replay_mutex_locked()); 1626f060969SPavel Dovgalyuk replay_account_executed_instructions(); 1636f060969SPavel Dovgalyuk res = replay_next_event_is(EVENT_INTERRUPT); 1646f060969SPavel Dovgalyuk } 1656f060969SPavel Dovgalyuk return res; 1666f060969SPavel Dovgalyuk } 167b60c48a7SPavel Dovgalyuk 168802f045aSEric Blake void replay_shutdown_request(ShutdownCause cause) 169b60c48a7SPavel Dovgalyuk { 170b60c48a7SPavel Dovgalyuk if (replay_mode == REPLAY_MODE_RECORD) { 171d759c951SAlex Bennée g_assert(replay_mutex_locked()); 172802f045aSEric Blake replay_put_event(EVENT_SHUTDOWN + cause); 173b60c48a7SPavel Dovgalyuk } 174b60c48a7SPavel Dovgalyuk } 1758bd7f71dSPavel Dovgalyuk 1768bd7f71dSPavel Dovgalyuk bool replay_checkpoint(ReplayCheckpoint checkpoint) 1778bd7f71dSPavel Dovgalyuk { 1788bd7f71dSPavel Dovgalyuk bool res = false; 17966eb7825SPavel Dovgalyuk static bool in_checkpoint; 1808bd7f71dSPavel Dovgalyuk assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST); 1818bd7f71dSPavel Dovgalyuk 1828bd7f71dSPavel Dovgalyuk if (!replay_file) { 1838bd7f71dSPavel Dovgalyuk return true; 1848bd7f71dSPavel Dovgalyuk } 1858bd7f71dSPavel Dovgalyuk 18666eb7825SPavel Dovgalyuk if (in_checkpoint) { 18766eb7825SPavel Dovgalyuk /* If we are already in checkpoint, then there is no need 18866eb7825SPavel Dovgalyuk for additional synchronization. 18966eb7825SPavel Dovgalyuk Recursion occurs when HW event modifies timers. 19066eb7825SPavel Dovgalyuk Timer modification may invoke the checkpoint and 19166eb7825SPavel Dovgalyuk proceed to recursion. */ 19266eb7825SPavel Dovgalyuk return true; 19366eb7825SPavel Dovgalyuk } 19466eb7825SPavel Dovgalyuk in_checkpoint = true; 19566eb7825SPavel Dovgalyuk 19666eb7825SPavel Dovgalyuk replay_save_instructions(); 1978bd7f71dSPavel Dovgalyuk 1988bd7f71dSPavel Dovgalyuk if (replay_mode == REPLAY_MODE_PLAY) { 199d759c951SAlex Bennée g_assert(replay_mutex_locked()); 2008bd7f71dSPavel Dovgalyuk if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) { 2018bd7f71dSPavel Dovgalyuk replay_finish_event(); 202f186d64dSPavel Dovgalyuk } else if (replay_state.data_kind != EVENT_ASYNC) { 2038bd7f71dSPavel Dovgalyuk res = false; 2048bd7f71dSPavel Dovgalyuk goto out; 2058bd7f71dSPavel Dovgalyuk } 2068bd7f71dSPavel Dovgalyuk replay_read_events(checkpoint); 2078bd7f71dSPavel Dovgalyuk /* replay_read_events may leave some unread events. 2088bd7f71dSPavel Dovgalyuk Return false if not all of the events associated with 2098bd7f71dSPavel Dovgalyuk checkpoint were processed */ 210f186d64dSPavel Dovgalyuk res = replay_state.data_kind != EVENT_ASYNC; 2118bd7f71dSPavel Dovgalyuk } else if (replay_mode == REPLAY_MODE_RECORD) { 212d759c951SAlex Bennée g_assert(replay_mutex_locked()); 2138bd7f71dSPavel Dovgalyuk replay_put_event(EVENT_CHECKPOINT + checkpoint); 21489e46eb4SPavel Dovgalyuk /* This checkpoint belongs to several threads. 21589e46eb4SPavel Dovgalyuk Processing events from different threads is 21689e46eb4SPavel Dovgalyuk non-deterministic */ 217ca9759c2SPavel Dovgalyuk if (checkpoint != CHECKPOINT_CLOCK_WARP_START 218ca9759c2SPavel Dovgalyuk /* FIXME: this is temporary fix, other checkpoints 219ca9759c2SPavel Dovgalyuk may also be invoked from the different threads someday. 220ca9759c2SPavel Dovgalyuk Asynchronous event processing should be refactored 221ca9759c2SPavel Dovgalyuk to create additional replay event kind which is 222ca9759c2SPavel Dovgalyuk nailed to the one of the threads and which processes 223ca9759c2SPavel Dovgalyuk the event queue. */ 224ca9759c2SPavel Dovgalyuk && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) { 2258bd7f71dSPavel Dovgalyuk replay_save_events(checkpoint); 22689e46eb4SPavel Dovgalyuk } 2278bd7f71dSPavel Dovgalyuk res = true; 2288bd7f71dSPavel Dovgalyuk } 2298bd7f71dSPavel Dovgalyuk out: 23066eb7825SPavel Dovgalyuk in_checkpoint = false; 2318bd7f71dSPavel Dovgalyuk return res; 2328bd7f71dSPavel Dovgalyuk } 2337615936eSPavel Dovgalyuk 2340c08185fSPavel Dovgalyuk bool replay_has_checkpoint(void) 2350c08185fSPavel Dovgalyuk { 2360c08185fSPavel Dovgalyuk bool res = false; 2370c08185fSPavel Dovgalyuk if (replay_mode == REPLAY_MODE_PLAY) { 2380c08185fSPavel Dovgalyuk g_assert(replay_mutex_locked()); 2390c08185fSPavel Dovgalyuk replay_account_executed_instructions(); 2400c08185fSPavel Dovgalyuk res = EVENT_CHECKPOINT <= replay_state.data_kind 2410c08185fSPavel Dovgalyuk && replay_state.data_kind <= EVENT_CHECKPOINT_LAST; 2420c08185fSPavel Dovgalyuk } 2430c08185fSPavel Dovgalyuk return res; 2440c08185fSPavel Dovgalyuk } 2450c08185fSPavel Dovgalyuk 2467615936eSPavel Dovgalyuk static void replay_enable(const char *fname, int mode) 2477615936eSPavel Dovgalyuk { 2487615936eSPavel Dovgalyuk const char *fmode = NULL; 2497615936eSPavel Dovgalyuk assert(!replay_file); 2507615936eSPavel Dovgalyuk 2517615936eSPavel Dovgalyuk switch (mode) { 2527615936eSPavel Dovgalyuk case REPLAY_MODE_RECORD: 2537615936eSPavel Dovgalyuk fmode = "wb"; 2547615936eSPavel Dovgalyuk break; 2557615936eSPavel Dovgalyuk case REPLAY_MODE_PLAY: 2567615936eSPavel Dovgalyuk fmode = "rb"; 2577615936eSPavel Dovgalyuk break; 2587615936eSPavel Dovgalyuk default: 2597615936eSPavel Dovgalyuk fprintf(stderr, "Replay: internal error: invalid replay mode\n"); 2607615936eSPavel Dovgalyuk exit(1); 2617615936eSPavel Dovgalyuk } 2627615936eSPavel Dovgalyuk 2637615936eSPavel Dovgalyuk atexit(replay_finish); 2647615936eSPavel Dovgalyuk 2657615936eSPavel Dovgalyuk replay_file = fopen(fname, fmode); 2667615936eSPavel Dovgalyuk if (replay_file == NULL) { 2677615936eSPavel Dovgalyuk fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno)); 2687615936eSPavel Dovgalyuk exit(1); 2697615936eSPavel Dovgalyuk } 2707615936eSPavel Dovgalyuk 2717615936eSPavel Dovgalyuk replay_filename = g_strdup(fname); 2727615936eSPavel Dovgalyuk replay_mode = mode; 273d759c951SAlex Bennée replay_mutex_init(); 274d759c951SAlex Bennée 275f186d64dSPavel Dovgalyuk replay_state.data_kind = -1; 27613f26713SPavel Dovgalyuk replay_state.instruction_count = 0; 27713f26713SPavel Dovgalyuk replay_state.current_icount = 0; 278f186d64dSPavel Dovgalyuk replay_state.has_unread_data = 0; 2797615936eSPavel Dovgalyuk 2807615936eSPavel Dovgalyuk /* skip file header for RECORD and check it for PLAY */ 2817615936eSPavel Dovgalyuk if (replay_mode == REPLAY_MODE_RECORD) { 2827615936eSPavel Dovgalyuk fseek(replay_file, HEADER_SIZE, SEEK_SET); 2837615936eSPavel Dovgalyuk } else if (replay_mode == REPLAY_MODE_PLAY) { 2847615936eSPavel Dovgalyuk unsigned int version = replay_get_dword(); 2857615936eSPavel Dovgalyuk if (version != REPLAY_VERSION) { 2867615936eSPavel Dovgalyuk fprintf(stderr, "Replay: invalid input log file version\n"); 2877615936eSPavel Dovgalyuk exit(1); 2887615936eSPavel Dovgalyuk } 2897615936eSPavel Dovgalyuk /* go to the beginning */ 2907615936eSPavel Dovgalyuk fseek(replay_file, HEADER_SIZE, SEEK_SET); 2917615936eSPavel Dovgalyuk replay_fetch_data_kind(); 2927615936eSPavel Dovgalyuk } 2937615936eSPavel Dovgalyuk 2947615936eSPavel Dovgalyuk replay_init_events(); 2957615936eSPavel Dovgalyuk } 2967615936eSPavel Dovgalyuk 2977615936eSPavel Dovgalyuk void replay_configure(QemuOpts *opts) 2987615936eSPavel Dovgalyuk { 2997615936eSPavel Dovgalyuk const char *fname; 3007615936eSPavel Dovgalyuk const char *rr; 3017615936eSPavel Dovgalyuk ReplayMode mode = REPLAY_MODE_NONE; 302890ad550SEduardo Habkost Location loc; 303890ad550SEduardo Habkost 304890ad550SEduardo Habkost if (!opts) { 305890ad550SEduardo Habkost return; 306890ad550SEduardo Habkost } 307890ad550SEduardo Habkost 308890ad550SEduardo Habkost loc_push_none(&loc); 309890ad550SEduardo Habkost qemu_opts_loc_restore(opts); 3107615936eSPavel Dovgalyuk 3117615936eSPavel Dovgalyuk rr = qemu_opt_get(opts, "rr"); 3127615936eSPavel Dovgalyuk if (!rr) { 3137615936eSPavel Dovgalyuk /* Just enabling icount */ 314d9d3aaeaSMarkus Armbruster goto out; 3157615936eSPavel Dovgalyuk } else if (!strcmp(rr, "record")) { 3167615936eSPavel Dovgalyuk mode = REPLAY_MODE_RECORD; 3177615936eSPavel Dovgalyuk } else if (!strcmp(rr, "replay")) { 3187615936eSPavel Dovgalyuk mode = REPLAY_MODE_PLAY; 3197615936eSPavel Dovgalyuk } else { 3207615936eSPavel Dovgalyuk error_report("Invalid icount rr option: %s", rr); 3217615936eSPavel Dovgalyuk exit(1); 3227615936eSPavel Dovgalyuk } 3237615936eSPavel Dovgalyuk 3247615936eSPavel Dovgalyuk fname = qemu_opt_get(opts, "rrfile"); 3257615936eSPavel Dovgalyuk if (!fname) { 3267615936eSPavel Dovgalyuk error_report("File name not specified for replay"); 3277615936eSPavel Dovgalyuk exit(1); 3287615936eSPavel Dovgalyuk } 3297615936eSPavel Dovgalyuk 3309c2037d0SPavel Dovgalyuk replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot")); 331306e196fSPavel Dovgalyuk replay_vmstate_register(); 3327615936eSPavel Dovgalyuk replay_enable(fname, mode); 333890ad550SEduardo Habkost 334d9d3aaeaSMarkus Armbruster out: 335890ad550SEduardo Habkost loc_pop(&loc); 3367615936eSPavel Dovgalyuk } 3377615936eSPavel Dovgalyuk 3387615936eSPavel Dovgalyuk void replay_start(void) 3397615936eSPavel Dovgalyuk { 3407615936eSPavel Dovgalyuk if (replay_mode == REPLAY_MODE_NONE) { 3417615936eSPavel Dovgalyuk return; 3427615936eSPavel Dovgalyuk } 3437615936eSPavel Dovgalyuk 3440194749aSPavel Dovgalyuk if (replay_blockers) { 345c29b77f9SMarkus Armbruster error_reportf_err(replay_blockers->data, "Record/replay: "); 3460194749aSPavel Dovgalyuk exit(1); 3470194749aSPavel Dovgalyuk } 3484c27b859SPavel Dovgalyuk if (!use_icount) { 3494c27b859SPavel Dovgalyuk error_report("Please enable icount to use record/replay"); 3504c27b859SPavel Dovgalyuk exit(1); 3514c27b859SPavel Dovgalyuk } 3520194749aSPavel Dovgalyuk 3537615936eSPavel Dovgalyuk /* Timer for snapshotting will be set up here. */ 3547615936eSPavel Dovgalyuk 3557615936eSPavel Dovgalyuk replay_enable_events(); 3567615936eSPavel Dovgalyuk } 3577615936eSPavel Dovgalyuk 3587615936eSPavel Dovgalyuk void replay_finish(void) 3597615936eSPavel Dovgalyuk { 3607615936eSPavel Dovgalyuk if (replay_mode == REPLAY_MODE_NONE) { 3617615936eSPavel Dovgalyuk return; 3627615936eSPavel Dovgalyuk } 3637615936eSPavel Dovgalyuk 3647615936eSPavel Dovgalyuk replay_save_instructions(); 3657615936eSPavel Dovgalyuk 3667615936eSPavel Dovgalyuk /* finalize the file */ 3677615936eSPavel Dovgalyuk if (replay_file) { 3687615936eSPavel Dovgalyuk if (replay_mode == REPLAY_MODE_RECORD) { 369ed5d7ff3SPavel Dovgalyuk /* 370ed5d7ff3SPavel Dovgalyuk * Can't do it in the signal handler, therefore 371ed5d7ff3SPavel Dovgalyuk * add shutdown event here for the case of Ctrl-C. 372ed5d7ff3SPavel Dovgalyuk */ 373ed5d7ff3SPavel Dovgalyuk replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL); 3747615936eSPavel Dovgalyuk /* write end event */ 3757615936eSPavel Dovgalyuk replay_put_event(EVENT_END); 3767615936eSPavel Dovgalyuk 3777615936eSPavel Dovgalyuk /* write header */ 3787615936eSPavel Dovgalyuk fseek(replay_file, 0, SEEK_SET); 3797615936eSPavel Dovgalyuk replay_put_dword(REPLAY_VERSION); 3807615936eSPavel Dovgalyuk } 3817615936eSPavel Dovgalyuk 3827615936eSPavel Dovgalyuk fclose(replay_file); 3837615936eSPavel Dovgalyuk replay_file = NULL; 3847615936eSPavel Dovgalyuk } 3857615936eSPavel Dovgalyuk if (replay_filename) { 3867615936eSPavel Dovgalyuk g_free(replay_filename); 3877615936eSPavel Dovgalyuk replay_filename = NULL; 3887615936eSPavel Dovgalyuk } 3897615936eSPavel Dovgalyuk 3909c2037d0SPavel Dovgalyuk g_free(replay_snapshot); 3919c2037d0SPavel Dovgalyuk replay_snapshot = NULL; 3929c2037d0SPavel Dovgalyuk 393ae25dccbSPavel Dovgalyuk replay_mode = REPLAY_MODE_NONE; 394ae25dccbSPavel Dovgalyuk 3957615936eSPavel Dovgalyuk replay_finish_events(); 3967615936eSPavel Dovgalyuk } 3970194749aSPavel Dovgalyuk 3980194749aSPavel Dovgalyuk void replay_add_blocker(Error *reason) 3990194749aSPavel Dovgalyuk { 4000194749aSPavel Dovgalyuk replay_blockers = g_slist_prepend(replay_blockers, reason); 4010194749aSPavel Dovgalyuk } 402