qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Alex Bennée" <alex.bennee@linaro.org>
To: Pavel Dovgalyuk <pavel.dovgalyuk@ispras.ru>
Cc: kwolf@redhat.com, wrampazz@redhat.com, ehabkost@redhat.com,
	mtosatti@redhat.com, qemu-devel@nongnu.org, armbru@redhat.com,
	stefanha@redhat.com, crosa@redhat.com, pbonzini@redhat.com,
	mreitz@redhat.com, philmd@redhat.com, zhiwei_liu@c-sky.com,
	rth@twiddle.net
Subject: Re: [PATCH v3 15/15] tests/acceptance: add reverse debugging test
Date: Tue, 08 Sep 2020 14:01:24 +0100	[thread overview]
Message-ID: <87tuw8pgm3.fsf@linaro.org> (raw)
In-Reply-To: <159903463379.28509.561479052940546124.stgit@pasha-ThinkPad-X280>


Pavel Dovgalyuk <pavel.dovgalyuk@ispras.ru> writes:

> From: Pavel Dovgalyuk <Pavel.Dovgaluk@gmail.com>
>
> This is a test for GDB reverse debugging commands: reverse step and reverse continue.
> Every test in this suite consists of two phases: record and replay.
> Recording saves the execution of some instructions and makes an initial
> VM snapshot to allow reverse execution.
> Replay saves the order of the first instructions and then checks that they
> are executed backwards in the correct order.
> After that the execution is replayed to the end, and reverse continue
> command is checked by setting several breakpoints, and asserting
> that the execution is stopped at the last of them.
>
> Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgalyuk@ispras.ru>
> ---
>  MAINTAINERS                           |    1 
>  tests/acceptance/reverse_debugging.py |  203 +++++++++++++++++++++++++++++++++
>  2 files changed, 204 insertions(+)
>  create mode 100644 tests/acceptance/reverse_debugging.py
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e49af700c9..76450f1bdf 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2644,6 +2644,7 @@ F: include/sysemu/replay.h
>  F: docs/replay.txt
>  F: stubs/replay.c
>  F: tests/acceptance/replay_kernel.py
> +F: tests/acceptance/reverse_debugging.py
>  F: qapi/replay.json
>  
>  IOVA Tree
> diff --git a/tests/acceptance/reverse_debugging.py b/tests/acceptance/reverse_debugging.py
> new file mode 100644
> index 0000000000..dda42e1c1a
> --- /dev/null
> +++ b/tests/acceptance/reverse_debugging.py
> @@ -0,0 +1,203 @@
> +# Reverse debugging test
> +#
> +# Copyright (c) 2020 ISP RAS
> +#
> +# Author:
> +#  Pavel Dovgalyuk <Pavel.Dovgalyuk@ispras.ru>
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2 or
> +# later.  See the COPYING file in the top-level directory.
> +import os
> +import logging
> +
> +from avocado_qemu import BUILD_DIR
> +from avocado.utils import gdb
> +from avocado.utils import process
> +from avocado.utils.path import find_command
> +from boot_linux_console import LinuxKernelTest
> +
> +class ReverseDebugging(LinuxKernelTest):
> +    """
> +    Test GDB reverse debugging commands: reverse step and reverse continue.
> +    Recording saves the execution of some instructions and makes an initial
> +    VM snapshot to allow reverse execution.
> +    Replay saves the order of the first instructions and then checks that they
> +    are executed backwards in the correct order.
> +    After that the execution is replayed to the end, and reverse continue
> +    command is checked by setting several breakpoints, and asserting
> +    that the execution is stopped at the last of them.
> +    """
> +
> +    timeout = 10
> +    STEPS = 10
> +    endian_is_le = True
> +
> +    def run_vm(self, record, shift, args, replay_path, image_path):
> +        logger = logging.getLogger('replay')
> +        vm = self.get_vm()
> +        vm.set_console()
> +        if record:
> +            logger.info('recording the execution...')
> +            mode = 'record'
> +        else:
> +            logger.info('replaying the execution...')
> +            mode = 'replay'
> +            vm.add_args('-s', '-S')
> +        vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s,rrsnapshot=init' %
> +                    (shift, mode, replay_path),
> +                    '-net', 'none')
> +        vm.add_args('-drive', 'file=%s,if=none' % image_path)
> +        if args:
> +            vm.add_args(*args)
> +        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)

These seem a little hacky. Can't we issue normal gdb commands. In the
SVE tests I use:

    frame = gdb.selected_frame()
    for i in range(0, 32):
        rname = "z%d" % (i)
        zreg = frame.read_register(rname)

which works with the symbolic name and doesn't need endian tricks to
sort it out.

> +
> +    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))
> +
> +    @staticmethod
> +    def gdb_step(g):
> +        g.cmd(b's', b'T05thread:01;')
> +
> +    @staticmethod
> +    def gdb_bstep(g):
> +        g.cmd(b'bs', b'T05thread:01;')

Hmm so these are packet commands? Can't we access via the python API? Clebber?

> +
> +    @staticmethod
> +    def vm_get_icount(vm):
> +        return vm.qmp('query-replay')['return']['icount']
> +
> +    def reverse_debugging(self, shift=7, args=None):
> +        logger = logging.getLogger('replay')
> +
> +        # create qcow2 for snapshots
> +        logger.info('creating qcow2 image for VM snapshots')
> +        image_path = os.path.join(self.workdir, 'disk.qcow2')
> +        qemu_img = os.path.join(BUILD_DIR, 'qemu-img')
> +        if not os.path.exists(qemu_img):
> +            qemu_img = find_command('qemu-img', False)
> +        if qemu_img is False:
> +            self.cancel('Could not find "qemu-img", which is required to '
> +                        'create the temporary qcow2 image')
> +        cmd = '%s create -f qcow2 %s 128M' % (qemu_img, image_path)
> +        process.run(cmd)
> +
> +        replay_path = os.path.join(self.workdir, 'replay.bin')
> +
> +        # record the log
> +        vm = self.run_vm(True, shift, args, replay_path, image_path)
> +        while self.vm_get_icount(vm) <= self.STEPS:
> +            pass
> +        last_icount = self.vm_get_icount(vm)
> +        vm.shutdown()
> +
> +        logger.info("recorded log with %s+ steps" % last_icount)
> +
> +        # replay and run debug commands
> +        vm = self.run_vm(False, shift, args, replay_path, image_path)
> +        logger.info('connecting to gdbstub')
> +        g = gdb.GDBRemote('127.0.0.1', 1234, 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:
> +            self.fail('Reverse step is not supported by QEMU')
> +        if b'ReverseContinue+' not in r:
> +            self.fail('Reverse continue is not supported by QEMU')
> +
> +        logger.info('stepping forward')
> +        steps = []
> +        # record first instruction addresses
> +        for _ in range(self.STEPS):
> +            pc = self.get_pc(g)
> +            logger.info('saving position %x' % pc)
> +            steps.append(pc)
> +            self.gdb_step(g)
> +
> +        # visit the recorded instruction in reverse order
> +        logger.info('stepping backward')
> +        for addr in steps[::-1]:
> +            self.gdb_bstep(g)
> +            self.check_pc(g, addr)
> +            logger.info('found position %x' % addr)
> +
> +        logger.info('seeking to the end (icount %s)' % (last_icount - 1))
> +        vm.qmp('replay-break', icount=last_icount - 1)
> +        # continue - will return after pausing
> +        g.cmd(b'c', b'T02thread:01;')
> +
> +        logger.info('setting breakpoints')
> +        for addr in steps:
> +            # hardware breakpoint at addr with len=1
> +            g.cmd(b'Z1,%x,1' % addr, b'OK')
> +
> +        logger.info('running reverse continue to reach %x' % steps[-1])
> +        # reverse continue - will return after stopping at the breakpoint
> +        g.cmd(b'bc', b'T05thread:01;')
> +
> +        # assume that none of the first instructions is executed again
> +        # breaking the order of the breakpoints
> +        self.check_pc(g, steps[-1])
> +        logger.info('successfully reached %x' % steps[-1])
> +
> +        logger.info('exitting gdb and qemu')
> +        vm.shutdown()
> +
> +class ReverseDebugging_X86_64(ReverseDebugging):
> +    REG_PC = 0x10
> +    REG_CS = 0x12
> +    def get_pc(self, g):
> +        return self.get_reg_le(g, self.REG_PC) \
> +            + self.get_reg_le(g, self.REG_CS) * 0x10
> +
> +    def test_x86_64_pc(self):
> +        """
> +        :avocado: tags=arch:x86_64
> +        :avocado: tags=machine:pc
> +        """
> +        # start with BIOS only
> +        self.reverse_debugging()
> +
> +class ReverseDebugging_AArch64(ReverseDebugging):
> +    REG_PC = 32
> +
> +    def test_aarch64_virt(self):
> +        """
> +        :avocado: tags=arch:aarch64
> +        :avocado: tags=machine:virt
> +        :avocado: tags=cpu:cortex-a53
> +        """
> +        kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
> +                      '/linux/releases/29/Everything/aarch64/os/images/pxeboot'
> +                      '/vmlinuz')
> +        kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493'
> +        kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
> +
> +        self.reverse_debugging(
> +            args=('-kernel', kernel_path, '-cpu', 'cortex-a53'))

Quibbles aside it's excellent to have a test that exercises the
functionality.

-- 
Alex Bennée


      reply	other threads:[~2020-09-08 13:02 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-02  8:15 [PATCH v3 00/15] Reverse debugging Pavel Dovgalyuk
2020-09-02  8:15 ` [PATCH v3 01/15] replay: don't record interrupt poll Pavel Dovgalyuk
2020-09-07 10:17   ` Alex Bennée
2020-09-02  8:15 ` [PATCH v3 02/15] replay: provide an accessor for rr filename Pavel Dovgalyuk
2020-09-02  8:16 ` [PATCH v3 03/15] qcow2: introduce icount field for snapshots Pavel Dovgalyuk
2020-09-02  8:16 ` [PATCH v3 04/15] migration: " Pavel Dovgalyuk
2020-09-02  8:16 ` [PATCH v3 05/15] iotests: update snapshot test for new output format Pavel Dovgalyuk
2020-09-07 15:26   ` Alex Bennée
2020-09-07 15:41     ` Pavel Dovgalyuk
2020-09-07 16:00       ` Alex Bennée
2020-09-07 16:05         ` Pavel Dovgalyuk
2020-09-08 13:10   ` Eric Blake
2020-09-02  8:16 ` [PATCH v3 06/15] qapi: introduce replay.json for record/replay-related stuff Pavel Dovgalyuk
2020-09-02  8:16 ` [PATCH v3 07/15] replay: introduce info hmp/qmp command Pavel Dovgalyuk
2020-09-02  8:16 ` [PATCH v3 08/15] replay: introduce breakpoint at the specified step Pavel Dovgalyuk
2020-09-02  8:16 ` [PATCH v3 09/15] replay: implement replay-seek command Pavel Dovgalyuk
2020-09-07 12:45   ` Alex Bennée
2020-09-07 13:32     ` Pavel Dovgalyuk
2020-09-07 12:58   ` Alex Bennée
2020-09-07 13:27     ` Pavel Dovgalyuk
2020-09-07 14:59       ` Alex Bennée
2020-09-07 15:46         ` Pavel Dovgalyuk
2020-09-07 16:25           ` Alex Bennée
2020-09-08  7:44             ` Pavel Dovgalyuk
2020-09-08  9:13               ` Alex Bennée
2020-09-08 10:57                 ` Pavel Dovgalyuk
2020-09-08 11:10                 ` Alex Bennée
2020-09-08 12:15                   ` Pavel Dovgalyuk
2020-09-08 10:54             ` Pavel Dovgalyuk
2020-09-02  8:16 ` [PATCH v3 10/15] replay: flush rr queue before loading the vmstate Pavel Dovgalyuk
2020-09-07 13:37   ` Alex Bennée
2020-09-02  8:16 ` [PATCH v3 11/15] gdbstub: add reverse step support in replay mode Pavel Dovgalyuk
2020-09-07 16:30   ` Alex Bennée
2020-09-08 11:16   ` Alex Bennée
2020-09-02  8:16 ` [PATCH v3 12/15] gdbstub: add reverse continue " Pavel Dovgalyuk
2020-09-02  8:17 ` [PATCH v3 13/15] replay: describe reverse debugging in docs/replay.txt Pavel Dovgalyuk
2020-09-08 11:27   ` Alex Bennée
2020-09-08 12:57     ` Pavel Dovgalyuk
2020-09-02  8:17 ` [PATCH v3 14/15] tests: bump avocado version Pavel Dovgalyuk
2020-09-02 17:02   ` Willian Rampazzo
2020-09-04 21:39   ` Cleber Rosa
2020-09-02  8:17 ` [PATCH v3 15/15] tests/acceptance: add reverse debugging test Pavel Dovgalyuk
2020-09-08 13:01   ` Alex Bennée [this message]

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=87tuw8pgm3.fsf@linaro.org \
    --to=alex.bennee@linaro.org \
    --cc=armbru@redhat.com \
    --cc=crosa@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=mtosatti@redhat.com \
    --cc=pavel.dovgalyuk@ispras.ru \
    --cc=pbonzini@redhat.com \
    --cc=philmd@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=rth@twiddle.net \
    --cc=stefanha@redhat.com \
    --cc=wrampazz@redhat.com \
    --cc=zhiwei_liu@c-sky.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).