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