Patchwork [RFC,06/16] Visitor: Debug output visitor

login
register
mail settings
Submitter Dave Gilbert
Date March 25, 2014, 8:17 p.m.
Message ID <1395778647-30925-7-git-send-email-dgilbert@redhat.com>
Download mbox | patch
Permalink /patch/333724/
State New
Headers show

Comments

Dave Gilbert - March 25, 2014, 8:17 p.m.
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>

A migration visitor whose output is textual and purely for human
consumption for debug.

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
---
 include/qapi/qemu-file-debug-output-visitor.h |  26 ++
 qapi/Makefile.objs                            |   1 +
 qapi/qemu-file-debug-output-visitor.c         | 471 ++++++++++++++++++++++++++
 3 files changed, 498 insertions(+)
 create mode 100644 include/qapi/qemu-file-debug-output-visitor.h
 create mode 100644 qapi/qemu-file-debug-output-visitor.c

Patch

diff --git a/include/qapi/qemu-file-debug-output-visitor.h b/include/qapi/qemu-file-debug-output-visitor.h
new file mode 100644
index 0000000..427d5b2
--- /dev/null
+++ b/include/qapi/qemu-file-debug-output-visitor.h
@@ -0,0 +1,26 @@ 
+/*
+ * QEMUFile Visitor
+ *
+ * Copyright Red Hat, Corp. 2014
+ *
+ * Authors:
+ *  David Gilbert <dgilbert@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_FILE_DEBUG_OUTPUT_VISITOR_H
+#define QEMU_FILE_DEBUG_OUTPUT_VISITOR_H
+
+#include "visitor.h"
+
+typedef struct QemuFileDebugOutputVisitor QemuFileDebugOutputVisitor;
+
+QemuFileDebugOutputVisitor *qemu_file_debug_output_visitor_new(QEMUFile *f);
+void qemu_file_debug_output_visitor_cleanup(QemuFileDebugOutputVisitor *d);
+
+Visitor *qemu_file_debug_output_get_visitor(QemuFileDebugOutputVisitor *v);
+
+#endif
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index a054d52..06c69e6 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -2,4 +2,5 @@  util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
 util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
 util-obj-y += string-input-visitor.o string-output-visitor.o
 util-obj-y += qemu-file-binary-output-visitor.o
+util-obj-y += qemu-file-debug-output-visitor.o
 util-obj-y += opts-visitor.o
diff --git a/qapi/qemu-file-debug-output-visitor.c b/qapi/qemu-file-debug-output-visitor.c
new file mode 100644
index 0000000..8694212
--- /dev/null
+++ b/qapi/qemu-file-debug-output-visitor.c
@@ -0,0 +1,471 @@ 
+/*
+ * QEMUFile Output Visitor
+ *
+ * Copyright Red Hat, 2014
+ *
+ * Authors:
+ *  David Gilbert  <dgilbert@redhat.com>
+ *
+ *  Based on the binary file output visitor
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ * Produce a textual output designed purely for ease of reading
+ */
+
+#include "qapi/qemu-file-binary-output-visitor.h"
+#include "qapi/qemu-file-debug-output-visitor.h"
+#include "qapi/visitor-impl.h"
+#include "qapi/qmp/qerror.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "migration/migration.h"
+
+struct QemuFileDebugOutputVisitor {
+    Visitor visitor;
+    int depth;
+    QEMUFile *file;
+
+    bool in_binwrapper; /* When true we're in a compat wrapper where everything
+                           should be going through the QEMUFile we provide it */
+    QEMUFile *binfile;  /* Passed to the caller in a compat wrapper */
+};
+
+static void qemufile_printf(QEMUFile *qf, int depth, const char *fmt, ...)
+{
+    char *tmp;
+    va_list va;
+    va_start(va, fmt);
+    assert(vasprintf(&tmp, fmt, va) != -1);
+    va_end(va);
+
+    while (depth--) {
+        qemu_put_buffer(qf, (uint8_t *)"  ", 2);
+    }
+
+    qemu_put_buffer(qf, (uint8_t *)tmp, strlen(tmp));
+    qemu_fflush(qf);
+    free(tmp);
+}
+
+#define DPRINTF(fmt, ...) \
+    qemufile_printf(ov->file, ov->depth*2, fmt, __VA_ARGS__)
+
+/*
+ * TDO: This should be shared somewhere, util/hexdump.c has one to output to
+ * FILE*, and util/iov.c uses that to output an iov to FILE*, neither includes
+ * the printables.
+ */
+/* We print the iov upto 'len' bytes because the iov is allocated in chunks */
+static void hexdump_to_qemufile(QEMUFile *qf, size_t len, size_t n_iov,
+                                struct iovec *iov, int depth)
+{
+    const unsigned int bytes_per_line = 16;
+    /*
+     * Of the form:    41 42 43 44 45 46 47 48 ABCDEFGH\0
+     *             3 byte>   <           1 byte> <
+     */
+    const unsigned int line_buf_len = (bytes_per_line * (3+1)) + 2;
+    char linebuf[line_buf_len];
+    size_t cur_iov, cur_offset;
+    unsigned int line_index = 0;
+
+    memset(linebuf, ' ', bytes_per_line * (3+1)+2);
+    linebuf[line_buf_len-1] = '\0';
+
+    for (cur_iov = 0; len && (cur_iov < n_iov); cur_iov++) {
+        for (cur_offset = 0;
+            len && (cur_offset < iov[cur_iov].iov_len);
+            cur_offset++, len--) {
+            uint8_t cur_byte = ((uint8_t *)iov[cur_iov].iov_base)[cur_offset];
+            const char *hexstring = "0123456789abcdef";
+
+            linebuf[line_index*3] = hexstring[(cur_byte >> 4) & 0xf];
+            linebuf[line_index*3+1] = hexstring[cur_byte & 0xf];
+            linebuf[bytes_per_line*3+1+line_index] =
+                isprint(cur_byte) ? cur_byte :  '.';
+            line_index++;
+
+            if (line_index == bytes_per_line) {
+                qemufile_printf(qf, depth, "%s\n", linebuf);
+                line_index = 0;
+            }
+        }
+    }
+    if (line_index) {
+        /* Still some bytes left */
+        if (line_index != bytes_per_line) {
+            memset(linebuf + line_index*3, '#', 3*(bytes_per_line-line_index));
+            memset(linebuf + bytes_per_line*3+1+line_index, '#',
+                   bytes_per_line-line_index);
+            qemufile_printf(qf, depth, "%s\n", linebuf);
+        }
+    }
+}
+
+static QemuFileDebugOutputVisitor *to_ov(Visitor *v)
+{
+    return container_of(v, QemuFileDebugOutputVisitor, visitor);
+}
+
+static void qfdo_push(QemuFileDebugOutputVisitor *ov)
+{
+    ov->depth++;
+}
+
+static void qfdo_pop(QemuFileDebugOutputVisitor *ov)
+{
+    ov->depth--;
+    assert(ov >= 0);
+    return;
+}
+
+static void qfdo_start_struct(Visitor *v, void **obj, const char *kind,
+                              const char *name, size_t unused,
+                              Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+
+    DPRINTF("<struct '%s' of '%s'\n", name, kind);
+    qfdo_push(ov);
+}
+
+static void qfdo_end_struct(Visitor *v, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+
+    qfdo_pop(ov);
+    DPRINTF("struct %s>\n", "");
+}
+
+static void qfdo_start_list(Visitor *v, const char *name,
+                            Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("<list '%s'\n", name);
+    qfdo_push(ov);
+}
+
+static GenericList *qfdo_next_list(Visitor *v, GenericList **list,
+                                   Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+
+    DPRINTF("|list %s\n", "");
+
+    return NULL; /* Not generally valid! */
+}
+
+static void qfdo_end_list(Visitor *v, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+
+    qfdo_pop(ov);
+    DPRINTF("list %s>\n", "");
+}
+
+static void qfdo_start_array(Visitor *v, void **obj,
+                             const char *name,
+                             size_t elem_count,
+                             size_t elem_size, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("<array '%s' [%zd] of %zd\n", name, elem_count, elem_size);
+
+    qfdo_push(ov);
+}
+
+static void qfdo_next_array(Visitor *v, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("|array %s\n", "");
+}
+
+static void qfdo_end_array(Visitor *v, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    qfdo_pop(ov);
+
+    DPRINTF("array %s>\n", "");
+}
+
+static void qfdo_type_str(Visitor *v, char **obj, const char *name,
+                          Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+
+    DPRINTF("string: '%s'=%s\n", name, *obj);
+}
+
+/* A string upto 256 bytes in length (including terminator)
+ *   output as length byte (not including term) followed by text
+ *   (also not including term)
+ */
+static void qfdo_type_str256(Visitor *v, char *obj, const char *name,
+                             Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+
+    DPRINTF("str256: '%s'=%s\n", name, obj);
+}
+
+static void qfdo_type_buffer(Visitor *v, void *data, size_t len, bool async,
+                             const char *name, Error **errp)
+{
+    struct iovec tmpiov;
+    tmpiov.iov_base = data;
+    tmpiov.iov_len = len;
+
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("Buffer of '%s' len=%zd async=%d\n", name, len, async);
+    hexdump_to_qemufile(ov->file, len,  1, &tmpiov, ov->depth * 2);
+}
+
+static void qfdo_type_uint8(Visitor *v, uint8_t *obj,
+                            const char *name, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("uint8_t %s: 0x%2x\n", name, *obj);
+}
+
+static void qfdo_type_uint16(Visitor *v, uint16_t *obj,
+                             const char *name, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("uint16_t %s: 0x%4x\n", name, *obj);
+}
+
+static void qfdo_type_uint32(Visitor *v, uint32_t *obj,
+                             const char *name, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("uint32_t %s: 0x%8x\n", name, *obj);
+}
+
+static void qfdo_type_uint64(Visitor *v, uint64_t *obj,
+                             const char *name, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("uint64_t %s: 0x%16lx\n", name, *obj);
+}
+
+static void qfdo_type_int8(Visitor *v, int8_t *obj,
+                           const char *name, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("int8_t %s: 0x%2x\n", name, *obj);
+}
+
+static void qfdo_type_int16(Visitor *v, int16_t *obj,
+                            const char *name, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("int16_t %s: 0x%4x\n", name, *obj);
+}
+
+static void qfdo_type_int32(Visitor *v, int32_t *obj,
+                            const char *name, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("int32_t %s: 0x%8x\n", name, *obj);
+}
+
+static void qfdo_type_int64(Visitor *v, int64_t *obj,
+                            const char *name, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("int64_t %s: 0x%16lx\n", name, *obj);
+}
+
+static void qfdo_type_bool(Visitor *v, bool *obj, const char *name,
+                           Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    DPRINTF("bool %s: %d\n", name, *obj);
+}
+
+static QEMUFile *qfdo_get_qemufile(Visitor *v)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+
+    return ov->file;
+}
+
+static void qfdo_get_next_type(Visitor *v, int *kind, const int *qobjects,
+                               const char *name, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+
+    DPRINTF("|next_type %s\n", name);
+}
+
+static void qfdo_start_sequence_compat(Visitor *v, const char *name,
+                                       Visit_seq_compat_mode compat_mode,
+                                       void *opaque, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+    SectionHeader *sh;
+    ramsecentry_header *rse_hdr;
+    const char *tmps;
+
+    switch (compat_mode) {
+    case VISIT_SEQ_COMPAT_FILE:
+        DPRINTF("<SEQCOMPAT SoF: %s\n", name);
+        break;
+
+    case VISIT_SEQ_COMPAT_SECTION_HEADER:
+        /*
+         * for VM_SECTION_FULL and VM_SECTION_START
+         * 'opaque' points to a struct Sectionheader
+         */
+        sh = opaque;
+        DPRINTF("<SEQCOMPAT Section (%s): %s (%d / %d version %d)\n", name,
+                sh->idstr, sh->section_id, sh->instance_id, sh->version_id);
+        break;
+
+    case VISIT_SEQ_COMPAT_SECTION_MIN:
+        /* VM_SECTION_PART/END where the section name->ID is already known */
+        sh = opaque;
+        DPRINTF("<SEQCOMPAT Section (%s): %d\n", name, sh->section_id);
+        break;
+
+
+    case VISIT_SEQ_COMPAT_BYTE0TERM:
+        DPRINTF("<SEQCOMPAT 0 term list (%s)\n", name);
+        break;
+
+    case VISIT_SEQ_COMPAT_SUBSECLIST:
+        DPRINTF("<SEQCOMPAT Subsection list (%s)\n", name);
+        break;
+
+    case VISIT_SEQ_COMPAT_SUBSECTION:
+        DPRINTF("<SEQCOMPAT Subsection (%s)\n", name);
+        break;
+
+    case VISIT_SEQ_COMPAT_RAMSECLIST:
+        DPRINTF("<SEQCOMPAT RAMseclist (%s)\n", name);
+        break;
+
+    case VISIT_SEQ_COMPAT_VMSTATE:
+        DPRINTF("<SEQCOMPAT VMState (%s)\n", name);
+        break;
+
+    case VISIT_SEQ_COMPAT_RAMSECENTRY:
+        rse_hdr = opaque;
+        if ((rse_hdr->flags &
+             (RAM_SAVE_FLAG_CONTINUE | RAM_SAVE_FLAG_MEM_SIZE |
+              RAM_SAVE_FLAG_HOOK)) == 0) {
+            tmps = rse_hdr->idstr;
+        } else {
+            tmps = "(cont)";
+        }
+        DPRINTF("<SEQCOMPAT RAMsecentry %s for 0x%lx flags=0x%x id=%s\n",
+                name, rse_hdr->addr, rse_hdr->flags, tmps);
+        break;
+
+    case VISIT_SEQ_COMPAT_BLOB:
+        DPRINTF("<SEQCOMPAT Blob (%s)\n", name);
+
+        /* Opaque is given a QEMUFile into which it writes the binary data */
+        ov->in_binwrapper = true;
+        ov->binfile = qemu_bufopen("w", NULL);
+        /* and give that wrapper a binary output visitor so that it keeps
+         * substructures in compatibility mode
+         */
+        QemuFileBinOutputVisitor *qfbov =
+            qemu_file_bin_output_visitor_new(ov->binfile);
+        Visitor *v = qemu_file_bin_output_get_visitor(qfbov);
+        qemu_file_set_tmp_visitor(ov->binfile, v);
+
+        *(QEMUFile **)opaque = ov->binfile;
+        break;
+    }
+
+    qfdo_push(ov);
+}
+
+static void qfdo_end_sequence_compat(Visitor *v, const char *name,
+                                     Visit_seq_compat_mode compat_mode,
+                                     Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+
+    /* bin wrappers can't nest - or at least if they did they'd have a new
+     * visitor instance
+     */
+    if (ov->in_binwrapper) {
+        Error *local_err = NULL;
+        Visitor *bv = qemu_file_get_tmp_visitor(ov->binfile);
+
+        visit_destroy(bv, &local_err);
+        const QEMUSizedBuffer *qsb = qemu_buf_get(ov->binfile);
+        size_t len = qsb_get_length(qsb);
+
+        hexdump_to_qemufile(ov->file, len, qsb->n_iov, qsb->iov, ov->depth*2);
+
+        qemu_fclose(ov->binfile);
+        ov->in_binwrapper = false;
+    }
+
+    qfdo_pop(ov);
+    DPRINTF("SEQCOMPAT (%d) %s>\n", compat_mode, name);
+}
+
+static void qfdo_destroy(Visitor *v, Error **errp)
+{
+    QemuFileDebugOutputVisitor *ov = to_ov(v);
+
+    qemu_file_debug_output_visitor_cleanup(ov);
+}
+
+Visitor *qemu_file_debug_output_get_visitor(QemuFileDebugOutputVisitor *v)
+{
+    return &v->visitor;
+}
+
+void qemu_file_debug_output_visitor_cleanup(QemuFileDebugOutputVisitor *ov)
+{
+    g_free(ov);
+}
+
+QemuFileDebugOutputVisitor *qemu_file_debug_output_visitor_new(QEMUFile *f)
+{
+    QemuFileDebugOutputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->file = f;
+
+    v->visitor.start_struct = qfdo_start_struct;
+    v->visitor.end_struct = qfdo_end_struct;
+    v->visitor.start_list = qfdo_start_list;
+    v->visitor.next_list = qfdo_next_list;
+    v->visitor.end_list = qfdo_end_list;
+    v->visitor.start_array = qfdo_start_array;
+    v->visitor.next_array = qfdo_next_array;
+    v->visitor.end_array = qfdo_end_array;
+    v->visitor.type_buffer = qfdo_type_buffer;
+    v->visitor.type_int = qfdo_type_int64;
+    v->visitor.type_uint8 = qfdo_type_uint8;
+    v->visitor.type_uint16 = qfdo_type_uint16;
+    v->visitor.type_uint32 = qfdo_type_uint32;
+    v->visitor.type_uint64 = qfdo_type_uint64;
+    v->visitor.type_int8 = qfdo_type_int8;
+    v->visitor.type_int16 = qfdo_type_int16;
+    v->visitor.type_int32 = qfdo_type_int32;
+    v->visitor.type_int64 = qfdo_type_int64;
+    v->visitor.type_bool = qfdo_type_bool;
+    v->visitor.type_str = qfdo_type_str;
+    v->visitor.type_str256 = qfdo_type_str256;
+    v->visitor.destroy = qfdo_destroy;
+    v->visitor.start_sequence_compat = qfdo_start_sequence_compat;
+    v->visitor.get_next_type = qfdo_get_next_type;
+    v->visitor.end_sequence_compat = qfdo_end_sequence_compat;
+    v->visitor.get_qemufile = qfdo_get_qemufile;
+
+    v->visitor.flags = VISITOR_SAVING;
+
+    return v;
+}