1e3b09ad2SPavel Dovgalyuk /* 2e3b09ad2SPavel Dovgalyuk * replay-debugging.c 3e3b09ad2SPavel Dovgalyuk * 4e3b09ad2SPavel Dovgalyuk * Copyright (c) 2010-2020 Institute for System Programming 5e3b09ad2SPavel Dovgalyuk * of the Russian Academy of Sciences. 6e3b09ad2SPavel Dovgalyuk * 7e3b09ad2SPavel Dovgalyuk * This work is licensed under the terms of the GNU GPL, version 2 or later. 8e3b09ad2SPavel Dovgalyuk * See the COPYING file in the top-level directory. 9e3b09ad2SPavel Dovgalyuk * 10e3b09ad2SPavel Dovgalyuk */ 11e3b09ad2SPavel Dovgalyuk 12e3b09ad2SPavel Dovgalyuk #include "qemu/osdep.h" 13e3b09ad2SPavel Dovgalyuk #include "qapi/error.h" 14e3b09ad2SPavel Dovgalyuk #include "sysemu/replay.h" 15e7510671SPavel Dovgalyuk #include "sysemu/runstate.h" 16e3b09ad2SPavel Dovgalyuk #include "replay-internal.h" 17e3b09ad2SPavel Dovgalyuk #include "monitor/hmp.h" 18e3b09ad2SPavel Dovgalyuk #include "monitor/monitor.h" 19e3b09ad2SPavel Dovgalyuk #include "qapi/qapi-commands-replay.h" 20e7510671SPavel Dovgalyuk #include "qapi/qmp/qdict.h" 21e7510671SPavel Dovgalyuk #include "qemu/timer.h" 22f6baed3dSPavel Dovgalyuk #include "block/snapshot.h" 23f6baed3dSPavel Dovgalyuk #include "migration/snapshot.h" 24e3b09ad2SPavel Dovgalyuk 25fda8458bSPavel Dovgalyuk static bool replay_is_debugging; 26cda38259SPavel Dovgalyuk static int64_t replay_last_breakpoint; 27cda38259SPavel Dovgalyuk static int64_t replay_last_snapshot; 28fda8458bSPavel Dovgalyuk 29fda8458bSPavel Dovgalyuk bool replay_running_debug(void) 30fda8458bSPavel Dovgalyuk { 31fda8458bSPavel Dovgalyuk return replay_is_debugging; 32fda8458bSPavel Dovgalyuk } 33fda8458bSPavel Dovgalyuk 34e3b09ad2SPavel Dovgalyuk void hmp_info_replay(Monitor *mon, const QDict *qdict) 35e3b09ad2SPavel Dovgalyuk { 36e3b09ad2SPavel Dovgalyuk if (replay_mode == REPLAY_MODE_NONE) { 37e3b09ad2SPavel Dovgalyuk monitor_printf(mon, "Record/replay is not active\n"); 38e3b09ad2SPavel Dovgalyuk } else { 39e3b09ad2SPavel Dovgalyuk monitor_printf(mon, 40e3b09ad2SPavel Dovgalyuk "%s execution '%s': instruction count = %"PRId64"\n", 41e3b09ad2SPavel Dovgalyuk replay_mode == REPLAY_MODE_RECORD ? "Recording" : "Replaying", 42e3b09ad2SPavel Dovgalyuk replay_get_filename(), replay_get_current_icount()); 43e3b09ad2SPavel Dovgalyuk } 44e3b09ad2SPavel Dovgalyuk } 45e3b09ad2SPavel Dovgalyuk 46e3b09ad2SPavel Dovgalyuk ReplayInfo *qmp_query_replay(Error **errp) 47e3b09ad2SPavel Dovgalyuk { 48e3b09ad2SPavel Dovgalyuk ReplayInfo *retval = g_new0(ReplayInfo, 1); 49e3b09ad2SPavel Dovgalyuk 50e3b09ad2SPavel Dovgalyuk retval->mode = replay_mode; 51e3b09ad2SPavel Dovgalyuk if (replay_get_filename()) { 52e3b09ad2SPavel Dovgalyuk retval->filename = g_strdup(replay_get_filename()); 53e3b09ad2SPavel Dovgalyuk retval->has_filename = true; 54e3b09ad2SPavel Dovgalyuk } 55e3b09ad2SPavel Dovgalyuk retval->icount = replay_get_current_icount(); 56e3b09ad2SPavel Dovgalyuk return retval; 57e3b09ad2SPavel Dovgalyuk } 58e7510671SPavel Dovgalyuk 59e7510671SPavel Dovgalyuk static void replay_break(uint64_t icount, QEMUTimerCB callback, void *opaque) 60e7510671SPavel Dovgalyuk { 61e7510671SPavel Dovgalyuk assert(replay_mode == REPLAY_MODE_PLAY); 62e7510671SPavel Dovgalyuk assert(replay_mutex_locked()); 63e7510671SPavel Dovgalyuk assert(replay_break_icount >= replay_get_current_icount()); 64e7510671SPavel Dovgalyuk assert(callback); 65e7510671SPavel Dovgalyuk 66e7510671SPavel Dovgalyuk replay_break_icount = icount; 67e7510671SPavel Dovgalyuk 68e7510671SPavel Dovgalyuk if (replay_break_timer) { 69e7510671SPavel Dovgalyuk timer_del(replay_break_timer); 70e7510671SPavel Dovgalyuk } 71e7510671SPavel Dovgalyuk replay_break_timer = timer_new_ns(QEMU_CLOCK_REALTIME, 72e7510671SPavel Dovgalyuk callback, opaque); 73e7510671SPavel Dovgalyuk } 74e7510671SPavel Dovgalyuk 75e7510671SPavel Dovgalyuk static void replay_delete_break(void) 76e7510671SPavel Dovgalyuk { 77e7510671SPavel Dovgalyuk assert(replay_mode == REPLAY_MODE_PLAY); 78e7510671SPavel Dovgalyuk assert(replay_mutex_locked()); 79e7510671SPavel Dovgalyuk 80e7510671SPavel Dovgalyuk if (replay_break_timer) { 81e7510671SPavel Dovgalyuk timer_free(replay_break_timer); 82e7510671SPavel Dovgalyuk replay_break_timer = NULL; 83e7510671SPavel Dovgalyuk } 84e7510671SPavel Dovgalyuk replay_break_icount = -1ULL; 85e7510671SPavel Dovgalyuk } 86e7510671SPavel Dovgalyuk 87e7510671SPavel Dovgalyuk static void replay_stop_vm(void *opaque) 88e7510671SPavel Dovgalyuk { 89e7510671SPavel Dovgalyuk vm_stop(RUN_STATE_PAUSED); 90e7510671SPavel Dovgalyuk replay_delete_break(); 91e7510671SPavel Dovgalyuk } 92e7510671SPavel Dovgalyuk 93e7510671SPavel Dovgalyuk void qmp_replay_break(int64_t icount, Error **errp) 94e7510671SPavel Dovgalyuk { 95e7510671SPavel Dovgalyuk if (replay_mode == REPLAY_MODE_PLAY) { 96e7510671SPavel Dovgalyuk if (icount >= replay_get_current_icount()) { 97e7510671SPavel Dovgalyuk replay_break(icount, replay_stop_vm, NULL); 98e7510671SPavel Dovgalyuk } else { 99e7510671SPavel Dovgalyuk error_setg(errp, 100e7510671SPavel Dovgalyuk "cannot set breakpoint at the instruction in the past"); 101e7510671SPavel Dovgalyuk } 102e7510671SPavel Dovgalyuk } else { 103e7510671SPavel Dovgalyuk error_setg(errp, "setting the breakpoint is allowed only in play mode"); 104e7510671SPavel Dovgalyuk } 105e7510671SPavel Dovgalyuk } 106e7510671SPavel Dovgalyuk 107e7510671SPavel Dovgalyuk void hmp_replay_break(Monitor *mon, const QDict *qdict) 108e7510671SPavel Dovgalyuk { 109e7510671SPavel Dovgalyuk int64_t icount = qdict_get_try_int(qdict, "icount", -1LL); 110e7510671SPavel Dovgalyuk Error *err = NULL; 111e7510671SPavel Dovgalyuk 112e7510671SPavel Dovgalyuk qmp_replay_break(icount, &err); 113e7510671SPavel Dovgalyuk if (err) { 114e7510671SPavel Dovgalyuk error_report_err(err); 115e7510671SPavel Dovgalyuk return; 116e7510671SPavel Dovgalyuk } 117e7510671SPavel Dovgalyuk } 118e7510671SPavel Dovgalyuk 119e7510671SPavel Dovgalyuk void qmp_replay_delete_break(Error **errp) 120e7510671SPavel Dovgalyuk { 121e7510671SPavel Dovgalyuk if (replay_mode == REPLAY_MODE_PLAY) { 122e7510671SPavel Dovgalyuk replay_delete_break(); 123e7510671SPavel Dovgalyuk } else { 124e7510671SPavel Dovgalyuk error_setg(errp, "replay breakpoints are allowed only in play mode"); 125e7510671SPavel Dovgalyuk } 126e7510671SPavel Dovgalyuk } 127e7510671SPavel Dovgalyuk 128e7510671SPavel Dovgalyuk void hmp_replay_delete_break(Monitor *mon, const QDict *qdict) 129e7510671SPavel Dovgalyuk { 130e7510671SPavel Dovgalyuk Error *err = NULL; 131e7510671SPavel Dovgalyuk 132e7510671SPavel Dovgalyuk qmp_replay_delete_break(&err); 133e7510671SPavel Dovgalyuk if (err) { 134e7510671SPavel Dovgalyuk error_report_err(err); 135e7510671SPavel Dovgalyuk return; 136e7510671SPavel Dovgalyuk } 137e7510671SPavel Dovgalyuk } 138f6baed3dSPavel Dovgalyuk 139f6baed3dSPavel Dovgalyuk static char *replay_find_nearest_snapshot(int64_t icount, 140f6baed3dSPavel Dovgalyuk int64_t *snapshot_icount) 141f6baed3dSPavel Dovgalyuk { 142f6baed3dSPavel Dovgalyuk BlockDriverState *bs; 143f6baed3dSPavel Dovgalyuk QEMUSnapshotInfo *sn_tab; 144f6baed3dSPavel Dovgalyuk QEMUSnapshotInfo *nearest = NULL; 145f6baed3dSPavel Dovgalyuk char *ret = NULL; 146f6baed3dSPavel Dovgalyuk int nb_sns, i; 147f6baed3dSPavel Dovgalyuk AioContext *aio_context; 148f6baed3dSPavel Dovgalyuk 149f6baed3dSPavel Dovgalyuk *snapshot_icount = -1; 150f6baed3dSPavel Dovgalyuk 151*c22d644cSDaniel P. Berrangé bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, NULL); 152f6baed3dSPavel Dovgalyuk if (!bs) { 153f6baed3dSPavel Dovgalyuk goto fail; 154f6baed3dSPavel Dovgalyuk } 155f6baed3dSPavel Dovgalyuk aio_context = bdrv_get_aio_context(bs); 156f6baed3dSPavel Dovgalyuk 157f6baed3dSPavel Dovgalyuk aio_context_acquire(aio_context); 158f6baed3dSPavel Dovgalyuk nb_sns = bdrv_snapshot_list(bs, &sn_tab); 159f6baed3dSPavel Dovgalyuk aio_context_release(aio_context); 160f6baed3dSPavel Dovgalyuk 161f6baed3dSPavel Dovgalyuk for (i = 0; i < nb_sns; i++) { 162cf3a74c9SDaniel P. Berrangé if (bdrv_all_find_snapshot(sn_tab[i].name, false, NULL, NULL) == 0) { 163f6baed3dSPavel Dovgalyuk if (sn_tab[i].icount != -1ULL 164f6baed3dSPavel Dovgalyuk && sn_tab[i].icount <= icount 165f6baed3dSPavel Dovgalyuk && (!nearest || nearest->icount < sn_tab[i].icount)) { 166f6baed3dSPavel Dovgalyuk nearest = &sn_tab[i]; 167f6baed3dSPavel Dovgalyuk } 168f6baed3dSPavel Dovgalyuk } 169f6baed3dSPavel Dovgalyuk } 170f6baed3dSPavel Dovgalyuk if (nearest) { 171f6baed3dSPavel Dovgalyuk ret = g_strdup(nearest->name); 172f6baed3dSPavel Dovgalyuk *snapshot_icount = nearest->icount; 173f6baed3dSPavel Dovgalyuk } 174f6baed3dSPavel Dovgalyuk g_free(sn_tab); 175f6baed3dSPavel Dovgalyuk 176f6baed3dSPavel Dovgalyuk fail: 177f6baed3dSPavel Dovgalyuk return ret; 178f6baed3dSPavel Dovgalyuk } 179f6baed3dSPavel Dovgalyuk 180f6baed3dSPavel Dovgalyuk static void replay_seek(int64_t icount, QEMUTimerCB callback, Error **errp) 181f6baed3dSPavel Dovgalyuk { 182f6baed3dSPavel Dovgalyuk char *snapshot = NULL; 183f6baed3dSPavel Dovgalyuk int64_t snapshot_icount; 184f6baed3dSPavel Dovgalyuk 185f6baed3dSPavel Dovgalyuk if (replay_mode != REPLAY_MODE_PLAY) { 186f6baed3dSPavel Dovgalyuk error_setg(errp, "replay must be enabled to seek"); 187f6baed3dSPavel Dovgalyuk return; 188f6baed3dSPavel Dovgalyuk } 189f6baed3dSPavel Dovgalyuk 190f6baed3dSPavel Dovgalyuk snapshot = replay_find_nearest_snapshot(icount, &snapshot_icount); 191f6baed3dSPavel Dovgalyuk if (snapshot) { 192f6baed3dSPavel Dovgalyuk if (icount < replay_get_current_icount() 193f6baed3dSPavel Dovgalyuk || replay_get_current_icount() < snapshot_icount) { 194f6baed3dSPavel Dovgalyuk vm_stop(RUN_STATE_RESTORE_VM); 195f6baed3dSPavel Dovgalyuk load_snapshot(snapshot, errp); 196f6baed3dSPavel Dovgalyuk } 197f6baed3dSPavel Dovgalyuk g_free(snapshot); 198f6baed3dSPavel Dovgalyuk } 199f6baed3dSPavel Dovgalyuk if (replay_get_current_icount() <= icount) { 200f6baed3dSPavel Dovgalyuk replay_break(icount, callback, NULL); 201f6baed3dSPavel Dovgalyuk vm_start(); 202f6baed3dSPavel Dovgalyuk } else { 203f6baed3dSPavel Dovgalyuk error_setg(errp, "cannot seek to the specified instruction count"); 204f6baed3dSPavel Dovgalyuk } 205f6baed3dSPavel Dovgalyuk } 206f6baed3dSPavel Dovgalyuk 207f6baed3dSPavel Dovgalyuk void qmp_replay_seek(int64_t icount, Error **errp) 208f6baed3dSPavel Dovgalyuk { 209f6baed3dSPavel Dovgalyuk replay_seek(icount, replay_stop_vm, errp); 210f6baed3dSPavel Dovgalyuk } 211f6baed3dSPavel Dovgalyuk 212f6baed3dSPavel Dovgalyuk void hmp_replay_seek(Monitor *mon, const QDict *qdict) 213f6baed3dSPavel Dovgalyuk { 214f6baed3dSPavel Dovgalyuk int64_t icount = qdict_get_try_int(qdict, "icount", -1LL); 215f6baed3dSPavel Dovgalyuk Error *err = NULL; 216f6baed3dSPavel Dovgalyuk 217f6baed3dSPavel Dovgalyuk qmp_replay_seek(icount, &err); 218f6baed3dSPavel Dovgalyuk if (err) { 219f6baed3dSPavel Dovgalyuk error_report_err(err); 220f6baed3dSPavel Dovgalyuk return; 221f6baed3dSPavel Dovgalyuk } 222f6baed3dSPavel Dovgalyuk } 223fda8458bSPavel Dovgalyuk 224fda8458bSPavel Dovgalyuk static void replay_stop_vm_debug(void *opaque) 225fda8458bSPavel Dovgalyuk { 226fda8458bSPavel Dovgalyuk replay_is_debugging = false; 227fda8458bSPavel Dovgalyuk vm_stop(RUN_STATE_DEBUG); 228fda8458bSPavel Dovgalyuk replay_delete_break(); 229fda8458bSPavel Dovgalyuk } 230fda8458bSPavel Dovgalyuk 231fda8458bSPavel Dovgalyuk bool replay_reverse_step(void) 232fda8458bSPavel Dovgalyuk { 233fda8458bSPavel Dovgalyuk Error *err = NULL; 234fda8458bSPavel Dovgalyuk 235fda8458bSPavel Dovgalyuk assert(replay_mode == REPLAY_MODE_PLAY); 236fda8458bSPavel Dovgalyuk 237fda8458bSPavel Dovgalyuk if (replay_get_current_icount() != 0) { 238fda8458bSPavel Dovgalyuk replay_seek(replay_get_current_icount() - 1, 239fda8458bSPavel Dovgalyuk replay_stop_vm_debug, &err); 240fda8458bSPavel Dovgalyuk if (err) { 241fda8458bSPavel Dovgalyuk error_free(err); 242fda8458bSPavel Dovgalyuk return false; 243fda8458bSPavel Dovgalyuk } 244fda8458bSPavel Dovgalyuk replay_is_debugging = true; 245fda8458bSPavel Dovgalyuk return true; 246fda8458bSPavel Dovgalyuk } 247fda8458bSPavel Dovgalyuk 248fda8458bSPavel Dovgalyuk return false; 249fda8458bSPavel Dovgalyuk } 250cda38259SPavel Dovgalyuk 251cda38259SPavel Dovgalyuk static void replay_continue_end(void) 252cda38259SPavel Dovgalyuk { 253cda38259SPavel Dovgalyuk replay_is_debugging = false; 254cda38259SPavel Dovgalyuk vm_stop(RUN_STATE_DEBUG); 255cda38259SPavel Dovgalyuk replay_delete_break(); 256cda38259SPavel Dovgalyuk } 257cda38259SPavel Dovgalyuk 258cda38259SPavel Dovgalyuk static void replay_continue_stop(void *opaque) 259cda38259SPavel Dovgalyuk { 260cda38259SPavel Dovgalyuk Error *err = NULL; 261cda38259SPavel Dovgalyuk if (replay_last_breakpoint != -1LL) { 262cda38259SPavel Dovgalyuk replay_seek(replay_last_breakpoint, replay_stop_vm_debug, &err); 263cda38259SPavel Dovgalyuk if (err) { 264cda38259SPavel Dovgalyuk error_free(err); 265cda38259SPavel Dovgalyuk replay_continue_end(); 266cda38259SPavel Dovgalyuk } 267cda38259SPavel Dovgalyuk return; 268cda38259SPavel Dovgalyuk } 269cda38259SPavel Dovgalyuk /* 270cda38259SPavel Dovgalyuk * No breakpoints since the last snapshot. 271cda38259SPavel Dovgalyuk * Find previous snapshot and try again. 272cda38259SPavel Dovgalyuk */ 273cda38259SPavel Dovgalyuk if (replay_last_snapshot != 0) { 274cda38259SPavel Dovgalyuk replay_seek(replay_last_snapshot - 1, replay_continue_stop, &err); 275cda38259SPavel Dovgalyuk if (err) { 276cda38259SPavel Dovgalyuk error_free(err); 277cda38259SPavel Dovgalyuk replay_continue_end(); 278cda38259SPavel Dovgalyuk } 279cda38259SPavel Dovgalyuk replay_last_snapshot = replay_get_current_icount(); 280cda38259SPavel Dovgalyuk } else { 281cda38259SPavel Dovgalyuk /* Seek to the very first step */ 282cda38259SPavel Dovgalyuk replay_seek(0, replay_stop_vm_debug, &err); 283cda38259SPavel Dovgalyuk if (err) { 284cda38259SPavel Dovgalyuk error_free(err); 285cda38259SPavel Dovgalyuk replay_continue_end(); 286cda38259SPavel Dovgalyuk } 287cda38259SPavel Dovgalyuk } 288cda38259SPavel Dovgalyuk } 289cda38259SPavel Dovgalyuk 290cda38259SPavel Dovgalyuk bool replay_reverse_continue(void) 291cda38259SPavel Dovgalyuk { 292cda38259SPavel Dovgalyuk Error *err = NULL; 293cda38259SPavel Dovgalyuk 294cda38259SPavel Dovgalyuk assert(replay_mode == REPLAY_MODE_PLAY); 295cda38259SPavel Dovgalyuk 296cda38259SPavel Dovgalyuk if (replay_get_current_icount() != 0) { 297cda38259SPavel Dovgalyuk replay_seek(replay_get_current_icount() - 1, 298cda38259SPavel Dovgalyuk replay_continue_stop, &err); 299cda38259SPavel Dovgalyuk if (err) { 300cda38259SPavel Dovgalyuk error_free(err); 301cda38259SPavel Dovgalyuk return false; 302cda38259SPavel Dovgalyuk } 303cda38259SPavel Dovgalyuk replay_last_breakpoint = -1LL; 304cda38259SPavel Dovgalyuk replay_is_debugging = true; 305cda38259SPavel Dovgalyuk replay_last_snapshot = replay_get_current_icount(); 306cda38259SPavel Dovgalyuk return true; 307cda38259SPavel Dovgalyuk } 308cda38259SPavel Dovgalyuk 309cda38259SPavel Dovgalyuk return false; 310cda38259SPavel Dovgalyuk } 311cda38259SPavel Dovgalyuk 312cda38259SPavel Dovgalyuk void replay_breakpoint(void) 313cda38259SPavel Dovgalyuk { 314cda38259SPavel Dovgalyuk assert(replay_mode == REPLAY_MODE_PLAY); 315cda38259SPavel Dovgalyuk replay_last_breakpoint = replay_get_current_icount(); 316cda38259SPavel Dovgalyuk } 31756357d80SPavel Dovgalyuk 31856357d80SPavel Dovgalyuk void replay_gdb_attached(void) 31956357d80SPavel Dovgalyuk { 32056357d80SPavel Dovgalyuk /* 32156357d80SPavel Dovgalyuk * Create VM snapshot on temporary overlay to allow reverse 32256357d80SPavel Dovgalyuk * debugging even if snapshots were not enabled. 32356357d80SPavel Dovgalyuk */ 32456357d80SPavel Dovgalyuk if (replay_mode == REPLAY_MODE_PLAY 32556357d80SPavel Dovgalyuk && !replay_snapshot) { 3267ea14df2SPhilippe Mathieu-Daudé if (!save_snapshot("start_debugging", NULL)) { 32756357d80SPavel Dovgalyuk /* Can't create the snapshot. Continue conventional debugging. */ 32856357d80SPavel Dovgalyuk } 32956357d80SPavel Dovgalyuk } 33056357d80SPavel Dovgalyuk } 331