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 required=True) 31 parser.add_argument("--gdb", help="The gdb binary to use", 32 default=None) 33 parser.add_argument("--output", help="A file to redirect output to") 34 35 return parser.parse_args() 36 37 38 def log(output, msg): 39 if output: 40 output.write(msg + "\n") 41 output.flush() 42 else: 43 print(msg) 44 45 46 if __name__ == '__main__': 47 args = get_args() 48 49 # Search for a gdb we can use 50 if not args.gdb: 51 args.gdb = shutil.which("gdb-multiarch") 52 if not args.gdb: 53 args.gdb = shutil.which("gdb") 54 if not args.gdb: 55 print("We need gdb to run the test") 56 exit(-1) 57 if args.output: 58 output = open(args.output, "w") 59 else: 60 output = None 61 62 socket_dir = TemporaryDirectory("qemu-gdbstub") 63 socket_name = os.path.join(socket_dir.name, "gdbstub.socket") 64 65 # Launch QEMU with binary 66 if "system" in args.qemu: 67 cmd = "%s %s %s -gdb unix:path=%s,server=on" % (args.qemu, 68 args.qargs, 69 args.binary, 70 socket_name) 71 else: 72 cmd = "%s %s -g %s %s" % (args.qemu, args.qargs, socket_name, 73 args.binary) 74 75 log(output, "QEMU CMD: %s" % (cmd)) 76 inferior = subprocess.Popen(shlex.split(cmd)) 77 78 # Now launch gdb with our test and collect the result 79 gdb_cmd = "%s %s" % (args.gdb, args.binary) 80 # run quietly and ignore .gdbinit 81 gdb_cmd += " -q -n -batch" 82 # disable prompts in case of crash 83 gdb_cmd += " -ex 'set confirm off'" 84 # connect to remote 85 gdb_cmd += " -ex 'target remote %s'" % (socket_name) 86 # finally the test script itself 87 gdb_cmd += " -x %s" % (args.test) 88 89 90 sleep(1) 91 log(output, "GDB CMD: %s" % (gdb_cmd)) 92 93 result = subprocess.call(gdb_cmd, shell=True, stdout=output) 94 95 # A negative result is the result of an internal gdb failure like 96 # a crash. We force a return of 0 so we don't fail the test on 97 # account of broken external tools. 98 if result < 0: 99 print("GDB crashed? SKIPPING") 100 exit(0) 101 102 try: 103 inferior.wait(2) 104 except subprocess.TimeoutExpired: 105 print("GDB never connected? Killed guest") 106 inferior.kill() 107 108 exit(result) 109