Skip to contentSkip to conversation

introducing a new, dedicated memory dump mechanism

RFC PATCH Superseded Wen Congyang 3 participants 26 messages Jan 19, 2012 – Jan 31, 2012 Kbuild

v5 of 15 Superseded Latest is v15 →

lore
Share & tools
lore
#0 lore raw

v5 cover introducing a new, dedicated memory dump mechanism

Hi, all

'virsh dump' can not work when host pci device is used by guest. We have discussed this issue here:
http://lists.nongnu.org/archive/html/qemu-devel/2011-10/msg00736.html

We have determined to introduce a new command dump to dump memory. The core file's format can be elf.

Note:
1. The guest should be x86 or x86_64. The other arch is not supported.
2. If you use old gdb, gdb may crash. I use gdb-7.3.1, and it does not crash.
3. If the OS is in the second kernel, gdb may not work well, and crash can work by specifying '--machdep phys_addr=xxx' in the command line. The reason is that the second kernel will update the page table, and we can not get the page table for the first kernel.
4. If the guest OS is 32 bit and the memory size is larger than 4G, the vmcore is elf64 format. You should use the gdb which is built with --enable-64-bit-bfd.
5. This patchset is based on the upstream tree, and apply one patch that is still in Luiz Capitulino's tree, because I use the API qemu_get_fd() in this patchset.

Changes from v4 to v5:
1. convert the new command dump to QAPI

Changes from v3 to v4:
1. support it to run asynchronously
2. add API to cancel dumping and query dumping progress
3. add API to control dumping speed
4. auto cancel dumping when the user resumes vm, and the status is failed.

Changes from v2 to v3:
1. address Jan Kiszka's comment

Changes from v1 to v2:
1. fix virt addr in the vmcore.

Wen Congyang (15):
monitor: introduce qemu_suspend_monitor()/qemu_resume_monitor() Add API to create memory mapping list
Add API to check whether a physical address is I/O address
target-i386: implement cpu_get_memory_mapping()
Add API to get memory mapping
target-i386: Add API to write elf notes to core file
target-i386: Add API to add extra memory mapping
target-i386: add API to get dump info
introduce a new monitor command 'dump' to dump guest's memory run dump at the background
support detached dump
support to cancel the current dumping
support to set dumping speed
support to query dumping status
auto cancel dumping after vm state is changed to run

 Makefile.target         |   11 +-
 cpu-all.h               |   18 +
 cpu-common.h            |    2 +
 dump.c                  |  787 +++++++++++++++++++++++++++++++++++++++++++++++
 dump.h                  |   13 +
 exec.c                  |   16 +
 hmp-commands.hx         |   47 +++
 hmp.c                   |   38 +++
 hmp.h                   |    4 +
 memory_mapping.c        |  185 +++++++++++
 memory_mapping.h        |   39 +++
 monitor.c               |   37 +++
 monitor.h               |    2 +
 qapi-schema.json        |   67 ++++
 qmp-commands.hx         |  117 +++++++
 target-i386/arch-dump.c |  574 ++++++++++++++++++++++++++++++++++
 vl.c                    |    5 +-
 17 files changed, 1955 insertions(+), 7 deletions(-)
 create mode 100644 dump.c
 create mode 100644 dump.h
 create mode 100644 memory_mapping.c
 create mode 100644 memory_mapping.h
 create mode 100644 target-i386/arch-dump.c
#1 lore raw

RFC PATCH v5 1/15 monitor: introduce qemu_suspend_monitor()/qemu_resume_monitor()

Sync command needs these two APIs to suspend/resume monitor.

---
 monitor.c |   27 +++++++++++++++++++++++++++
 monitor.h |    2 ++
 2 files changed, 29 insertions(+), 0 deletions(-)
monitor.c +27 -0
index 56f3778..ce2bc13 100644
@@ -4594,6 +4594,26 @@ static void monitor_command_cb(Monitor *mon, const char *cmdline, void *opaque)
     monitor_resume(mon);
 }
 
+int qemu_suspend_monitor(const char *fmt, ...)
+{
+    int ret;
+
+    if (cur_mon) {
+        ret = monitor_suspend(cur_mon);
+    } else {
+        ret = -ENOTTY;
+    }
+
+    if (ret < 0 && fmt) {
+        va_list ap;
+        va_start(ap, fmt);
+        monitor_vprintf(cur_mon, fmt, ap);
+        va_end(ap);
+    }
+
+    return ret;
+}
+
 int monitor_suspend(Monitor *mon)
 {
     if (!mon->rs)
@@ -4602,6 +4622,13 @@ int monitor_suspend(Monitor *mon)
     return 0;
 }
 
+void qemu_resume_monitor(void)
+{
+    if (cur_mon) {
+        monitor_resume(cur_mon);
+    }
+}
+
 void monitor_resume(Monitor *mon)
 {
     if (!mon->rs)
monitor.h +2 -0
index 274cd39..cc94bab 100644
@@ -43,7 +43,9 @@ int monitor_cur_is_qmp(void);
 void monitor_protocol_event(MonitorEvent event, QObject *data);
 void monitor_init(CharDriverState *chr, int flags);
 
+int qemu_suspend_monitor(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
 int monitor_suspend(Monitor *mon);
+void qemu_resume_monitor(void);
 void monitor_resume(Monitor *mon);
 
 int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
Signature · 1 line
1.7.1
#2 lore raw

RFC PATCH v5 2/15 Add API to create memory mapping list

In reply to Wen Congyang

The memory mapping list stores virtual address and physical address mapping. The folloing patch will use this information to create PT_LOAD in the vmcore.

---
 Makefile.target  |    1 +
 memory_mapping.c |  130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 memory_mapping.h |   38 ++++++++++++++++
 3 files changed, 169 insertions(+), 0 deletions(-)
 create mode 100644 memory_mapping.c
 create mode 100644 memory_mapping.h
Makefile.target +1 -0
index 06d79b8..fed5531 100644
@@ -197,6 +197,7 @@ obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o
 obj-$(CONFIG_KVM) += kvm.o kvm-all.o
 obj-$(CONFIG_NO_KVM) += kvm-stub.o
 obj-y += memory.o savevm.o
+obj-y += memory_mapping.o
 LIBS+=-lz
 
 QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
memory_mapping.c new file +130 -0

130 lines not on this page · raw message

memory_mapping.h new file +38 -0
new file mode 100644
index 0000000..a4b1532
@@ -0,0 +1,38 @@
+#ifndef MEMORY_MAPPING_H
+#define MEMORY_MAPPING_H
+
+#include "qemu-queue.h"
+
+typedef struct MemoryMapping {
+    target_phys_addr_t phys_addr;
+    target_ulong virt_addr;
+    ram_addr_t length;
+    QTAILQ_ENTRY(MemoryMapping) next;
+} MemoryMapping;
+
+typedef struct MemoryMappingList {
+    unsigned int num;
+    QTAILQ_HEAD(, MemoryMapping) head;
+} MemoryMappingList;
+
+/*
+ * crash needs some memory mapping should be at the head of the list. It will
+ * cause the list is not sorted. So the caller must add the special memory
+ * mapping after adding all the normal memory mapping into list.
+ */
+void create_new_memory_mapping_head(MemoryMappingList *list,
+                                    target_phys_addr_t phys_addr,
+                                    target_phys_addr_t virt_addr,
+                                    ram_addr_t length);
+/*
+ * add or merge the memory region into the memory mapping's list. The list is
+ * sorted by phys_addr.
+ */
+void add_to_memory_mapping(MemoryMappingList *list,
+                           target_phys_addr_t phys_addr,
+                           target_phys_addr_t virt_addr,
+                           ram_addr_t length);
+
+void free_memory_mapping_list(MemoryMappingList *list);
+
+#endif
Signature · 1 line
1.7.1
#3 lore raw

RFC PATCH v5 3/15 Add API to check whether a physical address is I/O address

In reply to Wen Congyang

This API will be used in the following patch.

---
 cpu-common.h |    2 ++
 exec.c       |   16 ++++++++++++++++
 2 files changed, 18 insertions(+), 0 deletions(-)
cpu-common.h +2 -0
index a40c57d..d047137 100644
@@ -71,6 +71,8 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len,
 void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque));
 void cpu_unregister_map_client(void *cookie);
 
+bool is_io_addr(target_phys_addr_t phys_addr);
+
 /* Coalesced MMIO regions are areas where write operations can be reordered.
  * This usually implies that write operations are side-effect free.  This allows
  * batching which can make a major impact on performance when using
exec.c +16 -0
index 7f9f730..adc4e74 100644
@@ -4411,3 +4411,19 @@ tb_page_addr_t get_page_addr_code(CPUState *env1, target_ulong addr)
 #undef env
 
 #endif
+
+bool is_io_addr(target_phys_addr_t phys_addr)
+{
+    ram_addr_t pd;
+    PhysPageDesc p;
+
+    p = phys_page_find(phys_addr >> TARGET_PAGE_BITS);
+    pd = p.phys_offset;
+
+    if (!is_ram_rom_romd(pd)) {
+        /* I/O region */
+        return true;
+    }
+
+    return false;
+}
Signature · 1 line
1.7.1
#4 lore raw

RFC PATCH v5 4/15 target-i386: implement cpu_get_memory_mapping()

In reply to Wen Congyang

Walk cpu's page table and collect all virtual address and physical address mapping. Then, add these mapping into memory mapping list.

---
 Makefile.target         |    2 +-
 cpu-all.h               |    7 ++
 target-i386/arch-dump.c |  254 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 262 insertions(+), 1 deletions(-)
 create mode 100644 target-i386/arch-dump.c
Makefile.target +1 -1
index fed5531..d869550 100644
@@ -73,7 +73,7 @@ libobj-$(CONFIG_TCG_INTERPRETER) += tci.o
 libobj-y += fpu/softfloat.o
 libobj-y += op_helper.o helper.o
 ifeq ($(TARGET_BASE_ARCH), i386)
-libobj-y += cpuid.o
+libobj-y += cpuid.o arch-dump.o
 endif
 libobj-$(TARGET_SPARC64) += vis_helper.o
 libobj-$(CONFIG_NEED_MMU) += mmu.o
cpu-all.h +7 -0
index e2c3c49..4cd7fbb 100644
@@ -22,6 +22,7 @@
 #include "qemu-common.h"
 #include "qemu-tls.h"
 #include "cpu-common.h"
+#include "memory_mapping.h"
 
 /* some important defines:
  *
@@ -523,4 +524,10 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf);
 int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
                         uint8_t *buf, int len, int is_write);
 
+#if defined(TARGET_I386)
+void cpu_get_memory_mapping(MemoryMappingList *list, CPUState *env);
+#else
+#define cpu_get_memory_mapping(list, env)
+#endif
+
 #endif /* CPU_ALL_H */
target-i386/arch-dump.c new file +254 -0

254 lines not on this page · raw message

Signature · 1 line
1.7.1
#5 lore raw

RFC PATCH v5 5/15 Add API to get memory mapping

In reply to Wen Congyang

Add API to get all virtual address and physical address mapping. If there is no virtual address for some physical address, the virtual address is 0.

---
 memory_mapping.c |   55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 memory_mapping.h |    1 +
 2 files changed, 56 insertions(+), 0 deletions(-)
memory_mapping.c +55 -0
index d83b7d7..55c9266 100644
@@ -128,3 +128,58 @@ void free_memory_mapping_list(MemoryMappingList *list)
 
     list->num = 0;
 }
+
+void get_memory_mapping(MemoryMappingList *list)
+{
+    CPUState *env;
+    MemoryMapping *memory_mapping;
+    RAMBlock *block;
+    ram_addr_t offset, length;
+
+    last_mapping = NULL;
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        cpu_get_memory_mapping(list, env);
+    }
+
+    /* some memory may be not mapped, add them into memory mapping's list */
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+        offset = block->offset;
+        length = block->length;
+
+        QTAILQ_FOREACH(memory_mapping, &list->head, next) {
+            if (memory_mapping->phys_addr >= (offset + length)) {
+                /*
+                 * memory_mapping'list does not conatin the region
+                 * [offset, offset+length)
+                 */
+                create_new_memory_mapping(list, offset, 0, length);
+                break;
+            }
+
+            if ((memory_mapping->phys_addr + memory_mapping->length) <=
+                offset) {
+                continue;
+            }
+
+            if (memory_mapping->phys_addr > offset) {
+                /*
+                 * memory_mapping'list does not conatin the region
+                 * [offset, memory_mapping->phys_addr)
+                 */
+                create_new_memory_mapping(list, offset, 0,
+                                          memory_mapping->phys_addr - offset);
+            }
+
+            if ((offset + length) <=
+                (memory_mapping->phys_addr + memory_mapping->length)) {
+                break;
+            }
+            length -= memory_mapping->phys_addr + memory_mapping->length -
+                      offset;
+            offset = memory_mapping->phys_addr + memory_mapping->length;
+        }
+    }
+
+    return;
+}
memory_mapping.h +1 -0
index a4b1532..679f9ef 100644
@@ -34,5 +34,6 @@ void add_to_memory_mapping(MemoryMappingList *list,
                            ram_addr_t length);
 
 void free_memory_mapping_list(MemoryMappingList *list);
+void get_memory_mapping(MemoryMappingList *list);
 
 #endif
Signature · 1 line
1.7.1
#6 lore raw

RFC PATCH v5 6/15 target-i386: Add API to write elf notes to core file

In reply to Wen Congyang

The core file contains register's value. These APIs write registers to core file, and them will be called in the following patch.

---
 cpu-all.h               |    6 +
 target-i386/arch-dump.c |  243 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 249 insertions(+), 0 deletions(-)
cpu-all.h +6 -0
index 4cd7fbb..efb5ba3 100644
@@ -526,8 +526,14 @@ int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
 
 #if defined(TARGET_I386)
 void cpu_get_memory_mapping(MemoryMappingList *list, CPUState *env);
+int cpu_write_elf64_note(int fd, CPUState *env, int cpuid,
+                         target_phys_addr_t *offset);
+int cpu_write_elf32_note(int fd, CPUState *env, int cpuid,
+                         target_phys_addr_t *offset);
 #else
 #define cpu_get_memory_mapping(list, env)
+#define cpu_write_elf64_note(fd, env, cpuid, offset) ({ -1; })
+#define cpu_write_elf32_note(fd, env, cpuid, offset) ({ -1; })
 #endif
 
 #endif /* CPU_ALL_H */
target-i386/arch-dump.c +243 -0

254 lines not on this page · raw message

Signature · 1 line
1.7.1
#7 lore raw

RFC PATCH v5 7/15 target-i386: Add API to add extra memory mapping

In reply to Wen Congyang

Crash needs extra memory mapping to determine phys_base.

---
 cpu-all.h               |    2 ++
 target-i386/arch-dump.c |   43 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+), 0 deletions(-)
cpu-all.h +2 -0
index efb5ba3..290c43a 100644
@@ -530,10 +530,12 @@ int cpu_write_elf64_note(int fd, CPUState *env, int cpuid,
                          target_phys_addr_t *offset);
 int cpu_write_elf32_note(int fd, CPUState *env, int cpuid,
                          target_phys_addr_t *offset);
+int cpu_add_extra_memory_mapping(MemoryMappingList *list);
 #else
 #define cpu_get_memory_mapping(list, env)
 #define cpu_write_elf64_note(fd, env, cpuid, offset) ({ -1; })
 #define cpu_write_elf32_note(fd, env, cpuid, offset) ({ -1; })
+#define cpu_add_extra_memory_mapping(list) ({ 0; })
 #endif
 
 #endif /* CPU_ALL_H */
target-i386/arch-dump.c +43 -0

46 lines not on this page · raw message

Signature · 1 line
1.7.1
#8 lore raw

RFC PATCH v5 8/15 target-i386: add API to get dump info

In reply to Wen Congyang

Dump info contains: endian, class and architecture. The next patch will use these information to create vmcore.

---
 cpu-all.h               |    3 +++
 dump.h                  |   10 ++++++++++
 target-i386/arch-dump.c |   34 ++++++++++++++++++++++++++++++++++
 3 files changed, 47 insertions(+), 0 deletions(-)
 create mode 100644 dump.h
cpu-all.h +3 -0
index 290c43a..268d1f6 100644
@@ -23,6 +23,7 @@
 #include "qemu-tls.h"
 #include "cpu-common.h"
 #include "memory_mapping.h"
+#include "dump.h"
 
 /* some important defines:
  *
@@ -531,11 +532,13 @@ int cpu_write_elf64_note(int fd, CPUState *env, int cpuid,
 int cpu_write_elf32_note(int fd, CPUState *env, int cpuid,
                          target_phys_addr_t *offset);
 int cpu_add_extra_memory_mapping(MemoryMappingList *list);
+int cpu_get_dump_info(ArchDumpInfo *info);
 #else
 #define cpu_get_memory_mapping(list, env)
 #define cpu_write_elf64_note(fd, env, cpuid, offset) ({ -1; })
 #define cpu_write_elf32_note(fd, env, cpuid, offset) ({ -1; })
 #define cpu_add_extra_memory_mapping(list) ({ 0; })
+#define cpu_get_dump_info(info) ({ -1; })
 #endif
 
 #endif /* CPU_ALL_H */
dump.h new file +10 -0
new file mode 100644
index 0000000..a36468b
@@ -0,0 +1,10 @@
+#ifndef DUMP_H
+#define DUMP_H
+
+typedef struct ArchDumpInfo {
+    int d_machine;  /* Architecture */
+    int d_endian;   /* ELFDATA2LSB or ELFDATA2MSB */
+    int d_class;    /* ELFCLASS32 or ELFCLASS64 */
+} ArchDumpInfo;
+
+#endif
target-i386/arch-dump.c +34 -0
index d96f6ae..92a53bc 100644
@@ -15,6 +15,7 @@
 
 #include "cpu.h"
 #include "cpu-all.h"
+#include "dump.h"
 #include "monitor.h"
 
 /* PAE Paging or IA-32e Paging */
@@ -538,3 +539,36 @@ int cpu_add_extra_memory_mapping(MemoryMappingList *list)
 #endif
     return 0;
 }
+
+int cpu_get_dump_info(ArchDumpInfo *info)
+{
+    bool lma = false;
+    RAMBlock *block;
+
+#ifdef TARGET_X86_64
+    lma = !!(first_cpu->hflags & HF_LMA_MASK);
+#endif
+
+    if (lma) {
+        info->d_machine = EM_X86_64;
+    } else {
+        info->d_machine = EM_386;
+    }
+    info->d_endian = ELFDATA2LSB;
+
+    if (lma) {
+        info->d_class = ELFCLASS64;
+    } else {
+        info->d_class = ELFCLASS32;
+    }
+
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+        if (!lma && (block->offset + block->length > UINT_MAX)) {
+            /* The memory size is greater than 4G */
+            info->d_class = ELFCLASS32;
+            break;
+        }
+    }
+
+    return 0;
+}
Signature · 1 line
1.7.1
#9 lore raw

RFC PATCH v5 9/15 introduce a new monitor command 'dump' to dump guest's memory

In reply to Wen Congyang
---
 Makefile.target  |    8 +-
 dump.c           |  590 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 dump.h           |    3 +
 hmp-commands.hx  |   16 ++
 hmp.c            |    9 +
 hmp.h            |    1 +
 monitor.c        |    3 +
 qapi-schema.json |   13 ++
 qmp-commands.hx  |   26 +++
 9 files changed, 665 insertions(+), 4 deletions(-)
 create mode 100644 dump.c
Makefile.target +4 -4
index d869550..abfb057 100644
@@ -110,7 +110,7 @@ $(call set-vpath, $(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR
 QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user
 obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \
       elfload.o linuxload.o uaccess.o gdbstub.o cpu-uname.o \
-      user-exec.o $(oslib-obj-y)
+      user-exec.o $(oslib-obj-y) dump.o
 
 obj-$(TARGET_HAS_BFLT) += flatload.o
 
@@ -148,7 +148,7 @@ LDFLAGS+=-Wl,-segaddr,__STD_PROG_ZONE,0x1000 -image_base 0x0e000000
 LIBS+=-lmx
 
 obj-y = main.o commpage.o machload.o mmap.o signal.o syscall.o thunk.o \
-        gdbstub.o user-exec.o
+        gdbstub.o user-exec.o dump.o
 
 obj-i386-y += ioport-user.o
 
@@ -170,7 +170,7 @@ $(call set-vpath, $(SRC_PATH)/bsd-user)
 QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH)
 
 obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \
-        gdbstub.o uaccess.o user-exec.o
+        gdbstub.o uaccess.o user-exec.o dump.o
 
 obj-i386-y += ioport-user.o
 
@@ -186,7 +186,7 @@ endif #CONFIG_BSD_USER
 # System emulator target
 ifdef CONFIG_SOFTMMU
 
-obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o ioport.o
+obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o ioport.o dump.o
 # virtio has to be here due to weird dependency between PCI and virtio-net.
 # need to fix this properly
 obj-$(CONFIG_NO_PCI) += pci-stub.o
dump.c new file +590 -0

590 lines not on this page · raw message

dump.h +3 -0
index a36468b..b413d18 100644
@@ -1,6 +1,9 @@
 #ifndef DUMP_H
 #define DUMP_H
 
+#include "qdict.h"
+#include "error.h"
+
 typedef struct ArchDumpInfo {
     int d_machine;  /* Architecture */
     int d_endian;   /* ELFDATA2LSB or ELFDATA2MSB */
hmp-commands.hx +16 -0
index a586498..c3615e3 100644
@@ -828,6 +828,22 @@ new parameters (if specified) once the vm migration finished successfully.
 ETEXI
 
     {
+        .name       = "dump",
+        .args_type  = "file:s",
+        .params     = "file",
+        .help       = "dump to file",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd = hmp_dump,
+    },
+
+
+STEXI
+@item dump @var{file}
+@findex dump
+Dump to @var{file}.
+ETEXI
+
+    {
         .name       = "snapshot_blkdev",
         .args_type  = "device:B,snapshot-file:s?,format:s?",
         .params     = "device [new-image-file] [format]",
hmp.c +9 -0
index 8a77780..11b4ce6 100644
@@ -681,3 +681,12 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
     int64_t value = qdict_get_int(qdict, "value");
     qmp_migrate_set_speed(value, NULL);
 }
+
+void hmp_dump(Monitor *mon, const QDict *qdict)
+{
+    Error *errp = NULL;
+    const char *file = qdict_get_str(qdict, "file");
+
+    qmp_dump(file, &errp);
+    hmp_handle_error(mon, &errp);
+}
hmp.h +1 -0
index 093242d..8d6a5d2 100644
@@ -49,5 +49,6 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
 void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
 void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
 void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
+void hmp_dump(Monitor *mon, const QDict *qdict);
 
 #endif
monitor.c +3 -0
index ce2bc13..24d4371 100644
@@ -73,6 +73,9 @@
 #endif
 #include "hw/lm32_pic.h"
 
+/* for dump */
+#include "dump.h"
+
 //#define DEBUG
 //#define DEBUG_COMPLETION
 
qapi-schema.json +13 -0
index 44cf764..84c2c9a 100644
@@ -1275,3 +1275,16 @@
 { 'command': 'qom-set',
   'data': { 'path': 'str', 'property': 'str', 'value': 'visitor' },
   'gen': 'no' }
+
+##
+# @dump
+#
+# Dump guest's memory to vmcore.
+#
+# @file: the filename or file descriptor of the vmcore.
+#
+# Returns: nothing on success
+#
+# Since: 1.1
+##
+{ 'command': 'dump', 'data': {'file': 'str'} }
qmp-commands.hx +26 -0
index 7e3f4b9..fefdae2 100644
@@ -572,6 +572,32 @@ Example:
 EQMP
 
     {
+        .name       = "dump",
+        .args_type  = "file:s",
+        .params     = "file",
+        .help       = "dump to file",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = qmp_marshal_input_dump,
+    },
+
+SQMP
+dump
+
+
+Dump to file.
+
+Arguments:
+
+- "file": Destination file (json-string)
+
+Example:
+
+-> { "execute": "dump", "arguments": { "file": "fd:dump" } }
+<- { "return": {} }
+
+EQMP
+
+    {
         .name       = "netdev_add",
         .args_type  = "netdev:O",
         .params     = "[user|tap|socket],id=str[,prop=value][,...]",
Signature · 1 line
1.7.1
#10 lore raw

Re: [Qemu-devel] [RFC][PATCH 09/15] introduce a new monitor command 'dump' to dump guest's memory

Quoted 20 lines · Wen Congyang
---
 Makefile.target  |    8 +-
 dump.c           |  590 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 dump.h           |    3 +
 hmp-commands.hx  |   16 ++
 hmp.c            |    9 +
 hmp.h            |    1 +
 monitor.c        |    3 +
 qapi-schema.json |   13 ++
 qmp-commands.hx  |   26 +++
 9 files changed, 665 insertions(+), 4 deletions(-)
 create mode 100644 dump.c
dump.c new file +5 -0
+void qmp_dump(const char *file, Error **errp)
+{
+    const char *p;
+    int fd = -1;
+    DumpState *s;

dump.c line 565

Quoted 10 lines
dump.c new file +10 -0
+
+#if !defined(WIN32)
+    if (strstart(file, "fd:", &p)) {
+        fd = qemu_get_fd(p);
+        if (fd == -1) {
+            error_set(errp, QERR_FD_NOT_FOUND, p);
+            return;
+        }
+    }
+#endif

Thanks for implementing fd support off the bat.

dump.c line 568

Quoted 3 lines
dump.c new file +3 -0
+
+    if  (strstart(file, "file:", &p)) {
+        fd = open(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);

Use of O_CREAT requires that you pass a third argument to open() specifying the mode_t to use.

hmp-commands.hx new parameters (if specified) once the vm migration finished successfully. · line 837

Quoted 12 lines

+++ b/hmp-commands.hx

hmp-commands.hx +7 -0
@@ -828,6 +828,22 @@ new parameters (if specified) once the vm migration finished successfully.
 ETEXI
 
     {
+        .name       = "dump",
+        .args_type  = "file:s",
+        .params     = "file",
+        .help       = "dump to file",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd = hmp_dump,
+    },

What if I want to dump only a fraction of the memory? I think you need optional start and length parameters, to limit how much memory to be dumped, rather than forcing me to dump all memory at once.

Signature · 2 lines
Eric Blake   [email protected]    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#11 lore raw

Re: [Qemu-devel] [RFC][PATCH 09/15] introduce a new monitor command 'dump' to dump guest's memory

At 01/20/2012 12:32 AM, Eric Blake Wrote:

Quoted 34 lines · Wen Congyang
Quoted 20 lines
---
 Makefile.target  |    8 +-
 dump.c           |  590 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 dump.h           |    3 +
 hmp-commands.hx  |   16 ++
 hmp.c            |    9 +
 hmp.h            |    1 +
 monitor.c        |    3 +
 qapi-schema.json |   13 ++
 qmp-commands.hx  |   26 +++
 9 files changed, 665 insertions(+), 4 deletions(-)
 create mode 100644 dump.c
dump.c new file +5 -0
+void qmp_dump(const char *file, Error **errp)
+{
+    const char *p;
+    int fd = -1;
+    DumpState *s;

dump.c line 565

Quoted 10 lines
dump.c new file +10 -0
+
+#if !defined(WIN32)
+    if (strstart(file, "fd:", &p)) {
+        fd = qemu_get_fd(p);
+        if (fd == -1) {
+            error_set(errp, QERR_FD_NOT_FOUND, p);
+            return;
+        }
+    }
+#endif

Thanks for implementing fd support off the bat.

Quoted 6 lines

dump.c line 568

Quoted 3 lines
dump.c new file +3 -0
+
+    if  (strstart(file, "file:", &p)) {
+        fd = open(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);

Use of O_CREAT requires that you pass a third argument to open() specifying the mode_t to use.

Yes, I forgot it, and will fix it.

Quoted 5 lines
Quoted 5 lines

+++ b/hmp-commands.hx

(quoted diff) +0 -0
@@ -828,6 +828,22 @@ new parameters (if specified) once the vm migration finished successfully.
 ETEXI
 
Quoted 13 lines

hmp-commands.hx new parameters (if specified) once the vm migration finished successfully. · line 837

Quoted 8 lines
hmp-commands.hx +7 -0
     {
+        .name       = "dump",
+        .args_type  = "file:s",
+        .params     = "file",
+        .help       = "dump to file",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd = hmp_dump,
+    },

What if I want to dump only a fraction of the memory? I think you need optional start and length parameters, to limit how much memory to be dumped, rather than forcing me to dump all memory at once.

It is OK to support it, but I do not know why do you want it?

The purpose of this command is dumping the memory when the guest is paniced. And then we can use crash/gdb(or other application) to investigate why the guest is paniced. So we should dump the whole memory.

Thanks
Wen Congyang

#12 lore raw

Re: [Qemu-devel] [RFC][PATCH 09/15] introduce a new monitor command 'dump' to dump guest's memory

Quoted 18 lines · Wen Congyang
Quoted 4 lines
Quoted 4 lines

+++ b/hmp-commands.hx

(quoted diff) +0 -0
@@ -828,6 +828,22 @@ new parameters (if specified) once the vm migration finished successfully.
 ETEXI
 
Quoted 13 lines

hmp-commands.hx new parameters (if specified) once the vm migration finished successfully. · line 837

Quoted 8 lines
hmp-commands.hx +7 -0
     {
+        .name       = "dump",
+        .args_type  = "file:s",
+        .params     = "file",
+        .help       = "dump to file",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd = hmp_dump,
+    },

What if I want to dump only a fraction of the memory? I think you need optional start and length parameters, to limit how much memory to be dumped, rather than forcing me to dump all memory at once.

Quoted 5 lines

It is OK to support it, but I do not know why do you want it?

The purpose of this command is dumping the memory when the guest is paniced. And then we can use crash/gdb(or other application) to investigate why the guest is paniced. So we should dump the whole memory.

That's one purpose, but not the only purpose. We shouldn't be artificially constraining things into requiring the entire memory region in order to use this command.

Libvirt provides virDomainMemoryPeek which currently wraps the 'memsave' and 'pmemsave' monitor commands, but these commands output raw memory. Your command is introducing a new memory format into ELF images, and if 'memsave' can already do a subset of memory, it also makes sense for 'dump' to do a subset when creating the ELF image. That is, if a management app every has a reason to access a subset of memory, then this reason exists whether the subset is raw or ELF formatted when presented to the management app.

Meanwhile, on the libvirt side, the virDomainMemoryPeek API to management apps is constrained - it sends the data inline with the command, rather than on a side channel. Someday, I'd like to enhance libvirt to have a dump-to-stream command, and reuse the existing libvirt ability to stream large amounts of data on side channels, in order to let management apps directly and atomically query a subset of memory into a file with the desired formatting, rather than the current approach of constraining the management app to only query 64k at a time and to have to manually pause the guest if they need to atomically inspect more memory.

Signature · 2 lines
Eric Blake   [email protected]    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#13 lore raw

Re: [Qemu-devel] [RFC][PATCH 09/15] introduce a new monitor command 'dump' to dump guest's memory

At 01/31/2012 01:19 AM, Eric Blake Wrote:

Quoted 29 lines · Wen Congyang
Quoted 18 lines
Quoted 4 lines
Quoted 4 lines

+++ b/hmp-commands.hx

(quoted diff) +0 -0
@@ -828,6 +828,22 @@ new parameters (if specified) once the vm migration finished successfully.
 ETEXI
 
Quoted 13 lines

hmp-commands.hx new parameters (if specified) once the vm migration finished successfully. · line 837

Quoted 8 lines
hmp-commands.hx +7 -0
     {
+        .name       = "dump",
+        .args_type  = "file:s",
+        .params     = "file",
+        .help       = "dump to file",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd = hmp_dump,
+    },

What if I want to dump only a fraction of the memory? I think you need optional start and length parameters, to limit how much memory to be dumped, rather than forcing me to dump all memory at once.

Quoted 5 lines

It is OK to support it, but I do not know why do you want it?

The purpose of this command is dumping the memory when the guest is paniced. And then we can use crash/gdb(or other application) to investigate why the guest is paniced. So we should dump the whole memory.

That's one purpose, but not the only purpose. We shouldn't be artificially constraining things into requiring the entire memory region in order to use this command.

Quoted 8 lines

Libvirt provides virDomainMemoryPeek which currently wraps the 'memsave' and 'pmemsave' monitor commands, but these commands output raw memory. Your command is introducing a new memory format into ELF images, and if 'memsave' can already do a subset of memory, it also makes sense for 'dump' to do a subset when creating the ELF image. That is, if a management app every has a reason to access a subset of memory, then this reason exists whether the subset is raw or ELF formatted when presented to the management app.

OK. I know why you want it, and will support it. Please wait for some days.

Thanks
Wen Congyang

Quoted 12 lines

Meanwhile, on the libvirt side, the virDomainMemoryPeek API to management apps is constrained - it sends the data inline with the command, rather than on a side channel. Someday, I'd like to enhance libvirt to have a dump-to-stream command, and reuse the existing libvirt ability to stream large amounts of data on side channels, in order to let management apps directly and atomically query a subset of memory into a file with the desired formatting, rather than the current approach of constraining the management app to only query 64k at a time and to have to manually pause the guest if they need to atomically inspect more memory.

#14 lore raw

RFC PATCH v5 10/15 run dump at the background

In reply to Wen Congyang

The new monitor command dump may take long time to finish. So we need run it at the background.

---
 dump.c |  155 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 136 insertions(+), 19 deletions(-)
dump.c +136 -19
index 2951b8b..b2cccd8 100644
@@ -77,12 +77,20 @@ typedef struct DumpState {
     char *error;
     int fd;
     target_phys_addr_t memory_offset;
+    int64_t bandwidth;
+    RAMBlock *block;
+    ram_addr_t start;
+    target_phys_addr_t offset;
+    QEMUTimer *timer;
 } DumpState;
 
+#define DEFAULT_THROTTLE  (32 << 20)      /* Default dump speed throttling */
+
 static DumpState *dump_get_current(void)
 {
     static DumpState current_dump = {
         .state = DUMP_STATE_SETUP,
+        .bandwidth = DEFAULT_THROTTLE,
     };
 
     return &current_dump;
@@ -93,11 +101,19 @@ static int dump_cleanup(DumpState *s)
     int ret = 0;
 
     free_memory_mapping_list(&s->list);
+
     if (s->fd != -1) {
         close(s->fd);
         s->fd = -1;
     }
 
+    if (s->timer) {
+        qemu_del_timer(s->timer);
+        qemu_free_timer(s->timer);
+    }
+
+    qemu_resume_monitor();
+
     return ret;
 }
 
@@ -332,25 +348,40 @@ static int write_data(DumpState *s, void *buf, int length,
 }
 
 /* write the memroy to vmcore. 1 page per I/O. */
-static int write_memory(DumpState *s, RAMBlock *block,
-                        target_phys_addr_t *offset)
+static int write_memory(DumpState *s, RAMBlock *block, ram_addr_t start,
+                        target_phys_addr_t *offset, int64_t *size,
+                        int64_t deadline)
 {
     int i, ret;
+    int64_t writen_size = 0;
+    int64_t time;
 
-    for (i = 0; i < block->length / TARGET_PAGE_SIZE; i++) {
-        ret = write_data(s, block->host + i * TARGET_PAGE_SIZE,
+    for (i = 0; i < *size / TARGET_PAGE_SIZE; i++) {
+        ret = write_data(s, block->host + start + i * TARGET_PAGE_SIZE,
                          TARGET_PAGE_SIZE, offset);
         if (ret < 0) {
             return -1;
         }
+        writen_size += TARGET_PAGE_SIZE;
+        time = qemu_get_clock_ms(rt_clock);
+        if (time >= deadline) {
+            /* time out */
+            *size = writen_size;
+            return 1;
+        }
     }
 
-    if ((block->length % TARGET_PAGE_SIZE) != 0) {
-        ret = write_data(s, block->host + i * TARGET_PAGE_SIZE,
-                         block->length % TARGET_PAGE_SIZE, offset);
+    if ((*size % TARGET_PAGE_SIZE) != 0) {
+        ret = write_data(s, block->host + start + i * TARGET_PAGE_SIZE,
+                         *size % TARGET_PAGE_SIZE, offset);
         if (ret < 0) {
             return -1;
         }
+        time = qemu_get_clock_ms(rt_clock);
+        if (time >= deadline) {
+            /* time out */
+            return 1;
+        }
     }
 
     return 0;
@@ -379,6 +410,7 @@ static DumpState *dump_init(int fd, Error **errp)
     CPUState *env;
     DumpState *s = dump_get_current();
     int ret;
+    const char *msg = NULL;
 
     vm_stop(RUN_STATE_PAUSED);
     s->state = DUMP_STATE_SETUP;
@@ -387,6 +419,9 @@ static DumpState *dump_init(int fd, Error **errp)
         s->error = NULL;
     }
     s->fd = fd;
+    s->block = QLIST_FIRST(&ram_list.blocks);
+    s->start = 0;
+    s->timer = NULL;
 
     /*
      * get dump info: endian, class and architecture.
@@ -429,6 +464,9 @@ static DumpState *dump_init(int fd, Error **errp)
         s->phdr_num += s->list.num;
     }
 
+    msg = "terminal does not allow synchronous dumping, continuing detached\n";
+    qemu_suspend_monitor("%s", msg);
+
     return s;
 }
 
@@ -486,6 +524,7 @@ static int dump_begin(DumpState *s)
     }
 
     s->memory_offset = offset;
+    s->offset = offset;
     return 0;
 }
 
@@ -513,38 +552,116 @@ static int dump_completed(DumpState *s)
     return 0;
 }
 
-/* write all memory to vmcore */
-static int dump_iterate(DumpState *s)
+/*
+ * write memory to vmcore.
+ *
+ * this function has three return values:
+ *  -1 : there was one error
+ *   0 : We haven't finished, caller have to go again
+ *   1 : We have finished, we can go to complete phase
+ */
+static int dump_iterate(DumpState *s, int64_t deadline)
 {
-    RAMBlock *block;
-    target_phys_addr_t offset = s->memory_offset;
+    RAMBlock *block = s->block;
+    target_phys_addr_t offset = s->offset;
+    int64_t size, remain, writen_size;
+    int64_t total = s->bandwidth / 10;
     int ret;
 
-    /* write all memory to vmcore */
-    QLIST_FOREACH(block, &ram_list.blocks, next) {
-        ret = write_memory(s, block, &offset);
+    if ((block->length - s->start) >= total) {
+        size = total;
+    } else {
+        size = block->length - s->start;
+    }
+
+    ret = write_memory(s, block, s->start, &offset, &size, deadline);
+    if (ret < 0) {
+        return -1;
+    }
+
+    if (size == total || ret == 1) {
+        if ((size + s->start) == block->length) {
+            s->block = QLIST_NEXT(block, next);
+            s->start = 0;
+        } else {
+            s->start += size;
+        }
+        goto end;
+    }
+
+    while (size < total) {
+        block = QLIST_NEXT(block, next);
+        if (!block) {
+            /* we have finished */
+            return 1;
+        }
+
+        remain = total - size;
+        if (remain >= block->length) {
+            writen_size = block->length;
+        } else {
+            writen_size = remain;
+        }
+        ret = write_memory(s, block, 0, &offset, &writen_size, deadline);
         if (ret < 0) {
             return -1;
+        } else if (ret == 1) {
+            break;
         }
+        size += writen_size;
+    }
+    if (writen_size == block->length) {
+        s->block = QLIST_NEXT(block, next);
+        s->start = 0;
+    } else {
+        s->block = block;
+        s->start = writen_size;
+    }
+
+end:
+    s->offset = offset;
+    if (!s->block) {
+        /* we have finished */
+        return 1;
     }
 
-    return dump_completed(s);
+    return 0;
 }
 
-static int create_vmcore(DumpState *s)
+static void dump_rate_tick(void *opaque)
 {
+    DumpState *s = opaque;
+    int64_t begin, end;
     int ret;
 
-    ret = dump_begin(s);
+    begin = qemu_get_clock_ms(rt_clock);
+    ret = dump_iterate(s, begin + 100);
     if (ret < 0) {
-        return -1;
+        return;
+    } else if (ret == 1) {
+        dump_completed(s);
+        return;
     }
+    end = qemu_get_clock_ms(rt_clock);
+    if (end - begin >= 100) {
+        qemu_mod_timer(s->timer, end + 10);
+    } else {
+        qemu_mod_timer(s->timer, begin + 100);
+    }
+}
 
-    ret = dump_iterate(s);
+static int create_vmcore(DumpState *s)
+{
+    int ret;
+
+    ret = dump_begin(s);
     if (ret < 0) {
         return -1;
     }
 
+    s->timer = qemu_new_timer_ms(rt_clock, dump_rate_tick, s);
+    qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 100);
+
     return 0;
 }
 
Signature · 1 line
1.7.1
#15 lore raw

RFC PATCH v5 11/15 support detached dump

In reply to Wen Congyang

Let the user to choose whether to block other monitor command while dumping.

---
 dump.c           |   12 ++++++++----
 hmp-commands.hx  |    8 ++++----
 hmp.c            |    3 ++-
 qapi-schema.json |    2 +-
 qmp-commands.hx  |    7 ++++---
 5 files changed, 19 insertions(+), 13 deletions(-)
dump.c +8 -4
index b2cccd8..14c2642 100644
@@ -76,6 +76,7 @@ typedef struct DumpState {
     int state;
     char *error;
     int fd;
+    bool detach;
     target_phys_addr_t memory_offset;
     int64_t bandwidth;
     RAMBlock *block;
@@ -405,7 +406,7 @@ static target_phys_addr_t get_offset(target_phys_addr_t phys_addr,
     return -1;
 }
 
-static DumpState *dump_init(int fd, Error **errp)
+static DumpState *dump_init(int fd, bool detach, Error **errp)
 {
     CPUState *env;
     DumpState *s = dump_get_current();
@@ -422,6 +423,7 @@ static DumpState *dump_init(int fd, Error **errp)
     s->block = QLIST_FIRST(&ram_list.blocks);
     s->start = 0;
     s->timer = NULL;
+    s->detach = detach;
 
     /*
      * get dump info: endian, class and architecture.
@@ -465,7 +467,9 @@ static DumpState *dump_init(int fd, Error **errp)
     }
 
     msg = "terminal does not allow synchronous dumping, continuing detached\n";
-    qemu_suspend_monitor("%s", msg);
+    if (!detach && qemu_suspend_monitor("%s", msg) != 0) {
+        s->detach = true;
+    }
 
     return s;
 }
@@ -665,7 +669,7 @@ static int create_vmcore(DumpState *s)
     return 0;
 }
 
-void qmp_dump(const char *file, Error **errp)
+void qmp_dump(bool detach, const char *file, Error **errp)
 {
     const char *p;
     int fd = -1;
@@ -694,7 +698,7 @@ void qmp_dump(const char *file, Error **errp)
         return;
     }
 
-    s = dump_init(fd, errp);
+    s = dump_init(fd, detach, errp);
     if (!s) {
         return;
     }
hmp-commands.hx +4 -4
index c3615e3..4582875 100644
@@ -829,18 +829,18 @@ ETEXI
 
     {
         .name       = "dump",
-        .args_type  = "file:s",
+        .args_type  = "detach:-d,file:s",
         .params     = "file",
-        .help       = "dump to file",
+        .help       = "dump to file (using -d to not wait for completion)",
         .user_print = monitor_user_noop,
         .mhandler.cmd = hmp_dump,
     },
 
 
 STEXI
-@item dump @var{file}
+@item dump [-d] @var{file}
 @findex dump
-Dump to @var{file}.
+Dump to @var{file} (using -d to not wait for completion).
 ETEXI
 
     {
hmp.c +2 -1
index 11b4ce6..1ae8926 100644
@@ -685,8 +685,9 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
 void hmp_dump(Monitor *mon, const QDict *qdict)
 {
     Error *errp = NULL;
+    bool detach = qdict_get_try_bool(qdict, "detach", 0);
     const char *file = qdict_get_str(qdict, "file");
 
-    qmp_dump(file, &errp);
+    qmp_dump(detach, file, &errp);
     hmp_handle_error(mon, &errp);
 }
qapi-schema.json +1 -1
index 84c2c9a..f96268a 100644
@@ -1287,4 +1287,4 @@
 #
 # Since: 1.1
 ##
-{ 'command': 'dump', 'data': {'file': 'str'} }
+{ 'command': 'dump', 'data': {'detach' : 'bool', 'file': 'str'} }
qmp-commands.hx +4 -3
index fefdae2..59ddd98 100644
@@ -573,9 +573,9 @@ EQMP
 
     {
         .name       = "dump",
-        .args_type  = "file:s",
+        .args_type  = "detach:-d,file:s",
         .params     = "file",
-        .help       = "dump to file",
+        .help       = "dump to file (using -d to not wait for completion)",
         .user_print = monitor_user_noop,
         .mhandler.cmd_new = qmp_marshal_input_dump,
     },
@@ -588,7 +588,8 @@ Dump to file.
 
 Arguments:
 
-- "file": Destination file (json-string)
+- "detach": detached dumping (json-bool, optional)
+- "file":   Destination file (json-string)
 
 Example:
 
Signature · 1 line
1.7.1
#16 lore raw

RFC PATCH v5 12/15 support to cancel the current dumping

In reply to Wen Congyang

Add API to allow the user to cancel the current dumping.

---
 dump.c           |   13 +++++++++++++
 hmp-commands.hx  |   14 ++++++++++++++
 hmp.c            |    5 +++++
 hmp.h            |    1 +
 qapi-schema.json |   13 +++++++++++++
 qmp-commands.hx  |   21 +++++++++++++++++++++
 6 files changed, 67 insertions(+), 0 deletions(-)
dump.c +13 -0
index 14c2642..485e3f3 100644
@@ -709,3 +709,16 @@ void qmp_dump(bool detach, const char *file, Error **errp)
 
     return;
 }
+
+void qmp_dump_cancel(Error **errp)
+{
+    DumpState *s = dump_get_current();
+
+    if (s->state != DUMP_STATE_ACTIVE) {
+        return;
+    }
+
+    s->state = DUMP_STATE_CANCELLED;
+    dump_cleanup(s);
+    return;
+}
hmp-commands.hx +14 -0
index 4582875..57f5a20 100644
@@ -844,6 +844,20 @@ Dump to @var{file} (using -d to not wait for completion).
 ETEXI
 
     {
+        .name       = "dump_cancel",
+        .args_type  = "",
+        .params     = "",
+        .help       = "cancel the current VM dumping",
+        .mhandler.cmd = hmp_dump_cancel,
+    },
+
+STEXI
+@item dump_cancel
+@findex dump_cancel
+Cancel the current VM dumping.
+ETEXI
+
+    {
         .name       = "snapshot_blkdev",
         .args_type  = "device:B,snapshot-file:s?,format:s?",
         .params     = "device [new-image-file] [format]",
hmp.c +5 -0
index 1ae8926..bca411b 100644
@@ -691,3 +691,8 @@ void hmp_dump(Monitor *mon, const QDict *qdict)
     qmp_dump(detach, file, &errp);
     hmp_handle_error(mon, &errp);
 }
+
+void hmp_dump_cancel(Monitor *mon, const QDict *qdict)
+{
+    qmp_dump_cancel(NULL);
+}
hmp.h +1 -0
index 8d6a5d2..c0037b6 100644
@@ -50,5 +50,6 @@ void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
 void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
 void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
 void hmp_dump(Monitor *mon, const QDict *qdict);
+void hmp_dump_cancel(Monitor *mon, const QDict *qdict);
 
 #endif
qapi-schema.json +13 -0
index f96268a..bbd2011 100644
@@ -1288,3 +1288,16 @@
 # Since: 1.1
 ##
 { 'command': 'dump', 'data': {'detach' : 'bool', 'file': 'str'} }
+
+##
+# @dump_cancel
+#
+# Cancel the current executing dumping process.
+#
+# Returns: nothing on success
+#
+# Notes: This command succeeds even if there is no dumping process running.
+#
+# Since: 1.1
+##
+{ 'command': 'dump_cancel' }
qmp-commands.hx +21 -0
index 59ddd98..055072d 100644
@@ -599,6 +599,27 @@ Example:
 EQMP
 
     {
+        .name       = "dump_cancel",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_marshal_input_dump_cancel,
+    },
+
+SQMP
+dump_cancel
+
+
+Cancel the current dumping.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "dump_cancel" }
+<- { "return": {} }
+
+EQMP
+
+    {
         .name       = "netdev_add",
         .args_type  = "netdev:O",
         .params     = "[user|tap|socket],id=str[,prop=value][,...]",
Signature · 1 line
1.7.1
#17 lore raw

RFC PATCH v5 13/15 support to set dumping speed

In reply to Wen Congyang

Add API to allow the user to control dumping speed

---
 dump.c           |   12 ++++++++++++
 hmp-commands.hx  |   15 +++++++++++++++
 hmp.c            |    6 ++++++
 hmp.h            |    1 +
 qapi-schema.json |   15 +++++++++++++++
 qmp-commands.hx  |   22 ++++++++++++++++++++++
 6 files changed, 71 insertions(+), 0 deletions(-)
dump.c +12 -0
index 485e3f3..39dc892 100644
@@ -86,6 +86,7 @@ typedef struct DumpState {
 } DumpState;
 
 #define DEFAULT_THROTTLE  (32 << 20)      /* Default dump speed throttling */
+#define MIN_THROTTLE  (1 << 10)           /* Miniumum dump speed */
 
 static DumpState *dump_get_current(void)
 {
@@ -722,3 +723,14 @@ void qmp_dump_cancel(Error **errp)
     dump_cleanup(s);
     return;
 }
+
+void qmp_dump_set_speed(int64_t value, Error **errp)
+{
+    DumpState *s = dump_get_current();
+
+    if (value < MIN_THROTTLE) {
+        value = MIN_THROTTLE;
+    }
+    s->bandwidth = value;
+    return;
+}
hmp-commands.hx +15 -0
index 57f5a20..e71a174 100644
@@ -858,6 +858,21 @@ Cancel the current VM dumping.
 ETEXI
 
     {
+        .name       = "dump_set_speed",
+        .args_type  = "value:o",
+        .params     = "value",
+        .help       = "set maximum speed (in bytes) for dumping. "
+        "Defaults to MB if no size suffix is specified, ie. B/K/M/G/T",
+        .mhandler.cmd = hmp_dump_set_speed,
+    },
+
+STEXI
+@item dump_set_speed @var{value}
+@findex dump_set_speed
+Set maximum speed to @var{value} (in bytes) for dumping.
+ETEXI
+
+    {
         .name       = "snapshot_blkdev",
         .args_type  = "device:B,snapshot-file:s?,format:s?",
         .params     = "device [new-image-file] [format]",
hmp.c +6 -0
index bca411b..4c015f9 100644
@@ -696,3 +696,9 @@ void hmp_dump_cancel(Monitor *mon, const QDict *qdict)
 {
     qmp_dump_cancel(NULL);
 }
+
+void hmp_dump_set_speed(Monitor *mon, const QDict *qdict)
+{
+    int64_t value = qdict_get_int(qdict, "value");
+    qmp_dump_set_speed(value, NULL);
+}
hmp.h +1 -0
index c0037b6..2d71343 100644
@@ -51,5 +51,6 @@ void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
 void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
 void hmp_dump(Monitor *mon, const QDict *qdict);
 void hmp_dump_cancel(Monitor *mon, const QDict *qdict);
+void hmp_dump_set_speed(Monitor *mon, const QDict *qdict);
 
 #endif
qapi-schema.json +15 -0
index bbd2011..ec19631 100644
@@ -1301,3 +1301,18 @@
 # Since: 1.1
 ##
 { 'command': 'dump_cancel' }
+
+##
+# @dump_set_speed
+#
+# Set maximum speed for dumping.
+#
+# @value: maximum speed in bytes.
+#
+# Returns: nothing on success
+#
+# Notes: A value lesser than 1024 will be automatically round up to 1024.
+#
+# Since: 1.1
+##
+{ 'command': 'dump_set_speed', 'data': {'value': 'int'} }
qmp-commands.hx +22 -0
index 055072d..509273e 100644
@@ -620,6 +620,28 @@ Example:
 EQMP
 
     {
+        .name       = "dump_set_speed",
+        .args_type  = "value:o",
+        .mhandler.cmd_new = qmp_marshal_input_dump_set_speed,
+    },
+
+SQMP
+dump_set_speed
+
+Set maximum speed for dumping.
+
+Arguments:
+
+- "value": maximum speed, in bytes per second (json-int)
+
+Example:
+
+-> { "execute": "dump_set_speed", "arguments": { "value": 1024 } }
+<- { "return": {} }
+
+EQMP
+
+    {
         .name       = "netdev_add",
         .args_type  = "netdev:O",
         .params     = "[user|tap|socket],id=str[,prop=value][,...]",
Signature · 1 line
1.7.1
#18 lore raw

RFC PATCH v5 14/15 support to query dumping status

In reply to Wen Congyang

Add API to allow the user to query dumping status.

---
 dump.c           |   32 ++++++++++++++++++++++++++++++++
 hmp-commands.hx  |    2 ++
 hmp.c            |   17 +++++++++++++++++
 hmp.h            |    1 +
 monitor.c        |    7 +++++++
 qapi-schema.json |   26 ++++++++++++++++++++++++++
 qmp-commands.hx  |   47 +++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 132 insertions(+), 0 deletions(-)
dump.c +32 -0
index 39dc892..b681a2e 100644
@@ -734,3 +734,35 @@ void qmp_dump_set_speed(int64_t value, Error **errp)
     s->bandwidth = value;
     return;
 }
+
+DumpInfo *qmp_query_dump(Error **errp)
+{
+    DumpInfo *info = g_malloc0(sizeof(*info));
+    DumpState *s = dump_get_current();
+
+    switch (s->state) {
+    case DUMP_STATE_SETUP:
+        /* no migration has happened ever */
+        break;
+    case DUMP_STATE_ACTIVE:
+        info->has_status = true;
+        info->status = g_strdup("active");
+        break;
+    case DUMP_STATE_COMPLETED:
+        info->has_status = true;
+        info->status = g_strdup("completed");
+        break;
+    case DUMP_STATE_ERROR:
+        info->has_status = true;
+        info->status = g_strdup("failed");
+        info->has_error = true;
+        info->error = g_strdup(s->error);
+        break;
+    case DUMP_STATE_CANCELLED:
+        info->has_status = true;
+        info->status = g_strdup("cancelled");
+        break;
+    }
+
+    return info;
+}
hmp-commands.hx +2 -0
index e71a174..5796786 100644
@@ -1395,6 +1395,8 @@ show device tree
 show qdev device model list
 @item info roms
 show roms
+@item info dump
+show dumping status
 @end table
 ETEXI
 
hmp.c +17 -0
index 4c015f9..087dae4 100644
@@ -702,3 +702,20 @@ void hmp_dump_set_speed(Monitor *mon, const QDict *qdict)
     int64_t value = qdict_get_int(qdict, "value");
     qmp_dump_set_speed(value, NULL);
 }
+
+void hmp_info_dump(Monitor *mon)
+{
+    DumpInfo *info;
+
+    info = qmp_query_dump(NULL);
+
+    if (info->has_status) {
+        monitor_printf(mon, "Dumping status: %s\n", info->status);
+    }
+
+    if (info->has_error) {
+        monitor_printf(mon, "Dumping failed reason: %s\n", info->error);
+    }
+
+    qapi_free_DumpInfo(info);
+}
hmp.h +1 -0
index 2d71343..b91deb8 100644
@@ -52,5 +52,6 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
 void hmp_dump(Monitor *mon, const QDict *qdict);
 void hmp_dump_cancel(Monitor *mon, const QDict *qdict);
 void hmp_dump_set_speed(Monitor *mon, const QDict *qdict);
+void hmp_info_dump(Monitor *mon);
 
 #endif
monitor.c +7 -0
index 24d4371..47e64b6 100644
@@ -2744,6 +2744,13 @@ static mon_cmd_t info_cmds[] = {
         .mhandler.info = do_trace_print_events,
     },
     {
+        .name       = "dump",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show dumping status",
+        .mhandler.info = hmp_info_dump,
+    },
+    {
         .name       = NULL,
     },
 };
qapi-schema.json +26 -0
index ec19631..5edf3d1 100644
@@ -1316,3 +1316,29 @@
 # Since: 1.1
 ##
 { 'command': 'dump_set_speed', 'data': {'value': 'int'} }
+
+##
+# @DumpInfo
+#
+# Information about current migration process.
+#
+# @status: #optional string describing the current dumping status.
+#          As of 1,1 this can be 'active', 'completed', 'failed' or
+#          'cancelled'. If this field is not returned, no migration process
+#          has been initiated
+#
+# Since: 1.1
+##
+{ 'type': 'DumpInfo',
+  'data': {'*status': 'str', '*error': 'str'} }
+
+##
+# @query-dump
+#
+# Returns information about current dumping process.
+#
+# Returns: @DumpInfo
+#
+# Since: 1.1
+##
+{ 'command': 'query-dump', 'returns': 'DumpInfo' }
qmp-commands.hx +47 -0

53 lines not on this page · raw message

Signature · 1 line
1.7.1
#19 lore raw
In reply to Wen Congyang
Quoted 8 lines · Wen Congyang

Hi, all

'virsh dump' can not work when host pci device is used by guest. We have discussed this issue here:
http://lists.nongnu.org/archive/html/qemu-devel/2011-10/msg00736.html

We have determined to introduce a new command dump to dump memory. The core file's format can be elf.

do you pause the guest when the dump happen, or you can somehow let it continue to run?

would be wonderful if you can do the latter, since dumping a guest memory can take a lot of time.

thanks,
Jun

#20 lore raw

At 01/19/2012 11:32 AM, Jun Koi Wrote:

Quoted 7 lines · Wen Congyang
Quoted 6 lines

Hi, all

'virsh dump' can not work when host pci device is used by guest. We have discussed this issue here:
http://lists.nongnu.org/archive/html/qemu-devel/2011-10/msg00736.html

Quoted 5 lines
Quoted 2 lines

We have determined to introduce a new command dump to dump memory. The core file's format can be elf.

do you pause the guest when the dump happen, or you can somehow let it continue to run?

I pause the guest when the dump happens.

Quoted 3 lines

would be wonderful if you can do the latter, since dumping a guest memory can take a lot of time.

Yes, it may tak a lot of time. But we dump a guest memory when the guest panics, and there is no need to continue to run the guest.

Thanks
Wen Congyang

Quoted 4 lines

thanks,
Jun

#21 lore raw
Quoted 14 lines · Wen Congyang

At 01/19/2012 11:32 AM, Jun Koi Wrote:

Quoted 7 lines · Wen Congyang
Quoted 6 lines

Hi, all

'virsh dump' can not work when host pci device is used by guest. We have discussed this issue here:
http://lists.nongnu.org/archive/html/qemu-devel/2011-10/msg00736.html

Quoted 5 lines
Quoted 2 lines

We have determined to introduce a new command dump to dump memory. The core file's format can be elf.

do you pause the guest when the dump happen, or you can somehow let it continue to run?

Quoted 8 lines

I pause the guest when the dump happens.

Quoted 3 lines

would be wonderful if you can do the latter, since dumping a guest memory can take a lot of time.

Yes, it may tak a lot of time. But we dump a guest memory when the guest panics, and there is no need to continue to run the guest.

Would it be possible to have both a dump from a certain point in time and still allow the guest to run unpaused?

I'm thinking something along the lines of pausing the guest, setting up control structures, then calling fork(). The parent can then unpause, and use the control structures to communicate the memory state from the child back out the monitor. Meanwhile, the guest has a copy-on-write clone of the entire memory state, so as long as the control structures guarantee that the child will not accept any monitor commands and not resume the guest, then the child process can be used to stream the memory contents as they were at the time of the dump command back over the control structure back to the parent process. I will admit, however, that following the fork(), you would be limited to async-signal-safe functions, so it may be a rather difficult task to design.

Signature · 2 lines
Eric Blake   [email protected]    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#22 lore raw

At 01/20/2012 12:34 AM, Eric Blake Wrote:

Quoted 27 lines · Wen Congyang
Quoted 14 lines

At 01/19/2012 11:32 AM, Jun Koi Wrote:

Quoted 7 lines · Wen Congyang
Quoted 6 lines

Hi, all

'virsh dump' can not work when host pci device is used by guest. We have discussed this issue here:
http://lists.nongnu.org/archive/html/qemu-devel/2011-10/msg00736.html

Quoted 5 lines
Quoted 2 lines

We have determined to introduce a new command dump to dump memory. The core file's format can be elf.

do you pause the guest when the dump happen, or you can somehow let it continue to run?

Quoted 8 lines

I pause the guest when the dump happens.

Quoted 3 lines

would be wonderful if you can do the latter, since dumping a guest memory can take a lot of time.

Yes, it may tak a lot of time. But we dump a guest memory when the guest panics, and there is no need to continue to run the guest.

Would it be possible to have both a dump from a certain point in time and still allow the guest to run unpaused?

Quoted 12 lines

I'm thinking something along the lines of pausing the guest, setting up control structures, then calling fork(). The parent can then unpause, and use the control structures to communicate the memory state from the child back out the monitor. Meanwhile, the guest has a copy-on-write clone of the entire memory state, so as long as the control structures guarantee that the child will not accept any monitor commands and not resume the guest, then the child process can be used to stream the memory contents as they were at the time of the dump command back over the control structure back to the parent process. I will admit, however, that following the fork(), you would be limited to async-signal-safe functions, so it may be a rather difficult task to design.

I do not understand this section. Do you say the reason of allowing the guest to run unpaused? Or do you say the way to do it?

Thanks
Wen Congyang

#23 lore raw
Quoted 19 lines · Wen Congyang
Quoted 6 lines
Quoted 2 lines

Yes, it may tak a lot of time. But we dump a guest memory when the guest panics, and there is no need to continue to run the guest.

Would it be possible to have both a dump from a certain point in time and still allow the guest to run unpaused?

Quoted 12 lines

I'm thinking something along the lines of pausing the guest, setting up control structures, then calling fork(). The parent can then unpause, and use the control structures to communicate the memory state from the child back out the monitor. Meanwhile, the guest has a copy-on-write clone of the entire memory state, so as long as the control structures guarantee that the child will not accept any monitor commands and not resume the guest, then the child process can be used to stream the memory contents as they were at the time of the dump command back over the control structure back to the parent process. I will admit, however, that following the fork(), you would be limited to async-signal-safe functions, so it may be a rather difficult task to design.

Quoted 2 lines

I do not understand this section. Do you say the reason of allowing the guest to run unpaused? Or do you say the way to do it?

Among other reasons for supporting a guest that runs in parallel with a memory dump, I'm envisioning a thin-provisioning setup. Right now, you have to get a common off-line base disk image state, then each cloned guest has to boot from scratch as a delta from that base state. But it would be a lot faster if you could get a common on-line VM state, and start cloned guests from the same memory state as the baseline. Taking it further, I can envision a forensic-style application, that runs what-if scenarios, by running two similar guests side by side and seeing what happens when only one of the two guests has a particular tweak made. Or suppose you have a production server, and want to apply a patch, but want to make sure the patch will work before committing to it
- the obvious way is to apply the patch to a temporary cloned VM. But because it is a production server, you can't afford to wait for a long downtime with the guest paused just to capture the machine state for cloning a temporary VM for testing out the patch.

Basically, in all of these scenarios, my idea is that it should be easy (well, at least easier than it currently is), to create a new guest based on the memory state of an existing guest, all without impacting the existing guest. And to do that, it would be nice to be able to reliably dump the state of memory of a guest at a given point of time, all while continuing to let the original guest run past that point in time.

But whether this has to be done right away, or whether it even fits in with your 'dump' command vs. needing a command of its own, I don't know.

Signature · 2 lines
Eric Blake   [email protected]    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#24 lore raw

At 01/31/2012 01:38 AM, Eric Blake Wrote:

Quoted 46 lines · Wen Congyang
Quoted 19 lines
Quoted 6 lines
Quoted 2 lines

Yes, it may tak a lot of time. But we dump a guest memory when the guest panics, and there is no need to continue to run the guest.

Would it be possible to have both a dump from a certain point in time and still allow the guest to run unpaused?

Quoted 12 lines

I'm thinking something along the lines of pausing the guest, setting up control structures, then calling fork(). The parent can then unpause, and use the control structures to communicate the memory state from the child back out the monitor. Meanwhile, the guest has a copy-on-write clone of the entire memory state, so as long as the control structures guarantee that the child will not accept any monitor commands and not resume the guest, then the child process can be used to stream the memory contents as they were at the time of the dump command back over the control structure back to the parent process. I will admit, however, that following the fork(), you would be limited to async-signal-safe functions, so it may be a rather difficult task to design.

Quoted 2 lines

I do not understand this section. Do you say the reason of allowing the guest to run unpaused? Or do you say the way to do it?

Among other reasons for supporting a guest that runs in parallel with a memory dump, I'm envisioning a thin-provisioning setup. Right now, you have to get a common off-line base disk image state, then each cloned guest has to boot from scratch as a delta from that base state. But it would be a lot faster if you could get a common on-line VM state, and start cloned guests from the same memory state as the baseline. Taking it further, I can envision a forensic-style application, that runs what-if scenarios, by running two similar guests side by side and seeing what happens when only one of the two guests has a particular tweak made. Or suppose you have a production server, and want to apply a patch, but want to make sure the patch will work before committing to it
- the obvious way is to apply the patch to a temporary cloned VM. But because it is a production server, you can't afford to wait for a long downtime with the guest paused just to capture the machine state for cloning a temporary VM for testing out the patch.

Basically, in all of these scenarios, my idea is that it should be easy (well, at least easier than it currently is), to create a new guest based on the memory state of an existing guest, all without impacting the existing guest. And to do that, it would be nice to be able to reliably dump the state of memory of a guest at a given point of time, all while continuing to let the original guest run past that point in time.

Quoted 3 lines

But whether this has to be done right away, or whether it even fits in with your 'dump' command vs. needing a command of its own, I don't know.

Thanks for your explantion. I know what you want, and I think it needs a command of its own. Because the 'dump' command only provides memory, and it does not include device's state, CPU's state, and so on. If the guest uses host devices, and i think it should not be cloned. If you want to get all the state of the guest, you may need a command like 'mirgate' command.

Thanks
Wen Congyang

#25 lore raw

RFC PATCH v5 15/15 auto cancel dumping after vm state is changed to run

In reply to Wen Congyang

The command dump does not support to dump while vm is running. If the user resume the vm, we should auto cancel dumping and set the status to failed.

---
 dump.c |   19 +++++++++++++++++++
 vl.c   |    5 +++--
 2 files changed, 22 insertions(+), 2 deletions(-)
dump.c +19 -0
index b681a2e..b3847a0 100644
@@ -83,6 +83,7 @@ typedef struct DumpState {
     ram_addr_t start;
     target_phys_addr_t offset;
     QEMUTimer *timer;
+    VMChangeStateEntry *handler;
 } DumpState;
 
 #define DEFAULT_THROTTLE  (32 << 20)      /* Default dump speed throttling */
@@ -114,6 +115,11 @@ static int dump_cleanup(DumpState *s)
         qemu_free_timer(s->timer);
     }
 
+    if (s->handler) {
+        qemu_del_vm_change_state_handler(s->handler);
+        s->handler = NULL;
+    }
+
     qemu_resume_monitor();
 
     return ret;
@@ -670,6 +676,17 @@ static int create_vmcore(DumpState *s)
     return 0;
 }
 
+static void dump_vm_state_change(void *opaque, int running, RunState state)
+{
+    DumpState *s = opaque;
+
+    if (running) {
+        qmp_dump_cancel(NULL);
+        s->state = DUMP_STATE_ERROR;
+        s->error = g_strdup("vm state is changed to run\n");
+    }
+}
+
 void qmp_dump(bool detach, const char *file, Error **errp)
 {
     const char *p;
@@ -704,6 +721,8 @@ void qmp_dump(bool detach, const char *file, Error **errp)
         return;
     }
 
+    s->handler = qemu_add_vm_change_state_handler(dump_vm_state_change, s);
+
     if (create_vmcore(s) < 0) {
         error_set(errp, QERR_IO_ERROR);
     }
vl.c +3 -2
index ba55b35..2d9df0f 100644
@@ -1247,11 +1247,12 @@ void qemu_del_vm_change_state_handler(VMChangeStateEntry *e)
 
 void vm_state_notify(int running, RunState state)
 {
-    VMChangeStateEntry *e;
+    VMChangeStateEntry *e, *next;
 
     trace_vm_state_notify(running, state);
 
-    for (e = vm_change_state_head.lh_first; e; e = e->entries.le_next) {
+    /* e->cb() may remove itself */
+    QLIST_FOREACH_SAFE(e, &vm_change_state_head, entries, next) {
         e->cb(e->opaque, running, state);
     }
 }
Signature · 1 line
1.7.1