qemu

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

replay.c (9816B)


      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  */
     11 
     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"
     22 
     23 /* Current version of the replay mechanism.
     24    Increase it when file format changes. */
     25 #define REPLAY_VERSION              0xe0200c
     26 /* Size of replay log header */
     27 #define HEADER_SIZE                 (sizeof(uint32_t) + sizeof(uint64_t))
     28 
     29 ReplayMode replay_mode = REPLAY_MODE_NONE;
     30 char *replay_snapshot;
     31 
     32 /* Name of replay file  */
     33 static char *replay_filename;
     34 ReplayState replay_state;
     35 static GSList *replay_blockers;
     36 
     37 /* Replay breakpoints */
     38 uint64_t replay_break_icount = -1ULL;
     39 QEMUTimer *replay_break_timer;
     40 
     41 bool replay_next_event_is(int event)
     42 {
     43     bool res = false;
     44 
     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     }
     50 
     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;
     67 }
     68 
     69 uint64_t replay_get_current_icount(void)
     70 {
     71     return icount_get_raw();
     72 }
     73 
     74 int replay_get_instructions(void)
     75 {
     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;
     90 }
     91 
     92 void replay_account_executed_instructions(void)
     93 {
     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     }
    100 }
    101 
    102 bool replay_exception(void)
    103 {
    104 
    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     }
    118 
    119     return true;
    120 }
    121 
    122 bool replay_has_exception(void)
    123 {
    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     }
    130 
    131     return res;
    132 }
    133 
    134 bool replay_interrupt(void)
    135 {
    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     }
    149 
    150     return true;
    151 }
    152 
    153 bool replay_has_interrupt(void)
    154 {
    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;
    162 }
    163 
    164 void replay_shutdown_request(ShutdownCause cause)
    165 {
    166     if (replay_mode == REPLAY_MODE_RECORD) {
    167         g_assert(replay_mutex_locked());
    168         replay_put_event(EVENT_SHUTDOWN + cause);
    169     }
    170 }
    171 
    172 bool replay_checkpoint(ReplayCheckpoint checkpoint)
    173 {
    174     assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
    175 
    176     replay_save_instructions();
    177 
    178     if (replay_mode == REPLAY_MODE_PLAY) {
    179         g_assert(replay_mutex_locked());
    180         if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
    181             replay_finish_event();
    182         } else {
    183             return false;
    184         }
    185     } else if (replay_mode == REPLAY_MODE_RECORD) {
    186         g_assert(replay_mutex_locked());
    187         replay_put_event(EVENT_CHECKPOINT + checkpoint);
    188     }
    189     return true;
    190 }
    191 
    192 void replay_async_events(void)
    193 {
    194     static bool processing = false;
    195     /*
    196      * If we are already processing the events, recursion may occur
    197      * in case of incorrect implementation when HW event modifies timers.
    198      * Timer modification may invoke the icount warp, event processing,
    199      * and cause the recursion.
    200      */
    201     g_assert(!processing);
    202     processing = true;
    203 
    204     replay_save_instructions();
    205 
    206     if (replay_mode == REPLAY_MODE_PLAY) {
    207         g_assert(replay_mutex_locked());
    208         replay_read_events();
    209     } else if (replay_mode == REPLAY_MODE_RECORD) {
    210         g_assert(replay_mutex_locked());
    211         replay_save_events();
    212     }
    213     processing = false;
    214 }
    215 
    216 bool replay_has_event(void)
    217 {
    218     bool res = false;
    219     if (replay_mode == REPLAY_MODE_PLAY) {
    220         g_assert(replay_mutex_locked());
    221         replay_account_executed_instructions();
    222         res = EVENT_CHECKPOINT <= replay_state.data_kind
    223               && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
    224         res = res || (EVENT_ASYNC <= replay_state.data_kind
    225                      && replay_state.data_kind <= EVENT_ASYNC_LAST);
    226     }
    227     return res;
    228 }
    229 
    230 static void replay_enable(const char *fname, int mode)
    231 {
    232     const char *fmode = NULL;
    233     assert(!replay_file);
    234 
    235     switch (mode) {
    236     case REPLAY_MODE_RECORD:
    237         fmode = "wb";
    238         break;
    239     case REPLAY_MODE_PLAY:
    240         fmode = "rb";
    241         break;
    242     default:
    243         fprintf(stderr, "Replay: internal error: invalid replay mode\n");
    244         exit(1);
    245     }
    246 
    247     atexit(replay_finish);
    248 
    249     replay_file = fopen(fname, fmode);
    250     if (replay_file == NULL) {
    251         fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
    252         exit(1);
    253     }
    254 
    255     replay_filename = g_strdup(fname);
    256     replay_mode = mode;
    257     replay_mutex_init();
    258 
    259     replay_state.data_kind = -1;
    260     replay_state.instruction_count = 0;
    261     replay_state.current_icount = 0;
    262     replay_state.has_unread_data = 0;
    263 
    264     /* skip file header for RECORD and check it for PLAY */
    265     if (replay_mode == REPLAY_MODE_RECORD) {
    266         fseek(replay_file, HEADER_SIZE, SEEK_SET);
    267     } else if (replay_mode == REPLAY_MODE_PLAY) {
    268         unsigned int version = replay_get_dword();
    269         if (version != REPLAY_VERSION) {
    270             fprintf(stderr, "Replay: invalid input log file version\n");
    271             exit(1);
    272         }
    273         /* go to the beginning */
    274         fseek(replay_file, HEADER_SIZE, SEEK_SET);
    275         replay_fetch_data_kind();
    276     }
    277 
    278     replay_init_events();
    279 }
    280 
    281 void replay_configure(QemuOpts *opts)
    282 {
    283     const char *fname;
    284     const char *rr;
    285     ReplayMode mode = REPLAY_MODE_NONE;
    286     Location loc;
    287 
    288     if (!opts) {
    289         return;
    290     }
    291 
    292     loc_push_none(&loc);
    293     qemu_opts_loc_restore(opts);
    294 
    295     rr = qemu_opt_get(opts, "rr");
    296     if (!rr) {
    297         /* Just enabling icount */
    298         goto out;
    299     } else if (!strcmp(rr, "record")) {
    300         mode = REPLAY_MODE_RECORD;
    301     } else if (!strcmp(rr, "replay")) {
    302         mode = REPLAY_MODE_PLAY;
    303     } else {
    304         error_report("Invalid icount rr option: %s", rr);
    305         exit(1);
    306     }
    307 
    308     fname = qemu_opt_get(opts, "rrfile");
    309     if (!fname) {
    310         error_report("File name not specified for replay");
    311         exit(1);
    312     }
    313 
    314     replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
    315     replay_vmstate_register();
    316     replay_enable(fname, mode);
    317 
    318 out:
    319     loc_pop(&loc);
    320 }
    321 
    322 void replay_start(void)
    323 {
    324     if (replay_mode == REPLAY_MODE_NONE) {
    325         return;
    326     }
    327 
    328     if (replay_blockers) {
    329         error_reportf_err(replay_blockers->data, "Record/replay: ");
    330         exit(1);
    331     }
    332     if (!icount_enabled()) {
    333         error_report("Please enable icount to use record/replay");
    334         exit(1);
    335     }
    336 
    337     /* Timer for snapshotting will be set up here. */
    338 
    339     replay_enable_events();
    340 }
    341 
    342 void replay_finish(void)
    343 {
    344     if (replay_mode == REPLAY_MODE_NONE) {
    345         return;
    346     }
    347 
    348     replay_save_instructions();
    349 
    350     /* finalize the file */
    351     if (replay_file) {
    352         if (replay_mode == REPLAY_MODE_RECORD) {
    353             /*
    354              * Can't do it in the signal handler, therefore
    355              * add shutdown event here for the case of Ctrl-C.
    356              */
    357             replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
    358             /* write end event */
    359             replay_put_event(EVENT_END);
    360 
    361             /* write header */
    362             fseek(replay_file, 0, SEEK_SET);
    363             replay_put_dword(REPLAY_VERSION);
    364         }
    365 
    366         fclose(replay_file);
    367         replay_file = NULL;
    368     }
    369     g_free(replay_filename);
    370     replay_filename = NULL;
    371 
    372     g_free(replay_snapshot);
    373     replay_snapshot = NULL;
    374 
    375     replay_finish_events();
    376     replay_mode = REPLAY_MODE_NONE;
    377 }
    378 
    379 void replay_add_blocker(Error *reason)
    380 {
    381     replay_blockers = g_slist_prepend(replay_blockers, reason);
    382 }
    383 
    384 const char *replay_get_filename(void)
    385 {
    386     return replay_filename;
    387 }