qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

replay-debugging.c (8491B)


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