3848409eb0
This commit adds support for passing arguments to the GDB test scripts so it's possible to parse the args in an "argparse way" in the test scripts launched by the runner. The arguments should be preceded by -- when passed to the runner. For example, passing "--help" arg to the GDB_TEST_SCRIPT: run-test.py [...] --test <GDB_TEST_SCRIPT> -- --help The test script should not use the argparse module directly but import arg_parser from test_gdbstub module. arg_parser then can be used just like the argparse.ArgumentParser class: from test_gdbstub import arg_parser p = arg_parser(prog="test-mytest.py", description="My test.") p.add_argument("--vowel", help="Select vowel", required=True, choices=['a','e','i','o','u']) [...] The arg_parser allows a smooth and informative exit if, for instance, the caller of the runner script passes an invalid argument or misses a required argument by the test script. Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240906143316.657436-4-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240910173900.4154726-9-alex.bennee@linaro.org>
128 lines
4.1 KiB
Python
Executable File
128 lines
4.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Run a gdbstub test case
|
|
#
|
|
# Copyright (c) 2019 Linaro
|
|
#
|
|
# Author: Alex Bennée <alex.bennee@linaro.org>
|
|
#
|
|
# This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
# See the COPYING file in the top-level directory.
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
import argparse
|
|
import subprocess
|
|
import shutil
|
|
import shlex
|
|
import os
|
|
from time import sleep
|
|
from tempfile import TemporaryDirectory
|
|
|
|
def get_args():
|
|
parser = argparse.ArgumentParser(description="A gdbstub test runner")
|
|
parser.add_argument("--qemu", help="Qemu binary for test",
|
|
required=True)
|
|
parser.add_argument("--qargs", help="Qemu arguments for test")
|
|
parser.add_argument("--binary", help="Binary to debug",
|
|
required=True)
|
|
parser.add_argument("--test", help="GDB test script")
|
|
parser.add_argument('test_args', nargs='*',
|
|
help="Additional args for GDB test script. "
|
|
"The args should be preceded by -- to avoid confusion "
|
|
"with flags for runner script")
|
|
parser.add_argument("--gdb", help="The gdb binary to use",
|
|
default=None)
|
|
parser.add_argument("--gdb-args", help="Additional gdb arguments")
|
|
parser.add_argument("--output", help="A file to redirect output to")
|
|
parser.add_argument("--stderr", help="A file to redirect stderr to")
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
def log(output, msg):
|
|
if output:
|
|
output.write(msg + "\n")
|
|
output.flush()
|
|
else:
|
|
print(msg)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
args = get_args()
|
|
|
|
# Search for a gdb we can use
|
|
if not args.gdb:
|
|
args.gdb = shutil.which("gdb-multiarch")
|
|
if not args.gdb:
|
|
args.gdb = shutil.which("gdb")
|
|
if not args.gdb:
|
|
print("We need gdb to run the test")
|
|
exit(-1)
|
|
if args.output:
|
|
output = open(args.output, "w")
|
|
else:
|
|
output = None
|
|
if args.stderr:
|
|
stderr = open(args.stderr, "w")
|
|
else:
|
|
stderr = None
|
|
|
|
socket_dir = TemporaryDirectory("qemu-gdbstub")
|
|
socket_name = os.path.join(socket_dir.name, "gdbstub.socket")
|
|
|
|
# Launch QEMU with binary
|
|
if "system" in args.qemu:
|
|
cmd = f'{args.qemu} {args.qargs} {args.binary}' \
|
|
f' -S -gdb unix:path={socket_name},server=on'
|
|
else:
|
|
cmd = f'{args.qemu} {args.qargs} -g {socket_name} {args.binary}'
|
|
|
|
log(output, "QEMU CMD: %s" % (cmd))
|
|
inferior = subprocess.Popen(shlex.split(cmd))
|
|
|
|
# Now launch gdb with our test and collect the result
|
|
gdb_cmd = "%s %s" % (args.gdb, args.binary)
|
|
if args.gdb_args:
|
|
gdb_cmd += " %s" % (args.gdb_args)
|
|
# run quietly and ignore .gdbinit
|
|
gdb_cmd += " -q -n -batch"
|
|
# disable pagination
|
|
gdb_cmd += " -ex 'set pagination off'"
|
|
# disable prompts in case of crash
|
|
gdb_cmd += " -ex 'set confirm off'"
|
|
# connect to remote
|
|
gdb_cmd += " -ex 'target remote %s'" % (socket_name)
|
|
# finally the test script itself
|
|
if args.test:
|
|
if args.test_args:
|
|
gdb_cmd += f" -ex \"py sys.argv={args.test_args}\""
|
|
gdb_cmd += " -x %s" % (args.test)
|
|
|
|
|
|
sleep(1)
|
|
log(output, "GDB CMD: %s" % (gdb_cmd))
|
|
|
|
gdb_env = dict(os.environ)
|
|
gdb_pythonpath = gdb_env.get("PYTHONPATH", "").split(os.pathsep)
|
|
gdb_pythonpath.append(os.path.dirname(os.path.realpath(__file__)))
|
|
gdb_env["PYTHONPATH"] = os.pathsep.join(gdb_pythonpath)
|
|
result = subprocess.call(gdb_cmd, shell=True, stdout=output, stderr=stderr,
|
|
env=gdb_env)
|
|
|
|
# A result of greater than 128 indicates a fatal signal (likely a
|
|
# crash due to gdb internal failure). That's a problem for GDB and
|
|
# not the test so we force a return of 0 so we don't fail the test on
|
|
# account of broken external tools.
|
|
if result > 128:
|
|
log(output, "GDB crashed? (%d, %d) SKIPPING" % (result, result - 128))
|
|
exit(0)
|
|
|
|
try:
|
|
inferior.wait(2)
|
|
except subprocess.TimeoutExpired:
|
|
log(output, "GDB never connected? Killed guest")
|
|
inferior.kill()
|
|
|
|
exit(result)
|