diff mbox

[RFC,14/22] Command-line options for record/replay

Message ID 008201cf951f$8024e4a0$806eade0$@Dovgaluk@ispras.ru
State New
Headers show

Commit Message

Pavel Dovgalyuk July 1, 2014, 11:27 a.m. UTC
This patch introduces command-line options for record and replay.

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@gmail.com>
---

     "                do not load default config files at startup\n",
@@ -3333,6 +3334,37 @@ STEXI
 @findex -msg
 prepend a timestamp to each log message.(default:on)
 ETEXI
+DEF("record", HAS_ARG, QEMU_OPTION_record,
+    "-record
fname=<filename>[,period=<period>,suffix=<suffix>,snapshot=<on/off>,debug=<on/off>,rtc=<period>,icou
nt=<icount>]\n"
+    "                writes replay file for latter replaying\n",
+    QEMU_ARCH_ALL)
+STEXI
+@item -record
fname=@var{file}[,period=@var{period},suffix=@var{suffix},snapshot=@var{snapshot},debug=@var{debug},
rtc=@var{rtc},icount=@var{icount}]
+Writes compact execution trace into @var{file}.
+VM state is auto saved every @var{period} second, 
+if this parameter is specified. Changes for disk images are written
+into separate files with @var{suffix} added. If no @var{suffix} is
+specified, "replay_qcow" is used as suffix.
+If @var{snapshot} parameter is set as off, then original disk image will be
+modified. Default value is on.
+@var{rtc} parameter enables periodic saving of RTC into the log.
+These RTC values can be used for log file navigation later.
+@var{icount} parameter is used for vm clock emulation.
+ETEXI
+
+DEF("replay", HAS_ARG, QEMU_OPTION_replay,
+    "-replay fname=<filename>[,suffix=<suffix>,snapshot=<on/off>,icount=<icount>]\n"
+    "                plays saved replay file\n", QEMU_ARCH_ALL)
+STEXI
+@item -replay
fname=@var{filename}[,suffix=@var{suffix},snapshot=@var{snapshot},icount=@var{icount}]
+Plays compact execution trace from @var{filename}.
+Changes for disk images and VM states are read
+from separate files with @var{suffix} added. If no @var{suffix} is
+specified, "replay_qcow" is used as suffix.
+If @var{snapshot} parameter is set as off, then original disk image will be
+modified. Default value is on.
+@var{icount} parameter is used for vm clock emulation.
+ETEXI
 
 DEF("dump-vmstate", HAS_ARG, QEMU_OPTION_dump_vmstate,
     "-dump-vmstate <file>\n"
diff mbox

Patch

diff --git a/vl.c b/vl.c
index 41ddcd2..adab76d
--- a/vl.c
+++ b/vl.c
@@ -118,6 +118,7 @@  int main(int argc, char **argv)
 #include "qapi/opts-visitor.h"
 #include "qom/object_interfaces.h"
 #include "qapi-event.h"
+#include "replay/replay.h"
 
 #define DEFAULT_RAM_SIZE 128
 
@@ -488,6 +489,63 @@  static QemuOptsList qemu_msg_opts = {
     },
 };
 
+static QemuOptsList qemu_record_opts = {
+    .name = "record",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_record_opts.head),
+    .desc = {
+        {
+            .name = "fname",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "period",
+            .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "suffix",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "snapshot",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "debug",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "rtc",
+            .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "icount",
+            .type = QEMU_OPT_NUMBER,
+        },
+        { /* end of list */ }
+    },
+};
+
+
+static QemuOptsList qemu_replay_opts = {
+    .name = "replay",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_replay_opts.head),
+    .desc = {
+        {
+            .name = "fname",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "suffix",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "snapshot",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "debug",
+            .type = QEMU_OPT_BOOL,
+        },{
+            .name = "icount",
+            .type = QEMU_OPT_NUMBER,
+        },
+        { /* end of list */ }
+    },
+};
+
+
+
 static QemuOptsList qemu_name_opts = {
     .name = "name",
     .implied_opt_name = "guest",
@@ -547,6 +605,7 @@  QemuOpts *qemu_get_machine_opts(void)
     return qemu_find_opts_singleton("machine");
 }
 
+
 const char *qemu_get_vm_name(void)
 {
     return qemu_name;
@@ -757,7 +816,7 @@  void vm_start(void)
 
 /***********************************************************/
 /* host time/date access */
-void qemu_get_timedate(struct tm *tm, int offset)
+void qemu_get_timedate_no_warning(struct tm *tm, int offset)
 {
     time_t ti;
 
@@ -774,9 +833,25 @@  void qemu_get_timedate(struct tm *tm, int offset)
     }
 }
 
+/* host time/date access */
+void qemu_get_timedate(struct tm *tm, int offset)
+{
+    if (replay_mode == REPLAY_SAVE)
+        fprintf(stderr, "REPLAY WARNING! qemu_get_timedate function may cause some troubles\n");
+    qemu_get_timedate_no_warning(tm,offset);
+}
+
 int qemu_timedate_diff(struct tm *tm)
 {
     time_t seconds;
+    time_t systime = time(NULL);
+
+    if (replay_mode == REPLAY_SAVE) {
+        replay_save_time_t(systime);
+    } else if (replay_mode == REPLAY_PLAY
+               && replay_get_play_submode() != REPLAY_PLAY_CHANGED) {
+        systime = replay_read_time_t();
+    }
 
     if (rtc_date_offset == -1)
         if (rtc_utc)
@@ -789,13 +864,23 @@  int qemu_timedate_diff(struct tm *tm)
     else
         seconds = mktimegm(tm) + rtc_date_offset;
 
-    return seconds - time(NULL);
+    return seconds - systime;
 }
 
 static void configure_rtc_date_offset(const char *startdate, int legacy)
 {
     time_t rtc_start_date;
     struct tm tm;
+    time_t systime = time(NULL);
+
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_save_time_t(systime);
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        systime = replay_read_time_t();
+    }
 
     if (!strcmp(startdate, "now") && legacy) {
         rtc_date_offset = -1;
@@ -827,7 +912,7 @@  static void configure_rtc_date_offset(const char *startdate, int legacy)
                             "'2006-06-17T16:01:21' or '2006-06-17'\n");
             exit(1);
         }
-        rtc_date_offset = time(NULL) - rtc_start_date;
+        rtc_date_offset = systime - rtc_start_date;
     }
 }
 
@@ -1130,7 +1215,7 @@  static int cleanup_add_fd(QemuOpts *opts, void *opaque)
 static int drive_init_func(QemuOpts *opts, void *opaque)
 {
     BlockInterfaceType *block_default_type = opaque;
-
+    
     return drive_new(opts, *block_default_type) == NULL;
 }
 
@@ -1852,10 +1937,11 @@  void qemu_system_reset_request(void)
 {
     if (no_reboot) {
         shutdown_requested = 1;
+        cpu_stop_current();
     } else {
         reset_requested = 1;
+        cpu_exit_current();
     }
-    cpu_stop_current();
     qemu_notify_event();
 }
 
@@ -1919,12 +2005,18 @@  void qemu_system_killed(int signal, pid_t pid)
     qemu_system_shutdown_request();
 }
 
-void qemu_system_shutdown_request(void)
+void qemu_system_shutdown_request_impl(void)
 {
     shutdown_requested = 1;
     qemu_notify_event();
 }
 
+void qemu_system_shutdown_request(void)
+{
+    replay_shutdown_request();
+    qemu_system_shutdown_request_impl();
+}
+
 static void qemu_system_powerdown(void)
 {
     qapi_event_send_powerdown(&error_abort);
@@ -1966,7 +2058,8 @@  static bool main_loop_should_exit(void)
             return true;
         }
     }
-    if (qemu_reset_requested()) {
+    if (qemu_reset_requested_get() && replay_checkpoint(7)) {
+        qemu_reset_requested();
         pause_all_vcpus();
         cpu_synchronize_all_states();
         qemu_system_reset(VMRESET_REPORT);
@@ -2504,7 +2597,8 @@  static int serial_parse(const char *devname)
         exit(1);
     }
     snprintf(label, sizeof(label), "serial%d", index);
-    serial_hds[index] = qemu_chr_new(label, devname, NULL);
+    serial_hds[index] = (replay_mode == REPLAY_NONE ? qemu_chr_new : qemu_chr_new_replay)
+                        (label, devname, NULL);
     if (!serial_hds[index]) {
         fprintf(stderr, "qemu: could not connect serial device"
                 " to character backend '%s'\n", devname);
@@ -2895,7 +2989,7 @@  out:
 int main(int argc, char **argv, char **envp)
 {
     int i;
-    int snapshot, linux_boot;
+    int snapshot, linux_boot, replay_snapshot;
     const char *icount_option = NULL;
     const char *initrd_filename;
     const char *kernel_filename, *kernel_cmdline;
@@ -2928,6 +3022,7 @@  int main(int argc, char **argv, char **envp)
     };
     const char *trace_events = NULL;
     const char *trace_file = NULL;
+
     const ram_addr_t default_ram_size = (ram_addr_t)DEFAULT_RAM_SIZE *
                                         1024 * 1024;
     ram_addr_t maxram_size = default_ram_size;
@@ -2967,6 +3062,8 @@  int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_msg_opts);
     qemu_add_opts(&qemu_name_opts);
     qemu_add_opts(&qemu_numa_opts);
+    qemu_add_opts(&qemu_replay_opts);
+    qemu_add_opts(&qemu_record_opts);
 
     runstate_init();
 
@@ -2980,6 +3077,7 @@  int main(int argc, char **argv, char **envp)
     cpu_model = NULL;
     ram_size = default_ram_size;
     snapshot = 0;
+    replay_snapshot = 1;
     cyls = heads = secs = 0;
     translation = BIOS_ATA_TRANSLATION_AUTO;
 
@@ -3097,6 +3195,7 @@  int main(int argc, char **argv, char **envp)
                 break;
             case QEMU_OPTION_pflash:
                 drive_add(IF_PFLASH, -1, optarg, PFLASH_OPTS);
+                not_compatible_replay_param++;
                 break;
             case QEMU_OPTION_snapshot:
                 snapshot = 1;
@@ -3253,6 +3352,7 @@  int main(int argc, char **argv, char **envp)
 #endif
             case QEMU_OPTION_bt:
                 add_device_config(DEV_BT, optarg);
+                not_compatible_replay_param++;
                 break;
             case QEMU_OPTION_audio_help:
                 AUD_help ();
@@ -3466,6 +3566,7 @@  int main(int argc, char **argv, char **envp)
                 if (!opts) {
                     exit(1);
                 }
+                not_compatible_replay_param++;
                 break;
             case QEMU_OPTION_fsdev:
                 olist = qemu_find_opts("fsdev");
@@ -3594,6 +3695,7 @@  int main(int argc, char **argv, char **envp)
                 if (strncmp(optarg, "mon:", 4) == 0) {
                     default_monitor = 0;
                 }
+                not_compatible_replay_param++;
                 break;
             case QEMU_OPTION_debugcon:
                 add_device_config(DEV_DEBUGCON, optarg);
@@ -3714,6 +3816,7 @@  int main(int argc, char **argv, char **envp)
                 if (!qemu_opts_parse(qemu_find_opts("smp-opts"), optarg, 1)) {
                     exit(1);
                 }
+                not_compatible_replay_param++;
                 break;
 	    case QEMU_OPTION_vnc:
 #ifdef CONFIG_VNC
@@ -3948,6 +4051,28 @@  int main(int argc, char **argv, char **envp)
                     exit(1);
                 }
                 break;
+            case QEMU_OPTION_record:
+                {
+                    opts = qemu_opts_parse(qemu_find_opts("record"), optarg, 0);
+                    if (!opts) {
+                        fprintf(stderr, "Invalid record options: %s\n", optarg);
+                        exit(1);
+                    }
+                    replay_configure(opts, REPLAY_SAVE);
+                    replay_snapshot = qemu_opt_get_bool(opts, "snapshot", 1);
+                    break;
+                }
+            case QEMU_OPTION_replay:
+                {
+                    opts = qemu_opts_parse(qemu_find_opts("replay"), optarg, 0);
+                    if (!opts) {
+                        fprintf(stderr, "Invalid replay options: %s\n", optarg);
+                        exit(1);
+                    }
+                    replay_configure(opts, REPLAY_PLAY);
+                    replay_snapshot = qemu_opt_get_bool(opts, "snapshot", 1);
+                    break;
+                }
             default:
                 os_parse_cmd_args(popt->index, optarg);
             }
@@ -3961,7 +4086,13 @@  int main(int argc, char **argv, char **envp)
         fprintf(stderr, "qemu_init_main_loop failed\n");
         exit(1);
     }
-
+    
+    if (not_compatible_replay_param && (replay_mode != REPLAY_NONE))
+    {
+        fprintf(stderr, "options -smp, -pflash, -chardev, -bt, -parallel not compatible with
replay\n");
+        exit(1);
+    }
+    
     if (qemu_opts_foreach(qemu_find_opts("sandbox"), parse_sandbox, NULL, 0)) {
         exit(1);
     }
@@ -4329,7 +4460,7 @@  int main(int argc, char **argv, char **envp)
     ram_mig_init();
 
     /* open the virtual block devices */
-    if (snapshot)
+    if (snapshot || (replay_mode != REPLAY_NONE && replay_snapshot))
         qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0);
     if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func,
                           &machine_class->block_default_type, 1) != 0) {
@@ -4497,7 +4628,6 @@  int main(int argc, char **argv, char **envp)
             autostart = 0;
         }
     }
-
     qdev_prop_check_global();
     if (vmstate_dump_file) {
         /* dump and exit */
@@ -4525,8 +4655,13 @@  int main(int argc, char **argv, char **envp)
             exit(1);
         }
     }
+ 
+    replay_init_timer();
 
     main_loop();
+    if (replay_mode != REPLAY_NONE) {
+        replay_disable_events();
+    }
     bdrv_close_all();
     pause_all_vcpus();
     res_free();

diff --git a/qemu-options.hx b/qemu-options.hx
index 9e54686..c51d196
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3228,6 +3228,7 @@  Write device configuration to @var{file}. The @var{file} can be either
filename
 command line and device configuration into file or dash @code{-}) character to print the
 output to stdout. This can be later used as input file for @code{-readconfig} option.
 ETEXI
+
 DEF("nodefconfig", 0, QEMU_OPTION_nodefconfig,
     "-nodefconfig\n"