[Crash-utility] [PATCH v3] vmware_guestdump: new input format

d.hatayama at fujitsu.com d.hatayama at fujitsu.com
Fri Oct 9 06:00:01 UTC 2020


Alexey,

> How to use: $ crash /path/to/debug_file.guest vmlinux
> Companion debug_file.vmem must be present in the same folder as

Please add this kind of explanation in man 8 crash, because

> debug_file.guest. Otherwise crash will shot a message:
>  vmw: Open the companion vmem file: /path/to/debug_file.vmem
>  crash: vmw: /path/to/debug_file.vmem: No such file or directory

I don't know such usage of vmcore-related files from this message.


________________________________________
From: Alexey Makhalov <amakhalov at vmware.com>
Sent: Thursday, October 8, 2020 11:12
To: crash-utility at redhat.com; amakhalov at vmware.com; k-hagio-ab at nec.com; Hatayama, Daisuke/畑山 大輔
Subject: [PATCH v3] vmware_guestdump: new input format

vmware_guestdump is extension to vmware_vmss with ability to debug
debug.guest and debug.vmem files.

debug.guest.gz and debug.vmem.gz can be obtained using following
.vmx options from VM running in debug mode:
    monitor.mini-suspend_on_panic = TRUE
    monitor.suspend_on_triplefault = TRUE

guestdump (debug.guest) is simplified version of *.vmss which does
not contain full VM state, but minimal guest state, such as memory
layout and CPUs state, needed for debugger. is_vmware_guestdump()
and vmware_guestdump_init() functions parse guestdump header and
populate vmss data structure (from vmware_vmss.c). As result, all
handlers (except mempry_dump) from vmware_vmss.c can be reused.

How to use: $ crash /path/to/debug_file.guest vmlinux
Companion debug_file.vmem must be present in the same folder as
debug_file.guest. Otherwise crash will shot a message:
 vmw: Open the companion vmem file: /path/to/debug_file.vmem
 crash: vmw: /path/to/debug_file.vmem: No such file or directory

Signed-off-by: Alexey Makhalov <amakhalov at vmware.com>
---
 Makefile           |   7 +-
 defs.h             |   8 ++
 filesys.c          |  12 +-
 main.c             |  14 +++
 memory.c           |   8 +-
 vmware_guestdump.c | 315 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 vmware_vmss.c      |   8 +-
 vmware_vmss.h      |   8 ++
 8 files changed, 366 insertions(+), 14 deletions(-)
 create mode 100644 vmware_guestdump.c

diff --git a/Makefile b/Makefile
index 7455410..d185719 100644
--- a/Makefile
+++ b/Makefile
@@ -70,7 +70,7 @@ CFILES=main.c tools.c global_data.c memory.c filesys.c help.c task.c \
        unwind_x86_32_64.c unwind_arm.c \
        xen_hyper.c xen_hyper_command.c xen_hyper_global_data.c \
        xen_hyper_dump_tables.c kvmdump.c qemu.c qemu-load.c sadump.c ipcs.c \
-       ramdump.c vmware_vmss.c \
+       ramdump.c vmware_vmss.c vmware_guestdump.c \
        xen_dom0.c kaslr_helper.c

 SOURCE_FILES=${CFILES} ${GENERIC_HFILES} ${MCORE_HFILES} \
@@ -89,7 +89,7 @@ OBJECT_FILES=main.o tools.o global_data.o memory.o filesys.o help.o task.o \
        unwind_x86_32_64.o unwind_arm.o \
        xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o \
        xen_hyper_dump_tables.o kvmdump.o qemu.o qemu-load.o sadump.o ipcs.o \
-       ramdump.o vmware_vmss.o \
+       ramdump.o vmware_vmss.o vmware_guestdump.o \
        xen_dom0.o kaslr_helper.o

 MEMORY_DRIVER_FILES=memory_driver/Makefile memory_driver/crash.c memory_driver/README
@@ -518,6 +518,9 @@ ramdump.o: ${GENERIC_HFILES} ${REDHAT_HFILES} ramdump.c
 vmware_vmss.o: ${GENERIC_HFILES} ${VMWARE_HFILES} vmware_vmss.c
        ${CC} -c ${CRASH_CFLAGS} vmware_vmss.c ${WARNING_OPTIONS} ${WARNING_ERROR}

+vmware_guestdump.o: ${GENERIC_HFILES} ${VMWARE_HFILES} vmware_guestdump.c
+       ${CC} -c ${CRASH_CFLAGS} vmware_guestdump.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+
 kaslr_helper.o: ${GENERIC_HFILES} kaslr_helper.c
        ${CC} -c ${CRASH_CFLAGS} kaslr_helper.c ${WARNING_OPTIONS} ${WARNING_ERROR}

diff --git a/defs.h b/defs.h
index c899fe2..3386709 100644
--- a/defs.h
+++ b/defs.h
@@ -544,6 +544,7 @@ struct program_context {
 #define is_excluded_vmemmap() (pc->flags2 & EXCLUDED_VMEMMAP)
 #define MEMSRC_LOCAL         (0x80000ULL)
 #define REDZONE             (0x100000ULL)
+#define VMWARE_VMSS_GUESTDUMP (0x200000ULL)
        char *cleanup;
        char *namelist_orig;
        char *namelist_debug_orig;
@@ -6679,6 +6680,13 @@ int vmware_vmss_phys_base(ulong *phys_base);
 int vmware_vmss_set_phys_base(ulong);

 /*
+ * vmware_guestdump.c
+ */
+int is_vmware_guestdump(char *filename);
+int vmware_guestdump_init(char *filename, FILE *ofp);
+int vmware_guestdump_memory_dump(FILE *);
+
+/*
  * kaslr_helper.c
  */
 int calc_kaslr_offset(ulong *, ulong *);
diff --git a/filesys.c b/filesys.c
index 2ec2b31..3361b6c 100644
--- a/filesys.c
+++ b/filesys.c
@@ -253,9 +253,15 @@ memory_source_init(void)
                                error(FATAL, "%s: initialization failed\n",
                                         pc->dumpfile);
                } else if (pc->flags & VMWARE_VMSS) {
-                       if (!vmware_vmss_init(pc->dumpfile, fp))
-                               error(FATAL, "%s: initialization failed\n",
-                                        pc->dumpfile);
+                       if (pc->flags2 & VMWARE_VMSS_GUESTDUMP) {
+                               if (!vmware_guestdump_init(pc->dumpfile, fp))
+                                       error(FATAL, "%s: initialization failed\n",
+                                               pc->dumpfile);
+                       } else {
+                               if (!vmware_vmss_init(pc->dumpfile, fp))
+                                       error(FATAL, "%s: initialization failed\n",
+                                               pc->dumpfile);
+                       }
                }
        }
 }
diff --git a/main.c b/main.c
index 7f562e6..388ac46 100644
--- a/main.c
+++ b/main.c
@@ -671,6 +671,18 @@ main(int argc, char **argv)
                                pc->readmem = read_vmware_vmss;
                                pc->writemem = write_vmware_vmss;

+                       } else if (is_vmware_guestdump(argv[optind])) {
+                                if (pc->flags & MEMORY_SOURCES) {
+                                        error(INFO,
+                                            "too many dumpfile arguments\n");
+                                        program_usage(SHORT_FORM);
+                                }
+                               pc->flags |= VMWARE_VMSS;
+                               pc->flags2 |= VMWARE_VMSS_GUESTDUMP;
+                               pc->dumpfile = argv[optind];
+                               pc->readmem = read_vmware_vmss;
+                               pc->writemem = write_vmware_vmss;
+
                        } else {
                                error(INFO,
                                    "%s: not a supported file format\n",
@@ -1486,6 +1498,8 @@ dump_program_context(void)
                fprintf(fp, "%sMEMSRC_LOCAL", others++ ? "|" : "");
        if (pc->flags2 & REDZONE)
                fprintf(fp, "%sREDZONE", others++ ? "|" : "");
+       if (pc->flags2 & VMWARE_VMSS_GUESTDUMP)
+               fprintf(fp, "%sVMWARE_VMSS_GUESTDUMP", others++ ? "|" : "");
        fprintf(fp, ")\n");

        fprintf(fp, "         namelist: %s\n", pc->namelist);
diff --git a/memory.c b/memory.c
index c951827..0848097 100644
--- a/memory.c
+++ b/memory.c
@@ -17115,8 +17115,12 @@ dumpfile_memory(int cmd)
                         retval = kcore_memory_dump(fp);
                else if (pc->flags & SADUMP)
                        retval = sadump_memory_dump(fp);
-               else if (pc->flags & VMWARE_VMSS)
-                       retval = vmware_vmss_memory_dump(fp);
+               else if (pc->flags & VMWARE_VMSS) {
+                       if (pc->flags2 & VMWARE_VMSS_GUESTDUMP)
+                               retval = vmware_guestdump_memory_dump(fp);
+                       else
+                               retval = vmware_vmss_memory_dump(fp);
+               }
                break;

        case DUMPFILE_ENVIRONMENT:
diff --git a/vmware_guestdump.c b/vmware_guestdump.c
new file mode 100644
index 0000000..d3fac59
--- /dev/null
+++ b/vmware_guestdump.c
@@ -0,0 +1,315 @@
+/*
+ * vmware_guestdump.c
+ *
+ * Copyright (c) 2020 VMware, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Author: Alexey Makhalov <amakhalov at vmware.com>
+ */
+
+#include "defs.h"
+#include "vmware_vmss.h"
+
+#define LOGPRX "vmw: "
+
+#define GUESTDUMP_VERSION 4
+#define GUESTDUMP_MAGIC1 1
+#define GUESTDUMP_MAGIC2 0
+
+struct guestdumpheader {
+       uint32_t version;
+       uint32_t num_vcpus;
+       uint8_t magic1;
+       uint8_t reserved1;
+       uint32_t cpu_vendor;
+       uint64_t magic2;
+       uint64_t last_addr;
+       uint64_t memsize_in_pages;
+       uint32_t reserved2;
+       uint32_t mem_holes;
+       struct memhole {
+               uint64_t ppn;
+               uint64_t pages;
+       } holes[2];
+} __attribute__((packed));
+
+struct vcpu_state {
+       uint32_t cr0;
+       uint64_t cr2;
+       uint64_t cr3;
+       uint64_t cr4;
+       uint64_t reserved1[10];
+       uint64_t idt_base;
+       uint16_t reserved2[21];
+       struct x86_64_pt_regs {
+               uint64_t r15;
+               uint64_t r14;
+               uint64_t r13;
+               uint64_t r12;
+               uint64_t rbp;
+               uint64_t rbx;
+               uint64_t r11;
+               uint64_t r10;
+               uint64_t r9;
+               uint64_t r8;
+               uint64_t rax;
+               uint64_t rcx;
+               uint64_t rdx;
+               uint64_t rsi;
+               uint64_t rdi;
+               uint64_t orig_rax;
+               uint64_t rip;
+               uint64_t cs;
+               uint64_t eflags;
+               uint64_t rsp;
+               uint64_t ss;
+       } regs64;
+       uint8_t reserved3[65];
+} __attribute__((packed));
+
+
+/*
+ * vmware_guestdump is extension to vmware_vmss with ability to debug
+ * debug.guest and debug.vmem files.
+ *
+ * debug.guest.gz and debug.vmem.gz can be obtained using following
+ * .vmx options from VM running in debug mode:
+ *     monitor.mini-suspend_on_panic = TRUE
+ *     monitor.suspend_on_triplefault = TRUE
+ *
+ * guestdump (debug.guest) is simplified version of *.vmss which does
+ * not contain full VM state, but minimal guest state, such as memory
+ * layout and CPUs state, needed for debugger. is_vmware_guestdump()
+ * and vmware_guestdump_init() functions parse guestdump header and
+ * populate vmss data structure (from vmware_vmss.c). As result, all
+ * handlers (except mempry_dump) from vmware_vmss.c can be reused.
+ *
+ * debug.guest does not have dedicated header magic or signature for
+ * its format. To probe debug.guest we need to perform header fields
+ * and file size validity. In addition, check for the filename
+ * extension, which must be ".guest".
+ */
+
+int
+is_vmware_guestdump(char *filename)
+{
+       struct guestdumpheader hdr;
+       FILE *fp;
+       uint64_t filesize, holes_sum = 0;
+       int i;
+
+       if (strcmp(filename + strlen(filename) - 6, ".guest"))
+               return FALSE;
+
+        if ((fp = fopen(filename, "r")) == NULL) {
+               error(INFO, LOGPRX"Failed to open '%s': [Error %d] %s\n",
+                     filename, errno, strerror(errno));
+               return FALSE;
+        }
+
+       if (fread(&hdr, sizeof(struct guestdumpheader), 1, fp) != 1) {
+               error(INFO, LOGPRX"Failed to read '%s': [Error %d] %s\n",
+                     filename, errno, strerror(errno));
+               fclose(fp);
+               return FALSE;
+       }
+
+       if (fseek(fp, 0L, SEEK_END) == -1) {
+               error(INFO, LOGPRX"Failed to fseek '%s': [Error %d] %s\n",
+                     filename, errno, strerror(errno));
+               fclose(fp);
+               return FALSE;
+       }
+       filesize = ftell(fp);
+       fclose(fp);
+
+       if (hdr.mem_holes > 2)
+               goto unrecognized;
+
+       for (i = 0; i < hdr.mem_holes; i++) {
+               /* hole start page */
+               vmss.regions[i].startpagenum = hdr.holes[i].ppn;
+               /* hole end page */
+               vmss.regions[i].startppn = hdr.holes[i].ppn + hdr.holes[i].pages;
+               holes_sum += hdr.holes[i].pages;
+       }
+
+       if (hdr.version != GUESTDUMP_VERSION ||
+           hdr.magic1 != GUESTDUMP_MAGIC1 ||
+           hdr.magic2 != GUESTDUMP_MAGIC2 ||
+           (hdr.last_addr + 1) != ((hdr.memsize_in_pages + holes_sum) << VMW_PAGE_SHIFT) ||
+           filesize != sizeof(struct guestdumpheader) +
+           hdr.num_vcpus * (sizeof (struct vcpu_state) + VMW_PAGE_SIZE))
+               goto unrecognized;
+
+       vmss.memsize = hdr.memsize_in_pages << VMW_PAGE_SHIFT;
+       vmss.regionscount = hdr.mem_holes + 1;
+       vmss.memoffset = 0;
+       vmss.num_vcpus = hdr.num_vcpus;
+       return TRUE;
+
+unrecognized:
+       if (CRASHDEBUG(1))
+               error(INFO, LOGPRX"Unrecognized debug.guest file.\n");
+       return FALSE;
+}
+
+int
+vmware_guestdump_init(char *filename, FILE *ofp)
+{
+       FILE *fp = NULL;
+       int i, result = TRUE;
+       char *vmem_filename = NULL;
+       struct vcpu_state vs;
+       char *p;
+
+       if (!machine_type("X86") && !machine_type("X86_64")) {
+               error(INFO,
+                     LOGPRX"Invalid or unsupported host architecture for .vmss file: %s\n",
+                     MACHINE_TYPE);
+               result = FALSE;
+               goto exit;
+       }
+
+        if ((fp = fopen(filename, "r")) == NULL) {
+               error(INFO, LOGPRX"Failed to open '%s': [Error %d] %s\n",
+                     filename, errno, strerror(errno));
+               result = FALSE;
+               goto exit;
+        }
+
+       if (fseek(fp, sizeof(struct guestdumpheader), SEEK_SET) == -1) {
+               error(INFO, LOGPRX"Failed to fseek '%s': [Error %d] %s\n",
+                     filename, errno, strerror(errno));
+               result = FALSE;
+               goto exit;
+       }
+
+       vmss.vcpu_regs = malloc(vmss.num_vcpus * sizeof(uint32_t));
+       vmss.regs64 = calloc(vmss.num_vcpus, sizeof(void *));
+       if (!vmss.vcpu_regs || !vmss.regs64) {
+               error(INFO, LOGPRX"Failed to allocate memory\n");
+               result = FALSE;
+               goto exit;
+       }
+
+       for (i = 0; i < vmss.num_vcpus; i++) {
+               if (fread(&vs, sizeof(struct vcpu_state), 1, fp) != 1) {
+                       error(INFO, LOGPRX"Failed to read '%s': [Error %d] %s\n",
+                                       filename, errno, strerror(errno));
+                       result = FALSE;
+                       goto exit;
+               }
+               vmss.regs64[i] = calloc(1, sizeof(vmssregs64));
+               if (!vmss.regs64[i]) {
+                       error(INFO, LOGPRX"Failed to allocate memory\n");
+                       result = FALSE;
+                       goto exit;
+               }
+               vmss.vcpu_regs[i] = 0;
+
+               vmss.regs64[i]->rax = vs.regs64.rax;
+               vmss.regs64[i]->rcx = vs.regs64.rcx;
+               vmss.regs64[i]->rdx = vs.regs64.rdx;
+               vmss.regs64[i]->rbx = vs.regs64.rbx;
+               vmss.regs64[i]->rbp = vs.regs64.rbp;
+               vmss.regs64[i]->rsp = vs.regs64.rsp;
+               vmss.regs64[i]->rsi = vs.regs64.rsi;
+               vmss.regs64[i]->rdi = vs.regs64.rdi;
+               vmss.regs64[i]->r8 = vs.regs64.r8;
+               vmss.regs64[i]->r9 = vs.regs64.r9;
+               vmss.regs64[i]->r10 = vs.regs64.r10;
+               vmss.regs64[i]->r11 = vs.regs64.r11;
+               vmss.regs64[i]->r12 = vs.regs64.r12;
+               vmss.regs64[i]->r13 = vs.regs64.r13;
+               vmss.regs64[i]->r14 = vs.regs64.r14;
+               vmss.regs64[i]->r15 = vs.regs64.r15;
+               vmss.regs64[i]->idtr = vs.idt_base;
+               vmss.regs64[i]->cr[0] = vs.cr0;
+               vmss.regs64[i]->cr[2] = vs.cr2;
+               vmss.regs64[i]->cr[3] = vs.cr3;
+               vmss.regs64[i]->cr[4] = vs.cr4;
+               vmss.regs64[i]->rip = vs.regs64.rip;
+               vmss.regs64[i]->rflags = vs.regs64.eflags;
+
+               vmss.vcpu_regs[i] = REGS_PRESENT_ALL;
+       }
+
+       vmem_filename = strdup(filename);
+       p = vmem_filename + strlen(vmem_filename) - 5;
+       if (strcmp(p, "guest") != 0) {
+               result = FALSE;
+               goto exit;
+       }
+       strcpy(p, "vmem");
+
+       fprintf(ofp, LOGPRX"Open the companion vmem file: %s\n", vmem_filename);
+       if ((vmss.dfp = fopen(vmem_filename, "r")) == NULL) {
+               error(INFO, LOGPRX"%s: %s\n", vmem_filename, strerror(errno));
+               result = FALSE;
+               goto exit;
+       }
+       fseek(vmss.dfp, 0L, SEEK_END);
+       if (vmss.memsize != ftell(vmss.dfp)) {
+               error(INFO, LOGPRX"%s: unexpected size\n", vmem_filename);
+               result = FALSE;
+               goto exit;
+       }
+       fseek(vmss.dfp, 0L, SEEK_SET);
+       fprintf(ofp, LOGPRX"vmem file: %s\n\n", vmem_filename);
+
+exit:
+       if (fp)
+               fclose(fp);
+       if (vmem_filename)
+               free(vmem_filename);
+       if (result == FALSE) {
+               if (vmss.dfp)
+                       fclose(vmss.dfp);
+               if (vmss.regs64) {
+                       for (i = 0; i < vmss.num_vcpus; i++) {
+                               if (vmss.regs64[i])
+                                       free(vmss.regs64[i]);
+                       }
+                       free(vmss.regs64);
+               }
+               if (vmss.vcpu_regs)
+                       free(vmss.vcpu_regs);
+       }
+       return result;
+}
+
+int
+vmware_guestdump_memory_dump(FILE *ofp)
+{
+       fprintf(ofp, "vmware_guestdump:\n");
+       fprintf(ofp, "    Header: version=%d num_vcpus=%ld\n",
+               GUESTDUMP_VERSION, vmss.num_vcpus);
+       fprintf(ofp, "Total memory: %ld\n", vmss.memsize);
+
+       if (vmss.regionscount > 1) {
+               uint64_t holes_sum = 0;
+               unsigned i;
+
+               fprintf(ofp, "Memory regions[%d]:\n", vmss.regionscount);
+               fprintf(ofp, "    [0x%016x-", 0);
+               for (i = 0; i < vmss.regionscount - 1; i++) {
+                       fprintf(ofp, "0x%016lx]\n", (uint64_t)vmss.regions[i].startpagenum << VMW_PAGE_SHIFT);
+                       fprintf(ofp, "    [0x%016lx-", (uint64_t)vmss.regions[i].startppn << VMW_PAGE_SHIFT);
+                       holes_sum += vmss.regions[i].startppn - vmss.regions[i].startpagenum;
+               }
+               fprintf(ofp, "0x%016lx]\n", vmss.memsize + (holes_sum << VMW_PAGE_SHIFT));
+       }
+
+       return TRUE;
+}
+
diff --git a/vmware_vmss.c b/vmware_vmss.c
index 252bfa2..b168f29 100644
--- a/vmware_vmss.c
+++ b/vmware_vmss.c
@@ -23,13 +23,7 @@

 #define LOGPRX "vmw: "

-/* VMware only supports X86/X86_64 virtual machines. */
-#define VMW_PAGE_SIZE (4096)
-#define VMW_PAGE_SHIFT (12)
-
-#define MAX_BLOCK_DUMP (128)
-
-static vmssdata vmss = { 0 };
+vmssdata vmss = { 0 };

 int
 is_vmware_vmss(char *filename)
diff --git a/vmware_vmss.h b/vmware_vmss.h
index a5828a0..01d9446 100644
--- a/vmware_vmss.h
+++ b/vmware_vmss.h
@@ -165,6 +165,14 @@ struct vmssdata {
 };
 typedef struct vmssdata vmssdata;

+/* VMware only supports X86/X86_64 virtual machines. */
+#define VMW_PAGE_SIZE (4096)
+#define VMW_PAGE_SHIFT (12)
+
+#define MAX_BLOCK_DUMP (128)
+
+extern vmssdata vmss;
+
 #define DEBUG_PARSE_PRINT(x)           \
 do {                                   \
        if (CRASHDEBUG(1)) {            \
--
2.11.0





More information about the Crash-utility mailing list