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