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