Add x-query-qtree QMP command returning HumanReadableText with the device tree. Supports an optional "brief" parameter to omit device properties, matching the -b flag of "info qtree".
Refactor the BusClass print_dev callback and qbus_print/qdev_print functions to write to a GString buffer instead of requiring a Monitor, so the QMP command can collect the output directly without needing monitor internals. Rewire hmp_info_qtree to use the new QMP command. Eventually, qom-list and properties retrieval should be enhanced to do tree traversal and make this pretty-printing method obsolete and return structured data instead. Signed-off-by: Marc-André Lureau <[email protected]> --- hw/char/virtio-serial-bus.c | 23 ++++++++-------------- hw/core/sysbus.c | 17 +++++------------ hw/misc/auxbus.c | 22 ++++++++------------- hw/pci/pci-hmp-cmds.c | 38 ------------------------------------- hw/pci/pci-internal.h | 2 +- hw/pci/pci.c | 40 +++++++++++++++++++++++++++++++++++++-- hw/usb/bus.c | 23 ++++++++-------------- hw/xen/xen-bus.c | 12 +++--------- include/hw/core/qdev.h | 4 +--- qapi/qdev.json | 31 ++++++++++++++++++++++++++++++ rust/bindings/hwcore-sys/lib.rs | 2 +- system/qdev-monitor.c | 42 +++++++++++++++++++++++++++-------------- 12 files changed, 132 insertions(+), 124 deletions(-) diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index c36e18a4cfb5..af438316f1a4 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -24,8 +24,6 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "migration/qemu-file-types.h" -#include "monitor/monitor.h" -#include "monitor/hmp.h" #include "qemu/error-report.h" #include "qemu/queue.h" #include "hw/core/qdev-properties.h" @@ -818,9 +816,7 @@ static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, return 0; } -#ifdef CONFIG_HMP -static void virtser_bus_dev_print(MonitorHMP *hmp, DeviceState *qdev, int indent); -#endif +static void virtser_bus_dev_print(GString *buf, DeviceState *qdev, int indent); static const Property virtser_props[] = { DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID), @@ -829,10 +825,8 @@ static const Property virtser_props[] = { static void virtser_bus_class_init(ObjectClass *klass, const void *data) { -#ifdef CONFIG_HMP BusClass *k = BUS_CLASS(klass); k->print_dev = virtser_bus_dev_print; -#endif } static const TypeInfo virtser_bus_info = { @@ -842,18 +836,17 @@ static const TypeInfo virtser_bus_info = { .class_init = virtser_bus_class_init, }; -#ifdef CONFIG_HMP -static void virtser_bus_dev_print(MonitorHMP *hmp, DeviceState *qdev, int indent) +static void virtser_bus_dev_print(GString *buf, DeviceState *qdev, int indent) { VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(qdev); - monitor_hmp_printf(hmp, "%*sport %d, guest %s, host %s, throttle %s\n", - indent, "", port->id, - port->guest_connected ? "on" : "off", - port->host_connected ? "on" : "off", - port->throttled ? "on" : "off"); + g_string_append_printf(buf, + "%*sport %d, guest %s, host %s, throttle %s\n", + indent, "", port->id, + port->guest_connected ? "on" : "off", + port->host_connected ? "on" : "off", + port->throttled ? "on" : "off"); } -#endif /* This function is only used if a port id is not provided by the user */ static uint32_t find_free_port_id(VirtIOSerial *vser) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index fe8f867a8d9c..3a5fd52192d8 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -20,13 +20,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/core/sysbus.h" -#include "monitor/monitor.h" -#include "monitor/hmp.h" #include "system/address-spaces.h" -#ifdef CONFIG_HMP -static void sysbus_dev_print(MonitorHMP *hmp, DeviceState *dev, int indent); -#endif +static void sysbus_dev_print(GString *buf, DeviceState *dev, int indent); static char *sysbus_get_fw_dev_path(DeviceState *dev); typedef struct SysBusFind { @@ -78,9 +74,7 @@ static void system_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); -#ifdef CONFIG_HMP k->print_dev = sysbus_dev_print; -#endif k->get_fw_dev_path = sysbus_get_fw_dev_path; } @@ -253,8 +247,7 @@ bool sysbus_realize_and_unref(SysBusDevice *dev, Error **errp) return qdev_realize_and_unref(DEVICE(dev), sysbus_get_default(), errp); } -#ifdef CONFIG_HMP -static void sysbus_dev_print(MonitorHMP *hmp, DeviceState *dev, int indent) +static void sysbus_dev_print(GString *buf, DeviceState *dev, int indent) { SysBusDevice *s = SYS_BUS_DEVICE(dev); hwaddr size; @@ -262,11 +255,11 @@ static void sysbus_dev_print(MonitorHMP *hmp, DeviceState *dev, int indent) for (i = 0; i < s->num_mmio; i++) { size = memory_region_size(s->mmio[i].memory); - monitor_hmp_printf(hmp, "%*smmio " HWADDR_FMT_plx "/" HWADDR_FMT_plx "\n", - indent, "", s->mmio[i].addr, size); + g_string_append_printf(buf, "%*smmio " + HWADDR_FMT_plx "/" HWADDR_FMT_plx "\n", + indent, "", s->mmio[i].addr, size); } } -#endif static char *sysbus_get_fw_dev_path(DeviceState *dev) { diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index 3f17784d9b8a..17033433147a 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -32,8 +32,6 @@ #include "qemu/module.h" #include "hw/misc/auxbus.h" #include "hw/i2c/i2c.h" -#include "monitor/monitor.h" -#include "monitor/hmp.h" #include "qapi/error.h" #ifndef DEBUG_AUX @@ -47,22 +45,18 @@ } while (0) -#ifdef CONFIG_HMP -static void aux_slave_dev_print(MonitorHMP *hmp, DeviceState *dev, int indent); -#endif +static void aux_slave_dev_print(GString *buf, DeviceState *dev, int indent); static inline I2CBus *aux_bridge_get_i2c_bus(AUXTOI2CState *bridge); /* aux-bus implementation (internal not public) */ static void aux_bus_class_init(ObjectClass *klass, const void *data) { -#ifdef CONFIG_HMP BusClass *k = BUS_CLASS(klass); /* AUXSlave has an MMIO so we need to change the way we print information * in monitor. */ k->print_dev = aux_slave_dev_print; -#endif } AUXBus *aux_bus_init(DeviceState *parent, const char *name) @@ -287,13 +281,12 @@ static const TypeInfo aux_to_i2c_type_info = { }; /* aux-slave implementation */ -#ifdef CONFIG_HMP static bool aux_bus_is_bridge(AUXBus *bus, DeviceState *dev) { return (dev == DEVICE(bus->bridge)); } -static void aux_slave_dev_print(MonitorHMP *hmp, DeviceState *dev, int indent) +static void aux_slave_dev_print(GString *buf, DeviceState *dev, int indent) { AUXBus *bus = AUX_BUS(qdev_get_parent_bus(dev)); AUXSlave *s; @@ -305,12 +298,13 @@ static void aux_slave_dev_print(MonitorHMP *hmp, DeviceState *dev, int indent) s = AUX_SLAVE(dev); - monitor_hmp_printf(hmp, "%*smemory " HWADDR_FMT_plx "/" HWADDR_FMT_plx "\n", - indent, "", - object_property_get_uint(OBJECT(s->mmio), "addr", NULL), - memory_region_size(s->mmio)); + g_string_append_printf(buf, + "%*smemory " HWADDR_FMT_plx "/" HWADDR_FMT_plx "\n", + indent, "", + object_property_get_uint(OBJECT(s->mmio), + "addr", NULL), + memory_region_size(s->mmio)); } -#endif void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio) { diff --git a/hw/pci/pci-hmp-cmds.c b/hw/pci/pci-hmp-cmds.c index 879011da1384..776762f18fa5 100644 --- a/hw/pci/pci-hmp-cmds.c +++ b/hw/pci/pci-hmp-cmds.c @@ -135,44 +135,6 @@ void hmp_info_pci(MonitorHMP *hmp, const QDict *qdict) qapi_free_PciInfoList(info_list); } -#ifdef CONFIG_HMP -void pcibus_dev_print(MonitorHMP *hmp, DeviceState *dev, int indent) -{ - PCIDevice *d = (PCIDevice *)dev; - int class = pci_get_word(d->config + PCI_CLASS_DEVICE); - const pci_class_desc *desc = get_class_desc(class); - char ctxt[64]; - PCIIORegion *r; - int i; - - if (desc->desc) { - snprintf(ctxt, sizeof(ctxt), "%s", desc->desc); - } else { - snprintf(ctxt, sizeof(ctxt), "Class %04x", class); - } - - monitor_hmp_printf(hmp, "%*sclass %s, addr %02x:%02x.%x, " - "pci id %04x:%04x (sub %04x:%04x)\n", - indent, "", ctxt, pci_dev_bus_num(d), - PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), - pci_get_word(d->config + PCI_VENDOR_ID), - pci_get_word(d->config + PCI_DEVICE_ID), - pci_get_word(d->config + PCI_SUBSYSTEM_VENDOR_ID), - pci_get_word(d->config + PCI_SUBSYSTEM_ID)); - for (i = 0; i < PCI_NUM_REGIONS; i++) { - r = &d->io_regions[i]; - if (!r->size) { - continue; - } - monitor_hmp_printf(hmp, "%*sbar %d: %s at 0x%"FMT_PCIBUS - " [0x%"FMT_PCIBUS"]\n", - indent, "", - i, r->type & PCI_BASE_ADDRESS_SPACE_IO ? "i/o" : "mem", - r->addr, r->addr + r->size - 1); - } -} -#endif - void hmp_pcie_aer_inject_error(MonitorHMP *hmp, const QDict *qdict) { Error *err = NULL; diff --git a/hw/pci/pci-internal.h b/hw/pci/pci-internal.h index b7231fab5dc9..68a169488d09 100644 --- a/hw/pci/pci-internal.h +++ b/hw/pci/pci-internal.h @@ -16,7 +16,7 @@ extern PCIHostStateList pci_host_bridges; const pci_class_desc *get_class_desc(int class); PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num); -void pcibus_dev_print(MonitorHMP *mon, DeviceState *dev, int indent); +void pcibus_dev_print(GString *buf, DeviceState *dev, int indent); int pcie_aer_parse_error_string(const char *error_name, uint32_t *status, bool *correctable); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 0efb4eb4bb03..433568440853 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -293,9 +293,7 @@ static void pci_bus_class_init(ObjectClass *klass, const void *data) ResettableClass *rc = RESETTABLE_CLASS(klass); FWCfgDataGeneratorClass *fwgc = FW_CFG_DATA_GENERATOR_CLASS(klass); -#ifdef CONFIG_HMP k->print_dev = pcibus_dev_print; -#endif k->get_dev_path = pcibus_get_dev_path; k->get_fw_dev_path = pcibus_get_fw_dev_path; k->realize = pci_bus_realize; @@ -2094,6 +2092,44 @@ const pci_class_desc *get_class_desc(int class) return desc; } +void pcibus_dev_print(GString *buf, DeviceState *dev, int indent) +{ + PCIDevice *d = (PCIDevice *)dev; + int class = pci_get_word(d->config + PCI_CLASS_DEVICE); + const pci_class_desc *desc = get_class_desc(class); + char ctxt[64]; + PCIIORegion *r; + int i; + + if (desc->desc) { + snprintf(ctxt, sizeof(ctxt), "%s", desc->desc); + } else { + snprintf(ctxt, sizeof(ctxt), "Class %04x", class); + } + + g_string_append_printf(buf, "%*sclass %s, addr %02x:%02x.%x, " + "pci id %04x:%04x (sub %04x:%04x)\n", + indent, "", ctxt, pci_dev_bus_num(d), + PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), + pci_get_word(d->config + PCI_VENDOR_ID), + pci_get_word(d->config + PCI_DEVICE_ID), + pci_get_word(d->config + PCI_SUBSYSTEM_VENDOR_ID), + pci_get_word(d->config + PCI_SUBSYSTEM_ID)); + for (i = 0; i < PCI_NUM_REGIONS; i++) { + r = &d->io_regions[i]; + if (!r->size) { + continue; + } + g_string_append_printf(buf, "%*sbar %d: %s at 0x%"FMT_PCIBUS + " [0x%"FMT_PCIBUS"]\n", + indent, "", + i, + r->type & PCI_BASE_ADDRESS_SPACE_IO + ? "i/o" : "mem", + r->addr, r->addr + r->size - 1); + } +} + void pci_init_nic_devices(PCIBus *bus, const char *default_model) { qemu_create_nic_bus_devices(&bus->qbus, TYPE_PCI_DEVICE, default_model, diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 5cc5ffec33a1..c6d65274771f 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -8,14 +8,10 @@ #include "qemu/module.h" #include "system/system.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" -#include "monitor/hmp.h" #include "trace.h" #include "qemu/cutils.h" -#ifdef CONFIG_HMP -static void usb_bus_dev_print(MonitorHMP *hmp, DeviceState *qdev, int indent); -#endif +static void usb_bus_dev_print(GString *buf, DeviceState *qdev, int indent); static char *usb_get_dev_path(DeviceState *dev); static char *usb_get_fw_dev_path(DeviceState *qdev); @@ -34,9 +30,7 @@ static void usb_bus_class_init(ObjectClass *klass, const void *data) BusClass *k = BUS_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); -#ifdef CONFIG_HMP k->print_dev = usb_bus_dev_print; -#endif k->get_dev_path = usb_get_dev_path; k->get_fw_dev_path = usb_get_fw_dev_path; hc->unplug = qdev_simple_device_unplug_cb; @@ -548,19 +542,18 @@ static const char *usb_speed(unsigned int speed) return txt[speed]; } -#ifdef CONFIG_HMP -static void usb_bus_dev_print(MonitorHMP *hmp, DeviceState *qdev, int indent) +static void usb_bus_dev_print(GString *buf, DeviceState *qdev, int indent) { USBDevice *dev = USB_DEVICE(qdev); USBBus *bus = usb_bus_from_device(dev); - monitor_hmp_printf(hmp, "%*saddr %d.%d, port %s, speed %s, name %s%s\n", - indent, "", bus->busnr, dev->addr, - dev->port ? dev->port->path : "-", - usb_speed(dev->speed), dev->product_desc, - dev->attached ? ", attached" : ""); + g_string_append_printf(buf, + "%*saddr %d.%d, port %s, speed %s, name %s%s\n", + indent, "", bus->busnr, dev->addr, + dev->port ? dev->port->path : "-", + usb_speed(dev->speed), dev->product_desc, + dev->attached ? ", attached" : ""); } -#endif static char *usb_get_dev_path(DeviceState *qdev) { diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 8def3bb68bc6..cc98034230e8 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -16,8 +16,6 @@ #include "hw/xen/xen-legacy-backend.h" /* xen_be_init() */ #include "hw/xen/xen-bus.h" #include "hw/xen/xen-bus-helper.h" -#include "monitor/monitor.h" -#include "monitor/hmp.h" #include "qapi/error.h" #include "qobject/qdict.h" #include "system/system.h" @@ -101,15 +99,13 @@ abort: qemu_xen_xs_transaction_end(xenbus->xsh, tid, true); } -#ifdef CONFIG_HMP -static void xen_bus_print_dev(MonitorHMP *hmp, DeviceState *dev, int indent) +static void xen_bus_print_dev(GString *buf, DeviceState *dev, int indent) { XenDevice *xendev = XEN_DEVICE(dev); - monitor_hmp_printf(hmp, "%*sname = '%s' frontend_id = %u\n", - indent, "", xendev->name, xendev->frontend_id); + g_string_append_printf(buf, "%*sname = '%s' frontend_id = %u\n", + indent, "", xendev->name, xendev->frontend_id); } -#endif static char *xen_bus_get_dev_path(DeviceState *dev) { @@ -388,9 +384,7 @@ static void xen_bus_class_init(ObjectClass *class, const void *data) BusClass *bus_class = BUS_CLASS(class); HotplugHandlerClass *hotplug_class = HOTPLUG_HANDLER_CLASS(class); -#ifdef CONFIG_HMP bus_class->print_dev = xen_bus_print_dev; -#endif bus_class->get_dev_path = xen_bus_get_dev_path; bus_class->realize = xen_bus_realize; bus_class->unrealize = xen_bus_unrealize; diff --git a/include/hw/core/qdev.h b/include/hw/core/qdev.h index 1f6bf3fc1abf..e8399d8541fb 100644 --- a/include/hw/core/qdev.h +++ b/include/hw/core/qdev.h @@ -323,10 +323,8 @@ DECLARE_OBJ_CHECKERS(BusState, BusClass, struct BusClass { ObjectClass parent_class; -#ifdef CONFIG_HMP /* FIXME first arg should be BusState */ - void (*print_dev)(MonitorHMP *mon, DeviceState *dev, int indent); -#endif + void (*print_dev)(GString *buf, DeviceState *dev, int indent); /* * Return a newly allocated string containing the path of the * device on this bus. diff --git a/qapi/qdev.json b/qapi/qdev.json index 974cf9c5830e..4b5ccfb37687 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -165,6 +165,37 @@ { 'event': 'DEVICE_UNPLUG_GUEST_ERROR', 'data': { '*device': 'str', 'path': 'str' } } +## +# @x-query-qtree: +# +# Query device tree +# +# @brief: if true, omit device properties. (default: false) +# +# Features: +# +# @unstable: This command is meant for debugging. +# +# Returns: device tree +# +# Since: 11.2 +# +# .. qmp-example:: +# +# -> { "execute": "x-query-qtree" } +# <- { "return": { "human-readable-text": "..." } } +# +# .. qmp-example:: +# +# -> { "execute": "x-query-qtree", +# "arguments": { "brief": true } } +# <- { "return": { "human-readable-text": "..." } } +## +{ 'command': 'x-query-qtree', + 'data': { '*brief': 'bool' }, + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } + ## # @device-sync-config: # diff --git a/rust/bindings/hwcore-sys/lib.rs b/rust/bindings/hwcore-sys/lib.rs index cecbf808dd09..a28f5ab90f69 100644 --- a/rust/bindings/hwcore-sys/lib.rs +++ b/rust/bindings/hwcore-sys/lib.rs @@ -21,7 +21,7 @@ use chardev_sys::Chardev; use common::Zeroable; -use glib_sys::GSList; +use glib_sys::{GSList, GString}; use migration_sys::VMStateDescription; use qom_sys::{ InterfaceClass, Object, ObjectClass, ObjectProperty, ObjectPropertyAccessor, diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 8185822daa71..1a4af35f9db1 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -27,6 +27,7 @@ #include "system/runstate.h" #include "qapi/error.h" #include "qapi/qapi-commands-qdev.h" +#include "qapi/type-helpers.h" #include "qapi/qmp-registry.h" #include "qobject/qdict.h" #include "qapi/qmp/qerror.h" @@ -770,11 +771,10 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) return ret; } -#ifdef CONFIG_HMP #define qdev_printf(fmt, ...) \ - monitor_hmp_printf(hmp, "%*s" fmt, indent, "", ## __VA_ARGS__) + g_string_append_printf(buf, "%*s" fmt, indent, "", ## __VA_ARGS__) -static void qdev_print_props(MonitorHMP *hmp, DeviceState *dev, DeviceClass *dc, +static void qdev_print_props(GString *buf, DeviceState *dev, DeviceClass *dc, int indent) { for (int i = 0, n = dc->props_count_; i < n; ++i) { @@ -798,16 +798,17 @@ static void qdev_print_props(MonitorHMP *hmp, DeviceState *dev, DeviceClass *dc, } } -static void bus_print_dev(BusState *bus, MonitorHMP *hmp, DeviceState *dev, int indent) +static void bus_print_dev(BusState *bus, GString *buf, DeviceState *dev, + int indent) { BusClass *bc = BUS_GET_CLASS(bus); if (bc->print_dev) { - bc->print_dev(hmp, dev, indent); + bc->print_dev(buf, dev, indent); } } -static void qdev_print(MonitorHMP *hmp, DeviceState *dev, int indent) +static void qdev_print(GString *buf, DeviceState *dev, int indent) { ObjectClass *class; NamedGPIOList *ngl; @@ -832,13 +833,13 @@ static void qdev_print(MonitorHMP *hmp, DeviceState *dev, int indent) } class = object_get_class(OBJECT(dev)); do { - qdev_print_props(hmp, dev, DEVICE_CLASS(class), indent); + qdev_print_props(buf, dev, DEVICE_CLASS(class), indent); class = object_class_get_parent(class); } while (class != object_class_by_name(TYPE_DEVICE)); - bus_print_dev(dev->parent_bus, hmp, dev, indent); + bus_print_dev(dev->parent_bus, buf, dev, indent); } -static void qbus_print(MonitorHMP *hmp, BusState *bus, int indent, bool details) +static void qbus_print(GString *buf, BusState *bus, int indent, bool details) { BusChild *kid; @@ -851,22 +852,35 @@ static void qbus_print(MonitorHMP *hmp, BusState *bus, int indent, bool details) qdev_printf("dev: %s, id \"%s\"\n", object_get_typename(OBJECT(dev)), dev->id ? dev->id : ""); if (details) { - qdev_print(hmp, dev, indent + 2); + qdev_print(buf, dev, indent + 2); } QLIST_FOREACH(child_bus, &dev->child_bus, sibling) { - qbus_print(hmp, child_bus, indent + 2, details); + qbus_print(buf, child_bus, indent + 2, details); } } } #undef qdev_printf -void hmp_info_qtree(MonitorHMP *hmp, const QDict *qdict) +HumanReadableText *qmp_x_query_qtree(bool has_brief, bool brief, Error **errp) { - bool details = !qdict_get_try_bool(qdict, "brief", false); + g_autoptr(GString) buf = g_string_new(""); + bool details = !(has_brief && brief); if (sysbus_get_default()) { - qbus_print(hmp, sysbus_get_default(), 0, details); + qbus_print(buf, sysbus_get_default(), 0, details); } + + return human_readable_text_from_str(buf); +} + +#ifdef CONFIG_HMP +void hmp_info_qtree(MonitorHMP *hmp, const QDict *qdict) +{ + bool brief = qdict_get_try_bool(qdict, "brief", false); + g_autoptr(HumanReadableText) info = + qmp_x_query_qtree(true, brief, &error_abort); + + monitor_hmp_printf(hmp, "%s", info->human_readable_text); } void hmp_info_qdm(MonitorHMP *hmp, const QDict *qdict) -- 2.56.0.rc0.29.g47ce80527c56
