From: "Dr. David Alan Gilbert" <dgilb...@redhat.com> A migration visitor whose output is textual and purely for human consumption for debug.
Signed-off-by: Dr. David Alan Gilbert <dgilb...@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 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 <dgilb...@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 <dgilb...@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; +} -- 1.8.5.3