Make monitor command 'dump-guest-memory' dump in kdump-compressed format. The command's usage: dump [-p] protocol [flags] [begin] [length] With 'flags' set, the core file will be in kdump-compress format, and without it, the format is ELF. 'flags' can be: 1. FLAG_DUMP_COMPRESS_ZLIB(0x1), compress vmcore with zlib 2. FLAG_DUMP_COMPRESS_LZO(0x2), compress vmcore with lzo 3. FLAG_DUMP_COMPRESS_SNAPPY(0x4), compress vmcore with snappy
Note: 1. The kdump-compressed format is readable only with the crash utility, and it can be smaller than the ELF format because of the compression support. 2. The kdump-compressed format is the 5th edition. Signed-off-by: Qiao Nuohan <qiaonuo...@cn.fujitsu.com> Signed-off-by: Zhang Xiaohe <zhan...@cn.fujitsu.com> --- configure | 50 +++++++++++++++++++++++++ dump.c | 96 ++++++++++++++++++++++++++++++++++++++++++++----- hmp-commands.hx | 8 +++-- hmp.c | 9 ++++- include/sysemu/dump.h | 8 ++++ qapi-schema.json | 6 ++- qmp-commands.hx | 5 ++- 7 files changed, 164 insertions(+), 18 deletions(-) diff --git a/configure b/configure index e818e8b..f4415c9 100755 --- a/configure +++ b/configure @@ -229,6 +229,8 @@ libusb="" usb_redir="" glx="" zlib="yes" +lzo="no" +snappy="no" guest_agent="yes" want_tools="yes" libiscsi="" @@ -898,6 +900,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" @@ -1494,6 +1500,42 @@ fi libs_softmmu="$libs_softmmu -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 @@ -3883,6 +3925,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=y" >> $config_host_mak fi diff --git a/dump.c b/dump.c index aa83744..fd81c07 100644 --- a/dump.c +++ b/dump.c @@ -1014,6 +1014,16 @@ static int create_header64(DumpState *s) return 0; } +static void get_max_mapnr(DumpState *s) +{ + MemoryMapping *memory_mapping; + + QTAILQ_FOREACH(memory_mapping, &s->list.head, next) { + s->max_mapnr = paddr_to_pfn(memory_mapping->phys_addr + + memory_mapping->length, s->page_shift); + } +} + /* * gather data of header and sub header */ @@ -1377,12 +1387,14 @@ out: return ret; } -static int dump_init(DumpState *s, int fd, bool paging, bool has_filter, - int64_t begin, int64_t length, Error **errp) +static int dump_init(DumpState *s, int fd, bool has_flags, int64_t flags, + bool paging, bool has_filter, int64_t begin, int64_t length, + Error **errp) { CPUArchState *env; int nr_cpus; int ret; + unsigned long tmp; if (runstate_is_running()) { vm_stop(RUN_STATE_SAVE_VM); @@ -1437,6 +1449,56 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter, qemu_get_guest_simple_memory_mapping(&s->list); } + /* init for kdump-compressed format */ + if (has_flags) { + switch (flags) { + case FLAG_DUMP_COMPRESS_ZLIB: + s->flag_compress = DUMP_DH_COMPRESSED_ZLIB; + break; + case FLAG_DUMP_COMPRESS_LZO: + s->flag_compress = DUMP_DH_COMPRESSED_LZO; + break; + case FLAG_DUMP_COMPRESS_SNAPPY: + s->flag_compress = DUMP_DH_COMPRESSED_SNAPPY; + break; + default: + s->flag_compress = 0; + } + + s->nr_cpus = nr_cpus; + s->page_size = PAGE_SIZE; + s->page_shift = ffs(s->page_size) - 1; + + get_max_mapnr(s); + + tmp = divideup(divideup(s->max_mapnr, BITPERBYTE), s->page_size); + s->len_dump_bitmap = tmp * s->page_size * 2; + + /* + * gather data of header, dump_bitmap, page_desc and page_data, then + * cache them in tmp files + */ + ret = create_header(s); + if (ret < 0) { + error_set(errp, QERR_UNSUPPORTED); + goto cleanup; + } + + ret = create_dump_bitmap(s); + if (ret < 0) { + error_set(errp, QERR_UNSUPPORTED); + goto cleanup; + } + + ret = create_pages(s); + if (ret < 0) { + error_set(errp, QERR_UNSUPPORTED); + goto cleanup; + } + + return 0; + } + if (s->has_filter) { memory_mapping_filter(&s->list, s->begin, s->length); } @@ -1510,15 +1572,22 @@ static void clean_state(DumpState *s) free_cache_data(s->page_data); } -void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, - int64_t begin, bool has_length, int64_t length, - Error **errp) +void qmp_dump_guest_memory(bool paging, const char *file, + bool has_flags, int64_t flags, + bool has_begin, int64_t begin, + bool has_length, int64_t length, Error **errp) { const char *p; int fd = -1; DumpState *s; int ret; + /* kdump-compressed format is invalid with paging or filter */ + if (has_flags && (paging || has_begin || has_length)) { + error_set(errp, QERR_INVALID_PARAMETER_COMBINATION); + return; + } + if (has_begin && !has_length) { error_set(errp, QERR_MISSING_PARAMETER, "length"); return; @@ -1550,17 +1619,26 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, return; } - s = g_malloc(sizeof(DumpState)); + /* initialize DumpState to zero */ + s = g_malloc0(sizeof(DumpState)); - ret = dump_init(s, fd, paging, has_begin, begin, length, errp); + ret = dump_init(s, fd, has_flags, flags, 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_flags) { + 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); + } } + clean_state(s); + g_free(s); } diff --git a/hmp-commands.hx b/hmp-commands.hx index 9cea415..c278a50 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -992,9 +992,10 @@ ETEXI #if defined(CONFIG_HAVE_CORE_DUMP) { .name = "dump-guest-memory", - .args_type = "paging:-p,filename:F,begin:i?,length:i?", - .params = "[-p] filename [begin] [length]", + .args_type = "paging:-p,filename:F,flags:i?,begin:i?,length:i?", + .params = "[-p] filename [flags] [begin] [length]", .help = "dump guest memory to file" + "\n\t\t\t flags: the type of compression" "\n\t\t\t begin(optional): the starting physical address" "\n\t\t\t length(optional): the memory size, in bytes", .mhandler.cmd = hmp_dump_guest_memory, @@ -1002,12 +1003,13 @@ ETEXI STEXI -@item dump-guest-memory [-p] @var{protocol} @var{begin} @var{length} +@item dump-guest-memory [-p] @var{protocol} @var{flags} @var{begin} @var{length} @findex dump-guest-memory Dump guest memory to @var{protocol}. The file can be processed with crash or gdb. filename: dump file name paging: do paging to get guest's memory mapping + flags: an optional parameter which describes the format of dump. begin: the starting physical address. It's optional, and should be specified with length together. length: the memory size, in bytes. It's optional, and should be specified diff --git a/hmp.c b/hmp.c index 4fb76ec..c7a7c2f 100644 --- a/hmp.c +++ b/hmp.c @@ -1183,12 +1183,17 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) Error *errp = NULL; int paging = qdict_get_try_bool(qdict, "paging", 0); const char *file = qdict_get_str(qdict, "filename"); + bool has_flags = qdict_haskey(qdict, "flags"); bool has_begin = qdict_haskey(qdict, "begin"); bool has_length = qdict_haskey(qdict, "length"); + int64_t flags = 0; int64_t begin = 0; int64_t length = 0; char *prot; + if (has_flags) { + flags = qdict_get_int(qdict, "flags"); + } if (has_begin) { begin = qdict_get_int(qdict, "begin"); } @@ -1198,8 +1203,8 @@ 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); + qmp_dump_guest_memory(paging, prot, has_flags, flags, has_begin, begin, + has_length, length, &errp); hmp_handle_error(mon, &errp); g_free(prot); } diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h index 4913468..b860ff0 100644 --- a/include/sysemu/dump.h +++ b/include/sysemu/dump.h @@ -38,6 +38,13 @@ #endif /* + * dump format + */ +#define FLAG_DUMP_COMPRESS_ZLIB (0x1) /* compressed with zlib */ +#define FLAG_DUMP_COMPRESS_LZO (0x2) /* compressed with lzo */ +#define FLAG_DUMP_COMPRESS_SNAPPY (0x4) /* compressed with snappy */ + +/* * flag used in page desc of kdump-compressed format */ #define DUMP_DH_COMPRESSED_ZLIB (0x1) @@ -47,6 +54,7 @@ #define KDUMP_SIGNATURE "KDUMP " #define SIG_LEN (sizeof(KDUMP_SIGNATURE) - 1) #define DISKDUMP_HEADER_BLOCKS (1) +#define PAGE_SIZE (4096) #define PHYS_BASE (0) #define DUMP_LEVEL (1) #define TMP_BUF_SIZE (1024) diff --git a/qapi-schema.json b/qapi-schema.json index 7797400..b1d0c5d 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2410,6 +2410,8 @@ # 2. fd: the protocol starts with "fd:", and the following string # is the fd's name. # +# @flags: #optional if specified, the format of dump file. +# # @begin: #optional if specified, the starting physical address. # # @length: #optional if specified, the memory size, in bytes. If you don't @@ -2421,8 +2423,8 @@ # Since: 1.2 ## { 'command': 'dump-guest-memory', - 'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int', - '*length': 'int' } } + 'data': { 'paging': 'bool', 'protocol': 'str', '*flags': 'int', + '*begin': 'int', '*length': 'int' } } ## # @netdev_add: diff --git a/qmp-commands.hx b/qmp-commands.hx index ffd130e..93dd61c 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -788,8 +788,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,flags:i?,begin:i?,length:i?", + .params = "-p protocol [flags] [begin] [length]", .help = "dump guest memory to file", .user_print = monitor_user_noop, .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory, @@ -806,6 +806,7 @@ Arguments: - "paging": do paging to get guest's memory mapping (json-bool) - "protocol": destination file(started with "file:") or destination file descriptor (started with "fd:") (json-string) +- "flags": dump flag. It's optional, and describes the format of dump (json-int) - "begin": the starting physical address. It's optional, and should be specified with length together (json-int) - "length": the memory size, in bytes. It's optional, and should be specified -- 1.7.1