提交 e00ef747 编写于 作者: P Peter Maydell

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: NPeter Maydell <peter.maydell@linaro.org>
......@@ -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
......@@ -1659,6 +1667,42 @@ EOF
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
......@@ -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);
}
......
......@@ -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"
......
......@@ -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:
......
......@@ -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",
......
......@@ -2794,6 +2794,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
#
......@@ -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:
......
......@@ -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;
}
......
......@@ -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,
......
......@@ -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:
......@@ -823,6 +826,26 @@ Notes:
(1) All boolean arguments default to false
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
{
......
......@@ -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 = ""
......
......@@ -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 *
......
......@@ -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'):
......
......@@ -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
......
......@@ -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
......
......@@ -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):
......
......@@ -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
......
......@@ -28,6 +28,7 @@
#define TARGET_LONG_BITS 64
#define ELF_MACHINE EM_S390
#define ELF_MACHINE_UNAME "S390X"
#define CPUArchState struct CPUS390XState
......
......@@ -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:
......
[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')]))]),
......
......@@ -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));
......
......@@ -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();
......
......@@ -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",
......
......@@ -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",
......
......@@ -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);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册