From: "Marc-André Lureau" <marcandre.lureau@redhat.com>
To: qemu-devel@nongnu.org
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Subject: [GIT PULL 5/8] tests/qtest: add dump-guest-memory test
Date: Tue, 30 Jun 2026 19:13:20 +0400 [thread overview]
Message-ID: <20260630-dump-v1-5-7df3087dffc9@redhat.com> (raw)
In-Reply-To: <20260630-dump-v1-0-7df3087dffc9@redhat.com>
From: "Denis V. Lunev" <den@openvz.org>
There is currently almost no coverage for the dump-guest-memory QMP
command beyond the test-hmp smoke test. Add a qtest that runs on a bare
machine (no guest OS) and checks:
- query-dump-guest-memory-capability always advertises 'elf';
- an ELF dump is produced and starts with the ELF magic;
- a non-raw kdump dump is emitted in makedumpfile flattened format;
- a raw kdump dump starts with the on-disk KDUMP header;
- an unknown protocol is rejected without killing the VM, and dumping
still works afterwards.
Signed-off-by: Denis V. Lunev <den@openvz.org>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20260619101834.228432-6-den@openvz.org>
---
MAINTAINERS | 1 +
tests/qtest/dump-test.c | 188 ++++++++++++++++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 1 +
3 files changed, 190 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 2ecfd7159db..7ff0ae09cdc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3309,6 +3309,7 @@ F: scripts/dump-guest-memory.py
F: stubs/dump.c
F: docs/specs/vmcoreinfo.rst
F: tests/qtest/vmcoreinfo-test.c
+F: tests/qtest/dump-test.c
Error reporting
M: Markus Armbruster <armbru@redhat.com>
diff --git a/tests/qtest/dump-test.c b/tests/qtest/dump-test.c
new file mode 100644
index 00000000000..eb188c9ccb8
--- /dev/null
+++ b/tests/qtest/dump-test.c
@@ -0,0 +1,188 @@
+/*
+ * QTest testcase for dump-guest-memory
+ *
+ * Generic coverage for the dump-guest-memory QMP command and the
+ * query-dump-guest-memory-capability reporting, exercised on a bare
+ * machine (no guest OS required).
+ *
+ * Copyright (c) 2026 Virtuozzo International GmbH
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qobject/qdict.h"
+#include "qobject/qlist.h"
+#include "qobject/qstring.h"
+#include "qemu/bswap.h"
+#include "elf.h"
+
+#define KDUMP_RAW_MAGIC "KDUMP "
+#define KDUMP_FLAT_MAGIC "makedumpfile"
+
+static QTestState *dump_test_start(void)
+{
+ return qtest_initf("-machine q35 -accel qtest -m 16");
+}
+
+static void assert_file_magic(const char *path, const char *magic, size_t len)
+{
+ g_autofree char *buf = g_malloc0(len);
+ FILE *f = fopen(path, "rb");
+
+ g_assert_nonnull(f);
+ g_assert_cmpint(fread(buf, 1, len, f), ==, len);
+ fclose(f);
+ g_assert_cmpint(memcmp(buf, magic, len), ==, 0);
+}
+
+/* validate that the file is a sane x86 ELF core, not just the leading magic */
+static void assert_valid_elf_core(const char *path)
+{
+ unsigned char e[64];
+ FILE *f = fopen(path, "rb");
+ uint16_t e_type, e_machine, e_phnum;
+
+ g_assert_nonnull(f);
+ g_assert_cmpint(fread(e, 1, sizeof(e), f), ==, sizeof(e));
+ fclose(f);
+
+ g_assert_cmpint(memcmp(e, ELFMAG, SELFMAG), ==, 0);
+
+ /* e_type and e_machine sit at the same offset for ELF32 and ELF64 */
+ e_type = lduw_le_p(e + 16);
+ e_machine = lduw_le_p(e + 18);
+ g_assert_cmpint(e_type, ==, ET_CORE);
+ g_assert(e_machine == EM_386 || e_machine == EM_X86_64);
+
+ /* e_phnum lives at a class-dependent offset */
+ if (e[EI_CLASS] == ELFCLASS64) {
+ e_phnum = lduw_le_p(e + 56);
+ } else {
+ e_phnum = lduw_le_p(e + 44);
+ }
+ g_assert_cmpint(e_phnum, >, 0);
+}
+
+/* dump-guest-memory to a fresh temp file; returns the path (caller frees) */
+static char *do_dump(QTestState *qts, const char *format)
+{
+ g_autofree char *tmp = NULL;
+ g_autofree char *proto = NULL;
+ GError *err = NULL;
+ int fd;
+
+ fd = g_file_open_tmp("dump-test-XXXXXX", &tmp, &err);
+ g_assert_no_error(err);
+ close(fd);
+ proto = g_strdup_printf("file:%s", tmp);
+
+ if (format) {
+ qtest_qmp_assert_success(qts,
+ "{ 'execute': 'dump-guest-memory',"
+ " 'arguments': { 'paging': false, 'protocol': %s,"
+ " 'format': %s } }", proto, format);
+ } else {
+ qtest_qmp_assert_success(qts,
+ "{ 'execute': 'dump-guest-memory',"
+ " 'arguments': { 'paging': false, 'protocol': %s } }", proto);
+ }
+
+ return g_steal_pointer(&tmp);
+}
+
+/* query-dump-guest-memory-capability must always advertise at least 'elf' */
+static void test_query_capability(void)
+{
+ QTestState *qts = dump_test_start();
+ QDict *resp, *ret;
+ QList *formats;
+ QListEntry *e;
+ bool has_elf = false;
+
+ resp = qtest_qmp(qts,
+ "{ 'execute': 'query-dump-guest-memory-capability' }");
+ g_assert(qdict_haskey(resp, "return"));
+ ret = qdict_get_qdict(resp, "return");
+ formats = qdict_get_qlist(ret, "formats");
+ g_assert_nonnull(formats);
+
+ QLIST_FOREACH_ENTRY(formats, e) {
+ QString *qs = qobject_to(QString, qlist_entry_obj(e));
+
+ if (g_str_equal(qstring_get_str(qs), "elf")) {
+ has_elf = true;
+ }
+ }
+ g_assert_true(has_elf);
+
+ qobject_unref(resp);
+ qtest_quit(qts);
+}
+
+static void test_dump_elf(void)
+{
+ QTestState *qts = dump_test_start();
+ g_autofree char *path = do_dump(qts, NULL);
+
+ assert_valid_elf_core(path);
+ unlink(path);
+ qtest_quit(qts);
+}
+
+/* non-raw kdump is emitted in makedumpfile flattened format */
+static void test_dump_kdump_zlib(void)
+{
+ QTestState *qts = dump_test_start();
+ g_autofree char *path = do_dump(qts, "kdump-zlib");
+
+ assert_file_magic(path, KDUMP_FLAT_MAGIC, strlen(KDUMP_FLAT_MAGIC));
+ unlink(path);
+ qtest_quit(qts);
+}
+
+/* raw kdump starts with the on-disk KDUMP header */
+static void test_dump_kdump_raw_zlib(void)
+{
+ QTestState *qts = dump_test_start();
+ g_autofree char *path = do_dump(qts, "kdump-raw-zlib");
+
+ assert_file_magic(path, KDUMP_RAW_MAGIC, strlen(KDUMP_RAW_MAGIC));
+ unlink(path);
+ qtest_quit(qts);
+}
+
+/* an unknown protocol must be rejected, not crash the VM */
+static void test_dump_invalid_protocol(void)
+{
+ QTestState *qts = dump_test_start();
+ g_autofree char *path = NULL;
+ QDict *resp;
+
+ resp = qtest_qmp(qts,
+ "{ 'execute': 'dump-guest-memory',"
+ " 'arguments': { 'paging': false, 'protocol': 'bogus:/x' } }");
+ g_assert(qdict_haskey(resp, "error"));
+ qobject_unref(resp);
+
+ /* VM is still alive and dumping still works afterwards */
+ path = do_dump(qts, NULL);
+ assert_valid_elf_core(path);
+ unlink(path);
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/dump/query-capability", test_query_capability);
+ qtest_add_func("/dump/elf", test_dump_elf);
+ qtest_add_func("/dump/kdump-zlib", test_dump_kdump_zlib);
+ qtest_add_func("/dump/kdump-raw-zlib", test_dump_kdump_raw_zlib);
+ qtest_add_func("/dump/invalid-protocol", test_dump_invalid_protocol);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 822e0bd2869..84062028f9c 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -59,6 +59,7 @@ qtests_i386 = \
(config_all_devices.has_key('CONFIG_FDC_ISA') ? ['fdc-test'] : []) + \
(config_all_devices.has_key('CONFIG_I440FX') ? ['fw_cfg-test'] : []) + \
(config_all_devices.has_key('CONFIG_FW_CFG_DMA') ? ['vmcoreinfo-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_Q35') ? ['dump-test'] : []) + \
(config_all_devices.has_key('CONFIG_I440FX') ? ['i440fx-test'] : []) + \
(config_all_devices.has_key('CONFIG_I440FX') ? ['ide-test'] : []) + \
(config_all_devices.has_key('CONFIG_I440FX') ? ['numa-test'] : []) + \
--
2.54.0
next prev parent reply other threads:[~2026-06-30 15:15 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-30 15:13 [GIT PULL 0/8] Dump patches for 2026-06-30 Marc-André Lureau
2026-06-30 15:13 ` [GIT PULL 1/8] migration: add migration_guest_ram_loading() helper Marc-André Lureau
2026-06-30 15:13 ` [GIT PULL 2/8] dump: refuse dump-guest-memory while guest RAM is being migrated Marc-André Lureau
2026-06-30 15:13 ` [GIT PULL 3/8] system/cpus: refuse memsave/pmemsave " Marc-André Lureau
2026-06-30 15:13 ` [GIT PULL 4/8] dump: make win_dump_available() check vmcoreinfo for a Windows dump header Marc-André Lureau
2026-06-30 15:13 ` Marc-André Lureau [this message]
2026-06-30 15:13 ` [GIT PULL 6/8] tests/qtest/dump: reject win-dmp without vmcoreinfo Marc-André Lureau
2026-06-30 15:13 ` [GIT PULL 7/8] tests/qtest/dump: cover win-dmp availability via vmcoreinfo Marc-André Lureau
2026-06-30 15:13 ` [GIT PULL 8/8] dump: fix misleading VMCOREINFO phys_base parse error Marc-André Lureau
2026-07-02 18:49 ` [GIT PULL 0/8] Dump patches for 2026-06-30 Stefan Hajnoczi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260630-dump-v1-5-7df3087dffc9@redhat.com \
--to=marcandre.lureau@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.