qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RFC PATCH v3 3/6] VMstate test: basic VMState testing mech


From: Sanidhya Kashyap
Subject: [Qemu-devel] [RFC PATCH v3 3/6] VMstate test: basic VMState testing mechanism
Date: Sat, 9 Aug 2014 02:26:32 -0400

This patch implements the basic way of testing the VMStates' information
whether it is correct or not while saving and loading the states. The qmp
interface - test-vmstates can take three parameters as an input to test
the device states. Now, one can check for any of the devices that have been
registered with the SaveVMHandlers aka the migration protocol. Similarly,
the hmp interface (test_vmstates) has only two input parameters - iterations and
period.

I have removed the following from the patch on previous comments:
* replaed DPRINTF with trace_##name.
* removed the noqdev and completely removed the support for resetting of devices
based on qemu_system_reset()

As Juan pointed out that there might be a memory leak as I did not close the
QEMUFile pointer. But, it is not required as that pointer is directly referenced
by the QEMUBuffer that has been implemented by David. So, IMHO the case pointed
out by Juan does not exist.


Signed-off-by: Sanidhya Kashyap <address@hidden>
---
 hmp-commands.hx  |  16 ++++
 hmp.c            |  17 ++++
 hmp.h            |   1 +
 qapi-schema.json |  22 ++++++
 qmp-commands.hx  |  33 ++++++++
 savevm.c         | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 trace-events     |   2 +
 7 files changed, 324 insertions(+)

diff --git a/hmp-commands.hx b/hmp-commands.hx
index a221459..e16e1ac 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1790,6 +1790,22 @@ STEXI
 show available trace events and their state
 ETEXI
 
+    {
+        .name       = "test_vmstates",
+        .args_type  = "iterations:i?,period:i?",
+        .params     = "iterations period",
+        .help       = "test the vmstates by dumping and loading form 
memory\n\t\t\t"
+                      "iterations: (optional) number of times, the vmstates 
will be tested\n\t\t\t"
+                      "period: (optional) sleep interval in milliseconds 
between each iteration",
+        .mhandler.cmd = hmp_test_vmstates,
+    },
+STEXI
address@hidden test_vmstates
address@hidden test_vmstates
+dumps and reads the device state's data from the memory for testing purpose
+ETEXI
+
+
 STEXI
 @end table
 ETEXI
diff --git a/hmp.c b/hmp.c
index 6e1802a..ca7664e 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1737,3 +1737,20 @@ void hmp_info_devices(Monitor *mon, const QDict *qdict)
 
     qapi_free_QemuDeviceList(device_list);
 }
+void hmp_test_vmstates(Monitor *mon, const QDict *qdict)
+{
+    int has_iterations = qdict_haskey(qdict, "iterations");
+    int64_t iterations = qdict_get_try_int(qdict, "iterations", 10);
+    int has_period = qdict_haskey(qdict, "period");
+    int64_t period = qdict_get_try_int(qdict, "period", 100);
+
+    Error *err = NULL;
+
+    qmp_test_vmstates(has_iterations, iterations, has_period, period,
+                      false, NULL, &err);
+
+    if (err) {
+        monitor_printf(mon, "test-vmstates: %s\n", error_get_pretty(err));
+        error_free(err);
+    }
+}
diff --git a/hmp.h b/hmp.h
index 9d6b577..56fb865 100644
--- a/hmp.h
+++ b/hmp.h
@@ -95,6 +95,7 @@ void hmp_cpu_add(Monitor *mon, const QDict *qdict);
 void hmp_object_add(Monitor *mon, const QDict *qdict);
 void hmp_object_del(Monitor *mon, const QDict *qdict);
 void hmp_info_memdev(Monitor *mon, const QDict *qdict);
+void hmp_test_vmstates(Monitor *mon, const QDict *qdict);
 void object_add_completion(ReadLineState *rs, int nb_args, const char *str);
 void object_del_completion(ReadLineState *rs, int nb_args, const char *str);
 void device_add_completion(ReadLineState *rs, int nb_args, const char *str);
diff --git a/qapi-schema.json b/qapi-schema.json
index 74d26fe..4c810d7 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3506,3 +3506,25 @@
 ##
 { 'command': 'query-devices',
   'returns': [ 'QemuDevice' ] }
+
+##
+# @test-vmstates
+#
+# tests the vmstates' value by dumping and loading in memory
+#
+# @iterations: (optional) The total iterations for vmstate testing.
+# The min and max defind value is 10 and 100 respectively.
+#
+# @period: (optional) sleep interval between iteration (in milliseconds).
+# The default interval is 100 milliseconds with min and max being
+# 1 and 10000 respectively.
+#
+# @devices: (optional) helps in resetting particular device(s) that
+# have been registered with SaveStateEntry.
+#
+# Since 2.2
+##
+{ 'command': 'test-vmstates',
+  'data': {'*iterations':  'int',
+           '*period':      'int',
+           '*devices':   [ 'QemuDevice' ] } }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index e489197..ae2bdca 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -3798,3 +3798,36 @@ Example (1):
      ]
    }
 EQMP
+
+    {
+        .name       = "test-vmstates",
+        .args_type  = "iterations:i?,period:i?,devices:O?",
+        .mhandler.cmd_new = qmp_marshal_input_test_vmstates,
+    },
+
+SQMP
+test-vmstates
+-------------
+
+Tests the vmstates' entry by dumping and loading in/from memory
+
+Arguments:
+
+- "iterations": (optional) The total iterations for vmstate testing.
+                 The min and max defined value is 10 and 100 respectively.
+
+- "period": (optional) sleep interval between iteration (in milliseconds).
+            The default interval is 100 milliseconds with min and max being
+            1 and 10000 respectively.
+
+- "devices": (optional) helps in resetting particular device(s)
+             that have been registered with SaveStateEntry.
+
+Example:
+
+-> { "execute": "test-vmstates",
+     "arguments": {
+        "iterations": 10,
+        "period": 100, } }
+<- { "return": {} }
+EQMP
diff --git a/savevm.c b/savevm.c
index 764ca71..cd67c5d 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1182,6 +1182,239 @@ QemuDeviceList *qmp_query_devices(Error **errp)
     return device_list;
 }
 
+/*
+ * VMState testing
+ */
+
+#define TEST_VMSTATE_MIN_TIMES 10
+#define TEST_VMSTATE_MAX_TIMES 1000
+
+#define TEST_VMSTATE_MIN_INTERVAL_MS 1
+#define TEST_VMSTATE_DEFAULT_INTERVAL_MS 100
+#define TEST_VMSTATE_MAX_INTERVAL_MS 10000
+
+typedef struct VMStateLogState VMStateLogState;
+typedef struct DeviceInfoEntry DeviceInfoEntry;
+
+struct DeviceInfoEntry {
+    QemuDevice *device;
+    DeviceState *dev;
+    QTAILQ_ENTRY(DeviceInfoEntry) entry;
+};
+
+struct VMStateLogState {
+    int64_t current_iteration;
+    int64_t iterations;
+    int64_t period;
+    bool active_state;
+    QEMUTimer *timer;
+    QTAILQ_HEAD(device_list, DeviceInfoEntry) device_list;
+};
+
+static VMStateLogState *test_vmstates_get_current_state(void)
+{
+    static VMStateLogState current_state = {
+        .active_state = false,
+    };
+
+    return &current_state;
+}
+
+static inline void test_vmstates_clear_device_list(VMStateLogState *v)
+{
+    DeviceInfoEntry *de, *new_de;
+    QTAILQ_FOREACH_SAFE(de, &v->device_list, entry, new_de) {
+        QTAILQ_REMOVE(&v->device_list, de, entry);
+    }
+}
+
+static void test_vmstates_reset_devices(VMStateLogState *v)
+{
+    SaveStateEntry *se;
+    DeviceInfoEntry *de;
+
+    if (QTAILQ_EMPTY(&v->device_list)) {
+        QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+            if (se->dev) {
+                trace_test_vmstates_reset_devices(se->vmsd->name);
+                device_reset(se->dev);
+            }
+        }
+    } else {
+        QTAILQ_FOREACH(de, &v->device_list, entry) {
+            trace_test_vmstates_reset_devices(de->device->device_name);
+            device_reset(de->dev);
+        }
+    }
+}
+
+static inline bool test_vmstates_check_device_name(VMStateLogState *v,
+                                                   QemuDeviceList *devices,
+                                                   Error **errp)
+{
+    SaveStateEntry *se;
+    bool device_present;
+    QTAILQ_INIT(&v->device_list);
+
+    /* now, checking against each one */
+    while (devices->next) {
+        DeviceInfoEntry *new_de;
+        device_present = false;
+        QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+            if (!strcmp(se->vmsd->name, devices->value->device_name)) {
+                device_present = true;
+                break;
+            }
+        }
+        if (!device_present) {
+            test_vmstates_clear_device_list(v);
+            error_setg(errp, "Incorrect device name - %s\n",
+                       devices->value->device_name);
+            return false;
+        } else {
+            new_de = g_malloc0(sizeof(DeviceInfoEntry));
+            new_de->device = devices->value;
+            new_de->dev = se->dev;
+            QTAILQ_INSERT_TAIL(&v->device_list, new_de, entry);
+        }
+    }
+    return true;
+}
+
+static void test_vmstates_cb(void *opaque)
+{
+    VMStateLogState *v = opaque;
+    int saved_vm_running = runstate_is_running();
+    const QEMUSizedBuffer *qsb;
+    QEMUFile *f;
+    int ret;
+    int64_t save_vmstates_duration, load_vmstates_duration;
+    int64_t start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+
+    /* executing the steps for a single time with the help of timer */
+    if (++(v->current_iteration) <= v->iterations) {
+        saved_vm_running = runstate_is_running();
+
+        /* stopping the VM before dumping the vmstates */
+        vm_stop(RUN_STATE_SAVE_VM);
+
+        f = qemu_bufopen("w", NULL);
+        if (!f) {
+            goto testing_end;
+        }
+
+        cpu_synchronize_all_states();
+
+        /* saving the vmsates to memory buffer */
+        ret = qemu_save_device_state(f);
+        if (ret < 0) {
+            goto testing_end;
+        }
+        save_vmstates_duration = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
+                                 start_time;
+        trace_test_vmstates_cb(v->current_iteration, save_vmstates_duration,
+                               "save time (ms)");
+
+        qsb = qemu_buf_get(f);
+
+        /* clearing the states of the guest */
+        test_vmstates_reset_devices(v);
+
+        start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+        f = qemu_bufopen("r", (QEMUSizedBuffer *)qsb);
+        if (!f) {
+            goto testing_end;
+        }
+
+        /* loading the device states from the saved buffer */
+        ret = qemu_loadvm_state(f);
+        qemu_fclose(f);
+        if (ret < 0) {
+            goto testing_end;
+        }
+        load_vmstates_duration = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
+                                 start_time;
+        trace_test_vmstates_cb(v->current_iteration, load_vmstates_duration,
+                               "load time (ms)");
+
+        if (saved_vm_running) {
+            vm_start();
+        }
+        timer_mod(v->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) +
+                                              v->period);
+        return;
+    }
+
+ testing_end:
+    if (saved_vm_running) {
+        vm_start();
+    }
+    timer_del(v->timer);
+    timer_free(v->timer);
+    test_vmstates_clear_device_list(v);
+    v->active_state = false;
+    return;
+}
+
+void qmp_test_vmstates(bool has_iterations, int64_t iterations,
+                       bool has_period, int64_t period, bool has_devices,
+                       QemuDeviceList *devices, Error **errp)
+{
+    VMStateLogState *v = test_vmstates_get_current_state();
+    Error *local_err;
+
+    if (v->active_state) {
+        error_setg(errp, "VMState testing already in progress\n");
+        return;
+    }
+
+    v->active_state = true;
+
+    /* checking the value of total iterations to be in the defined range */
+    if (!has_iterations) {
+        v->iterations = TEST_VMSTATE_MIN_TIMES;
+    } else if (iterations >= TEST_VMSTATE_MIN_TIMES &&
+               iterations <= TEST_VMSTATE_MAX_TIMES) {
+        v->iterations = iterations;
+    } else {
+        error_setg(errp, "iterations value must be in the range [%d, %d]\n",
+                   TEST_VMSTATE_MIN_TIMES, TEST_VMSTATE_MAX_TIMES);
+        v->active_state = false;
+        return;
+    }
+
+    /* checking for the value of period to be in the defined range */
+    if (!has_period) {
+        v->period = TEST_VMSTATE_DEFAULT_INTERVAL_MS;
+    } else if (period >= TEST_VMSTATE_MIN_INTERVAL_MS &&
+               period <= TEST_VMSTATE_MAX_INTERVAL_MS) {
+        v->period = period;
+    } else {
+        error_setg(errp, "sleep interval (period) value must be "
+                   "in the defined range [%d, %d](ms)\n",
+                   TEST_VMSTATE_MIN_INTERVAL_MS, TEST_VMSTATE_MAX_INTERVAL_MS);
+        v->active_state = false;
+        return;
+    }
+
+    if (!has_devices) {
+        QTAILQ_INIT(&v->device_list);
+    } else if (!test_vmstates_check_device_name(v, devices, &local_err)) {
+        if (local_err) {
+            error_propagate(errp, local_err);
+        }
+        return;
+    }
+
+    v->current_iteration = 0;
+    v->timer = timer_new_ms(QEMU_CLOCK_REALTIME, test_vmstates_cb, v);
+    timer_mod(v->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ */
+
 void qmp_xen_save_devices_state(const char *filename, Error **errp)
 {
     QEMUFile *f;
diff --git a/trace-events b/trace-events
index 11a17a8..bb00661 100644
--- a/trace-events
+++ b/trace-events
@@ -1086,6 +1086,8 @@ vmstate_save(const char *idstr, const char *vmsd_name) 
"%s, %s"
 vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s"
 vmstate_load_field_error(const char *field, int ret) "field \"%s\" load 
failed, ret = %d"
 qemu_announce_self_iter(const char *mac) "%s"
+test_vmstates_reset_devices(const char *name) "resetting device: \"%s\""
+test_vmstates_cb(int64_t iteration, int64_t time, const char *info) 
"iteration: %ld, %s: %ld"
 
 # qemu-file.c
 qemu_file_fclose(void) ""
-- 
1.9.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]