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