xref: /qemu/tests/guest-debug/run-test.py (revision 4d48c1bc079147a02ec1a3df9a41497c515d2e61)
1db2ea0ddSAlex Bennée#!/usr/bin/env python3
2db2ea0ddSAlex Bennée#
3db2ea0ddSAlex Bennée# Run a gdbstub test case
4db2ea0ddSAlex Bennée#
5db2ea0ddSAlex Bennée# Copyright (c) 2019 Linaro
6db2ea0ddSAlex Bennée#
7db2ea0ddSAlex Bennée# Author: Alex Bennée <alex.bennee@linaro.org>
8db2ea0ddSAlex Bennée#
9db2ea0ddSAlex Bennée# This work is licensed under the terms of the GNU GPL, version 2 or later.
10db2ea0ddSAlex Bennée# See the COPYING file in the top-level directory.
11db2ea0ddSAlex Bennée#
12db2ea0ddSAlex Bennée# SPDX-License-Identifier: GPL-2.0-or-later
13db2ea0ddSAlex Bennée
14db2ea0ddSAlex Bennéeimport argparse
15db2ea0ddSAlex Bennéeimport subprocess
16db2ea0ddSAlex Bennéeimport shutil
17db2ea0ddSAlex Bennéeimport shlex
18b0dc2a8bSAlex Bennéeimport os
19c00506aaSAlex Bennéefrom time import sleep
20b0dc2a8bSAlex Bennéefrom tempfile import TemporaryDirectory
21db2ea0ddSAlex Bennée
22db2ea0ddSAlex Bennéedef get_args():
23db2ea0ddSAlex Bennée    parser = argparse.ArgumentParser(description="A gdbstub test runner")
24db2ea0ddSAlex Bennée    parser.add_argument("--qemu", help="Qemu binary for test",
25db2ea0ddSAlex Bennée                        required=True)
26db2ea0ddSAlex Bennée    parser.add_argument("--qargs", help="Qemu arguments for test")
27db2ea0ddSAlex Bennée    parser.add_argument("--binary", help="Binary to debug",
28db2ea0ddSAlex Bennée                        required=True)
29dae66a3fSMatheus Tavares Bernardino    parser.add_argument("--test", help="GDB test script")
30c00506aaSAlex Bennée    parser.add_argument("--gdb", help="The gdb binary to use",
31c00506aaSAlex Bennée                        default=None)
32dae66a3fSMatheus Tavares Bernardino    parser.add_argument("--gdb-args", help="Additional gdb arguments")
33c00506aaSAlex Bennée    parser.add_argument("--output", help="A file to redirect output to")
34dae66a3fSMatheus Tavares Bernardino    parser.add_argument("--stderr", help="A file to redirect stderr to")
35db2ea0ddSAlex Bennée
36db2ea0ddSAlex Bennée    return parser.parse_args()
37db2ea0ddSAlex Bennée
38c00506aaSAlex Bennée
39c00506aaSAlex Bennéedef log(output, msg):
40c00506aaSAlex Bennée    if output:
41c00506aaSAlex Bennée        output.write(msg + "\n")
42c00506aaSAlex Bennée        output.flush()
43c00506aaSAlex Bennée    else:
44c00506aaSAlex Bennée        print(msg)
45c00506aaSAlex Bennée
46c00506aaSAlex Bennée
47db2ea0ddSAlex Bennéeif __name__ == '__main__':
48db2ea0ddSAlex Bennée    args = get_args()
49db2ea0ddSAlex Bennée
50db2ea0ddSAlex Bennée    # Search for a gdb we can use
51db2ea0ddSAlex Bennée    if not args.gdb:
52db2ea0ddSAlex Bennée        args.gdb = shutil.which("gdb-multiarch")
53db2ea0ddSAlex Bennée    if not args.gdb:
54db2ea0ddSAlex Bennée        args.gdb = shutil.which("gdb")
55db2ea0ddSAlex Bennée    if not args.gdb:
56db2ea0ddSAlex Bennée        print("We need gdb to run the test")
57db2ea0ddSAlex Bennée        exit(-1)
58c00506aaSAlex Bennée    if args.output:
59c00506aaSAlex Bennée        output = open(args.output, "w")
60c00506aaSAlex Bennée    else:
61c00506aaSAlex Bennée        output = None
62dae66a3fSMatheus Tavares Bernardino    if args.stderr:
63dae66a3fSMatheus Tavares Bernardino        stderr = open(args.stderr, "w")
64dae66a3fSMatheus Tavares Bernardino    else:
65dae66a3fSMatheus Tavares Bernardino        stderr = None
66db2ea0ddSAlex Bennée
67b0dc2a8bSAlex Bennée    socket_dir = TemporaryDirectory("qemu-gdbstub")
68b0dc2a8bSAlex Bennée    socket_name = os.path.join(socket_dir.name, "gdbstub.socket")
69b0dc2a8bSAlex Bennée
70db2ea0ddSAlex Bennée    # Launch QEMU with binary
71db2ea0ddSAlex Bennée    if "system" in args.qemu:
72dad1036fSAlex Bennée        cmd = f'{args.qemu} {args.qargs} {args.binary}' \
73dad1036fSAlex Bennée            f' -S -gdb unix:path={socket_name},server=on'
74db2ea0ddSAlex Bennée    else:
75dad1036fSAlex Bennée        cmd = f'{args.qemu} {args.qargs} -g {socket_name} {args.binary}'
76db2ea0ddSAlex Bennée
77c00506aaSAlex Bennée    log(output, "QEMU CMD: %s" % (cmd))
78db2ea0ddSAlex Bennée    inferior = subprocess.Popen(shlex.split(cmd))
79db2ea0ddSAlex Bennée
80db2ea0ddSAlex Bennée    # Now launch gdb with our test and collect the result
81d2fefdedSAlex Bennée    gdb_cmd = "%s %s" % (args.gdb, args.binary)
82dae66a3fSMatheus Tavares Bernardino    if args.gdb_args:
83dae66a3fSMatheus Tavares Bernardino        gdb_cmd += " %s" % (args.gdb_args)
84d2fefdedSAlex Bennée    # run quietly and ignore .gdbinit
85d2fefdedSAlex Bennée    gdb_cmd += " -q -n -batch"
86a8fea70fSAlex Bennée    # disable pagination
87a8fea70fSAlex Bennée    gdb_cmd += " -ex 'set pagination off'"
88d2fefdedSAlex Bennée    # disable prompts in case of crash
89d2fefdedSAlex Bennée    gdb_cmd += " -ex 'set confirm off'"
90d2fefdedSAlex Bennée    # connect to remote
91b0dc2a8bSAlex Bennée    gdb_cmd += " -ex 'target remote %s'" % (socket_name)
92d2fefdedSAlex Bennée    # finally the test script itself
93dae66a3fSMatheus Tavares Bernardino    if args.test:
94d2fefdedSAlex Bennée        gdb_cmd += " -x %s" % (args.test)
95d2fefdedSAlex Bennée
96db2ea0ddSAlex Bennée
97c00506aaSAlex Bennée    sleep(1)
98c00506aaSAlex Bennée    log(output, "GDB CMD: %s" % (gdb_cmd))
99c00506aaSAlex Bennée
100*4d48c1bcSIlya Leoshkevich    gdb_env = dict(os.environ)
101*4d48c1bcSIlya Leoshkevich    gdb_pythonpath = gdb_env.get("PYTHONPATH", "").split(os.pathsep)
102*4d48c1bcSIlya Leoshkevich    gdb_pythonpath.append(os.path.dirname(os.path.realpath(__file__)))
103*4d48c1bcSIlya Leoshkevich    gdb_env["PYTHONPATH"] = os.pathsep.join(gdb_pythonpath)
104*4d48c1bcSIlya Leoshkevich    result = subprocess.call(gdb_cmd, shell=True, stdout=output, stderr=stderr,
105*4d48c1bcSIlya Leoshkevich                             env=gdb_env)
106db2ea0ddSAlex Bennée
107caccf599SAlex Bennée    # A result of greater than 128 indicates a fatal signal (likely a
108caccf599SAlex Bennée    # crash due to gdb internal failure). That's a problem for GDB and
109caccf599SAlex Bennée    # not the test so we force a return of 0 so we don't fail the test on
110d2fefdedSAlex Bennée    # account of broken external tools.
111caccf599SAlex Bennée    if result > 128:
112caccf599SAlex Bennée        log(output, "GDB crashed? (%d, %d) SKIPPING" % (result, result - 128))
113d2fefdedSAlex Bennée        exit(0)
114d2fefdedSAlex Bennée
115b03e4fffSAlex Bennée    try:
116b03e4fffSAlex Bennée        inferior.wait(2)
117b03e4fffSAlex Bennée    except subprocess.TimeoutExpired:
118caccf599SAlex Bennée        log(output, "GDB never connected? Killed guest")
119b03e4fffSAlex Bennée        inferior.kill()
120b03e4fffSAlex Bennée
121db2ea0ddSAlex Bennée    exit(result)
122