diff options
Diffstat (limited to 'src/focaccia/tools/validate_qemu.py')
| -rwxr-xr-x | src/focaccia/tools/validate_qemu.py | 124 |
1 files changed, 79 insertions, 45 deletions
diff --git a/src/focaccia/tools/validate_qemu.py b/src/focaccia/tools/validate_qemu.py index 2b7e65c..edef9ae 100755 --- a/src/focaccia/tools/validate_qemu.py +++ b/src/focaccia/tools/validate_qemu.py @@ -6,6 +6,8 @@ Spawn GDB, connect to QEMU's GDB server, and read test states from that. We need two scripts (this one and the primary `qemu_tool.py`) because we can't pass arguments to scripts executed via `gdb -x <script>`. +Alternatively, we connect to the Focaccia QEMU plugin when a socket is given. + This script (`validate_qemu.py`) is the one the user interfaces with. It eventually calls `execv` to spawn a GDB process that calls the main `qemu_tool.py` script; `python validate_qemu.py` essentially behaves as if @@ -21,6 +23,8 @@ import sysconfig import subprocess from focaccia.compare import ErrorTypes +from focaccia.arch import supported_architectures +from focaccia.tools.validation_server import start_validation_server verbosity = { 'info': ErrorTypes.INFO, @@ -50,6 +54,7 @@ memory, and stepping forward by single instructions. action='store_true', help='Don\'t print a verification result.') prog.add_argument('-o', '--output', + type=str, help='If specified with a file name, the recorded' ' emulator states will be written to that file.') prog.add_argument('--error-level', @@ -59,6 +64,23 @@ memory, and stepping forward by single instructions. default=None, help='The executable executed under QEMU, overrides the auto-detected' \ 'executable') + prog.add_argument('--use-socket', + type=str, + nargs='?', + const='/tmp/focaccia.sock', + help='Use QEMU plugin interface given by socket instead of GDB') + prog.add_argument('--guest-arch', + type=str, + choices=supported_architectures.keys(), + help='Architecture of the emulated guest' + '(Only required when using --use-socket)') + prog.add_argument('--remote', + type=str, + help='The hostname:port pair at which to find a QEMU GDB server.') + prog.add_argument('--gdb', + type=str, + default='gdb', + help='GDB binary to invoke.') return prog def quoted(s: str) -> str: @@ -71,50 +93,62 @@ def try_remove(l: list, v): pass def main(): - prog = make_argparser() - prog.add_argument('--gdb', default='gdb', - help='GDB binary to invoke.') - args = prog.parse_args() - - script_dirname = os.path.dirname(__file__) - qemu_tool_path = os.path.join(script_dirname, '_qemu_tool.py') - - # We have to remove all arguments we don't want to pass to the qemu tool - # manually here. Not nice, but what can you do.. - argv = sys.argv - try_remove(argv, '--gdb') - try_remove(argv, args.gdb) - - # Assemble the argv array passed to the qemu tool. GDB does not have a - # mechanism to pass arguments to a script that it executes, so we - # overwrite `sys.argv` manually before invoking the script. - argv_str = f'[{", ".join(quoted(a) for a in argv)}]' - path_str = f'[{", ".join(quoted(s) for s in sys.path)}]' - - paths = sysconfig.get_paths() - candidates = [paths["purelib"], paths["platlib"]] - entries = [p for p in candidates if p and os.path.isdir(p)] - venv_site = entries[0] + argparser = make_argparser() + args = argparser.parse_args() + + # Get environment env = os.environ.copy() - env["PYTHONPATH"] = ','.join([script_dirname, venv_site]) - - print(f"GDB started with Python Path: {env['PYTHONPATH']}") - gdb_cmd = [ - args.gdb, - '-nx', # Don't parse any .gdbinits - '--batch', - '-ex', f'py import sys', - '-ex', f'py sys.argv = {argv_str}', - '-ex', f'py sys.path = {path_str}', - "-ex", f"py import site; site.addsitedir({venv_site!r})", - "-ex", f"py import site; site.addsitedir({script_dirname!r})", - '-x', qemu_tool_path - ] - proc = subprocess.Popen(gdb_cmd, env=env) - - ret = proc.wait() - exit(ret) - -if __name__ == "__main__": - main() + + # Differentiate between the QEMU GDB server and QEMU plugin interfaces + if args.use_socket: + if not args.guest_arch: + argparser.error('--guest-arch is required when --use-socket is specified') + + # QEMU plugin interface + start_validation_server(args.symb_trace, + args.output, + args.use_socket, + args.guest_arch, + env, + verbosity[args.error_level], + args.quiet) + else: + # QEMU GDB interface + script_dirname = os.path.dirname(__file__) + qemu_tool_path = os.path.join(script_dirname, '_qemu_tool.py') + + # We have to remove all arguments we don't want to pass to the qemu tool + # manually here. Not nice, but what can you do.. + argv = sys.argv + try_remove(argv, '--gdb') + try_remove(argv, args.gdb) + + # Assemble the argv array passed to the qemu tool. GDB does not have a + # mechanism to pass arguments to a script that it executes, so we + # overwrite `sys.argv` manually before invoking the script. + argv_str = f'[{", ".join(quoted(a) for a in argv)}]' + path_str = f'[{", ".join(quoted(s) for s in sys.path)}]' + + paths = sysconfig.get_paths() + candidates = [paths["purelib"], paths["platlib"]] + entries = [p for p in candidates if p and os.path.isdir(p)] + venv_site = entries[0] + env["PYTHONPATH"] = ','.join([script_dirname, venv_site]) + + print(f"GDB started with Python Path: {env['PYTHONPATH']}") + gdb_cmd = [ + args.gdb, + '-nx', # Don't parse any .gdbinits + '--batch', + '-ex', 'py import sys', + '-ex', f'py sys.argv = {argv_str}', + '-ex', f'py sys.path = {path_str}', + "-ex", f'py import site; site.addsitedir({venv_site!r})', + "-ex", f'py import site; site.addsitedir({script_dirname!r})', + '-x', qemu_tool_path + ] + proc = subprocess.Popen(gdb_cmd, env=env) + + ret = proc.wait() + exit(ret) |