Make monitor command 'dump-guest-memory' be able to dump in kdump-compressed format. The command's usage:
dump [-p] protocol [begin] [length] [format] 'format' is used to specified the format of vmcore and can be: 1. 'elf': ELF format, without compression 2. 'kdump-zlib': kdump-compressed format, with zlib-compressed 3. 'kdump-lzo': kdump-compressed format, with lzo-compressed 4. 'kdump-snappy': kdump-compressed format, with snappy-compressed And without 'format' being set, it is same as 'elf'. 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> --- dump.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++--- hmp-commands.hx | 12 +++- hmp.c | 23 +++++++- include/sysemu/dump.h | 1 + qapi-schema.json | 22 +++++++- qmp-commands.hx | 6 +- 6 files changed, 201 insertions(+), 17 deletions(-) diff --git a/dump.c b/dump.c index a28e162..a78ac8d 100644 --- a/dump.c +++ b/dump.c @@ -1377,6 +1377,70 @@ 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) | + * | : | + * | page desc for pfn Z (struct page_desc) | + * |------------------------------------------| (not aligned by block) + * | page data (pfn 0) | + * | page data (pfn 1) | + * | : | + * | page data (pfn Z) | + * +------------------------------------------+ offset_eraseinfo + * | : | + * +------------------------------------------+ + */ + + if (s->flag_flatten) { + ret = write_start_flat_header(s->fd); + if (ret < 0) { + 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; + } + + if (s->flag_flatten) { + ret = write_end_flat_header(s->fd); + if (ret < 0) { + return -1; + } + } + + dump_completed(s); + + return 0; +} + static ram_addr_t get_start_block(DumpState *s) { RAMBlock *block; @@ -1405,7 +1469,27 @@ 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 bool use_flatten_format(int fd) +{ + if (lseek(fd, 0, SEEK_SET) < 0) { + return true; + } + + return false; +} + +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); + } +} + +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) { CPUArchState *env; @@ -1470,6 +1554,44 @@ 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_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; + } + + /* + * check to see if fd is available to seek. + * if not, flatten format is used to avoid seek + */ + s->flag_flatten = use_flatten_format(fd); + + s->nr_cpus = nr_cpus; + s->page_size = PAGE_SIZE; + s->page_shift = ffs(s->page_size) - 1; + + get_max_mapnr(s); + + size_t tmp; + tmp = divideup(divideup(s->max_mapnr, CHAR_BIT), s->page_size); + s->len_dump_bitmap = tmp * s->page_size * 2; + + return 0; + } + if (s->has_filter) { memory_mapping_filter(&s->list, s->begin, s->length); } @@ -1527,8 +1649,9 @@ 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; @@ -1543,6 +1666,12 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, error_set(errp, QERR_MISSING_PARAMETER, "begin"); return; } + /* kdump-compressed format doesn't support paging or filter */ + if ((has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) && + (paging || has_begin || has_length)) { + error_set(errp, QERR_INVALID_PARAMETER_COMBINATION); + return; + } #if !defined(WIN32) if (strstart(file, "fd:", &p)) { @@ -1566,17 +1695,24 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, return; } - s = g_malloc(sizeof(DumpState)); + 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; + goto out; } - 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); + } } +out: g_free(s); } diff --git a/hmp-commands.hx b/hmp-commands.hx index d1cdcfb..7b99912 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -993,17 +993,19 @@ ETEXI { .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,begin:i?,length:i?,format:s?", + .params = "[-p] filename [begin] [length] [format]", .help = "dump guest memory to file" "\n\t\t\t begin(optional): the starting physical address" - "\n\t\t\t length(optional): the memory size, in bytes", + "\n\t\t\t length(optional): the memory size, in bytes" + "\n\t\t\t format(optional): the format of guest memory dump," + "\n\t\t\t it can be elf|kdump-zlib|kdump-lzo|kdump-snappy", .mhandler.cmd = hmp_dump_guest_memory, }, STEXI -@item dump-guest-memory [-p] @var{protocol} @var{begin} @var{length} +@item dump-guest-memory [-p] @var{protocol} @var{begin} @var{length} @var{format} @findex dump-guest-memory Dump guest memory to @var{protocol}. The file can be processed with crash or gdb. @@ -1013,6 +1015,8 @@ gdb. specified with length together. length: the memory size, in bytes. It's optional, and should be specified with begin together. + format: the format of guest memory dump. It's optional, and can be + elf|kdump-zlib|kdump-lzo|kdump-snappy ETEXI { diff --git a/hmp.c b/hmp.c index 2daed43..4204866 100644 --- a/hmp.c +++ b/hmp.c @@ -1225,9 +1225,12 @@ 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"); + bool has_format = qdict_haskey(qdict, "format"); int64_t begin = 0; int64_t length = 0; + const char *format = NULL; char *prot; + enum DumpGuestMemoryFormat dump_format; if (has_begin) { begin = qdict_get_int(qdict, "begin"); @@ -1235,11 +1238,29 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) if (has_length) { length = qdict_get_int(qdict, "length"); } + if (has_format) { + format = qdict_get_str(qdict, "format"); + } + + if (strcmp(format, "elf") == 0) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF; + } else if (strcmp(format, "kdump-zlib") == 0) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB; + } else if (strcmp(format, "kdump-lzo") == 0) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO; + } else if (strcmp(format, "kdump-snappy") == 0) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY; + } else { + error_set(&errp, QERR_INVALID_PARAMETER_VALUE, + "format", "elf|kdump-zlib|kdump-lzo|kdump-snappy"); + hmp_handle_error(mon, &errp); + return; + } 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/sysemu/dump.h b/include/sysemu/dump.h index c30fcc2..06fc33e 100644 --- a/include/sysemu/dump.h +++ b/include/sysemu/dump.h @@ -27,6 +27,7 @@ #define DUMP_DH_COMPRESSED_LZO (0x2) #define DUMP_DH_COMPRESSED_SNAPPY (0x4) +#define PAGE_SIZE (4096) #define KDUMP_SIGNATURE "KDUMP " #define SIG_LEN (sizeof(KDUMP_SIGNATURE) - 1) #define PHYS_BASE (0) diff --git a/qapi-schema.json b/qapi-schema.json index 5c32528..181bd20 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2489,6 +2489,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 zlib-compressed +# +# @kdump-snappy: kdump-compressed format with zlib-compressed +# +# Since: 1.6 +## +{ '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 @@ -2524,13 +2542,15 @@ # want to dump all guest's memory, please specify the start @begin # and @length # +# @format: #optional if specified, the format of guest memory dump. (since 1.6) +# # Returns: nothing on success # # Since: 1.2 ## { 'command': 'dump-guest-memory', 'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int', - '*length': 'int' } } + '*length': 'int', '*format': 'DumpGuestMemoryFormat' } } ## # @netdev_add: diff --git a/qmp-commands.hx b/qmp-commands.hx index 362f0e1..a58f0ae 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,begin:i?,length: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, @@ -810,6 +810,8 @@ 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 (json-string) Example: -- 1.7.1