|
|
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 |
|