xref: /qemu/tests/guest-debug/run-test.py (revision ffaf7f0376f8040ce9068d71ae9ae8722505c42e)
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")
303848409eSGustavo Romero    parser.add_argument('test_args', nargs='*',
313848409eSGustavo Romero                        help="Additional args for GDB test script. "
323848409eSGustavo Romero                        "The args should be preceded by -- to avoid confusion "
333848409eSGustavo Romero                        "with flags for runner script")
34c00506aaSAlex Bennée    parser.add_argument("--gdb", help="The gdb binary to use",
35c00506aaSAlex Bennée                        default=None)
36dae66a3fSMatheus Tavares Bernardino    parser.add_argument("--gdb-args", help="Additional gdb arguments")
37c00506aaSAlex Bennée    parser.add_argument("--output", help="A file to redirect output to")
38dae66a3fSMatheus Tavares Bernardino    parser.add_argument("--stderr", help="A file to redirect stderr to")
39*24c61663SIlya Leoshkevich    parser.add_argument("--no-suspend", action="store_true",
40*24c61663SIlya Leoshkevich                        help="Ask the binary to not wait for GDB connection")
41db2ea0ddSAlex Bennée
42db2ea0ddSAlex Bennée    return parser.parse_args()
43db2ea0ddSAlex Bennée
44c00506aaSAlex Bennée
45c00506aaSAlex Bennéedef log(output, msg):
46c00506aaSAlex Bennée    if output:
47c00506aaSAlex Bennée        output.write(msg + "\n")
48c00506aaSAlex Bennée        output.flush()
49c00506aaSAlex Bennée    else:
50c00506aaSAlex Bennée        print(msg)
51c00506aaSAlex Bennée
52c00506aaSAlex Bennée
53db2ea0ddSAlex Bennéeif __name__ == '__main__':
54db2ea0ddSAlex Bennée    args = get_args()
55db2ea0ddSAlex Bennée
56db2ea0ddSAlex Bennée    # Search for a gdb we can use
57db2ea0ddSAlex Bennée    if not args.gdb:
58db2ea0ddSAlex Bennée        args.gdb = shutil.which("gdb-multiarch")
59db2ea0ddSAlex Bennée    if not args.gdb:
60db2ea0ddSAlex Bennée        args.gdb = shutil.which("gdb")
61db2ea0ddSAlex Bennée    if not args.gdb:
62db2ea0ddSAlex Bennée        print("We need gdb to run the test")
63db2ea0ddSAlex Bennée        exit(-1)
64c00506aaSAlex Bennée    if args.output:
65c00506aaSAlex Bennée        output = open(args.output, "w")
66c00506aaSAlex Bennée    else:
67c00506aaSAlex Bennée        output = None
68dae66a3fSMatheus Tavares Bernardino    if args.stderr:
69dae66a3fSMatheus Tavares Bernardino        stderr = open(args.stderr, "w")
70dae66a3fSMatheus Tavares Bernardino    else:
71dae66a3fSMatheus Tavares Bernardino        stderr = None
72db2ea0ddSAlex Bennée
73b0dc2a8bSAlex Bennée    socket_dir = TemporaryDirectory("qemu-gdbstub")
74b0dc2a8bSAlex Bennée    socket_name = os.path.join(socket_dir.name, "gdbstub.socket")
75b0dc2a8bSAlex Bennée
76db2ea0ddSAlex Bennée    # Launch QEMU with binary
77db2ea0ddSAlex Bennée    if "system" in args.qemu:
78*24c61663SIlya Leoshkevich        if args.no_suspend:
79*24c61663SIlya Leoshkevich            suspend = ''
80db2ea0ddSAlex Bennée        else:
81*24c61663SIlya Leoshkevich            suspend = ' -S'
82*24c61663SIlya Leoshkevich        cmd = f'{args.qemu} {args.qargs} {args.binary}' \
83*24c61663SIlya Leoshkevich            f'{suspend} -gdb unix:path={socket_name},server=on'
84*24c61663SIlya Leoshkevich    else:
85*24c61663SIlya Leoshkevich        if args.no_suspend:
86*24c61663SIlya Leoshkevich            suspend = ',suspend=n'
87*24c61663SIlya Leoshkevich        else:
88*24c61663SIlya Leoshkevich            suspend = ''
89*24c61663SIlya Leoshkevich        cmd = f'{args.qemu} {args.qargs} -g {socket_name}{suspend}' \
90*24c61663SIlya Leoshkevich            f' {args.binary}'
91db2ea0ddSAlex Bennée
92c00506aaSAlex Bennée    log(output, "QEMU CMD: %s" % (cmd))
93db2ea0ddSAlex Bennée    inferior = subprocess.Popen(shlex.split(cmd))
94db2ea0ddSAlex Bennée
95db2ea0ddSAlex Bennée    # Now launch gdb with our test and collect the result
96d2fefdedSAlex Bennée    gdb_cmd = "%s %s" % (args.gdb, args.binary)
97dae66a3fSMatheus Tavares Bernardino    if args.gdb_args:
98dae66a3fSMatheus Tavares Bernardino        gdb_cmd += " %s" % (args.gdb_args)
99d2fefdedSAlex Bennée    # run quietly and ignore .gdbinit
100d2fefdedSAlex Bennée    gdb_cmd += " -q -n -batch"
101a8fea70fSAlex Bennée    # disable pagination
102a8fea70fSAlex Bennée    gdb_cmd += " -ex 'set pagination off'"
103d2fefdedSAlex Bennée    # disable prompts in case of crash
104d2fefdedSAlex Bennée    gdb_cmd += " -ex 'set confirm off'"
105d2fefdedSAlex Bennée    # connect to remote
106b0dc2a8bSAlex Bennée    gdb_cmd += " -ex 'target remote %s'" % (socket_name)
107d2fefdedSAlex Bennée    # finally the test script itself
108dae66a3fSMatheus Tavares Bernardino    if args.test:
1093848409eSGustavo Romero        if args.test_args:
1103848409eSGustavo Romero            gdb_cmd += f" -ex \"py sys.argv={args.test_args}\""
111d2fefdedSAlex Bennée        gdb_cmd += " -x %s" % (args.test)
112d2fefdedSAlex Bennée
113db2ea0ddSAlex Bennée
114c00506aaSAlex Bennée    sleep(1)
115c00506aaSAlex Bennée    log(output, "GDB CMD: %s" % (gdb_cmd))
116c00506aaSAlex Bennée
1174d48c1bcSIlya Leoshkevich    gdb_env = dict(os.environ)
1184d48c1bcSIlya Leoshkevich    gdb_pythonpath = gdb_env.get("PYTHONPATH", "").split(os.pathsep)
1194d48c1bcSIlya Leoshkevich    gdb_pythonpath.append(os.path.dirname(os.path.realpath(__file__)))
1204d48c1bcSIlya Leoshkevich    gdb_env["PYTHONPATH"] = os.pathsep.join(gdb_pythonpath)
1214d48c1bcSIlya Leoshkevich    result = subprocess.call(gdb_cmd, shell=True, stdout=output, stderr=stderr,
1224d48c1bcSIlya Leoshkevich                             env=gdb_env)
123db2ea0ddSAlex Bennée
124caccf599SAlex Bennée    # A result of greater than 128 indicates a fatal signal (likely a
125caccf599SAlex Bennée    # crash due to gdb internal failure). That's a problem for GDB and
126caccf599SAlex Bennée    # not the test so we force a return of 0 so we don't fail the test on
127d2fefdedSAlex Bennée    # account of broken external tools.
128caccf599SAlex Bennée    if result > 128:
129caccf599SAlex Bennée        log(output, "GDB crashed? (%d, %d) SKIPPING" % (result, result - 128))
130d2fefdedSAlex Bennée        exit(0)
131d2fefdedSAlex Bennée
132b03e4fffSAlex Bennée    try:
133b03e4fffSAlex Bennée        inferior.wait(2)
134b03e4fffSAlex Bennée    except subprocess.TimeoutExpired:
135caccf599SAlex Bennée        log(output, "GDB never connected? Killed guest")
136b03e4fffSAlex Bennée        inferior.kill()
137b03e4fffSAlex Bennée
138db2ea0ddSAlex Bennée    exit(result)
139