From: Jan Kiszka <jan.kiszka@web.de>
To: Markus Armbruster <armbru@redhat.com>
Cc: Anthony Liguori <aliguori@us.ibm.com>,
Juan Quintela <quintela@redhat.com>, Avi Kivity <avi@redhat.com>,
qemu-devel@nongnu.org, Luiz Capitulino <lcapitulino@redhat.com>
Subject: [Qemu-devel] Re: [PATCH 4/8] monitor: Add basic device state visualization
Date: Tue, 18 May 2010 19:09:00 +0200 [thread overview]
Message-ID: <4BF2C9AC.4060601@web.de> (raw)
In-Reply-To: <m3vdalmmsr.fsf@blackfin.pond.sub.org>
[-- Attachment #1: Type: text/plain, Size: 14996 bytes --]
Markus Armbruster wrote:
> Jan Kiszka <jan.kiszka@siemens.com> writes:
>
>> This introduces device_show, a monitor command that saves the vmstate of
>> a qdev device and visualizes it. QMP is also supported. 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.
>>
>> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
>
> Neato! Comments inline.
>
>> ---
>> hw/hw.h | 2 +
>> hw/qdev.c | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> hw/qdev.h | 2 +
>> qemu-monitor.hx | 16 +++
>> 4 files changed, 307 insertions(+), 0 deletions(-)
>>
>> diff --git a/hw/hw.h b/hw/hw.h
>> index 328b704..1ff8e40 100644
>> --- a/hw/hw.h
>> +++ b/hw/hw.h
>> @@ -299,6 +299,7 @@ enum VMStateFlags {
>>
>> typedef struct {
>> const char *name;
>> + const char *start_index;
>> size_t offset;
>> size_t size;
>> size_t start;
>
> Why is start_index a string?
It can, actually should be a symbolic constant (from e1000:
"mac_reg[RA+03]: ...", ie. "RA" is the start index here).
>
>> @@ -414,6 +415,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_VARRAY_INT32(_field, _state, _field_num, _version, _info, _type) {\
>> diff --git a/hw/qdev.c b/hw/qdev.c
>> index fe49e71..c989010 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;
>>
>> @@ -824,3 +827,287 @@ int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
>> }
>> return qdev_unplug(dev);
>> }
>> +
>> +static int print_array_index(Monitor *mon, const char *start, int index)
>> +{
>> + char index_str[32];
>> + int len;
>> +
>> + if (start) {
>> + len = snprintf(index_str, sizeof(index_str), "[%s+%02x]", start,
>> + index);
>> + } else {
>> + len = snprintf(index_str, sizeof(index_str), "[%02x]", index);
>> + }
>> + monitor_printf(mon, index_str);
>> + return len;
>
> You go via the snprintf() detour just because monitor_printf() doesn't
> return the number of characters printed. Wouldn't it be better to fix
> that?
Yeah, good point. Will address this.
>
>> +}
>> +
>> +#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*x\n", (int)size * 2,
>> + (int)qint_get_int(qobject_to_qint(qelem)));
>
> I'm confused. Doesn't casting qint_get_int() to int lose bits?
You are right, will fix!
>
>> + 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);
>> + pos += print_array_index(mon, start, 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\"\n",
>> + qdict_get_str(qdict, "device"),
>> + qdict_get_str(qdict, "id"));
>> +
>> + QLIST_FOREACH_ENTRY(qlist, entry) {
>> + print_field(mon, qobject_to_qdict(qlist_entry_obj(entry)), 2);
>> + }
>> +}
>> +
>> +static void parse_vmstate(const VMStateDescription *vmsd, void *opaque,
>> + QList *qlist, int full_buffers)
>> +{
>> + VMStateField *field = vmsd->fields;
>> +
>> + 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;
>> + 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;
>> + }
>> + }
>> + qdict_put_obj(qfield, "size", QOBJECT(qint_from_int(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) {
>> + parse_vmstate(field->vmsd, addr, sub_elems, full_buffers);
>> + } else {
>> + if (field->flags & (VMS_BUFFER | VMS_VBUFFER)) {
>> + if (!full_buffers && size > 16) {
>> + size = 16;
>> + }
>> + qlist_append_obj(sub_elems,
>> + QOBJECT(qbuffer_from_data(addr,
>> + 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)));
>> + }
>> + }
>> + }
>> + }
>> + field++;
>> + }
>> +}
>> +
>> +static DeviceState *qdev_find(const char *path)
>> +{
>> + const char *dev_name;
>> + DeviceState *dev;
>> + char *bus_path;
>> + BusState *bus;
>> +
>> + dev_name = strrchr(path, '/');
>> + if (!dev_name) {
>> + bus = main_system_bus;
>> + dev_name = path;
>> + } else {
>> + dev_name++;
>> + bus_path = qemu_strdup(path);
>> + bus_path[dev_name - path] = 0;
>> +
>> + bus = qbus_find(bus_path);
>> + qemu_free(bus_path);
>> +
>> + if (!bus) {
>> + /* qbus_find already reported the error */
>> + return NULL;
>> + }
>> + }
>> + dev = qbus_find_dev(bus, dev_name);
>> + if (!dev) {
>> + qerror_report(QERR_DEVICE_NOT_FOUND, dev_name);
>> + if (!monitor_cur_is_qmp()) {
>> + qbus_list_dev(bus);
>> + }
>> + }
>> + return dev;
>> +}
>> +
>> +int do_device_show(Monitor *mon, const QDict *qdict, QObject **ret_data)
>
> Command documentation missing.
In the make (depends on QMP doc infrastructure).
>
>> +{
>> + const char *path = qdict_get_str(qdict, "path");
>> + const VMStateDescription *vmsd;
>> + DeviceState *dev;
>> + QList *qlist;
>> +
>> + dev = qdev_find_recursive(main_system_bus, path);
>> + if (!dev) {
>> + dev = qdev_find(path);
>> + if (!dev) {
>> + return -1;
>> + }
>> + }
>
> This is inconsistent with how do_device_del() looks up devices by ID.
> Let's agree on one lookup, put it into a function, and use it
> everywhere.
Will adjust device_del to the same scheme.
BTW, qdev path completion will also be available for both commands in my
next series.
>
>> +
>> + vmsd = dev->info->vmsd;
>> + if (!vmsd) {
>> + qerror_report(QERR_UNDEFINED_ERROR);
>> + if (!monitor_cur_is_qmp()) {
>> + error_printf_unless_qmp("Device '%s' does not support state "
>> + "dumping\n", path);
>> + }
>
> Please use a suitable QERR_. Define one if necessary.
Already fixed in my tree (forgot this bit for v1). Just this redundant
qmp check was still present.
>
>> + return -1;
>> + }
>> +
>> + *ret_data = qobject_from_jsonf("{ 'device': %s, 'id': %s }",
>> + dev->info->name, dev->id ? : "");
>> + qlist = qlist_new();
>> + parse_vmstate(vmsd, dev, qlist, qdict_get_int(qdict, "full"));
>> + qdict_put_obj(qobject_to_qdict(*ret_data), "fields", QOBJECT(qlist));
>> +
>> + return 0;
>> +}
>> diff --git a/hw/qdev.h b/hw/qdev.h
>> index d8fbc73..f8436ec 100644
>> --- a/hw/qdev.h
>> +++ b/hw/qdev.h
>> @@ -177,6 +177,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/qemu-monitor.hx b/qemu-monitor.hx
>> index a8f194c..449f012 100644
>> --- a/qemu-monitor.hx
>> +++ b/qemu-monitor.hx
>> @@ -599,6 +599,22 @@ Remove device @var{id}.
>> ETEXI
>>
>> {
>> + .name = "device_show",
>> + .args_type = "path:s,full:-f",
>> + .params = "device [-f]",
>> + .help = "show device state (specify -f for full buffer dumping)",
>> + .user_print = device_user_print,
>> + .mhandler.cmd_new = do_device_show,
>> + },
>> +
>> +STEXI
>> +@item device_show @var{id} [@code{-f}]
>> +
>> +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",
>
>
Thanks for the comments!
Jan
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 257 bytes --]
next prev parent reply other threads:[~2010-05-18 17:18 UTC|newest]
Thread overview: 53+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-05-14 13:20 [Qemu-devel] [PATCH 0/8] Basic device state visualization Jan Kiszka
2010-05-14 13:20 ` [Qemu-devel] [PATCH 1/8] qdev: Allow device addressing via 'driver.instance' Jan Kiszka
2010-05-18 12:15 ` Markus Armbruster
2010-05-18 12:31 ` Gerd Hoffmann
2010-05-18 12:38 ` [Qemu-devel] " Juan Quintela
2010-05-18 13:06 ` Gerd Hoffmann
2010-05-18 16:54 ` Jan Kiszka
2010-05-19 8:29 ` [Qemu-devel] " Avi Kivity
2010-05-14 13:20 ` [Qemu-devel] [PATCH 2/8] Add base64 encoder/decoder Jan Kiszka
2010-05-14 13:20 ` [Qemu-devel] [PATCH 3/8] Add QBuffer Jan Kiszka
2010-05-14 18:15 ` Anthony Liguori
2010-05-15 8:45 ` [Qemu-devel] " Jan Kiszka
2010-05-15 8:49 ` Avi Kivity
2010-05-15 8:59 ` Jan Kiszka
2010-05-15 17:31 ` Avi Kivity
2010-05-16 9:37 ` Paolo Bonzini
2010-05-16 9:50 ` Avi Kivity
2010-05-16 10:15 ` Jan Kiszka
2010-05-16 10:16 ` Paolo Bonzini
2010-05-16 10:49 ` Avi Kivity
2010-05-16 10:04 ` Jan Kiszka
2010-05-16 17:38 ` [Qemu-devel] " Jamie Lokier
2010-05-16 18:03 ` [Qemu-devel] " Jan Kiszka
2010-05-17 20:20 ` Jamie Lokier
2010-05-17 0:12 ` [Qemu-devel] " Anthony Liguori
2010-05-17 6:48 ` Avi Kivity
2010-05-17 7:40 ` [Qemu-devel] " Jan Kiszka
2010-05-17 7:45 ` Avi Kivity
2010-05-17 7:57 ` Jan Kiszka
2010-05-17 8:10 ` Avi Kivity
2010-05-17 8:13 ` Avi Kivity
2010-05-17 8:55 ` Jan Kiszka
2010-05-17 8:59 ` Avi Kivity
2010-05-17 9:17 ` Jan Kiszka
2010-05-17 9:29 ` Avi Kivity
2010-05-18 12:27 ` Markus Armbruster
2010-05-18 17:24 ` Avi Kivity
2010-05-17 13:05 ` Anthony Liguori
2010-05-18 12:28 ` Markus Armbruster
2010-05-14 13:20 ` [Qemu-devel] [PATCH 4/8] monitor: Add basic device state visualization Jan Kiszka
2010-05-18 12:12 ` Markus Armbruster
2010-05-18 17:09 ` Jan Kiszka [this message]
2010-05-14 13:20 ` [Qemu-devel] [PATCH 5/8] qmp: Teach basic capability negotiation to python example Jan Kiszka
2010-05-14 13:20 ` [Qemu-devel] [PATCH 6/8] qmp: Fix python helper /wrt long return strings Jan Kiszka
2010-05-14 13:20 ` [Qemu-devel] [PATCH 7/8] Add QLIST_INSERT_TAIL Jan Kiszka
2010-05-16 9:38 ` [Qemu-devel] " Paolo Bonzini
2010-05-16 10:16 ` Jan Kiszka
2010-05-14 13:20 ` [Qemu-devel] [PATCH 8/8] qdev: Add new devices/buses at the tail Jan Kiszka
2010-05-14 16:12 ` [Qemu-devel] Re: [PATCH 0/8] Basic device state visualization Avi Kivity
2010-05-14 16:24 ` Jan Kiszka
2010-05-14 16:38 ` Avi Kivity
2010-05-14 18:16 ` [Qemu-devel] " Anthony Liguori
2010-05-14 18:50 ` Blue Swirl
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4BF2C9AC.4060601@web.de \
--to=jan.kiszka@web.de \
--cc=aliguori@us.ibm.com \
--cc=armbru@redhat.com \
--cc=avi@redhat.com \
--cc=lcapitulino@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=quintela@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.