diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2014-03-04 17:01:07 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2014-03-04 17:01:07 +0000 |
commit | e00ef747f08db1c7dd58a5366db4dc3038a7307f (patch) | |
tree | 270bc41abb51663b18142fd6aeeb58c64f820230 | |
parent | 4a29420ea1c5a34191281855f5f51e70deab8940 (diff) | |
parent | b774539743c52ef605c6e2cbac19376c2757cb86 (diff) |
Merge remote-tracking branch 'remotes/qmp-unstable/queue/qmp' into staging
* remotes/qmp-unstable/queue/qmp: (32 commits)
qapi: Add missing null check to opts_start_struct()
qapi: Clean up superfluous null check in qapi_dealloc_type_str()
qapi: Clean up null checking in generated visitors
qapi: Drop unused code in qapi-commands.py
qapi: Drop nonsensical header guard in generated qapi-visit.c
qapi: Fix licensing of scripts
tests/qapi-schema: Cover flat union types
tests/qapi-schema: Cover union types with base
tests/qapi-schema: Cover complex types with base
tests/qapi-schema: Cover anonymous union types
tests/qapi-schema: Cover simple argument types
tests/qapi-schema: Cover optional command arguments
tests/qapi-schema: Actually check successful QMP command response
monitor: Remove left-over code in do_info_profile.
qerror: Improve QERR_DEVICE_NOT_ACTIVE message
qmp: Check for returned data from __json_read in get_events
dump: add 'query-dump-guest-memory-capability' command
Define the architecture for compressed dump format
dump: make kdump-compressed format available for 'dump-guest-memory'
dump: add API to write dump pages
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | MAINTAINERS | 6 | ||||
-rwxr-xr-x | configure | 54 | ||||
-rw-r--r-- | dump.c | 960 | ||||
-rw-r--r-- | hmp.c | 5 | ||||
-rw-r--r-- | include/qapi/qmp/qerror.h | 2 | ||||
-rw-r--r-- | include/qom/cpu.h | 3 | ||||
-rw-r--r-- | include/sysemu/dump.h | 138 | ||||
-rw-r--r-- | monitor.c | 4 | ||||
-rw-r--r-- | qapi-schema.json | 49 | ||||
-rw-r--r-- | qapi/opts-visitor.c | 4 | ||||
-rw-r--r-- | qapi/qapi-dealloc-visitor.c | 4 | ||||
-rw-r--r-- | qmp-commands.hx | 27 | ||||
-rw-r--r-- | scripts/qapi-commands.py | 24 | ||||
-rw-r--r-- | scripts/qapi-types.py | 4 | ||||
-rw-r--r-- | scripts/qapi-visit.py | 20 | ||||
-rw-r--r-- | scripts/qapi.py | 4 | ||||
-rwxr-xr-x | scripts/qmp/qmp-shell | 20 | ||||
-rw-r--r-- | scripts/qmp/qmp.py | 7 | ||||
-rw-r--r-- | target-i386/cpu.h | 2 | ||||
-rw-r--r-- | target-s390x/cpu.h | 1 | ||||
-rw-r--r-- | tests/qapi-schema/qapi-schema-test.json | 24 | ||||
-rw-r--r-- | tests/qapi-schema/qapi-schema-test.out | 19 | ||||
-rw-r--r-- | tests/test-qmp-commands.c | 78 | ||||
-rw-r--r-- | tests/test-qmp-input-strict.c | 69 | ||||
-rw-r--r-- | tests/test-qmp-input-visitor.c | 45 | ||||
-rw-r--r-- | tests/test-qmp-output-visitor.c | 67 | ||||
-rw-r--r-- | tests/test-visitor-serialization.c | 14 |
27 files changed, 1546 insertions, 108 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 026ea4f021..7efaccf5fb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -726,7 +726,7 @@ F: vl.c Human Monitor (HMP) M: Luiz Capitulino <lcapitulino@redhat.com> -S: Supported +S: Maintained F: monitor.c F: hmp.c F: hmp-commands.hx @@ -758,7 +758,7 @@ T: git git://github.com/bonzini/qemu.git nbd-next QAPI M: Luiz Capitulino <lcapitulino@redhat.com> M: Michael Roth <mdroth@linux.vnet.ibm.com> -S: Supported +S: Maintained F: qapi/ T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp @@ -772,7 +772,7 @@ T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp QMP M: Luiz Capitulino <lcapitulino@redhat.com> -S: Supported +S: Maintained F: qmp.c F: monitor.c F: qmp-commands.hx @@ -283,6 +283,8 @@ libusb="" usb_redir="" glx="" zlib="yes" +lzo="no" +snappy="no" guest_agent="" guest_agent_with_vss="no" vss_win32_sdk="" @@ -995,6 +997,10 @@ for opt do ;; --disable-zlib-test) zlib="no" ;; + --enable-lzo) lzo="yes" + ;; + --enable-snappy) snappy="yes" + ;; --enable-guest-agent) guest_agent="yes" ;; --disable-guest-agent) guest_agent="no" @@ -1289,6 +1295,8 @@ Advanced options (experts only): --enable-libusb enable libusb (for usb passthrough) --disable-usb-redir disable usb network redirection support --enable-usb-redir enable usb network redirection support + --enable-lzo enable the support of lzo compression library + --enable-snappy enable the support of snappy compression library --disable-guest-agent disable building of the QEMU Guest Agent --enable-guest-agent enable building of the QEMU Guest Agent --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent @@ -1660,6 +1668,42 @@ fi LIBS="$LIBS -lz" ########################################## +# lzo check + +if test "$lzo" != "no" ; then + cat > $TMPC << EOF +#include <lzo/lzo1x.h> +int main(void) { lzo_version(); return 0; } +EOF + if compile_prog "" "-llzo2" ; then + : + else + error_exit "lzo check failed" \ + "Make sure to have the lzo libs and headers installed." + fi + + libs_softmmu="$libs_softmmu -llzo2" +fi + +########################################## +# snappy check + +if test "$snappy" != "no" ; then + cat > $TMPC << EOF +#include <snappy-c.h> +int main(void) { snappy_max_compressed_length(4096); return 0; } +EOF + if compile_prog "" "-lsnappy" ; then + : + else + error_exit "snappy check failed" \ + "Make sure to have the snappy libs and headers installed." + fi + + libs_softmmu="$libs_softmmu -lsnappy" +fi + +########################################## # libseccomp check if test "$seccomp" != "no" ; then @@ -4045,6 +4089,8 @@ echo "TPM passthrough $tpm_passthrough" echo "QOM debugging $qom_cast_debug" echo "vhdx $vhdx" echo "Quorum $quorum" +echo "lzo support $lzo" +echo "snappy support $snappy" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -4368,6 +4414,14 @@ if test "$glx" = "yes" ; then echo "GLX_LIBS=$glx_libs" >> $config_host_mak fi +if test "$lzo" = "yes" ; then + echo "CONFIG_LZO=y" >> $config_host_mak +fi + +if test "$snappy" = "yes" ; then + echo "CONFIG_SNAPPY=y" >> $config_host_mak +fi + if test "$libiscsi" = "yes" ; then echo "CONFIG_LIBISCSI=m" >> $config_host_mak if test "$libiscsi_version" = "1.4.0"; then @@ -25,6 +25,17 @@ #include "qapi/error.h" #include "qmp-commands.h" +#include <zlib.h> +#ifdef CONFIG_LZO +#include <lzo/lzo1x.h> +#endif +#ifdef CONFIG_SNAPPY +#include <snappy-c.h> +#endif +#ifndef ELF_MACHINE_UNAME +#define ELF_MACHINE_UNAME "Unknown" +#endif + static uint16_t cpu_convert_to_target16(uint16_t val, int endian) { if (endian == ELFDATA2LSB) { @@ -76,6 +87,19 @@ typedef struct DumpState { int64_t begin; int64_t length; Error **errp; + + uint8_t *note_buf; /* buffer for notes */ + size_t note_buf_offset; /* the writing place in note_buf */ + uint32_t nr_cpus; /* number of guest's cpu */ + size_t page_size; /* guest's page size */ + uint32_t page_shift; /* guest's page shift */ + uint64_t max_mapnr; /* the biggest guest's phys-mem's number */ + size_t len_dump_bitmap; /* the size of the place used to store + dump_bitmap in vmcore */ + off_t offset_dump_bitmap; /* offset of dump_bitmap part in vmcore */ + off_t offset_page; /* offset of page part in vmcore */ + size_t num_dumpable; /* number of page that can be dumped */ + uint32_t flag_compress; /* indicate the compression format */ } DumpState; static int dump_cleanup(DumpState *s) @@ -99,7 +123,7 @@ static void dump_error(DumpState *s, const char *reason) dump_cleanup(s); } -static int fd_write_vmcore(void *buf, size_t size, void *opaque) +static int fd_write_vmcore(const void *buf, size_t size, void *opaque) { DumpState *s = opaque; size_t written_size; @@ -271,7 +295,7 @@ static inline int cpu_index(CPUState *cpu) return cpu->cpu_index + 1; } -static int write_elf64_notes(DumpState *s) +static int write_elf64_notes(WriteCoreDumpFunction f, DumpState *s) { CPUState *cpu; int ret; @@ -279,7 +303,7 @@ static int write_elf64_notes(DumpState *s) CPU_FOREACH(cpu) { id = cpu_index(cpu); - ret = cpu_write_elf64_note(fd_write_vmcore, cpu, id, s); + ret = cpu_write_elf64_note(f, cpu, id, s); if (ret < 0) { dump_error(s, "dump: failed to write elf notes.\n"); return -1; @@ -287,7 +311,7 @@ static int write_elf64_notes(DumpState *s) } CPU_FOREACH(cpu) { - ret = cpu_write_elf64_qemunote(fd_write_vmcore, cpu, s); + ret = cpu_write_elf64_qemunote(f, cpu, s); if (ret < 0) { dump_error(s, "dump: failed to write CPU status.\n"); return -1; @@ -321,7 +345,7 @@ static int write_elf32_note(DumpState *s) return 0; } -static int write_elf32_notes(DumpState *s) +static int write_elf32_notes(WriteCoreDumpFunction f, DumpState *s) { CPUState *cpu; int ret; @@ -329,7 +353,7 @@ static int write_elf32_notes(DumpState *s) CPU_FOREACH(cpu) { id = cpu_index(cpu); - ret = cpu_write_elf32_note(fd_write_vmcore, cpu, id, s); + ret = cpu_write_elf32_note(f, cpu, id, s); if (ret < 0) { dump_error(s, "dump: failed to write elf notes.\n"); return -1; @@ -337,7 +361,7 @@ static int write_elf32_notes(DumpState *s) } CPU_FOREACH(cpu) { - ret = cpu_write_elf32_qemunote(fd_write_vmcore, cpu, s); + ret = cpu_write_elf32_qemunote(f, cpu, s); if (ret < 0) { dump_error(s, "dump: failed to write CPU status.\n"); return -1; @@ -574,7 +598,7 @@ static int dump_begin(DumpState *s) } /* write notes to vmcore */ - if (write_elf64_notes(s) < 0) { + if (write_elf64_notes(fd_write_vmcore, s) < 0) { return -1; } @@ -597,7 +621,7 @@ static int dump_begin(DumpState *s) } /* write notes to vmcore */ - if (write_elf32_notes(s) < 0) { + if (write_elf32_notes(fd_write_vmcore, s) < 0) { return -1; } } @@ -686,6 +710,800 @@ static int create_vmcore(DumpState *s) return 0; } +static int write_start_flat_header(int fd) +{ + uint8_t *buf; + MakedumpfileHeader mh; + int ret = 0; + + memset(&mh, 0, sizeof(mh)); + strncpy(mh.signature, MAKEDUMPFILE_SIGNATURE, + strlen(MAKEDUMPFILE_SIGNATURE)); + + mh.type = cpu_to_be64(TYPE_FLAT_HEADER); + mh.version = cpu_to_be64(VERSION_FLAT_HEADER); + + buf = g_malloc0(MAX_SIZE_MDF_HEADER); + memcpy(buf, &mh, sizeof(mh)); + + size_t written_size; + written_size = qemu_write_full(fd, buf, MAX_SIZE_MDF_HEADER); + if (written_size != MAX_SIZE_MDF_HEADER) { + ret = -1; + } + + g_free(buf); + return ret; +} + +static int write_end_flat_header(int fd) +{ + MakedumpfileDataHeader mdh; + + mdh.offset = END_FLAG_FLAT_HEADER; + mdh.buf_size = END_FLAG_FLAT_HEADER; + + size_t written_size; + written_size = qemu_write_full(fd, &mdh, sizeof(mdh)); + if (written_size != sizeof(mdh)) { + return -1; + } + + return 0; +} + +static int write_buffer(int fd, off_t offset, const void *buf, size_t size) +{ + size_t written_size; + MakedumpfileDataHeader mdh; + + mdh.offset = cpu_to_be64(offset); + mdh.buf_size = cpu_to_be64(size); + + written_size = qemu_write_full(fd, &mdh, sizeof(mdh)); + if (written_size != sizeof(mdh)) { + return -1; + } + + written_size = qemu_write_full(fd, buf, size); + if (written_size != size) { + return -1; + } + + return 0; +} + +static int buf_write_note(const void *buf, size_t size, void *opaque) +{ + DumpState *s = opaque; + + /* note_buf is not enough */ + if (s->note_buf_offset + size > s->note_size) { + return -1; + } + + memcpy(s->note_buf + s->note_buf_offset, buf, size); + + s->note_buf_offset += size; + + return 0; +} + +/* write common header, sub header and elf note to vmcore */ +static int create_header32(DumpState *s) +{ + int ret = 0; + DiskDumpHeader32 *dh = NULL; + KdumpSubHeader32 *kh = NULL; + size_t size; + int endian = s->dump_info.d_endian; + uint32_t block_size; + uint32_t sub_hdr_size; + uint32_t bitmap_blocks; + uint32_t status = 0; + uint64_t offset_note; + + /* write common header, the version of kdump-compressed format is 6th */ + size = sizeof(DiskDumpHeader32); + dh = g_malloc0(size); + + strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE)); + dh->header_version = cpu_convert_to_target32(6, endian); + block_size = s->page_size; + dh->block_size = cpu_convert_to_target32(block_size, endian); + sub_hdr_size = sizeof(struct KdumpSubHeader32) + s->note_size; + sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size); + dh->sub_hdr_size = cpu_convert_to_target32(sub_hdr_size, endian); + /* dh->max_mapnr may be truncated, full 64bit is in kh.max_mapnr_64 */ + dh->max_mapnr = cpu_convert_to_target32(MIN(s->max_mapnr, UINT_MAX), + endian); + dh->nr_cpus = cpu_convert_to_target32(s->nr_cpus, endian); + bitmap_blocks = DIV_ROUND_UP(s->len_dump_bitmap, block_size) * 2; + dh->bitmap_blocks = cpu_convert_to_target32(bitmap_blocks, endian); + strncpy(dh->utsname.machine, ELF_MACHINE_UNAME, sizeof(dh->utsname.machine)); + + if (s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) { + status |= DUMP_DH_COMPRESSED_ZLIB; + } +#ifdef CONFIG_LZO + if (s->flag_compress & DUMP_DH_COMPRESSED_LZO) { + status |= DUMP_DH_COMPRESSED_LZO; + } +#endif +#ifdef CONFIG_SNAPPY + if (s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) { + status |= DUMP_DH_COMPRESSED_SNAPPY; + } +#endif + dh->status = cpu_convert_to_target32(status, endian); + + if (write_buffer(s->fd, 0, dh, size) < 0) { + dump_error(s, "dump: failed to write disk dump header.\n"); + ret = -1; + goto out; + } + + /* write sub header */ + size = sizeof(KdumpSubHeader32); + kh = g_malloc0(size); + + /* 64bit max_mapnr_64 */ + kh->max_mapnr_64 = cpu_convert_to_target64(s->max_mapnr, endian); + kh->phys_base = cpu_convert_to_target32(PHYS_BASE, endian); + kh->dump_level = cpu_convert_to_target32(DUMP_LEVEL, endian); + + offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size; + kh->offset_note = cpu_convert_to_target64(offset_note, endian); + kh->note_size = cpu_convert_to_target32(s->note_size, endian); + + if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * + block_size, kh, size) < 0) { + dump_error(s, "dump: failed to write kdump sub header.\n"); + ret = -1; + goto out; + } + + /* write note */ + s->note_buf = g_malloc0(s->note_size); + s->note_buf_offset = 0; + + /* use s->note_buf to store notes temporarily */ + if (write_elf32_notes(buf_write_note, s) < 0) { + ret = -1; + goto out; + } + + if (write_buffer(s->fd, offset_note, s->note_buf, + s->note_size) < 0) { + dump_error(s, "dump: failed to write notes"); + ret = -1; + goto out; + } + + /* get offset of dump_bitmap */ + s->offset_dump_bitmap = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size) * + block_size; + + /* get offset of page */ + s->offset_page = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size + bitmap_blocks) * + block_size; + +out: + g_free(dh); + g_free(kh); + g_free(s->note_buf); + + return ret; +} + +/* write common header, sub header and elf note to vmcore */ +static int create_header64(DumpState *s) +{ + int ret = 0; + DiskDumpHeader64 *dh = NULL; + KdumpSubHeader64 *kh = NULL; + size_t size; + int endian = s->dump_info.d_endian; + uint32_t block_size; + uint32_t sub_hdr_size; + uint32_t bitmap_blocks; + uint32_t status = 0; + uint64_t offset_note; + + /* write common header, the version of kdump-compressed format is 6th */ + size = sizeof(DiskDumpHeader64); + dh = g_malloc0(size); + + strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE)); + dh->header_version = cpu_convert_to_target32(6, endian); + block_size = s->page_size; + dh->block_size = cpu_convert_to_target32(block_size, endian); + sub_hdr_size = sizeof(struct KdumpSubHeader64) + s->note_size; + sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size); + dh->sub_hdr_size = cpu_convert_to_target32(sub_hdr_size, endian); + /* dh->max_mapnr may be truncated, full 64bit is in kh.max_mapnr_64 */ + dh->max_mapnr = cpu_convert_to_target32(MIN(s->max_mapnr, UINT_MAX), + endian); + dh->nr_cpus = cpu_convert_to_target32(s->nr_cpus, endian); + bitmap_blocks = DIV_ROUND_UP(s->len_dump_bitmap, block_size) * 2; + dh->bitmap_blocks = cpu_convert_to_target32(bitmap_blocks, endian); + strncpy(dh->utsname.machine, ELF_MACHINE_UNAME, sizeof(dh->utsname.machine)); + + if (s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) { + status |= DUMP_DH_COMPRESSED_ZLIB; + } +#ifdef CONFIG_LZO + if (s->flag_compress & DUMP_DH_COMPRESSED_LZO) { + status |= DUMP_DH_COMPRESSED_LZO; + } +#endif +#ifdef CONFIG_SNAPPY + if (s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) { + status |= DUMP_DH_COMPRESSED_SNAPPY; + } +#endif + dh->status = cpu_convert_to_target32(status, endian); + + if (write_buffer(s->fd, 0, dh, size) < 0) { + dump_error(s, "dump: failed to write disk dump header.\n"); + ret = -1; + goto out; + } + + /* write sub header */ + size = sizeof(KdumpSubHeader64); + kh = g_malloc0(size); + + /* 64bit max_mapnr_64 */ + kh->max_mapnr_64 = cpu_convert_to_target64(s->max_mapnr, endian); + kh->phys_base = cpu_convert_to_target64(PHYS_BASE, endian); + kh->dump_level = cpu_convert_to_target32(DUMP_LEVEL, endian); + + offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size; + kh->offset_note = cpu_convert_to_target64(offset_note, endian); + kh->note_size = cpu_convert_to_target64(s->note_size, endian); + + if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS * + block_size, kh, size) < 0) { + dump_error(s, "dump: failed to write kdump sub header.\n"); + ret = -1; + goto out; + } + + /* write note */ + s->note_buf = g_malloc0(s->note_size); + s->note_buf_offset = 0; + + /* use s->note_buf to store notes temporarily */ + if (write_elf64_notes(buf_write_note, s) < 0) { + ret = -1; + goto out; + } + + if (write_buffer(s->fd, offset_note, s->note_buf, + s->note_size) < 0) { + dump_error(s, "dump: failed to write notes"); + ret = -1; + goto out; + } + + /* get offset of dump_bitmap */ + s->offset_dump_bitmap = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size) * + block_size; + + /* get offset of page */ + s->offset_page = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size + bitmap_blocks) * + block_size; + +out: + g_free(dh); + g_free(kh); + g_free(s->note_buf); + + return ret; +} + +static int write_dump_header(DumpState *s) +{ + if (s->dump_info.d_machine == EM_386) { + return create_header32(s); + } else { + return create_header64(s); + } +} + +/* + * set dump_bitmap sequencely. the bit before last_pfn is not allowed to be + * rewritten, so if need to set the first bit, set last_pfn and pfn to 0. + * set_dump_bitmap will always leave the recently set bit un-sync. And setting + * (last bit + sizeof(buf) * 8) to 0 will do flushing the content in buf into + * vmcore, ie. synchronizing un-sync bit into vmcore. + */ +static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value, + uint8_t *buf, DumpState *s) +{ + off_t old_offset, new_offset; + off_t offset_bitmap1, offset_bitmap2; + uint32_t byte, bit; + + /* should not set the previous place */ + assert(last_pfn <= pfn); + + /* + * if the bit needed to be set is not cached in buf, flush the data in buf + * to vmcore firstly. + * making new_offset be bigger than old_offset can also sync remained data + * into vmcore. + */ + old_offset = BUFSIZE_BITMAP * (last_pfn / PFN_BUFBITMAP); + new_offset = BUFSIZE_BITMAP * (pfn / PFN_BUFBITMAP); + + while (old_offset < new_offset) { + /* calculate the offset and write dump_bitmap */ + offset_bitmap1 = s->offset_dump_bitmap + old_offset; + if (write_buffer(s->fd, offset_bitmap1, buf, + BUFSIZE_BITMAP) < 0) { + return -1; + } + + /* dump level 1 is chosen, so 1st and 2nd bitmap are same */ + offset_bitmap2 = s->offset_dump_bitmap + s->len_dump_bitmap + + old_offset; + if (write_buffer(s->fd, offset_bitmap2, buf, + BUFSIZE_BITMAP) < 0) { + return -1; + } + + memset(buf, 0, BUFSIZE_BITMAP); + old_offset += BUFSIZE_BITMAP; + } + + /* get the exact place of the bit in the buf, and set it */ + byte = (pfn % PFN_BUFBITMAP) / CHAR_BIT; + bit = (pfn % PFN_BUFBITMAP) % CHAR_BIT; + if (value) { + buf[byte] |= 1u << bit; + } else { + buf[byte] &= ~(1u << bit); + } + + return 0; +} + +/* + * exam every page and return the page frame number and the address of the page. + * bufptr can be NULL. note: the blocks here is supposed to reflect guest-phys + * blocks, so block->target_start and block->target_end should be interal + * multiples of the target page size. + */ +static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr, + uint8_t **bufptr, DumpState *s) +{ + GuestPhysBlock *block = *blockptr; + hwaddr addr; + uint8_t *buf; + + /* block == NULL means the start of the iteration */ + if (!block) { + block = QTAILQ_FIRST(&s->guest_phys_blocks.head); + *blockptr = block; + assert(block->target_start % s->page_size == 0); + assert(block->target_end % s->page_size == 0); + *pfnptr = paddr_to_pfn(block->target_start, s->page_shift); + if (bufptr) { + *bufptr = block->host_addr; + } + return true; + } + + *pfnptr = *pfnptr + 1; + addr = pfn_to_paddr(*pfnptr, s->page_shift); + + if ((addr >= block->target_start) && + (addr + s->page_size <= block->target_end)) { + buf = block->host_addr + (addr - block->target_start); + } else { + /* the next page is in the next block */ + block = QTAILQ_NEXT(block, next); + *blockptr = block; + if (!block) { + return false; + } + assert(block->target_start % s->page_size == 0); + assert(block->target_end % s->page_size == 0); + *pfnptr = paddr_to_pfn(block->target_start, s->page_shift); + buf = block->host_addr; + } + + if (bufptr) { + *bufptr = buf; + } + + return true; +} + +static int write_dump_bitmap(DumpState *s) +{ + int ret = 0; + uint64_t last_pfn, pfn; + void *dump_bitmap_buf; + size_t num_dumpable; + GuestPhysBlock *block_iter = NULL; + + /* dump_bitmap_buf is used to store dump_bitmap temporarily */ + dump_bitmap_buf = g_malloc0(BUFSIZE_BITMAP); + + num_dumpable = 0; + last_pfn = 0; + + /* + * exam memory page by page, and set the bit in dump_bitmap corresponded + * to the existing page. + */ + while (get_next_page(&block_iter, &pfn, NULL, s)) { + ret = set_dump_bitmap(last_pfn, pfn, true, dump_bitmap_buf, s); + if (ret < 0) { + dump_error(s, "dump: failed to set dump_bitmap.\n"); + ret = -1; + goto out; + } + + last_pfn = pfn; + num_dumpable++; + } + + /* + * set_dump_bitmap will always leave the recently set bit un-sync. Here we + * set last_pfn + PFN_BUFBITMAP to 0 and those set but un-sync bit will be + * synchronized into vmcore. + */ + if (num_dumpable > 0) { + ret = set_dump_bitmap(last_pfn, last_pfn + PFN_BUFBITMAP, false, + dump_bitmap_buf, s); + if (ret < 0) { + dump_error(s, "dump: failed to sync dump_bitmap.\n"); + ret = -1; + goto out; + } + } + + /* number of dumpable pages that will be dumped later */ + s->num_dumpable = num_dumpable; + +out: + g_free(dump_bitmap_buf); + + return ret; +} + +static void prepare_data_cache(DataCache *data_cache, DumpState *s, + off_t offset) +{ + data_cache->fd = s->fd; + data_cache->data_size = 0; + data_cache->buf_size = BUFSIZE_DATA_CACHE; + data_cache->buf = g_malloc0(BUFSIZE_DATA_CACHE); + data_cache->offset = offset; +} + +static int write_cache(DataCache *dc, const void *buf, size_t size, + bool flag_sync) +{ + /* + * dc->buf_size should not be less than size, otherwise dc will never be + * enough + */ + assert(size <= dc->buf_size); + + /* + * if flag_sync is set, synchronize data in dc->buf into vmcore. + * otherwise check if the space is enough for caching data in buf, if not, + * write the data in dc->buf to dc->fd and reset dc->buf + */ + if ((!flag_sync && dc->data_size + size > dc->buf_size) || + (flag_sync && dc->data_size > 0)) { + if (write_buffer(dc->fd, dc->offset, dc->buf, dc->data_size) < 0) { + return -1; + } + + dc->offset += dc->data_size; + dc->data_size = 0; + } + + if (!flag_sync) { + memcpy(dc->buf + dc->data_size, buf, size); + dc->data_size += size; + } + + return 0; +} + +static void free_data_cache(DataCache *data_cache) +{ + g_free(data_cache->buf); +} + +static size_t get_len_buf_out(size_t page_size, uint32_t flag_compress) +{ + size_t len_buf_out_zlib, len_buf_out_lzo, len_buf_out_snappy; + size_t len_buf_out; + + /* init buf_out */ + len_buf_out_zlib = len_buf_out_lzo = len_buf_out_snappy = 0; + + /* buf size for zlib */ + len_buf_out_zlib = compressBound(page_size); + + /* buf size for lzo */ +#ifdef CONFIG_LZO + if (flag_compress & DUMP_DH_COMPRESSED_LZO) { + if (lzo_init() != LZO_E_OK) { + /* return 0 to indicate lzo is unavailable */ + return 0; + } + } + + /* + * LZO will expand incompressible data by a little amount. please check the + * following URL to see the expansion calculation: + * http://www.oberhumer.com/opensource/lzo/lzofaq.php + */ + len_buf_out_lzo = page_size + page_size / 16 + 64 + 3; +#endif + +#ifdef CONFIG_SNAPPY + /* buf size for snappy */ + len_buf_out_snappy = snappy_max_compressed_length(page_size); +#endif + + /* get the biggest that can store all kinds of compressed page */ + len_buf_out = MAX(len_buf_out_zlib, + MAX(len_buf_out_lzo, len_buf_out_snappy)); + + return len_buf_out; +} + +/* + * check if the page is all 0 + */ +static inline bool is_zero_page(const uint8_t *buf, size_t page_size) +{ + return buffer_is_zero(buf, page_size); +} + +static int write_dump_pages(DumpState *s) +{ + int ret = 0; + DataCache page_desc, page_data; + size_t len_buf_out, size_out; +#ifdef CONFIG_LZO + lzo_bytep wrkmem = NULL; +#endif + uint8_t *buf_out = NULL; + off_t offset_desc, offset_data; + PageDescriptor pd, pd_zero; + uint8_t *buf; + int endian = s->dump_info.d_endian; + GuestPhysBlock *block_iter = NULL; + uint64_t pfn_iter; + + /* get offset of page_desc and page_data in dump file */ + offset_desc = s->offset_page; + offset_data = offset_desc + sizeof(PageDescriptor) * s->num_dumpable; + + prepare_data_cache(&page_desc, s, offset_desc); + prepare_data_cache(&page_data, s, offset_data); + + /* prepare buffer to store compressed data */ + len_buf_out = get_len_buf_out(s->page_size, s->flag_compress); + if (len_buf_out == 0) { + dump_error(s, "dump: failed to get length of output buffer.\n"); + goto out; + } + +#ifdef CONFIG_LZO + wrkmem = g_malloc(LZO1X_1_MEM_COMPRESS); +#endif + + buf_out = g_malloc(len_buf_out); + + /* + * init zero page's page_desc and page_data, because every zero page + * uses the same page_data + */ + pd_zero.size = cpu_convert_to_target32(s->page_size, endian); + pd_zero.flags = cpu_convert_to_target32(0, endian); + pd_zero.offset = cpu_convert_to_target64(offset_data, endian); + pd_zero.page_flags = cpu_convert_to_target64(0, endian); + buf = g_malloc0(s->page_size); + ret = write_cache(&page_data, buf, s->page_size, false); + g_free(buf); + if (ret < 0) { + dump_error(s, "dump: failed to write page data(zero page).\n"); + goto out; + } + + offset_data += s->page_size; + + /* + * dump memory to vmcore page by page. zero page will all be resided in the + * first page of page section + */ + while (get_next_page(&block_iter, &pfn_iter, &buf, s)) { + /* check zero page */ + if (is_zero_page(buf, s->page_size)) { + ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor), + false); + if (ret < 0) { + dump_error(s, "dump: failed to write page desc.\n"); + goto out; + } + } else { + /* + * not zero page, then: + * 1. compress the page + * 2. write the compressed page into the cache of page_data + * 3. get page desc of the compressed page and write it into the + * cache of page_desc + * + * only one compression format will be used here, for + * s->flag_compress is set. But when compression fails to work, + * we fall back to save in plaintext. + */ + size_out = len_buf_out; + if ((s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) && + (compress2(buf_out, (uLongf *)&size_out, buf, s->page_size, + Z_BEST_SPEED) == Z_OK) && (size_out < s->page_size)) { + pd.flags = cpu_convert_to_target32(DUMP_DH_COMPRESSED_ZLIB, + endian); + pd.size = cpu_convert_to_target32(size_out, endian); + + ret = write_cache(&page_data, buf_out, size_out, false); + if (ret < 0) { + dump_error(s, "dump: failed to write page data.\n"); + goto out; + } +#ifdef CONFIG_LZO + } else if ((s->flag_compress & DUMP_DH_COMPRESSED_LZO) && + (lzo1x_1_compress(buf, s->page_size, buf_out, + (lzo_uint *)&size_out, wrkmem) == LZO_E_OK) && + (size_out < s->page_size)) { + pd.flags = cpu_convert_to_target32(DUMP_DH_COMPRESSED_LZO, + endian); + pd.size = cpu_convert_to_target32(size_out, endian); + + ret = write_cache(&page_data, buf_out, size_out, false); + if (ret < 0) { + dump_error(s, "dump: failed to write page data.\n"); + goto out; + } +#endif +#ifdef CONFIG_SNAPPY + } else if ((s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) && + (snappy_compress((char *)buf, s->page_size, + (char *)buf_out, &size_out) == SNAPPY_OK) && + (size_out < s->page_size)) { + pd.flags = cpu_convert_to_target32( + DUMP_DH_COMPRESSED_SNAPPY, endian); + pd.size = cpu_convert_to_target32(size_out, endian); + + ret = write_cache(&page_data, buf_out, size_out, false); + if (ret < 0) { + dump_error(s, "dump: failed to write page data.\n"); + goto out; + } +#endif + } else { + /* + * fall back to save in plaintext, size_out should be + * assigned to s->page_size + */ + pd.flags = cpu_convert_to_target32(0, endian); + size_out = s->page_size; + pd.size = cpu_convert_to_target32(size_out, endian); + + ret = write_cache(&page_data, buf, s->page_size, false); + if (ret < 0) { + dump_error(s, "dump: failed to write page data.\n"); + goto out; + } + } + + /* get and write page desc here */ + pd.page_flags = cpu_convert_to_target64(0, endian); + pd.offset = cpu_convert_to_target64(offset_data, endian); + offset_data += size_out; + + ret = write_cache(&page_desc, &pd, sizeof(PageDescriptor), false); + if (ret < 0) { + dump_error(s, "dump: failed to write page desc.\n"); + goto out; + } + } + } + + ret = write_cache(&page_desc, NULL, 0, true); + if (ret < 0) { + dump_error(s, "dump: failed to sync cache for page_desc.\n"); + goto out; + } + ret = write_cache(&page_data, NULL, 0, true); + if (ret < 0) { + dump_error(s, "dump: failed to sync cache for page_data.\n"); + goto out; + } + +out: + free_data_cache(&page_desc); + free_data_cache(&page_data); + +#ifdef CONFIG_LZO + g_free(wrkmem); +#endif + + g_free(buf_out); + + return ret; +} + +static int create_kdump_vmcore(DumpState *s) +{ + int ret; + + /* + * the kdump-compressed format is: + * File offset + * +------------------------------------------+ 0x0 + * | main header (struct disk_dump_header) | + * |------------------------------------------+ block 1 + * | sub header (struct kdump_sub_header) | + * |------------------------------------------+ block 2 + * | 1st-dump_bitmap | + * |------------------------------------------+ block 2 + X blocks + * | 2nd-dump_bitmap | (aligned by block) + * |------------------------------------------+ block 2 + 2 * X blocks + * | page desc for pfn 0 (struct page_desc) | (aligned by block) + * | page desc for pfn 1 (struct page_desc) | + * | : | + * |------------------------------------------| (not aligned by block) + * | page data (pfn 0) | + * | page data (pfn 1) | + * | : | + * +------------------------------------------+ + */ + + ret = write_start_flat_header(s->fd); + if (ret < 0) { + dump_error(s, "dump: failed to write start flat header.\n"); + return -1; + } + + ret = write_dump_header(s); + if (ret < 0) { + return -1; + } + + ret = write_dump_bitmap(s); + if (ret < 0) { + return -1; + } + + ret = write_dump_pages(s); + if (ret < 0) { + return -1; + } + + ret = write_end_flat_header(s->fd); + if (ret < 0) { + dump_error(s, "dump: failed to write end flat header.\n"); + return -1; + } + + dump_completed(s); + + return 0; +} + static ram_addr_t get_start_block(DumpState *s) { GuestPhysBlock *block; @@ -714,7 +1532,16 @@ static ram_addr_t get_start_block(DumpState *s) return -1; } -static int dump_init(DumpState *s, int fd, bool paging, bool has_filter, +static void get_max_mapnr(DumpState *s) +{ + GuestPhysBlock *last_block; + + last_block = QTAILQ_LAST(&s->guest_phys_blocks.head, GuestPhysBlockHead); + s->max_mapnr = paddr_to_pfn(last_block->target_end, s->page_shift); +} + +static int dump_init(DumpState *s, int fd, bool has_format, + DumpGuestMemoryFormat format, bool paging, bool has_filter, int64_t begin, int64_t length, Error **errp) { CPUState *cpu; @@ -722,6 +1549,11 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter, Error *err = NULL; int ret; + /* kdump-compressed is conflict with paging and filter */ + if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { + assert(!paging && !has_filter); + } + if (runstate_is_running()) { vm_stop(RUN_STATE_SAVE_VM); s->resume = true; @@ -782,6 +1614,38 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter, qemu_get_guest_simple_memory_mapping(&s->list, &s->guest_phys_blocks); } + s->nr_cpus = nr_cpus; + s->page_size = TARGET_PAGE_SIZE; + s->page_shift = ffs(s->page_size) - 1; + + get_max_mapnr(s); + + uint64_t tmp; + tmp = DIV_ROUND_UP(DIV_ROUND_UP(s->max_mapnr, CHAR_BIT), s->page_size); + s->len_dump_bitmap = tmp * s->page_size; + + /* init for kdump-compressed format */ + if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { + switch (format) { + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB: + s->flag_compress = DUMP_DH_COMPRESSED_ZLIB; + break; + + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO: + s->flag_compress = DUMP_DH_COMPRESSED_LZO; + break; + + case DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY: + s->flag_compress = DUMP_DH_COMPRESSED_SNAPPY; + break; + + default: + s->flag_compress = 0; + } + + return 0; + } + if (s->has_filter) { memory_mapping_filter(&s->list, s->begin, s->length); } @@ -841,14 +1705,25 @@ cleanup: } void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, - int64_t begin, bool has_length, int64_t length, - Error **errp) + int64_t begin, bool has_length, + int64_t length, bool has_format, + DumpGuestMemoryFormat format, Error **errp) { const char *p; int fd = -1; DumpState *s; int ret; + /* + * kdump-compressed format need the whole memory dumped, so paging or + * filter is not supported here. + */ + if ((has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) && + (paging || has_begin || has_length)) { + error_setg(errp, "kdump-compressed format doesn't support paging or " + "filter"); + return; + } if (has_begin && !has_length) { error_set(errp, QERR_MISSING_PARAMETER, "length"); return; @@ -858,6 +1733,21 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, return; } + /* check whether lzo/snappy is supported */ +#ifndef CONFIG_LZO + if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO) { + error_setg(errp, "kdump-lzo is not available now"); + return; + } +#endif + +#ifndef CONFIG_SNAPPY + if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY) { + error_setg(errp, "kdump-snappy is not available now"); + return; + } +#endif + #if !defined(WIN32) if (strstart(file, "fd:", &p)) { fd = monitor_get_fd(cur_mon, p, errp); @@ -882,15 +1772,55 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, s = g_malloc0(sizeof(DumpState)); - ret = dump_init(s, fd, paging, has_begin, begin, length, errp); + ret = dump_init(s, fd, has_format, format, paging, has_begin, + begin, length, errp); if (ret < 0) { g_free(s); return; } - if (create_vmcore(s) < 0 && !error_is_set(s->errp)) { - error_set(errp, QERR_IO_ERROR); + if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) { + if (create_kdump_vmcore(s) < 0 && !error_is_set(s->errp)) { + error_set(errp, QERR_IO_ERROR); + } + } else { + if (create_vmcore(s) < 0 && !error_is_set(s->errp)) { + error_set(errp, QERR_IO_ERROR); + } } g_free(s); } + +DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp) +{ + DumpGuestMemoryFormatList *item; + DumpGuestMemoryCapability *cap = + g_malloc0(sizeof(DumpGuestMemoryCapability)); + + /* elf is always available */ + item = g_malloc0(sizeof(DumpGuestMemoryFormatList)); + cap->formats = item; + item->value = DUMP_GUEST_MEMORY_FORMAT_ELF; + + /* kdump-zlib is always available */ + item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList)); + item = item->next; + item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB; + + /* add new item if kdump-lzo is available */ +#ifdef CONFIG_LZO + item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList)); + item = item->next; + item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO; +#endif + + /* add new item if kdump-snappy is available */ +#ifdef CONFIG_SNAPPY + item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList)); + item = item->next; + item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY; +#endif + + return cap; +} @@ -1311,8 +1311,11 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) const char *file = qdict_get_str(qdict, "filename"); bool has_begin = qdict_haskey(qdict, "begin"); bool has_length = qdict_haskey(qdict, "length"); + /* kdump-compressed format is not supported for HMP */ + bool has_format = false; int64_t begin = 0; int64_t length = 0; + enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF; char *prot; if (has_begin) { @@ -1325,7 +1328,7 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) prot = g_strconcat("file:", file, NULL); qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length, - &errp); + has_format, dump_format, &errp); hmp_handle_error(mon, &errp); g_free(prot); } diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 73c67b70c2..25193c943b 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -105,7 +105,7 @@ void qerror_report_err(Error *err); ERROR_CLASS_GENERIC_ERROR, "Device '%s' does not support hotplugging" #define QERR_DEVICE_NOT_ACTIVE \ - ERROR_CLASS_DEVICE_NOT_ACTIVE, "Device '%s' has not been activated" + ERROR_CLASS_DEVICE_NOT_ACTIVE, "No %s device has been activated" #define QERR_DEVICE_NOT_ENCRYPTED \ ERROR_CLASS_GENERIC_ERROR, "Device '%s' is not encrypted" diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 367eda17d1..d734be8a40 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -28,7 +28,8 @@ #include "qemu/tls.h" #include "qemu/typedefs.h" -typedef int (*WriteCoreDumpFunction)(void *buf, size_t size, void *opaque); +typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size, + void *opaque); /** * vaddr: diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h index 19fafb2cf9..efab7a32f3 100644 --- a/include/sysemu/dump.h +++ b/include/sysemu/dump.h @@ -14,12 +14,150 @@ #ifndef DUMP_H #define DUMP_H +#define MAKEDUMPFILE_SIGNATURE "makedumpfile" +#define MAX_SIZE_MDF_HEADER (4096) /* max size of makedumpfile_header */ +#define TYPE_FLAT_HEADER (1) /* type of flattened format */ +#define VERSION_FLAT_HEADER (1) /* version of flattened format */ +#define END_FLAG_FLAT_HEADER (-1) + +#define ARCH_PFN_OFFSET (0) + +#define paddr_to_pfn(X, page_shift) \ + (((unsigned long long)(X) >> (page_shift)) - ARCH_PFN_OFFSET) +#define pfn_to_paddr(X, page_shift) \ + (((unsigned long long)(X) + ARCH_PFN_OFFSET) << (page_shift)) + +/* + * flag for compressed format + */ +#define DUMP_DH_COMPRESSED_ZLIB (0x1) +#define DUMP_DH_COMPRESSED_LZO (0x2) +#define DUMP_DH_COMPRESSED_SNAPPY (0x4) + +#define KDUMP_SIGNATURE "KDUMP " +#define SIG_LEN (sizeof(KDUMP_SIGNATURE) - 1) +#define PHYS_BASE (0) +#define DUMP_LEVEL (1) +#define DISKDUMP_HEADER_BLOCKS (1) +#define BUFSIZE_BITMAP (TARGET_PAGE_SIZE) +#define PFN_BUFBITMAP (CHAR_BIT * BUFSIZE_BITMAP) +#define BUFSIZE_DATA_CACHE (TARGET_PAGE_SIZE * 4) + typedef struct ArchDumpInfo { int d_machine; /* Architecture */ int d_endian; /* ELFDATA2LSB or ELFDATA2MSB */ int d_class; /* ELFCLASS32 or ELFCLASS64 */ } ArchDumpInfo; +typedef struct QEMU_PACKED MakedumpfileHeader { + char signature[16]; /* = "makedumpfile" */ + int64_t type; + int64_t version; +} MakedumpfileHeader; + +typedef struct QEMU_PACKED MakedumpfileDataHeader { + int64_t offset; + int64_t buf_size; +} MakedumpfileDataHeader; + +typedef struct QEMU_PACKED NewUtsname { + char sysname[65]; + char nodename[65]; + char release[65]; + char version[65]; + char machine[65]; + char domainname[65]; +} NewUtsname; + +typedef struct QEMU_PACKED DiskDumpHeader32 { + char signature[SIG_LEN]; /* = "KDUMP " */ + uint32_t header_version; /* Dump header version */ + NewUtsname utsname; /* copy of system_utsname */ + char timestamp[10]; /* Time stamp */ + uint32_t status; /* Above flags */ + uint32_t block_size; /* Size of a block in byte */ + uint32_t sub_hdr_size; /* Size of arch dependent header in block */ + uint32_t bitmap_blocks; /* Size of Memory bitmap in block */ + uint32_t max_mapnr; /* = max_mapnr , + obsoleted in header_version 6 */ + uint32_t total_ram_blocks; /* Number of blocks should be written */ + uint32_t device_blocks; /* Number of total blocks in dump device */ + uint32_t written_blocks; /* Number of written blocks */ + uint32_t current_cpu; /* CPU# which handles dump */ + uint32_t nr_cpus; /* Number of CPUs */ +} DiskDumpHeader32; + +typedef struct QEMU_PACKED DiskDumpHeader64 { + char signature[SIG_LEN]; /* = "KDUMP " */ + uint32_t header_version; /* Dump header version */ + NewUtsname utsname; /* copy of system_utsname */ + char timestamp[22]; /* Time stamp */ + uint32_t status; /* Above flags */ + uint32_t block_size; /* Size of a block in byte */ + uint32_t sub_hdr_size; /* Size of arch dependent header in block */ + uint32_t bitmap_blocks; /* Size of Memory bitmap in block */ + uint32_t max_mapnr; /* = max_mapnr, + obsoleted in header_version 6 */ + uint32_t total_ram_blocks; /* Number of blocks should be written */ + uint32_t device_blocks; /* Number of total blocks in dump device */ + uint32_t written_blocks; /* Number of written blocks */ + uint32_t current_cpu; /* CPU# which handles dump */ + uint32_t nr_cpus; /* Number of CPUs */ +} DiskDumpHeader64; + +typedef struct QEMU_PACKED KdumpSubHeader32 { + uint32_t phys_base; + uint32_t dump_level; /* header_version 1 and later */ + uint32_t split; /* header_version 2 and later */ + uint32_t start_pfn; /* header_version 2 and later, + obsoleted in header_version 6 */ + uint32_t end_pfn; /* header_version 2 and later, + obsoleted in header_version 6 */ + uint64_t offset_vmcoreinfo; /* header_version 3 and later */ + uint32_t size_vmcoreinfo; /* header_version 3 and later */ + uint64_t offset_note; /* header_version 4 and later */ + uint32_t note_size; /* header_version 4 and later */ + uint64_t offset_eraseinfo; /* header_version 5 and later */ + uint32_t size_eraseinfo; /* header_version 5 and later */ + uint64_t start_pfn_64; /* header_version 6 and later */ + uint64_t end_pfn_64; /* header_version 6 and later */ + uint64_t max_mapnr_64; /* header_version 6 and later */ +} KdumpSubHeader32; + +typedef struct QEMU_PACKED KdumpSubHeader64 { + uint64_t phys_base; + uint32_t dump_level; /* header_version 1 and later */ + uint32_t split; /* header_version 2 and later */ + uint64_t start_pfn; /* header_version 2 and later, + obsoleted in header_version 6 */ + uint64_t end_pfn; /* header_version 2 and later, + obsoleted in header_version 6 */ + uint64_t offset_vmcoreinfo; /* header_version 3 and later */ + uint64_t size_vmcoreinfo; /* header_version 3 and later */ + uint64_t offset_note; /* header_version 4 and later */ + uint64_t note_size; /* header_version 4 and later */ + uint64_t offset_eraseinfo; /* header_version 5 and later */ + uint64_t size_eraseinfo; /* header_version 5 and later */ + uint64_t start_pfn_64; /* header_version 6 and later */ + uint64_t end_pfn_64; /* header_version 6 and later */ + uint64_t max_mapnr_64; /* header_version 6 and later */ +} KdumpSubHeader64; + +typedef struct DataCache { + int fd; /* fd of the file where to write the cached data */ + uint8_t *buf; /* buffer for cached data */ + size_t buf_size; /* size of the buf */ + size_t data_size; /* size of cached data in buf */ + off_t offset; /* offset of the file */ +} DataCache; + +typedef struct QEMU_PACKED PageDescriptor { + uint64_t offset; /* the offset of the page data*/ + uint32_t size; /* the size of this dump page */ + uint32_t flags; /* flags */ + uint64_t page_flags; /* page flags */ +} PageDescriptor; + struct GuestPhysBlockList; /* memory_mapping.h */ int cpu_get_dump_info(ArchDumpInfo *info, const struct GuestPhysBlockList *guest_phys_blocks); @@ -2021,10 +2021,6 @@ int64_t dev_time; static void do_info_profile(Monitor *mon, const QDict *qdict) { - int64_t total; - total = qemu_time; - if (total == 0) - total = 1; monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n", dev_time, dev_time / (double)get_ticks_per_sec()); monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n", diff --git a/qapi-schema.json b/qapi-schema.json index c3592f6c11..193e7e4d3f 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2795,6 +2795,24 @@ { 'command': 'device_del', 'data': {'id': 'str'} } ## +# @DumpGuestMemoryFormat: +# +# An enumeration of guest-memory-dump's format. +# +# @elf: elf format +# +# @kdump-zlib: kdump-compressed format with zlib-compressed +# +# @kdump-lzo: kdump-compressed format with lzo-compressed +# +# @kdump-snappy: kdump-compressed format with snappy-compressed +# +# Since: 2.0 +## +{ 'enum': 'DumpGuestMemoryFormat', + 'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy' ] } + +## # @dump-guest-memory # # Dump guest's memory to vmcore. It is a synchronous operation that can take @@ -2830,13 +2848,42 @@ # want to dump all guest's memory, please specify the start @begin # and @length # +# @format: #optional if specified, the format of guest memory dump. But non-elf +# format is conflict with paging and filter, ie. @paging, @begin and +# @length is not allowed to be specified with non-elf @format at the +# same time (since 2.0) +# # Returns: nothing on success # # Since: 1.2 ## { 'command': 'dump-guest-memory', 'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int', - '*length': 'int' } } + '*length': 'int', '*format': 'DumpGuestMemoryFormat' } } + +## +# @DumpGuestMemoryCapability: +# +# A list of the available formats for dump-guest-memory +# +# Since: 2.0 +## +{ 'type': 'DumpGuestMemoryCapability', + 'data': { + 'formats': ['DumpGuestMemoryFormat'] } } + +## +# @query-dump-guest-memory-capability: +# +# Returns the available formats for dump-guest-memory +# +# Returns: A @DumpGuestMemoryCapability object listing available formats for +# dump-guest-memory +# +# Since: 2.0 +## +{ 'command': 'query-dump-guest-memory-capability', + 'returns': 'DumpGuestMemoryCapability' } ## # @netdev_add: diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 96ed85899d..5d830a2b56 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -124,7 +124,9 @@ opts_start_struct(Visitor *v, void **obj, const char *kind, OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); const QemuOpt *opt; - *obj = g_malloc0(size > 0 ? size : 1); + if (obj) { + *obj = g_malloc0(size > 0 ? size : 1); + } if (ov->depth++ > 0) { return; } diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index dc53545fa5..d0ea118fe3 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -131,9 +131,7 @@ static void qapi_dealloc_end_list(Visitor *v, Error **errp) static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name, Error **errp) { - if (obj) { - g_free(*obj); - } + g_free(*obj); } static void qapi_dealloc_type_int(Visitor *v, int64_t *obj, const char *name, diff --git a/qmp-commands.hx b/qmp-commands.hx index 8a0e8320c6..d982cd62b9 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -791,8 +791,8 @@ EQMP { .name = "dump-guest-memory", - .args_type = "paging:b,protocol:s,begin:i?,end:i?", - .params = "-p protocol [begin] [length]", + .args_type = "paging:b,protocol:s,begin:i?,end:i?,format:s?", + .params = "-p protocol [begin] [length] [format]", .help = "dump guest memory to file", .user_print = monitor_user_noop, .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory, @@ -813,6 +813,9 @@ Arguments: with length together (json-int) - "length": the memory size, in bytes. It's optional, and should be specified with begin together (json-int) +- "format": the format of guest memory dump. It's optional, and can be + elf|kdump-zlib|kdump-lzo|kdump-snappy, but non-elf formats will + conflict with paging and filter, ie. begin and length (json-string) Example: @@ -826,6 +829,26 @@ Notes: EQMP { + .name = "query-dump-guest-memory-capability", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_dump_guest_memory_capability, + }, + +SQMP +query-dump-guest-memory-capability +---------- + +Show available formats for 'dump-guest-memory' + +Example: + +-> { "execute": "query-dump-guest-memory-capability" } +<- { "return": { "formats": + ["elf", "kdump-zlib", "kdump-lzo", "kdump-snappy"] } + +EQMP + + { .name = "netdev_add", .args_type = "netdev:O", .mhandler.cmd_new = qmp_netdev_add, diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index b12b6964ef..9734ab0a53 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -7,8 +7,8 @@ # Anthony Liguori <aliguori@us.ibm.com> # Michael Roth <mdroth@linux.vnet.ibm.com> # -# This work is licensed under the terms of the GNU GPLv2. -# See the COPYING.LIB file in the top-level directory. +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. from ordereddict import OrderedDict from qapi import * @@ -23,13 +23,6 @@ def type_visitor(name): else: return 'visit_type_%s' % name -def generate_decl_enum(name, members, genlist=True): - return mcgen(''' - -void %(visitor)s(Visitor *m, %(name)s * obj, const char *name, Error **errp); -''', - visitor=type_visitor(name)) - def generate_command_decl(name, args, ret_type): arglist="" for argname, argtype, optional, structured in parse_args(args): @@ -76,19 +69,6 @@ def gen_marshal_output_call(name, ret_type): return "" return "qmp_marshal_output_%s(retval, ret, errp);" % c_fun(name) -def gen_visitor_output_containers_decl(ret_type): - ret = "" - push_indent() - if ret_type: - ret += mcgen(''' -QmpOutputVisitor *mo; -QapiDeallocVisitor *md; -Visitor *v; -''') - pop_indent() - - return ret - def gen_visitor_input_containers_decl(args): ret = "" diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 4a1652b56f..2c6e0dcd5c 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -6,8 +6,8 @@ # Authors: # Anthony Liguori <aliguori@us.ibm.com> # -# This work is licensed under the terms of the GNU GPLv2. -# See the COPYING.LIB file in the top-level directory. +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. from ordereddict import OrderedDict from qapi import * diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 65f1a54ee7..c6de9aeed4 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -7,8 +7,8 @@ # Anthony Liguori <aliguori@us.ibm.com> # Michael Roth <mdroth@linux.vnet.ibm.com> # -# This work is licensed under the terms of the GNU GPLv2. -# See the COPYING.LIB file in the top-level directory. +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. from ordereddict import OrderedDict from qapi import * @@ -47,9 +47,9 @@ static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error * if base: ret += mcgen(''' -visit_start_implicit_struct(m, obj ? (void**) &(*obj)->%(c_name)s : NULL, sizeof(%(type)s), &err); +visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(type)s), &err); if (!err) { - visit_type_%(type)s_fields(m, obj ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, &err); + visit_type_%(type)s_fields(m, &(*obj)->%(c_prefix)s%(c_name)s, &err); error_propagate(errp, err); err = NULL; visit_end_implicit_struct(m, &err); @@ -61,8 +61,8 @@ if (!err) { for argname, argentry, optional, structured in parse_args(members): if optional: ret += mcgen(''' -visit_start_optional(m, obj ? &(*obj)->%(c_prefix)shas_%(c_name)s : NULL, "%(name)s", &err); -if (obj && (*obj)->%(prefix)shas_%(c_name)s) { +visit_start_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err); +if ((*obj)->%(prefix)shas_%(c_name)s) { ''', c_prefix=c_var(field_prefix), prefix=field_prefix, c_name=c_var(argname), name=argname) @@ -72,7 +72,7 @@ if (obj && (*obj)->%(prefix)shas_%(c_name)s) { ret += generate_visit_struct_body(full_name, argname, argentry) else: ret += mcgen(''' -visit_type_%(type)s(m, obj ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, "%(name)s", &err); +visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err); ''', c_prefix=c_var(field_prefix), prefix=field_prefix, type=type_name(argentry), c_name=c_var(argname), @@ -121,7 +121,7 @@ visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); ret += mcgen(''' if (!err) { - if (!obj || *obj) { + if (*obj) { visit_type_%(name)s_fields(m, obj, &err); error_propagate(errp, err); err = NULL; @@ -273,7 +273,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** if (!error_is_set(errp)) { visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); if (!err) { - if (obj && *obj) { + if (*obj) { ''', name=name) @@ -494,10 +494,8 @@ fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL")) # have the functions defined, so we use -b option to provide control # over these cases if do_builtins: - fdef.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DEF")) for typename in builtin_types: fdef.write(generate_visit_list(typename, None)) - fdef.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DEF")) for expr in exprs: if expr.has_key('type'): diff --git a/scripts/qapi.py b/scripts/qapi.py index 9b3de4c7c3..f3c2a2037a 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -8,8 +8,8 @@ # Anthony Liguori <aliguori@us.ibm.com> # Markus Armbruster <armbru@redhat.com> # -# This work is licensed under the terms of the GNU GPLv2. -# See the COPYING.LIB file in the top-level directory. +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. from ordereddict import OrderedDict import sys diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index d374b357aa..e0e848bc30 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -112,13 +112,29 @@ class QMPShell(qmp.QEMUMonitorProtocol): value = json.loads(opt[1]) else: value = opt[1] - qmpcmd['arguments'][opt[0]] = value + optpath = opt[0].split('.') + parent = qmpcmd['arguments'] + curpath = [] + for p in optpath[:-1]: + curpath.append(p) + d = parent.get(p, {}) + if type(d) is not dict: + raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath)) + parent[p] = d + parent = d + if optpath[-1] in parent: + if type(parent[optpath[-1]]) is dict: + raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath)) + else: + raise QMPShellError('Cannot set "%s" multiple times' % opt[0]) + parent[optpath[-1]] = value return qmpcmd def _execute_cmd(self, cmdline): try: qmpcmd = self.__build_cmd(cmdline) - except: + except Exception, e: + print 'Error while parsing command line: %s' % e print 'command format: <command-name> ', print '[arg-name1=arg1] ... [arg-nameN=argN]' return True diff --git a/scripts/qmp/qmp.py b/scripts/qmp/qmp.py index 5c9717594f..20b6ec795e 100644 --- a/scripts/qmp/qmp.py +++ b/scripts/qmp/qmp.py @@ -171,7 +171,12 @@ class QEMUMonitorProtocol: pass self.__sock.setblocking(1) if not self.__events and wait: - self.__json_read(only_event=True) + ret = self.__json_read(only_event=True) + if ret == None: + # We are in blocking mode, if don't get anything, something + # went wrong + raise QMPConnectError("Error while reading from socket") + return self.__events def clear_events(self): diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 5d3f143075..0014acca7b 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -38,8 +38,10 @@ #ifdef TARGET_X86_64 #define ELF_MACHINE EM_X86_64 +#define ELF_MACHINE_UNAME "x86_64" #else #define ELF_MACHINE EM_386 +#define ELF_MACHINE_UNAME "i686" #endif #define CPUArchState struct CPUX86State diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index 96738384c7..effe84b102 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -28,6 +28,7 @@ #define TARGET_LONG_BITS 64 #define ELF_MACHINE EM_S390 +#define ELF_MACHINE_UNAME "S390X" #define CPUArchState struct CPUS390XState diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index fe5af756c5..471ba47dde 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -7,8 +7,12 @@ 'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } } # for testing nested structs +{ 'type': 'UserDefZero', + 'data': { 'integer': 'int' } } + { 'type': 'UserDefOne', - 'data': { 'integer': 'int', 'string': 'str', '*enum1': 'EnumOne' } } + 'base': 'UserDefZero', + 'data': { 'string': 'str', '*enum1': 'EnumOne' } } { 'type': 'UserDefTwo', 'data': { 'string': 'str', @@ -30,8 +34,20 @@ 'data': { 'integer': 'int' } } { 'union': 'UserDefUnion', + 'base': 'UserDefZero', 'data': { 'a' : 'UserDefA', 'b' : 'UserDefB' } } +{ 'union': 'UserDefFlatUnion', + 'base': 'UserDefOne', + 'discriminator': 'string', + 'data': { 'a' : 'UserDefA', 'b' : 'UserDefB' } } +# FIXME generated struct UserDefFlatUnion has members for direct base +# UserDefOne, but lacks members for indirect base UserDefZero + +{ 'union': 'UserDefAnonUnion', + 'discriminator': {}, + 'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } } + # for testing native lists { 'union': 'UserDefNativeListUnion', 'data': { 'integer': ['int'], @@ -50,7 +66,11 @@ # testing commands { 'command': 'user_def_cmd', 'data': {} } { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} } -{ 'command': 'user_def_cmd2', 'data': {'ud1a': 'UserDefOne', 'ud1b': 'UserDefOne'}, 'returns': 'UserDefTwo' } +{ 'command': 'user_def_cmd2', + 'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'}, + 'returns': 'UserDefTwo' } +{ 'command': 'user_def_cmd3', 'data': {'a': 'int', '*b': 'int' }, + 'returns': 'int' } # For testing integer range flattening in opts-visitor. The following schema # corresponds to the option format: diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 3851880de3..89b53d4d4d 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -1,19 +1,28 @@ [OrderedDict([('enum', 'EnumOne'), ('data', ['value1', 'value2', 'value3'])]), OrderedDict([('type', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]), - OrderedDict([('type', 'UserDefOne'), ('data', OrderedDict([('integer', 'int'), ('string', 'str'), ('*enum1', 'EnumOne')]))]), + OrderedDict([('type', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), + OrderedDict([('type', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]), OrderedDict([('type', 'UserDefTwo'), ('data', OrderedDict([('string', 'str'), ('dict', OrderedDict([('string', 'str'), ('dict', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')])), ('*dict2', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]))]))]), OrderedDict([('type', 'UserDefNested'), ('data', OrderedDict([('string0', 'str'), ('dict1', OrderedDict([('string1', 'str'), ('dict2', OrderedDict([('userdef1', 'UserDefOne'), ('string2', 'str')])), ('*dict3', OrderedDict([('userdef2', 'UserDefOne'), ('string3', 'str')]))]))]))]), OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]), OrderedDict([('type', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]), - OrderedDict([('union', 'UserDefUnion'), ('data', OrderedDict([('a', 'UserDefA'), ('b', 'UserDefB')]))]), + OrderedDict([('union', 'UserDefUnion'), ('base', 'UserDefZero'), ('data', OrderedDict([('a', 'UserDefA'), ('b', 'UserDefB')]))]), + OrderedDict([('union', 'UserDefFlatUnion'), ('base', 'UserDefOne'), ('discriminator', 'string'), ('data', OrderedDict([('a', 'UserDefA'), ('b', 'UserDefB')]))]), + OrderedDict([('union', 'UserDefAnonUnion'), ('discriminator', OrderedDict()), ('data', OrderedDict([('uda', 'UserDefA'), ('s', 'str'), ('i', 'int')]))]), OrderedDict([('union', 'UserDefNativeListUnion'), ('data', OrderedDict([('integer', ['int']), ('s8', ['int8']), ('s16', ['int16']), ('s32', ['int32']), ('s64', ['int64']), ('u8', ['uint8']), ('u16', ['uint16']), ('u32', ['uint32']), ('u64', ['uint64']), ('number', ['number']), ('boolean', ['bool']), ('string', ['str'])]))]), OrderedDict([('command', 'user_def_cmd'), ('data', OrderedDict())]), OrderedDict([('command', 'user_def_cmd1'), ('data', OrderedDict([('ud1a', 'UserDefOne')]))]), - OrderedDict([('command', 'user_def_cmd2'), ('data', OrderedDict([('ud1a', 'UserDefOne'), ('ud1b', 'UserDefOne')])), ('returns', 'UserDefTwo')]), + OrderedDict([('command', 'user_def_cmd2'), ('data', OrderedDict([('ud1a', 'UserDefOne'), ('*ud1b', 'UserDefOne')])), ('returns', 'UserDefTwo')]), + OrderedDict([('command', 'user_def_cmd3'), ('data', OrderedDict([('a', 'int'), ('*b', 'int')])), ('returns', 'int')]), OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))])] -['EnumOne', 'UserDefUnionKind', 'UserDefNativeListUnionKind'] +['EnumOne', + 'UserDefUnionKind', + 'UserDefFlatUnionKind', + 'UserDefAnonUnionKind', + 'UserDefNativeListUnionKind'] [OrderedDict([('type', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]), - OrderedDict([('type', 'UserDefOne'), ('data', OrderedDict([('integer', 'int'), ('string', 'str'), ('*enum1', 'EnumOne')]))]), + OrderedDict([('type', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), + OrderedDict([('type', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]), OrderedDict([('type', 'UserDefTwo'), ('data', OrderedDict([('string', 'str'), ('dict', OrderedDict([('string', 'str'), ('dict', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')])), ('*dict2', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]))]))]), OrderedDict([('type', 'UserDefNested'), ('data', OrderedDict([('string0', 'str'), ('dict1', OrderedDict([('string1', 'str'), ('dict2', OrderedDict([('userdef1', 'UserDefOne'), ('string2', 'str')])), ('*dict3', OrderedDict([('userdef2', 'UserDefOne'), ('string3', 'str')]))]))]))]), OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]), diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c index 5a3e82a854..8e62c2d8ad 100644 --- a/tests/test-qmp-commands.c +++ b/tests/test-qmp-commands.c @@ -16,16 +16,20 @@ void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) { } -UserDefTwo * qmp_user_def_cmd2(UserDefOne * ud1a, UserDefOne * ud1b, Error **errp) +UserDefTwo *qmp_user_def_cmd2(UserDefOne *ud1a, + bool has_udb1, UserDefOne *ud1b, + Error **errp) { UserDefTwo *ret; UserDefOne *ud1c = g_malloc0(sizeof(UserDefOne)); UserDefOne *ud1d = g_malloc0(sizeof(UserDefOne)); ud1c->string = strdup(ud1a->string); - ud1c->integer = ud1a->integer; - ud1d->string = strdup(ud1b->string); - ud1d->integer = ud1b->integer; + ud1c->base = g_new0(UserDefZero, 1); + ud1c->base->integer = ud1a->base->integer; + ud1d->string = strdup(has_udb1 ? ud1b->string : "blah0"); + ud1d->base = g_new0(UserDefZero, 1); + ud1d->base->integer = has_udb1 ? ud1b->base->integer : 0; ret = g_malloc0(sizeof(UserDefTwo)); ret->string = strdup("blah1"); @@ -39,6 +43,11 @@ UserDefTwo * qmp_user_def_cmd2(UserDefOne * ud1a, UserDefOne * ud1b, Error **err return ret; } +int64_t qmp_user_def_cmd3(int64_t a, bool has_b, int64_t b, Error **errp) +{ + return a + (has_b ? b : 0); +} + /* test commands with no input and no return value */ static void test_dispatch_cmd(void) { @@ -71,14 +80,34 @@ static void test_dispatch_cmd_error(void) QDECREF(req); } +static QObject *test_qmp_dispatch(QDict *req) +{ + QObject *resp_obj; + QDict *resp; + QObject *ret; + + resp_obj = qmp_dispatch(QOBJECT(req)); + assert(resp_obj); + resp = qobject_to_qdict(resp_obj); + assert(resp && !qdict_haskey(resp, "error")); + ret = qdict_get(resp, "return"); + assert(ret); + qobject_incref(ret); + qobject_decref(resp_obj); + return ret; +} + /* test commands that involve both input parameters and return values */ static void test_dispatch_cmd_io(void) { QDict *req = qdict_new(); QDict *args = qdict_new(); + QDict *args3 = qdict_new(); QDict *ud1a = qdict_new(); QDict *ud1b = qdict_new(); - QObject *resp; + QDict *ret, *ret_dict, *ret_dict_dict, *ret_dict_dict_userdef; + QDict *ret_dict_dict2, *ret_dict_dict2_userdef; + QInt *ret3; qdict_put_obj(ud1a, "integer", QOBJECT(qint_from_int(42))); qdict_put_obj(ud1a, "string", QOBJECT(qstring_from_str("hello"))); @@ -87,15 +116,33 @@ static void test_dispatch_cmd_io(void) qdict_put_obj(args, "ud1a", QOBJECT(ud1a)); qdict_put_obj(args, "ud1b", QOBJECT(ud1b)); qdict_put_obj(req, "arguments", QOBJECT(args)); - qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2"))); - /* TODO: put in full payload and check for errors */ - resp = qmp_dispatch(QOBJECT(req)); - assert(resp != NULL); - assert(!qdict_haskey(qobject_to_qdict(resp), "error")); + ret = qobject_to_qdict(test_qmp_dispatch(req)); + + assert(!strcmp(qdict_get_str(ret, "string"), "blah1")); + ret_dict = qdict_get_qdict(ret, "dict"); + assert(!strcmp(qdict_get_str(ret_dict, "string"), "blah2")); + ret_dict_dict = qdict_get_qdict(ret_dict, "dict"); + ret_dict_dict_userdef = qdict_get_qdict(ret_dict_dict, "userdef"); + assert(qdict_get_int(ret_dict_dict_userdef, "integer") == 42); + assert(!strcmp(qdict_get_str(ret_dict_dict_userdef, "string"), "hello")); + assert(!strcmp(qdict_get_str(ret_dict_dict, "string"), "blah3")); + ret_dict_dict2 = qdict_get_qdict(ret_dict, "dict2"); + ret_dict_dict2_userdef = qdict_get_qdict(ret_dict_dict2, "userdef"); + assert(qdict_get_int(ret_dict_dict2_userdef, "integer") == 422); + assert(!strcmp(qdict_get_str(ret_dict_dict2_userdef, "string"), "hello2")); + assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); + QDECREF(ret); + + qdict_put(args3, "a", qint_from_int(66)); + qdict_put(req, "arguments", args3); + qdict_put(req, "execute", qstring_from_str("user_def_cmd3")); + + ret3 = qobject_to_qint(test_qmp_dispatch(req)); + assert(qint_get_int(ret3) == 66); + QDECREF(ret); - qobject_decref(resp); QDECREF(req); } @@ -106,17 +153,20 @@ static void test_dealloc_types(void) UserDefOneList *ud1list; ud1test = g_malloc0(sizeof(UserDefOne)); - ud1test->integer = 42; + ud1test->base = g_new0(UserDefZero, 1); + ud1test->base->integer = 42; ud1test->string = g_strdup("hi there 42"); qapi_free_UserDefOne(ud1test); ud1a = g_malloc0(sizeof(UserDefOne)); - ud1a->integer = 43; + ud1a->base = g_new0(UserDefZero, 1); + ud1a->base->integer = 43; ud1a->string = g_strdup("hi there 43"); ud1b = g_malloc0(sizeof(UserDefOne)); - ud1b->integer = 44; + ud1b->base = g_new0(UserDefZero, 1); + ud1b->base->integer = 44; ud1b->string = g_strdup("hi there 44"); ud1list = g_malloc0(sizeof(UserDefOneList)); diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c index 38bdf5ec7c..64d72f6f05 100644 --- a/tests/test-qmp-input-strict.c +++ b/tests/test-qmp-input-strict.c @@ -132,13 +132,42 @@ static void test_validate_union(TestInputVisitorData *data, Visitor *v; Error *errp = NULL; - v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }"); + v = validate_test_init(data, "{ 'type': 'b', 'integer': 41, 'data' : { 'integer': 42 } }"); visit_type_UserDefUnion(v, &tmp, NULL, &errp); g_assert(!errp); qapi_free_UserDefUnion(tmp); } +static void test_validate_union_flat(TestInputVisitorData *data, + const void *unused) +{ + UserDefFlatUnion *tmp = NULL; + Visitor *v; + Error *errp = NULL; + + v = validate_test_init(data, "{ 'string': 'a', 'boolean': true }"); + /* TODO when generator bug is fixed, add 'integer': 41 */ + + visit_type_UserDefFlatUnion(v, &tmp, NULL, &errp); + g_assert(!error_is_set(&errp)); + qapi_free_UserDefFlatUnion(tmp); +} + +static void test_validate_union_anon(TestInputVisitorData *data, + const void *unused) +{ + UserDefAnonUnion *tmp = NULL; + Visitor *v; + Error *errp = NULL; + + v = validate_test_init(data, "42"); + + visit_type_UserDefAnonUnion(v, &tmp, NULL, &errp); + g_assert(!error_is_set(&errp)); + qapi_free_UserDefAnonUnion(tmp); +} + static void test_validate_fail_struct(TestInputVisitorData *data, const void *unused) { @@ -191,13 +220,41 @@ static void test_validate_fail_union(TestInputVisitorData *data, Error *errp = NULL; Visitor *v; - v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 }, 'extra': 'yyy' }"); + v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }"); visit_type_UserDefUnion(v, &tmp, NULL, &errp); g_assert(errp); qapi_free_UserDefUnion(tmp); } +static void test_validate_fail_union_flat(TestInputVisitorData *data, + const void *unused) +{ + UserDefFlatUnion *tmp = NULL; + Error *errp = NULL; + Visitor *v; + + v = validate_test_init(data, "{ 'string': 'c', 'integer': 41, 'boolean': true }"); + + visit_type_UserDefFlatUnion(v, &tmp, NULL, &errp); + g_assert(error_is_set(&errp)); + qapi_free_UserDefFlatUnion(tmp); +} + +static void test_validate_fail_union_anon(TestInputVisitorData *data, + const void *unused) +{ + UserDefAnonUnion *tmp = NULL; + Visitor *v; + Error *errp = NULL; + + v = validate_test_init(data, "3.14"); + + visit_type_UserDefAnonUnion(v, &tmp, NULL, &errp); + g_assert(error_is_set(&errp)); + qapi_free_UserDefAnonUnion(tmp); +} + static void validate_test_add(const char *testpath, TestInputVisitorData *data, void (*test_func)(TestInputVisitorData *data, const void *user_data)) @@ -220,6 +277,10 @@ int main(int argc, char **argv) &testdata, test_validate_list); validate_test_add("/visitor/input-strict/pass/union", &testdata, test_validate_union); + validate_test_add("/visitor/input-strict/pass/union-flat", + &testdata, test_validate_union_flat); + validate_test_add("/visitor/input-strict/pass/union-anon", + &testdata, test_validate_union_anon); validate_test_add("/visitor/input-strict/fail/struct", &testdata, test_validate_fail_struct); validate_test_add("/visitor/input-strict/fail/struct-nested", @@ -228,6 +289,10 @@ int main(int argc, char **argv) &testdata, test_validate_fail_list); validate_test_add("/visitor/input-strict/fail/union", &testdata, test_validate_fail_union); + validate_test_add("/visitor/input-strict/fail/union-flat", + &testdata, test_validate_fail_union_flat); + validate_test_add("/visitor/input-strict/fail/union-anon", + &testdata, test_validate_fail_union_anon); g_test_run(); diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index 6eb7dc5bcf..2dffafc1f4 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -252,7 +252,7 @@ static void test_visitor_in_struct_nested(TestInputVisitorData *data, check_and_free_str(udp->string0, "string0"); check_and_free_str(udp->dict1.string1, "string1"); - g_assert_cmpint(udp->dict1.dict2.userdef1->integer, ==, 42); + g_assert_cmpint(udp->dict1.dict2.userdef1->base->integer, ==, 42); check_and_free_str(udp->dict1.dict2.userdef1->string, "string"); check_and_free_str(udp->dict1.dict2.string2, "string2"); g_assert(udp->dict1.has_dict3 == false); @@ -280,7 +280,7 @@ static void test_visitor_in_list(TestInputVisitorData *data, snprintf(string, sizeof(string), "string%d", i); g_assert_cmpstr(item->value->string, ==, string); - g_assert_cmpint(item->value->integer, ==, 42 + i); + g_assert_cmpint(item->value->base->integer, ==, 42 + i); } qapi_free_UserDefOneList(head); @@ -293,15 +293,50 @@ static void test_visitor_in_union(TestInputVisitorData *data, Error *err = NULL; UserDefUnion *tmp; - v = visitor_input_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }"); + v = visitor_input_test_init(data, "{ 'type': 'b', 'integer': 41, 'data' : { 'integer': 42 } }"); visit_type_UserDefUnion(v, &tmp, NULL, &err); g_assert(err == NULL); g_assert_cmpint(tmp->kind, ==, USER_DEF_UNION_KIND_B); + g_assert_cmpint(tmp->integer, ==, 41); g_assert_cmpint(tmp->b->integer, ==, 42); qapi_free_UserDefUnion(tmp); } +static void test_visitor_in_union_flat(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + Error *err = NULL; + UserDefFlatUnion *tmp; + + v = visitor_input_test_init(data, "{ 'string': 'a', 'boolean': true }"); + /* TODO when generator bug is fixed, add 'integer': 41 */ + + visit_type_UserDefFlatUnion(v, &tmp, NULL, &err); + g_assert(err == NULL); + g_assert_cmpint(tmp->kind, ==, USER_DEF_UNION_KIND_A); + /* TODO g_assert_cmpint(tmp->integer, ==, 41); */ + g_assert_cmpint(tmp->a->boolean, ==, true); + qapi_free_UserDefFlatUnion(tmp); +} + +static void test_visitor_in_union_anon(TestInputVisitorData *data, + const void *unused) +{ + Visitor *v; + Error *err = NULL; + UserDefAnonUnion *tmp; + + v = visitor_input_test_init(data, "42"); + + visit_type_UserDefAnonUnion(v, &tmp, NULL, &err); + g_assert(err == NULL); + g_assert_cmpint(tmp->kind, ==, USER_DEF_ANON_UNION_KIND_I); + g_assert_cmpint(tmp->i, ==, 42); + qapi_free_UserDefAnonUnion(tmp); +} + static void test_native_list_integer_helper(TestInputVisitorData *data, const void *unused, UserDefNativeListUnionKind kind) @@ -635,6 +670,10 @@ int main(int argc, char **argv) &in_visitor_data, test_visitor_in_list); input_visitor_test_add("/visitor/input/union", &in_visitor_data, test_visitor_in_union); + input_visitor_test_add("/visitor/input/union-flat", + &in_visitor_data, test_visitor_in_union_flat); + input_visitor_test_add("/visitor/input/union-anon", + &in_visitor_data, test_visitor_in_union_anon); input_visitor_test_add("/visitor/input/errors", &in_visitor_data, test_visitor_in_errors); input_visitor_test_add("/visitor/input/native_list/int", diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c index f31d168d63..105f4cf94b 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -231,13 +231,15 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data, ud2->dict1.string1 = g_strdup(strings[1]); ud2->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne)); ud2->dict1.dict2.userdef1->string = g_strdup(string); - ud2->dict1.dict2.userdef1->integer = value; + ud2->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1); + ud2->dict1.dict2.userdef1->base->integer = value; ud2->dict1.dict2.string2 = g_strdup(strings[2]); ud2->dict1.has_dict3 = true; ud2->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne)); ud2->dict1.dict3.userdef2->string = g_strdup(string); - ud2->dict1.dict3.userdef2->integer = value; + ud2->dict1.dict3.userdef2->base = g_new0(UserDefZero, 1); + ud2->dict1.dict3.userdef2->base->integer = value; ud2->dict1.dict3.string3 = g_strdup(strings[3]); visit_type_UserDefNested(data->ov, &ud2, "unused", &errp); @@ -279,7 +281,8 @@ static void test_visitor_out_struct_errors(TestOutputVisitorData *data, const void *unused) { EnumOne bad_values[] = { ENUM_ONE_MAX, -1 }; - UserDefOne u = { 0 }, *pu = &u; + UserDefZero b; + UserDefOne u = { .base = &b }, *pu = &u; Error *errp; int i; @@ -391,7 +394,8 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data, p->value->dict1.string1 = g_strdup(string); p->value->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne)); p->value->dict1.dict2.userdef1->string = g_strdup(string); - p->value->dict1.dict2.userdef1->integer = 42; + p->value->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1); + p->value->dict1.dict2.userdef1->base->integer = 42; p->value->dict1.dict2.string2 = g_strdup(string); p->value->dict1.has_dict3 = false; @@ -412,6 +416,7 @@ static void test_visitor_out_union(TestOutputVisitorData *data, UserDefUnion *tmp = g_malloc0(sizeof(UserDefUnion)); tmp->kind = USER_DEF_UNION_KIND_A; + tmp->integer = 41; tmp->a = g_malloc0(sizeof(UserDefA)); tmp->a->boolean = true; @@ -423,6 +428,7 @@ static void test_visitor_out_union(TestOutputVisitorData *data, qdict = qobject_to_qdict(arg); g_assert_cmpstr(qdict_get_str(qdict, "type"), ==, "a"); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41); qvalue = qdict_get(qdict, "data"); g_assert(data != NULL); @@ -434,6 +440,55 @@ static void test_visitor_out_union(TestOutputVisitorData *data, QDECREF(qdict); } +static void test_visitor_out_union_flat(TestOutputVisitorData *data, + const void *unused) +{ + QObject *arg; + QDict *qdict; + + Error *err = NULL; + + UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion)); + tmp->kind = USER_DEF_UNION_KIND_A; + tmp->a = g_malloc0(sizeof(UserDefA)); + /* TODO when generator bug is fixed: tmp->integer = 41; */ + tmp->a->boolean = true; + + visit_type_UserDefFlatUnion(data->ov, &tmp, NULL, &err); + g_assert(err == NULL); + arg = qmp_output_get_qobject(data->qov); + + g_assert(qobject_type(arg) == QTYPE_QDICT); + qdict = qobject_to_qdict(arg); + + g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "a"); + /* TODO g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41); */ + g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true); + + qapi_free_UserDefFlatUnion(tmp); + QDECREF(qdict); +} + +static void test_visitor_out_union_anon(TestOutputVisitorData *data, + const void *unused) +{ + QObject *arg; + Error *err = NULL; + + UserDefAnonUnion *tmp = g_malloc0(sizeof(UserDefAnonUnion)); + tmp->kind = USER_DEF_ANON_UNION_KIND_I; + tmp->i = 42; + + visit_type_UserDefAnonUnion(data->ov, &tmp, NULL, &err); + g_assert(err == NULL); + arg = qmp_output_get_qobject(data->qov); + + g_assert(qobject_type(arg) == QTYPE_QINT); + g_assert_cmpint(qint_get_int(qobject_to_qint(arg)), ==, 42); + + qapi_free_UserDefAnonUnion(tmp); +} + static void init_native_list(UserDefNativeListUnion *cvalue) { int i; @@ -782,6 +837,10 @@ int main(int argc, char **argv) &out_visitor_data, test_visitor_out_list_qapi_free); output_visitor_test_add("/visitor/output/union", &out_visitor_data, test_visitor_out_union); + output_visitor_test_add("/visitor/output/union-flat", + &out_visitor_data, test_visitor_out_union_flat); + output_visitor_test_add("/visitor/output/union-anon", + &out_visitor_data, test_visitor_out_union_anon); output_visitor_test_add("/visitor/output/native_list/int", &out_visitor_data, test_visitor_out_native_list_int); output_visitor_test_add("/visitor/output/native_list/int8", diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index 6bff950eb6..8166cf1b05 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -239,12 +239,14 @@ static UserDefNested *nested_struct_create(void) udnp->string0 = strdup("test_string0"); udnp->dict1.string1 = strdup("test_string1"); udnp->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne)); - udnp->dict1.dict2.userdef1->integer = 42; + udnp->dict1.dict2.userdef1->base = g_new0(UserDefZero, 1); + udnp->dict1.dict2.userdef1->base->integer = 42; udnp->dict1.dict2.userdef1->string = strdup("test_string"); udnp->dict1.dict2.string2 = strdup("test_string2"); udnp->dict1.has_dict3 = true; udnp->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne)); - udnp->dict1.dict3.userdef2->integer = 43; + udnp->dict1.dict3.userdef2->base = g_new0(UserDefZero, 1); + udnp->dict1.dict3.userdef2->base->integer = 43; udnp->dict1.dict3.userdef2->string = strdup("test_string"); udnp->dict1.dict3.string3 = strdup("test_string3"); return udnp; @@ -256,14 +258,14 @@ static void nested_struct_compare(UserDefNested *udnp1, UserDefNested *udnp2) g_assert(udnp2); g_assert_cmpstr(udnp1->string0, ==, udnp2->string0); g_assert_cmpstr(udnp1->dict1.string1, ==, udnp2->dict1.string1); - g_assert_cmpint(udnp1->dict1.dict2.userdef1->integer, ==, - udnp2->dict1.dict2.userdef1->integer); + g_assert_cmpint(udnp1->dict1.dict2.userdef1->base->integer, ==, + udnp2->dict1.dict2.userdef1->base->integer); g_assert_cmpstr(udnp1->dict1.dict2.userdef1->string, ==, udnp2->dict1.dict2.userdef1->string); g_assert_cmpstr(udnp1->dict1.dict2.string2, ==, udnp2->dict1.dict2.string2); g_assert(udnp1->dict1.has_dict3 == udnp2->dict1.has_dict3); - g_assert_cmpint(udnp1->dict1.dict3.userdef2->integer, ==, - udnp2->dict1.dict3.userdef2->integer); + g_assert_cmpint(udnp1->dict1.dict3.userdef2->base->integer, ==, + udnp2->dict1.dict3.userdef2->base->integer); g_assert_cmpstr(udnp1->dict1.dict3.userdef2->string, ==, udnp2->dict1.dict3.userdef2->string); g_assert_cmpstr(udnp1->dict1.dict3.string3, ==, udnp2->dict1.dict3.string3); |