123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- /*
- * replay-debugging.c
- *
- * Copyright (c) 2010-2020 Institute for System Programming
- * of the Russian Academy of Sciences.
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
- #include "qemu/osdep.h"
- #include "qapi/error.h"
- #include "sysemu/replay.h"
- #include "sysemu/runstate.h"
- #include "replay-internal.h"
- #include "monitor/hmp.h"
- #include "monitor/monitor.h"
- #include "qapi/qapi-commands-replay.h"
- #include "qapi/qmp/qdict.h"
- #include "qemu/timer.h"
- #include "block/snapshot.h"
- #include "migration/snapshot.h"
- static bool replay_is_debugging;
- static int64_t replay_last_breakpoint;
- static int64_t replay_last_snapshot;
- bool replay_running_debug(void)
- {
- return replay_is_debugging;
- }
- void hmp_info_replay(Monitor *mon, const QDict *qdict)
- {
- if (replay_mode == REPLAY_MODE_NONE) {
- monitor_printf(mon, "Record/replay is not active\n");
- } else {
- monitor_printf(mon,
- "%s execution '%s': instruction count = %"PRId64"\n",
- replay_mode == REPLAY_MODE_RECORD ? "Recording" : "Replaying",
- replay_get_filename(), replay_get_current_icount());
- }
- }
- ReplayInfo *qmp_query_replay(Error **errp)
- {
- ReplayInfo *retval = g_new0(ReplayInfo, 1);
- retval->mode = replay_mode;
- if (replay_get_filename()) {
- retval->filename = g_strdup(replay_get_filename());
- }
- retval->icount = replay_get_current_icount();
- return retval;
- }
- static void replay_break(uint64_t icount, QEMUTimerCB callback, void *opaque)
- {
- assert(replay_mode == REPLAY_MODE_PLAY);
- assert(replay_mutex_locked());
- assert(replay_break_icount >= replay_get_current_icount());
- assert(callback);
- replay_break_icount = icount;
- if (replay_break_timer) {
- timer_del(replay_break_timer);
- }
- replay_break_timer = timer_new_ns(QEMU_CLOCK_REALTIME,
- callback, opaque);
- }
- static void replay_delete_break(void)
- {
- assert(replay_mode == REPLAY_MODE_PLAY);
- assert(replay_mutex_locked());
- if (replay_break_timer) {
- timer_free(replay_break_timer);
- replay_break_timer = NULL;
- }
- replay_break_icount = -1ULL;
- }
- static void replay_stop_vm(void *opaque)
- {
- vm_stop(RUN_STATE_PAUSED);
- replay_delete_break();
- }
- void qmp_replay_break(int64_t icount, Error **errp)
- {
- if (replay_mode == REPLAY_MODE_PLAY) {
- if (icount >= replay_get_current_icount()) {
- replay_break(icount, replay_stop_vm, NULL);
- } else {
- error_setg(errp,
- "cannot set breakpoint at the instruction in the past");
- }
- } else {
- error_setg(errp, "setting the breakpoint is allowed only in play mode");
- }
- }
- void hmp_replay_break(Monitor *mon, const QDict *qdict)
- {
- int64_t icount = qdict_get_try_int(qdict, "icount", -1LL);
- Error *err = NULL;
- qmp_replay_break(icount, &err);
- if (err) {
- error_report_err(err);
- return;
- }
- }
- void qmp_replay_delete_break(Error **errp)
- {
- if (replay_mode == REPLAY_MODE_PLAY) {
- replay_delete_break();
- } else {
- error_setg(errp, "replay breakpoints are allowed only in play mode");
- }
- }
- void hmp_replay_delete_break(Monitor *mon, const QDict *qdict)
- {
- Error *err = NULL;
- qmp_replay_delete_break(&err);
- if (err) {
- error_report_err(err);
- return;
- }
- }
- static char *replay_find_nearest_snapshot(int64_t icount,
- int64_t *snapshot_icount)
- {
- BlockDriverState *bs;
- QEMUSnapshotInfo *sn_tab;
- QEMUSnapshotInfo *nearest = NULL;
- char *ret = NULL;
- int rv;
- int nb_sns, i;
- *snapshot_icount = -1;
- bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, NULL);
- if (!bs) {
- goto fail;
- }
- nb_sns = bdrv_snapshot_list(bs, &sn_tab);
- for (i = 0; i < nb_sns; i++) {
- rv = bdrv_all_has_snapshot(sn_tab[i].name, false, NULL, NULL);
- if (rv < 0)
- goto fail;
- if (rv == 1) {
- if (sn_tab[i].icount != -1ULL
- && sn_tab[i].icount <= icount
- && (!nearest || nearest->icount < sn_tab[i].icount)) {
- nearest = &sn_tab[i];
- }
- }
- }
- if (nearest) {
- ret = g_strdup(nearest->name);
- *snapshot_icount = nearest->icount;
- }
- g_free(sn_tab);
- fail:
- return ret;
- }
- static void replay_seek(int64_t icount, QEMUTimerCB callback, Error **errp)
- {
- char *snapshot = NULL;
- int64_t snapshot_icount;
- if (replay_mode != REPLAY_MODE_PLAY) {
- error_setg(errp, "replay must be enabled to seek");
- return;
- }
- snapshot = replay_find_nearest_snapshot(icount, &snapshot_icount);
- if (snapshot) {
- if (icount < replay_get_current_icount()
- || replay_get_current_icount() < snapshot_icount) {
- vm_stop(RUN_STATE_RESTORE_VM);
- load_snapshot(snapshot, NULL, false, NULL, errp);
- }
- g_free(snapshot);
- }
- if (replay_get_current_icount() <= icount) {
- replay_break(icount, callback, NULL);
- vm_start();
- } else {
- error_setg(errp, "cannot seek to the specified instruction count");
- }
- }
- void qmp_replay_seek(int64_t icount, Error **errp)
- {
- replay_seek(icount, replay_stop_vm, errp);
- }
- void hmp_replay_seek(Monitor *mon, const QDict *qdict)
- {
- int64_t icount = qdict_get_try_int(qdict, "icount", -1LL);
- Error *err = NULL;
- qmp_replay_seek(icount, &err);
- if (err) {
- error_report_err(err);
- return;
- }
- }
- static void replay_stop_vm_debug(void *opaque)
- {
- replay_is_debugging = false;
- vm_stop(RUN_STATE_DEBUG);
- replay_delete_break();
- }
- bool replay_reverse_step(void)
- {
- Error *err = NULL;
- assert(replay_mode == REPLAY_MODE_PLAY);
- if (replay_get_current_icount() != 0) {
- replay_seek(replay_get_current_icount() - 1,
- replay_stop_vm_debug, &err);
- if (err) {
- error_free(err);
- return false;
- }
- replay_is_debugging = true;
- return true;
- }
- return false;
- }
- static void replay_continue_end(void)
- {
- replay_is_debugging = false;
- vm_stop(RUN_STATE_DEBUG);
- replay_delete_break();
- }
- static void replay_continue_stop(void *opaque)
- {
- Error *err = NULL;
- if (replay_last_breakpoint != -1LL) {
- replay_seek(replay_last_breakpoint, replay_stop_vm_debug, &err);
- if (err) {
- error_free(err);
- replay_continue_end();
- }
- return;
- }
- /*
- * No breakpoints since the last snapshot.
- * Find previous snapshot and try again.
- */
- if (replay_last_snapshot != 0) {
- replay_seek(replay_last_snapshot - 1, replay_continue_stop, &err);
- if (err) {
- error_free(err);
- replay_continue_end();
- }
- replay_last_snapshot = replay_get_current_icount();
- } else {
- /* Seek to the very first step */
- replay_seek(0, replay_stop_vm_debug, &err);
- if (err) {
- error_free(err);
- replay_continue_end();
- }
- }
- }
- bool replay_reverse_continue(void)
- {
- Error *err = NULL;
- assert(replay_mode == REPLAY_MODE_PLAY);
- if (replay_get_current_icount() != 0) {
- replay_seek(replay_get_current_icount() - 1,
- replay_continue_stop, &err);
- if (err) {
- error_free(err);
- return false;
- }
- replay_last_breakpoint = -1LL;
- replay_is_debugging = true;
- replay_last_snapshot = replay_get_current_icount();
- return true;
- }
- return false;
- }
- void replay_breakpoint(void)
- {
- assert(replay_mode == REPLAY_MODE_PLAY);
- replay_last_breakpoint = replay_get_current_icount();
- }
- void replay_gdb_attached(void)
- {
- /*
- * Create VM snapshot on temporary overlay to allow reverse
- * debugging even if snapshots were not enabled.
- */
- if (replay_mode == REPLAY_MODE_PLAY
- && !replay_snapshot) {
- if (!save_snapshot("start_debugging", true, NULL, false, NULL, NULL)) {
- /* Can't create the snapshot. Continue conventional debugging. */
- }
- }
- }
|