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