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