From patchwork Tue Mar 25 20:17:17 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. David Alan Gilbert" X-Patchwork-Id: 333724 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 9E2861400A3 for ; Wed, 26 Mar 2014 08:44:33 +1100 (EST) Received: from localhost ([::1]:44010 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WSXoJ-0002sG-75 for incoming@patchwork.ozlabs.org; Tue, 25 Mar 2014 16:18:59 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:46127) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WSXnG-0001t1-Cm for qemu-devel@nongnu.org; Tue, 25 Mar 2014 16:18:00 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WSXnA-0008CY-AU for qemu-devel@nongnu.org; Tue, 25 Mar 2014 16:17:54 -0400 Received: from mx1.redhat.com ([209.132.183.28]:62545) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WSXn9-0008B9-W5 for qemu-devel@nongnu.org; Tue, 25 Mar 2014 16:17:48 -0400 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s2PKHhnL018639 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Tue, 25 Mar 2014 16:17:43 -0400 Received: from dgilbert-t530.home.treblig.org (vpn1-7-106.ams2.redhat.com [10.36.7.106]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s2PKHSSa029837; Tue, 25 Mar 2014 16:17:41 -0400 From: "Dr. David Alan Gilbert (git)" To: qemu-devel@nongnu.org Date: Tue, 25 Mar 2014 20:17:17 +0000 Message-Id: <1395778647-30925-7-git-send-email-dgilbert@redhat.com> In-Reply-To: <1395778647-30925-1-git-send-email-dgilbert@redhat.com> References: <1395778647-30925-1-git-send-email-dgilbert@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: stefanb@linux.vnet.ibm.com, quintela@redhat.com, mdroth@linux.vnet.ibm.com, agraf@suse.de, mst@redhat.com, aliguori@amazon.com, afaerber@suse.de Subject: [Qemu-devel] [RFC PATCH 06/16] Visitor: Debug output visitor X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: "Dr. David Alan Gilbert" A migration visitor whose output is textual and purely for human consumption for debug. Signed-off-by: Dr. David Alan Gilbert --- 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 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 + * + * 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 + * + * 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("\n", ""); +} + +static void qfdo_start_list(Visitor *v, const char *name, + Error **errp) +{ + QemuFileDebugOutputVisitor *ov = to_ov(v); + DPRINTF("\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("\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("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("section_id); + break; + + + case VISIT_SEQ_COMPAT_BYTE0TERM: + DPRINTF("flags & + (RAM_SAVE_FLAG_CONTINUE | RAM_SAVE_FLAG_MEM_SIZE | + RAM_SAVE_FLAG_HOOK)) == 0) { + tmps = rse_hdr->idstr; + } else { + tmps = "(cont)"; + } + DPRINTF("addr, rse_hdr->flags, tmps); + break; + + case VISIT_SEQ_COMPAT_BLOB: + DPRINTF("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; +}