qemu/tests/guest-debug/run-test.py
<<
>>
Prefs
   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('test_args', nargs='*',
  31                        help="Additional args for GDB test script. "
  32                        "The args should be preceded by -- to avoid confusion "
  33                        "with flags for runner script")
  34    parser.add_argument("--gdb", help="The gdb binary to use",
  35                        default=None)
  36    parser.add_argument("--gdb-args", help="Additional gdb arguments")
  37    parser.add_argument("--output", help="A file to redirect output to")
  38    parser.add_argument("--stderr", help="A file to redirect stderr to")
  39    parser.add_argument("--no-suspend", action="store_true",
  40                        help="Ask the binary to not wait for GDB connection")
  41
  42    return parser.parse_args()
  43
  44
  45def log(output, msg):
  46    if output:
  47        output.write(msg + "\n")
  48        output.flush()
  49    else:
  50        print(msg)
  51
  52
  53if __name__ == '__main__':
  54    args = get_args()
  55
  56    # Search for a gdb we can use
  57    if not args.gdb:
  58        args.gdb = shutil.which("gdb-multiarch")
  59    if not args.gdb:
  60        args.gdb = shutil.which("gdb")
  61    if not args.gdb:
  62        print("We need gdb to run the test")
  63        exit(-1)
  64    if args.output:
  65        output = open(args.output, "w")
  66    else:
  67        output = None
  68    if args.stderr:
  69        stderr = open(args.stderr, "w")
  70    else:
  71        stderr = None
  72
  73    socket_dir = TemporaryDirectory("qemu-gdbstub")
  74    socket_name = os.path.join(socket_dir.name, "gdbstub.socket")
  75
  76    # Launch QEMU with binary
  77    if "system" in args.qemu:
  78        if args.no_suspend:
  79            suspend = ''
  80        else:
  81            suspend = ' -S'
  82        cmd = f'{args.qemu} {args.qargs} {args.binary}' \
  83            f'{suspend} -gdb unix:path={socket_name},server=on'
  84    else:
  85        if args.no_suspend:
  86            suspend = ',suspend=n'
  87        else:
  88            suspend = ''
  89        cmd = f'{args.qemu} {args.qargs} -g {socket_name}{suspend}' \
  90            f' {args.binary}'
  91
  92    log(output, "QEMU CMD: %s" % (cmd))
  93    inferior = subprocess.Popen(shlex.split(cmd))
  94
  95    # Now launch gdb with our test and collect the result
  96    gdb_cmd = "%s %s" % (args.gdb, args.binary)
  97    if args.gdb_args:
  98        gdb_cmd += " %s" % (args.gdb_args)
  99    # run quietly and ignore .gdbinit
 100    gdb_cmd += " -q -n -batch"
 101    # disable pagination
 102    gdb_cmd += " -ex 'set pagination off'"
 103    # disable prompts in case of crash
 104    gdb_cmd += " -ex 'set confirm off'"
 105    # connect to remote
 106    gdb_cmd += " -ex 'target remote %s'" % (socket_name)
 107    # finally the test script itself
 108    if args.test:
 109        if args.test_args:
 110            gdb_cmd += f" -ex \"py sys.argv={args.test_args}\""
 111        gdb_cmd += " -x %s" % (args.test)
 112
 113
 114    sleep(1)
 115    log(output, "GDB CMD: %s" % (gdb_cmd))
 116
 117    gdb_env = dict(os.environ)
 118    gdb_pythonpath = gdb_env.get("PYTHONPATH", "").split(os.pathsep)
 119    gdb_pythonpath.append(os.path.dirname(os.path.realpath(__file__)))
 120    gdb_env["PYTHONPATH"] = os.pathsep.join(gdb_pythonpath)
 121    result = subprocess.call(gdb_cmd, shell=True, stdout=output, stderr=stderr,
 122                             env=gdb_env)
 123
 124    # A result of greater than 128 indicates a fatal signal (likely a
 125    # crash due to gdb internal failure). That's a problem for GDB and
 126    # not the test so we force a return of 0 so we don't fail the test on
 127    # account of broken external tools.
 128    if result > 128:
 129        log(output, "GDB crashed? (%d, %d) SKIPPING" % (result, result - 128))
 130        exit(0)
 131
 132    try:
 133        inferior.wait(2)
 134    except subprocess.TimeoutExpired:
 135        log(output, "GDB never connected? Killed guest")
 136        inferior.kill()
 137
 138    exit(result)
 139