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