Hi Thomas,
On 9/26/25 05:44, Thomas Huth wrote:
On 26/09/2025 07.15, Gustavo Romero wrote:
This commit removes Avocado as a dependency for running the
reverse_debugging test.
The main benefit, beyond eliminating an extra dependency, is that there
is no longer any need to handle GDB packets manually. This removes the
need for ad-hoc functions dealing with endianness and arch-specific
register numbers, making the test easier to read. The timeout variable
is also removed, since Meson now manages timeouts automatically.
reverse_debugging now uses the pygdbmi module to interact with GDB, if
it's available in the test environment, otherwise the test is skipped.
GDB is detect via the QEMU_TEST_GDB env. variable.
This commit also significantly improves the output for the test and
now prints all the GDB commands used in sequence. It also adds
some clarifications to existing comments, for example, clarifying that
once the replay-break is reached, a SIGINT is captured in GDB.
reverse_debugging is kept "skipped" for aarch64, ppc64, and x86_64, so
won't run unless QEMU_TEST_FLAKY_TESTS=1 is set in the test environment,
before running 'make check-functional' or 'meson test [...]'.
Signed-off-by: Gustavo Romero <[email protected]>
---
...
@@ -53,49 +55,11 @@ def run_vm(self, record, shift, args, replay_path,
image_path, port):
vm.launch()
return vm
- @staticmethod
- def get_reg_le(g, reg):
- res = g.cmd(b'p%x' % reg)
- num = 0
- for i in range(len(res))[-2::-2]:
- num = 0x100 * num + int(res[i:i + 2], 16)
- return num
-
- @staticmethod
- def get_reg_be(g, reg):
- res = g.cmd(b'p%x' % reg)
- return int(res, 16)
-
- def get_reg(self, g, reg):
- # value may be encoded in BE or LE order
- if self.endian_is_le:
- return self.get_reg_le(g, reg)
- else:
- return self.get_reg_be(g, reg)
-
- def get_pc(self, g):
- return self.get_reg(g, self.REG_PC)
-
- def check_pc(self, g, addr):
- pc = self.get_pc(g)
- if pc != addr:
- self.fail('Invalid PC (read %x instead of %x)' % (pc, addr))
I think it would make sense to keep wrapper functions get_pc() and check_pc(), since that
functionality is still used in a bunch of places (e.g. "gdb.cli("print
$pc").get_addr()" is used a couple of times).
Yeah, I considered that, but I really think that using the
wrapper functions doesn't add to this test (as in the original test).
First because I like reading the test code and easily map it to
its output and, second, the original test that used check_pc() was
actually failing with this "Invalid PC ... " message in all the cases,
which is not informative. In my version, because I check PC in place,
I also fail with a specific message for each case, like "Forward stepping
failed¨,
"Reverse stepping failed", "reverse-continue", etc.
If you don't mind I'd like to keep the test this way.
@@ -124,68 +88,99 @@ def reverse_debugging(self, shift=7, args=None):
with Ports() as ports:
port = ports.find_free_port()
vm = self.run_vm(False, shift, args, replay_path, image_path,
port)
- logger.info('connecting to gdbstub')
- g = gdb.GDBRemote('127.0.0.1', port, False, False)
- g.connect()
- r = g.cmd(b'qSupported')
- if b'qXfer:features:read+' in r:
- g.cmd(b'qXfer:features:read:target.xml:0,ffb')
- if b'ReverseStep+' not in r:
+
+ try:
+ logger.info('Connecting to gdbstub...')
+ self.reverse_debugging_run(vm, port, last_icount)
+ logger.info('Test passed.')
+ except GdbTimeoutError:
+ self.fail("Connection to gdbstub timeouted...")
I'm not a native English speaker, but I think "timeout" is not a verb. So maybe better:
"Timeout while connecting to gdbstub" or something similar?
+ @skipIfMissingEnv("QEMU_TEST_GDB")
Somehow this SKIP is always triggered on my system, even if I correctly pointed
"configure" to the right GDB with the "--gdb" option ... not sure why this
happens, but I'll try to find out...
hmm maybe you could play with the env. vars by running:
$ ./pyvenv/bin/meson test --verbose --no-rebuild -t 1 --setup thorough
--suite func-thorough func-aarch64-reverse_debug
which has "--verbose" so you can see and use the "raw" command containing the
env. variables, e.g.:
gromero@gromero0:/mnt/git/qemu_/build$ G_TEST_SLOW=1
QEMU_TEST_QEMU_IMG=/mnt/git/qemu_/build/qemu-img
ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1
MESON_TEST_ITERATION=1 RUST_BACKTRACE=1 MALLOC_PERTURB_=186 SPEED=thorough
LD_LIBRARY_PATH=/mnt/git/qemu_/build/contrib/plugins:/mnt/git/qemu_/build/tests/tcg/plugins
MSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1
QEMU_TEST_QEMU_BINARY=/mnt/git/qemu_/build/qemu-system-aarch64
QEMU_BUILD_ROOT=/mnt/git/qemu_/build
UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1
PYTHONPATH=/mnt/git/qemu_/python:/mnt/git/qemu_/tests/functional
/mnt/git/qemu_/build/pyvenv/bin/python3
/mnt/git/qemu_/tests/functional/aarch64/test_reverse_debug.py
TAP version 13
ok 1 test_reverse_debug.ReverseDebugging_AArch64.test_aarch64_virt # SKIP
Missing env var(s): QEMU_TEST_GDB
1..1
gromero@gromero0:/mnt/git/qemu_/build$ echo $QEMU_TEST_GDB
gromero@gromero0:/mnt/git/qemu_/build$ which gdb-multiarch
/usr/bin/gdb-multiarch
gromero@gromero0:/mnt/git/qemu_/build$ export
QEMU_TEST_GDB=/usr/bin/gdb-multiarch
gromero@gromero0:/mnt/git/qemu_/build$ G_TEST_SLOW=1
QEMU_TEST_QEMU_IMG=/mnt/git/qemu_/build/qemu-img
ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1
MESON_TEST_ITERATION=1 RUST_BACKTRACE=1 MALLOC_PERTURB_=186 SPEED=thorough
LD_LIBRARY_PATH=/mnt/git/qemu_/build/contrib/plugins:/mnt/git/qemu_/build/tests/tcg/plugins
MSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1
QEMU_TEST_QEMU_BINARY=/mnt/git/qemu_/build/qemu-system-aarch64
QEMU_BUILD_ROOT=/mnt/git/qemu_/build
UBSAN_OPTIONS=halt_on_error=1:abort_on_error=1:print_summary=1:print_stacktrace=1
PYTHONPATH=/mnt/git/qemu_/python:/mnt/git/qemu_/tests/functional
/mnt/git/qemu_/build/pyvenv/bin/python3
/mnt/git/qemu_/tests/functional/aarch64/test_reverse_debug.py
TAP version 13
# $ set debug remote 1
# $ target remote localhost:64512
# Remote debugging using localhost:64512
# 0x0000000040000000 in ?? ()
# $ set debug remote 0
# $ print $pc
# $1 = (void (*)()) 0x40000000
# $ stepi
# 0x0000000040000004 in ?? ()
# $ print $pc
# $2 = (void (*)()) 0x40000004
# $ stepi
# 0x0000000040000008 in ?? ()
[...]
Let me know what you find :) Thanks!
Cheers,
Gustavo