[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 5/6] monitor: Add basic device state visualization
From: |
Jan Kiszka |
Subject: |
[Qemu-devel] [PATCH 5/6] monitor: Add basic device state visualization |
Date: |
Fri, 26 Aug 2011 16:48:15 +0200 |
This introduces device_show, a monitor command that saves the vmstate of
a qdev device and visualizes it. Buffers are cut after 16 byte by
default, but the full content can be requested via '-f'. To pretty-print
sub-arrays, vmstate is extended to store the start index name. A new
qerror is introduced to signal a missing vmstate. QMP is not supported
as we cannot provide a stable interface, at least at this point.
CC: Luiz Capitulino <address@hidden>
CC: Markus Armbruster <address@hidden>
Signed-off-by: Jan Kiszka <address@hidden>
---
hmp-commands.hx | 16 ++++
hw/hw.h | 2 +
hw/qdev.c | 248 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/qdev.h | 2 +
qerror.c | 4 +
qerror.h | 3 +
6 files changed, 275 insertions(+), 0 deletions(-)
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 0ccfb28..2fc5b3b 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -586,6 +586,22 @@ Remove device @var{id}.
ETEXI
{
+ .name = "device_show",
+ .args_type = "full:-f,id:s",
+ .params = "[-f] device",
+ .help = "show device state (specify -f for full buffer dumping)",
+ .user_print = device_user_print,
+ .mhandler.cmd_new = do_device_show,
+ },
+
+STEXI
address@hidden device_show address@hidden @var{id}
+
+Show state of device @var{id} in a human-readable form. Buffers are cut
+after 16 bytes unless a full dump is requested via @code{-f}.
+ETEXI
+
+ {
.name = "cpu",
.args_type = "index:i",
.params = "index",
diff --git a/hw/hw.h b/hw/hw.h
index a124da9..5bc55ac 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -304,6 +304,7 @@ enum VMStateFlags {
typedef struct {
const char *name;
+ const char *start_index;
size_t offset;
size_t size;
size_t start;
@@ -437,6 +438,7 @@ extern const VMStateInfo vmstate_info_unused_buffer;
.size = sizeof(_type), \
.flags = VMS_ARRAY, \
.offset = vmstate_offset_sub_array(_state, _field, _type, _start), \
+ .start_index = (stringify(_start)), \
}
#define VMSTATE_ARRAY_INT32_UNSAFE(_field, _state, _field_num, _info, _type) {\
diff --git a/hw/qdev.c b/hw/qdev.c
index c463c52..3b0e1af 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -29,6 +29,9 @@
#include "qdev.h"
#include "sysemu.h"
#include "monitor.h"
+#include "qjson.h"
+#include "qint.h"
+#include "qbuffer.h"
static int qdev_hotplug = 0;
static bool qdev_hot_added = false;
@@ -956,3 +959,248 @@ char* qdev_get_fw_dev_path(DeviceState *dev)
return strdup(path);
}
+
+#define NAME_COLUMN_WIDTH 23
+
+static void print_field(Monitor *mon, const QDict *qfield, int indent);
+
+static void print_elem(Monitor *mon, const QObject *qelem, size_t size,
+ int column_pos, int indent)
+{
+ int64_t data_size;
+ const void *data;
+ int n;
+
+ if (qobject_type(qelem) == QTYPE_QDICT) {
+ if (column_pos >= 0) {
+ monitor_printf(mon, ".\n");
+ }
+ } else {
+ monitor_printf(mon, ":");
+ column_pos++;
+ if (column_pos < NAME_COLUMN_WIDTH) {
+ monitor_printf(mon, "%*c", NAME_COLUMN_WIDTH - column_pos, ' ');
+ }
+ }
+
+ switch (qobject_type(qelem)) {
+ case QTYPE_QDICT:
+ print_field(mon, qobject_to_qdict(qelem), indent + 2);
+ break;
+ case QTYPE_QBUFFER:
+ data = qbuffer_get_data(qobject_to_qbuffer(qelem));
+ data_size = qbuffer_get_size(qobject_to_qbuffer(qelem));
+ for (n = 0; n < data_size; ) {
+ monitor_printf(mon, " %02x", *((uint8_t *)data+n));
+ if (++n < size) {
+ if (n % 16 == 0) {
+ monitor_printf(mon, "\n%*c", NAME_COLUMN_WIDTH, ' ');
+ } else if (n % 8 == 0) {
+ monitor_printf(mon, " -");
+ }
+ }
+ }
+ if (data_size < size) {
+ monitor_printf(mon, " ...");
+ }
+ monitor_printf(mon, "\n");
+ break;
+ case QTYPE_QINT:
+ monitor_printf(mon, " %0*" PRIx64 "\n", (int)size * 2,
+ qint_get_int(qobject_to_qint(qelem)));
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void print_field(Monitor *mon, const QDict *qfield, int indent)
+{
+ const char *name = qdict_get_str(qfield, "name");
+ const char *start = qdict_get_try_str(qfield, "start");
+ int64_t size = qdict_get_int(qfield, "size");
+ QList *qlist = qdict_get_qlist(qfield, "elems");
+ QListEntry *entry, *sub_entry;
+ QList *sub_list;
+ int elem_no = 0;
+
+ QLIST_FOREACH_ENTRY(qlist, entry) {
+ QObject *qelem = qlist_entry_obj(entry);
+ int pos = indent + strlen(name);
+
+ if (qobject_type(qelem) == QTYPE_QLIST) {
+ monitor_printf(mon, "%*c%s", indent, ' ', name);
+ if (start) {
+ pos += monitor_printf(mon, "[%s+%02x]", start, elem_no);
+ } else {
+ pos += monitor_printf(mon, "[%02x]", elem_no);
+ }
+ sub_list = qobject_to_qlist(qelem);
+ QLIST_FOREACH_ENTRY(sub_list, sub_entry) {
+ print_elem(mon, qlist_entry_obj(sub_entry), size, pos,
+ indent + 2);
+ pos = -1;
+ }
+ } else {
+ if (elem_no == 0) {
+ monitor_printf(mon, "%*c%s", indent, ' ', name);
+ } else {
+ pos = -1;
+ }
+ print_elem(mon, qelem, size, pos, indent);
+ }
+ elem_no++;
+ }
+}
+
+void device_user_print(Monitor *mon, const QObject *data)
+{
+ QDict *qdict = qobject_to_qdict(data);
+ QList *qlist = qdict_get_qlist(qdict, "fields");
+ QListEntry *entry;
+
+ monitor_printf(mon, "dev: %s, id \"%s\", version %" PRId64 "\n",
+ qdict_get_str(qdict, "device"),
+ qdict_get_str(qdict, "id"),
+ qdict_get_int(qdict, "version"));
+
+ QLIST_FOREACH_ENTRY(qlist, entry) {
+ print_field(mon, qobject_to_qdict(qlist_entry_obj(entry)), 2);
+ }
+}
+
+static size_t parse_vmstate(const VMStateDescription *vmsd, void *opaque,
+ QList *qlist, bool full_buffers)
+{
+ VMStateField *field = vmsd->fields;
+ size_t overall_size = 0;
+
+ if (vmsd->pre_save) {
+ vmsd->pre_save(opaque);
+ }
+ while (field->name) {
+ if (!field->field_exists ||
+ field->field_exists(opaque, vmsd->version_id)) {
+ void *base_addr = opaque + field->offset;
+ int i, n_elems = 1;
+ int is_array = 1;
+ size_t size = field->size;
+ size_t real_size = 0;
+ size_t dump_size;
+ QDict *qfield = qdict_new();
+ QList *qelems = qlist_new();
+
+ qlist_append_obj(qlist, QOBJECT(qfield));
+
+ qdict_put_obj(qfield, "name",
+ QOBJECT(qstring_from_str(field->name)));
+ qdict_put_obj(qfield, "elems", QOBJECT(qelems));
+
+ if (field->flags & VMS_VBUFFER) {
+ size = *(int32_t *)(opaque + field->size_offset);
+ if (field->flags & VMS_MULTIPLY) {
+ size *= field->size;
+ }
+ }
+ if (field->start_index) {
+ qdict_put_obj(qfield, "start",
+ QOBJECT(qstring_from_str(field->start_index)));
+ }
+
+ if (field->flags & VMS_ARRAY) {
+ n_elems = field->num;
+ } else if (field->flags & VMS_VARRAY_INT32) {
+ n_elems = *(int32_t *)(opaque + field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT16) {
+ n_elems = *(uint16_t *)(opaque + field->num_offset);
+ } else {
+ is_array = 0;
+ }
+ if (field->flags & VMS_POINTER) {
+ base_addr = *(void **)base_addr + field->start;
+ }
+ for (i = 0; i < n_elems; i++) {
+ void *addr = base_addr + size * i;
+ QList *sub_elems = qelems;
+ int val;
+
+ if (is_array) {
+ sub_elems = qlist_new();
+ qlist_append_obj(qelems, QOBJECT(sub_elems));
+ }
+ if (field->flags & VMS_ARRAY_OF_POINTER) {
+ addr = *(void **)addr;
+ }
+ if (field->flags & VMS_STRUCT) {
+ real_size = parse_vmstate(field->vmsd, addr,
+ sub_elems, full_buffers);
+ } else {
+ real_size = size;
+ if (field->flags & (VMS_BUFFER | VMS_VBUFFER)) {
+ dump_size = (full_buffers || size <= 16) ? size : 16;
+ qlist_append_obj(sub_elems,
+ QOBJECT(qbuffer_from_data(addr, dump_size)));
+ } else {
+ switch (size) {
+ case 1:
+ val = *(uint8_t *)addr;
+ break;
+ case 2:
+ val = *(uint16_t *)addr;
+ break;
+ case 4:
+ val = *(uint32_t *)addr;
+ break;
+ case 8:
+ val = *(uint64_t *)addr;
+ break;
+ default:
+ assert(0);
+ }
+ qlist_append_obj(sub_elems,
+ QOBJECT(qint_from_int(val)));
+ }
+ }
+ overall_size += real_size;
+ }
+ qdict_put_obj(qfield, "size", QOBJECT(qint_from_int(real_size)));
+ }
+ field++;
+ }
+ return overall_size;
+}
+
+int do_device_show(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *id = qdict_get_str(qdict, "id");
+ const VMStateDescription *vmsd;
+ DeviceState *dev;
+ QList *qlist;
+ int name_len;
+ char *name;
+
+ dev = qdev_find_recursive(main_system_bus, id);
+ if (!dev) {
+ return -1;
+ }
+
+ vmsd = dev->info->vmsd;
+ if (!vmsd || vmsd->unmigratable) {
+ qerror_report(QERR_DEVICE_NO_STATE, dev->info->name);
+ error_printf_unless_qmp("Note: device may simply lack complete qdev "
+ "conversion\n");
+ return -1;
+ }
+
+ name_len = strlen(dev->info->name) + 16;
+ name = g_malloc(name_len);
+ snprintf(name, name_len, "%s", dev->info->name);
+ *ret_data = qobject_from_jsonf("{ 'device': %s, 'id': %s, 'version': %d }",
+ name, dev->id ? : "", vmsd->version_id);
+ g_free(name);
+ qlist = qlist_new();
+ parse_vmstate(vmsd, dev, qlist, qdict_get_try_bool(qdict, "full", false));
+ qdict_put_obj(qobject_to_qdict(*ret_data), "fields", QOBJECT(qlist));
+
+ return 0;
+}
diff --git a/hw/qdev.h b/hw/qdev.h
index 8a13ec9..912fc45 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -215,6 +215,8 @@ void do_info_qtree(Monitor *mon);
void do_info_qdm(Monitor *mon);
int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
+void device_user_print(Monitor *mon, const QObject *data);
+int do_device_show(Monitor *mon, const QDict *qdict, QObject **ret_data);
/*** qdev-properties.c ***/
diff --git a/qerror.c b/qerror.c
index 3d64b80..8aed20c 100644
--- a/qerror.c
+++ b/qerror.c
@@ -105,6 +105,10 @@ static const QErrorStringTable qerror_table[] = {
.desc = "Device '%(device)' does not support hotplugging",
},
{
+ .error_fmt = QERR_DEVICE_NO_STATE,
+ .desc = "No state available for device '%(device)'",
+ },
+ {
.error_fmt = QERR_DUPLICATE_ID,
.desc = "Duplicate ID '%(id)' for %(object)",
},
diff --git a/qerror.h b/qerror.h
index 8058456..17b40ab 100644
--- a/qerror.h
+++ b/qerror.h
@@ -94,6 +94,9 @@ QError *qobject_to_qerror(const QObject *obj);
#define QERR_DEVICE_NO_HOTPLUG \
"{ 'class': 'DeviceNoHotplug', 'data': { 'device': %s } }"
+#define QERR_DEVICE_NO_STATE \
+ "{ 'class': 'DeviceNoState', 'data': { 'device': %s } }"
+
#define QERR_DUPLICATE_ID \
"{ 'class': 'DuplicateId', 'data': { 'id': %s, 'object': %s } }"
--
1.7.3.4
[Qemu-devel] [PATCH 5/6] monitor: Add basic device state visualization,
Jan Kiszka <=
[Qemu-devel] [PATCH 6/6] qdev: Generate IDs for anonymous devices, Jan Kiszka, 2011/08/26
Re: [Qemu-devel] [PATCH 0/6] Device state visualization reloaded, Anthony Liguori, 2011/08/29