9ae3a8
From 2f243f0313211d416fa24a1db39b923216ce267f Mon Sep 17 00:00:00 2001
9ae3a8
From: Amit Shah <amit.shah@redhat.com>
9ae3a8
Date: Fri, 11 Jul 2014 10:08:58 -0500
9ae3a8
Subject: [CHANGE 02/29] migration: dump vmstate info as a json file for static
9ae3a8
 analysis
9ae3a8
To: rhvirt-patches@redhat.com,
9ae3a8
    jen@redhat.com
9ae3a8
9ae3a8
RH-Author: Amit Shah <amit.shah@redhat.com>
9ae3a8
Message-id: <024e1bc4c9618850da0624d2d2bbe0bc106cee45.1405072585.git.amit.shah@redhat.com>
9ae3a8
Patchwork-id: 59782
9ae3a8
O-Subject: [RHEL7.1 qemu-kvm PATCH 01/18] migration: dump vmstate info as a json file for static analysis
9ae3a8
Bugzilla: 1118707
9ae3a8
RH-Acked-by: Juan Quintela <quintela@redhat.com>
9ae3a8
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
RH-Acked-by: Dr. David Alan Gilbert (git) <dgilbert@redhat.com>
9ae3a8
9ae3a8
This commit adds a new command, '-dump-vmstate', that takes a filename
9ae3a8
as an argument.  When executed, QEMU will dump the vmstate information
9ae3a8
for the machine type it's invoked with to the file, and quit.
9ae3a8
9ae3a8
The JSON-format output can then be used to compare the vmstate info for
9ae3a8
different QEMU versions, specifically to test whether live migration
9ae3a8
would break due to changes in the vmstate data.
9ae3a8
9ae3a8
A Python script that compares the output of such JSON dumps is included
9ae3a8
in the following commit.
9ae3a8
9ae3a8
Signed-off-by: Amit Shah <amit.shah@redhat.com>
9ae3a8
Reviewed-by: Juan Quintela <quintela@redhat.com>
9ae3a8
Signed-off-by: Juan Quintela <quintela@redhat.com>
9ae3a8
(cherry picked from commit abfd9ce341ec66eb2e63756b9da43f77c054788e)
9ae3a8
Signed-off-by: Amit Shah <amit.shah@redhat.com>
9ae3a8
Signed-off-by: jen <jen@redhat.com>
9ae3a8
9ae3a8
Conflicts:
9ae3a8
	include/migration/vmstate.h
9ae3a8
	vl.c
9ae3a8
9ae3a8
Signed-off-by: jen <jen@redhat.com>
9ae3a8
---
9ae3a8
 include/migration/vmstate.h |   2 +
9ae3a8
 qemu-options.hx             |  14 +++++
9ae3a8
 savevm.c                    | 135 ++++++++++++++++++++++++++++++++++++++++++++
9ae3a8
 vl.c                        |  14 +++++
9ae3a8
 4 files changed, 165 insertions(+)
9ae3a8
9ae3a8
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
9ae3a8
index f2652e6..cae26d0 100644
9ae3a8
--- a/include/migration/vmstate.h
9ae3a8
+++ b/include/migration/vmstate.h
9ae3a8
@@ -747,4 +747,6 @@ void vmstate_register_ram(struct MemoryRegion *memory, DeviceState *dev);
9ae3a8
 void vmstate_unregister_ram(struct MemoryRegion *memory, DeviceState *dev);
9ae3a8
 void vmstate_register_ram_global(struct MemoryRegion *memory);
9ae3a8
 
9ae3a8
+void dump_vmstate_json_to_file(FILE *out_fp);
9ae3a8
+
9ae3a8
 #endif
9ae3a8
diff --git a/qemu-options.hx b/qemu-options.hx
9ae3a8
index 1f2bb57..5d0f2cd 100644
9ae3a8
--- a/qemu-options.hx
9ae3a8
+++ b/qemu-options.hx
9ae3a8
@@ -3098,6 +3098,20 @@ STEXI
9ae3a8
 prepend a timestamp to each log message.(default:on)
9ae3a8
 ETEXI
9ae3a8
 
9ae3a8
+DEF("dump-vmstate", HAS_ARG, QEMU_OPTION_dump_vmstate,
9ae3a8
+    "-dump-vmstate <file>\n"
9ae3a8
+    "                Output vmstate information in JSON format to file.\n"
9ae3a8
+    "                Use the scripts/vmstate-static-checker.py file to\n"
9ae3a8
+    "                check for possible regressions in migration code\n"
9ae3a8
+    "                by comparing two such vmstate dumps.",
9ae3a8
+    QEMU_ARCH_ALL)
9ae3a8
+STEXI
9ae3a8
+@item -dump-vmstate @var{file}
9ae3a8
+@findex -dump-vmstate
9ae3a8
+Dump json-encoded vmstate information for current machine type to file
9ae3a8
+in @var{file}
9ae3a8
+ETEXI
9ae3a8
+
9ae3a8
 HXCOMM This is the last statement. Insert new options before this line!
9ae3a8
 STEXI
9ae3a8
 @end table
9ae3a8
diff --git a/savevm.c b/savevm.c
9ae3a8
index 94512dd..963dc7d 100644
9ae3a8
--- a/savevm.c
9ae3a8
+++ b/savevm.c
9ae3a8
@@ -24,6 +24,7 @@
9ae3a8
 
9ae3a8
 #include "config-host.h"
9ae3a8
 #include "qemu-common.h"
9ae3a8
+#include "hw/boards.h"
9ae3a8
 #include "hw/hw.h"
9ae3a8
 #include "hw/qdev.h"
9ae3a8
 #include "net/net.h"
9ae3a8
@@ -1484,6 +1485,140 @@ static QTAILQ_HEAD(savevm_handlers, SaveStateEntry) savevm_handlers =
9ae3a8
     QTAILQ_HEAD_INITIALIZER(savevm_handlers);
9ae3a8
 static int global_section_id;
9ae3a8
 
9ae3a8
+static void dump_vmstate_vmsd(FILE *out_file,
9ae3a8
+                              const VMStateDescription *vmsd, int indent,
9ae3a8
+                              bool is_subsection);
9ae3a8
+
9ae3a8
+static void dump_vmstate_vmsf(FILE *out_file, const VMStateField *field,
9ae3a8
+                              int indent)
9ae3a8
+{
9ae3a8
+    fprintf(out_file, "%*s{\n", indent, "");
9ae3a8
+    indent += 2;
9ae3a8
+    fprintf(out_file, "%*s\"field\": \"%s\",\n", indent, "", field->name);
9ae3a8
+    fprintf(out_file, "%*s\"version_id\": %d,\n", indent, "",
9ae3a8
+            field->version_id);
9ae3a8
+    fprintf(out_file, "%*s\"field_exists\": %s,\n", indent, "",
9ae3a8
+            field->field_exists ? "true" : "false");
9ae3a8
+    fprintf(out_file, "%*s\"size\": %zu", indent, "", field->size);
9ae3a8
+    if (field->vmsd != NULL) {
9ae3a8
+        fprintf(out_file, ",\n");
9ae3a8
+        dump_vmstate_vmsd(out_file, field->vmsd, indent, false);
9ae3a8
+    }
9ae3a8
+    fprintf(out_file, "\n%*s}", indent - 2, "");
9ae3a8
+}
9ae3a8
+
9ae3a8
+static void dump_vmstate_vmss(FILE *out_file,
9ae3a8
+                              const VMStateSubsection *subsection,
9ae3a8
+                              int indent)
9ae3a8
+{
9ae3a8
+    if (subsection->vmsd != NULL) {
9ae3a8
+        dump_vmstate_vmsd(out_file, subsection->vmsd, indent, true);
9ae3a8
+    }
9ae3a8
+}
9ae3a8
+
9ae3a8
+static void dump_vmstate_vmsd(FILE *out_file,
9ae3a8
+                              const VMStateDescription *vmsd, int indent,
9ae3a8
+                              bool is_subsection)
9ae3a8
+{
9ae3a8
+    if (is_subsection) {
9ae3a8
+        fprintf(out_file, "%*s{\n", indent, "");
9ae3a8
+    } else {
9ae3a8
+        fprintf(out_file, "%*s\"%s\": {\n", indent, "", "Description");
9ae3a8
+    }
9ae3a8
+    indent += 2;
9ae3a8
+    fprintf(out_file, "%*s\"name\": \"%s\",\n", indent, "", vmsd->name);
9ae3a8
+    fprintf(out_file, "%*s\"version_id\": %d,\n", indent, "",
9ae3a8
+            vmsd->version_id);
9ae3a8
+    fprintf(out_file, "%*s\"minimum_version_id\": %d", indent, "",
9ae3a8
+            vmsd->minimum_version_id);
9ae3a8
+    if (vmsd->fields != NULL) {
9ae3a8
+        const VMStateField *field = vmsd->fields;
9ae3a8
+        bool first;
9ae3a8
+
9ae3a8
+        fprintf(out_file, ",\n%*s\"Fields\": [\n", indent, "");
9ae3a8
+        first = true;
9ae3a8
+        while (field->name != NULL) {
9ae3a8
+            if (field->flags & VMS_MUST_EXIST) {
9ae3a8
+                /* Ignore VMSTATE_VALIDATE bits; these don't get migrated */
9ae3a8
+                field++;
9ae3a8
+                continue;
9ae3a8
+            }
9ae3a8
+            if (!first) {
9ae3a8
+                fprintf(out_file, ",\n");
9ae3a8
+            }
9ae3a8
+            dump_vmstate_vmsf(out_file, field, indent + 2);
9ae3a8
+            field++;
9ae3a8
+            first = false;
9ae3a8
+        }
9ae3a8
+        fprintf(out_file, "\n%*s]", indent, "");
9ae3a8
+    }
9ae3a8
+    if (vmsd->subsections != NULL) {
9ae3a8
+        const VMStateSubsection *subsection = vmsd->subsections;
9ae3a8
+        bool first;
9ae3a8
+
9ae3a8
+        fprintf(out_file, ",\n%*s\"Subsections\": [\n", indent, "");
9ae3a8
+        first = true;
9ae3a8
+        while (subsection->vmsd != NULL) {
9ae3a8
+            if (!first) {
9ae3a8
+                fprintf(out_file, ",\n");
9ae3a8
+            }
9ae3a8
+            dump_vmstate_vmss(out_file, subsection, indent + 2);
9ae3a8
+            subsection++;
9ae3a8
+            first = false;
9ae3a8
+        }
9ae3a8
+        fprintf(out_file, "\n%*s]", indent, "");
9ae3a8
+    }
9ae3a8
+    fprintf(out_file, "\n%*s}", indent - 2, "");
9ae3a8
+}
9ae3a8
+
9ae3a8
+static void dump_machine_type(FILE *out_file)
9ae3a8
+{
9ae3a8
+    fprintf(out_file, "  \"vmschkmachine\": {\n");
9ae3a8
+    fprintf(out_file, "    \"Name\": \"%s\"\n", current_machine->name);
9ae3a8
+    fprintf(out_file, "  },\n");
9ae3a8
+}
9ae3a8
+
9ae3a8
+void dump_vmstate_json_to_file(FILE *out_file)
9ae3a8
+{
9ae3a8
+    GSList *list, *elt;
9ae3a8
+    bool first;
9ae3a8
+
9ae3a8
+    fprintf(out_file, "{\n");
9ae3a8
+    dump_machine_type(out_file);
9ae3a8
+
9ae3a8
+    first = true;
9ae3a8
+    list = object_class_get_list(TYPE_DEVICE, true);
9ae3a8
+    for (elt = list; elt; elt = elt->next) {
9ae3a8
+        DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, elt->data,
9ae3a8
+                                             TYPE_DEVICE);
9ae3a8
+        const char *name;
9ae3a8
+        int indent = 2;
9ae3a8
+
9ae3a8
+        if (!dc->vmsd) {
9ae3a8
+            continue;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        if (!first) {
9ae3a8
+            fprintf(out_file, ",\n");
9ae3a8
+        }
9ae3a8
+        name = object_class_get_name(OBJECT_CLASS(dc));
9ae3a8
+        fprintf(out_file, "%*s\"%s\": {\n", indent, "", name);
9ae3a8
+        indent += 2;
9ae3a8
+        fprintf(out_file, "%*s\"Name\": \"%s\",\n", indent, "", name);
9ae3a8
+        fprintf(out_file, "%*s\"version_id\": %d,\n", indent, "",
9ae3a8
+                dc->vmsd->version_id);
9ae3a8
+        fprintf(out_file, "%*s\"minimum_version_id\": %d,\n", indent, "",
9ae3a8
+                dc->vmsd->minimum_version_id);
9ae3a8
+
9ae3a8
+        dump_vmstate_vmsd(out_file, dc->vmsd, indent, false);
9ae3a8
+
9ae3a8
+        fprintf(out_file, "\n%*s}", indent - 2, "");
9ae3a8
+        first = false;
9ae3a8
+    }
9ae3a8
+    fprintf(out_file, "\n}\n");
9ae3a8
+    fclose(out_file);
9ae3a8
+}
9ae3a8
+
9ae3a8
 static int calculate_new_instance_id(const char *idstr)
9ae3a8
 {
9ae3a8
     SaveStateEntry *se;
9ae3a8
diff --git a/vl.c b/vl.c
9ae3a8
index a0099f6..ca3d498 100644
9ae3a8
--- a/vl.c
9ae3a8
+++ b/vl.c
9ae3a8
@@ -2838,6 +2838,7 @@ int main(int argc, char **argv, char **envp)
9ae3a8
     };
9ae3a8
     const char *trace_events = NULL;
9ae3a8
     const char *trace_file = NULL;
9ae3a8
+    FILE *vmstate_dump_file = NULL;
9ae3a8
 
9ae3a8
     atexit(qemu_run_exit_notifiers);
9ae3a8
     error_set_progname(argv[0]);
9ae3a8
@@ -3794,6 +3795,13 @@ int main(int argc, char **argv, char **envp)
9ae3a8
                 }
9ae3a8
                 configure_msg(opts);
9ae3a8
                 break;
9ae3a8
+            case QEMU_OPTION_dump_vmstate:
9ae3a8
+                vmstate_dump_file = fopen(optarg, "w");
9ae3a8
+                if (vmstate_dump_file == NULL) {
9ae3a8
+                    fprintf(stderr, "open %s: %s\n", optarg, strerror(errno));
9ae3a8
+                    exit(1);
9ae3a8
+                }
9ae3a8
+                break;
9ae3a8
             default:
9ae3a8
                 os_parse_cmd_args(popt->index, optarg);
9ae3a8
             }
9ae3a8
@@ -4346,6 +4354,12 @@ int main(int argc, char **argv, char **envp)
9ae3a8
         }
9ae3a8
     }
9ae3a8
 
9ae3a8
+    if (vmstate_dump_file) {
9ae3a8
+        /* dump and exit */
9ae3a8
+        dump_vmstate_json_to_file(vmstate_dump_file);
9ae3a8
+        return 0;
9ae3a8
+    }
9ae3a8
+
9ae3a8
     if (incoming) {
9ae3a8
         Error *local_err = NULL;
9ae3a8
         qemu_start_incoming_migration(incoming, &local_err);
9ae3a8
-- 
9ae3a8
1.9.3
9ae3a8