Add gdb-repr directive for tests/debuginfo - #160377
Conversation
|
|
| class ReprCommand(gdb.Command): | ||
| def __init__(self): | ||
| super().__init__("repr", gdb.COMMAND_OBSCURE) | ||
|
|
||
| def invoke(self, argument: str, from_tty: bool): | ||
| print(f"(gdb) repr {argument}") | ||
|
|
||
| global REPR_COMMAND_RUN | ||
| REPR_COMMAND_RUN = True | ||
| try: | ||
| if check(argument) == Result.Mismatch: | ||
| global REPR_ERROR | ||
| REPR_ERROR = True | ||
| except Exception as e: | ||
| import sys | ||
| import traceback | ||
|
|
||
| traceback.print_exception(type(e), e, e.__traceback__, file=sys.stdout) | ||
| gdb.execute("exit 1") | ||
|
|
||
|
|
||
| ReprCommand() | ||
|
|
||
|
|
||
| class ReprFinalize(gdb.Command): | ||
| def __init__(self): | ||
| super().__init__("repr_finalize", gdb.COMMAND_OBSCURE) | ||
|
|
||
| def invoke(self, argument: str, from_tty: bool): | ||
| if not REPR_COMMAND_RUN: | ||
| return | ||
|
|
||
| if not tested_all_variables() or not tested_all_types(): | ||
| gdb.execute("exit 1") | ||
|
|
||
| if BLESS and not REPR_ERROR: | ||
| gdb_version = gdb.execute("show version", to_string=True).splitlines()[0] | ||
| metadata = BlessMetadata(sys.version, gdb_version) | ||
| INPUT_DATA.save_blessing(metadata) | ||
|
|
||
|
|
||
| ReprFinalize() |
There was a problem hiding this comment.
Here's where we create and register the custom CLI commands.
| TYPES_TESTED: dict[str, Result] = {} | ||
| """Since types are unique and unchanging, we only need to test each type once. This also helps | ||
| ensure we have tested all types in `INPUT_DATA` | ||
| """ | ||
|
|
||
|
|
||
| VARS_TESTED: list[dict[str, Result]] = [] | ||
| """Used to help ensure all expected variables were tested. Each element of the list corresponds to a | ||
| breakpoint, and contains a set of all of the variable names tested for that breakpoint.""" | ||
|
|
||
|
|
||
| def tested_all_types() -> bool: | ||
| """Returns true if all types in INPUT_DATA were tested this run.""" | ||
|
|
||
| expected_types = set(INPUT_DATA.types) | ||
| untested_types = expected_types.difference(TYPES_TESTED.keys()) | ||
|
|
||
| if len(untested_types) != 0: | ||
| print( | ||
| f"{ANSI_RED}[repr error]{ANSI_END} The following types were expected, but were not \ | ||
| tested:\n {untested_types}" | ||
| ) | ||
|
|
||
| return len(untested_types) == 0 | ||
|
|
||
|
|
||
| def tested_all_variables() -> bool: | ||
| expected_vars = [set(vars) for vars in INPUT_DATA.breakpoints] | ||
| untested_vars = [ | ||
| expected.difference(tested.keys()) | ||
| for expected, tested in zip(expected_vars, VARS_TESTED) | ||
| ] | ||
|
|
||
| tested_not_expected = [ | ||
| set(tested.keys()).difference(expected) | ||
| for expected, tested in zip(expected_vars, VARS_TESTED) | ||
| ] | ||
|
|
||
| result = True | ||
|
|
||
| for i, v in enumerate(untested_vars): | ||
| if len(v) == 0: | ||
| continue | ||
|
|
||
| result = False | ||
| print( | ||
| f"{ANSI_RED}[repr error]{ANSI_END} The following variables were expected at \ | ||
| breakpoint#{i}, but were not tested:\n {v}" | ||
| ) | ||
|
|
||
| for i, v in enumerate(tested_not_expected): | ||
| if len(v) == 0: | ||
| continue | ||
|
|
||
| result = False | ||
| print( | ||
| f"{ANSI_RED}[repr error]{ANSI_END} The following variables were tested, but do not \ | ||
| exist in the input data at breakpoint#{i}:\n {v}" | ||
| ) | ||
|
|
||
| return result |
There was a problem hiding this comment.
These were just moved from check_lldb.py.
I might be able to factor more of the checking logic out and share it between lldb and gdb. There are less differences for their API than I was expecting. It would probably require passing a few wrappers to the check function that abstract away the debugger-specific accesses, but we'll see.
| gdb.args(debugger_opts).env("PYTHONPATH", pythonpath); | ||
| gdb.args(debugger_opts) | ||
| .env("PYTHONPATH", pythonpath) | ||
| .env("BATCHMODE_DEBUGGER", "gdb") |
There was a problem hiding this comment.
The bike-shedding I mentioned more or less has to do with these env vars and the lldb_batchmode package. With GDB support, we'll probably want to re-name lldb_batchmode (and probably rename runner.py to lldb_batchmode.py), but I'm not super happy with any of the names that came to mind. Maybe just batchmode?
This comment has been minimized.
This comment has been minimized.
|
Oh yeah, i need to gate it so it doesn't check for the file path until a |
Part of #148483 and followup to #158298. Applies (approximately) identical logic to GDB.
I still need to test things on
windows-gnu(and locally try a few more tests to see if there's any glaring issues). There's also a few bike-sheddy things.The main way this differs from the LLDB implementation is that GDB doesn't use
lldb_batchmode/runner.pyto orchestrate the commands. Instead, I havecompiletestimportfrom_gdb.pywhich registers a customreprCLI command. GDB's batch processing works as normal, but dispatches to our logic automatically whenever it encountersrepr <var_name>.An additional
repr-finalizeCLI command is also registered to implement the checks that ensure we encountered all expected types/vars, and verifies that there were no errors before saving blessed data.r? @Kobzol, @jieyouxu