diff options
author | Tobias Hieta <tobias@hieta.se> | 2023-05-17 16:59:41 +0200 |
---|---|---|
committer | Tobias Hieta <tobias@hieta.se> | 2023-05-25 11:17:05 +0200 |
commit | f98ee40f4b5d7474fc67e82824bf6abbaedb7b1c (patch) | |
tree | 98a310ca2d529f568804993c98d6a301f9f2886e /cross-project-tests | |
parent | 35ea4e2dbf32b338c14a2facfc603812e78c8239 (diff) | |
download | llvm-f98ee40f4b5d7474fc67e82824bf6abbaedb7b1c.zip llvm-f98ee40f4b5d7474fc67e82824bf6abbaedb7b1c.tar.gz llvm-f98ee40f4b5d7474fc67e82824bf6abbaedb7b1c.tar.bz2 |
[NFC][Py Reformat] Reformat python files in the rest of the dirs
This is an ongoing series of commits that are reformatting our
Python code. This catches the last of the python files to
reformat. Since they where so few I bunched them together.
Reformatting is done with `black`.
If you end up having problems merging this commit because you
have made changes to a python file, the best way to handle that
is to run git checkout --ours <yourfile> and then reformat it
with black.
If you run into any problems, post to discourse about it and
we will try to help.
RFC Thread below:
https://discourse.llvm.org/t/rfc-document-and-standardize-python-code-style
Reviewed By: jhenderson, #libc, Mordante, sivachandra
Differential Revision: https://reviews.llvm.org/D150784
Diffstat (limited to 'cross-project-tests')
91 files changed, 3929 insertions, 3232 deletions
diff --git a/cross-project-tests/amdgpu/lit.local.cfg b/cross-project-tests/amdgpu/lit.local.cfg index 231c8ae..23bb1a5 100644 --- a/cross-project-tests/amdgpu/lit.local.cfg +++ b/cross-project-tests/amdgpu/lit.local.cfg @@ -1,2 +1,2 @@ -if 'clang' not in config.available_features or 'AMDGPU' not in config.targets_to_build: +if "clang" not in config.available_features or "AMDGPU" not in config.targets_to_build: config.unsupported = True diff --git a/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/lit.local.cfg b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/lit.local.cfg index 3ce1bcf..2f7d525 100644 --- a/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/lit.local.cfg +++ b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/lit.local.cfg @@ -1,3 +1,3 @@ # In MSVC mode DWARF isn't produced & is needed for these tests -if 'native' not in config.available_features or config.is_msvc: +if "native" not in config.available_features or config.is_msvc: config.unsupported = True diff --git a/cross-project-tests/debuginfo-tests/dexter-tests/lit.local.cfg b/cross-project-tests/debuginfo-tests/dexter-tests/lit.local.cfg index a081e8e..bace385 100644 --- a/cross-project-tests/debuginfo-tests/dexter-tests/lit.local.cfg +++ b/cross-project-tests/debuginfo-tests/dexter-tests/lit.local.cfg @@ -1,2 +1,2 @@ -if 'dexter' not in config.available_features: +if "dexter" not in config.available_features: config.unsupported = True diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/__init__.py b/cross-project-tests/debuginfo-tests/dexter/dex/__init__.py index d2a290b..e79a8b3 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/__init__.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/__init__.py @@ -5,4 +5,4 @@ # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -__version__ = '1.0.0' +__version__ = "1.0.0" diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/builder/Builder.py b/cross-project-tests/debuginfo-tests/dexter/dex/builder/Builder.py index 153c768..1d64876 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/builder/Builder.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/builder/Builder.py @@ -16,40 +16,44 @@ from dex.utils.Exceptions import BuildScriptException def _quotify(text): - if '"' in text or ' ' not in text: + if '"' in text or " " not in text: return text return '"{}"'.format(text) -def _get_script_environment(source_files, compiler_options, - linker_options, executable_file): +def _get_script_environment( + source_files, compiler_options, linker_options, executable_file +): source_files = [_quotify(f) for f in source_files] - object_files = [ - _quotify('{}.o'.format(os.path.basename(f))) for f in source_files - ] - source_indexes = ['{:02d}'.format(i + 1) for i in range(len(source_files))] + object_files = [_quotify("{}.o".format(os.path.basename(f))) for f in source_files] + source_indexes = ["{:02d}".format(i + 1) for i in range(len(source_files))] env_variables = {} - env_variables['SOURCE_INDEXES'] = ' '.join(source_indexes) - env_variables['SOURCE_FILES'] = ' '.join(source_files) - env_variables['OBJECT_FILES'] = ' '.join(object_files) - env_variables['LINKER_OPTIONS'] = linker_options + env_variables["SOURCE_INDEXES"] = " ".join(source_indexes) + env_variables["SOURCE_FILES"] = " ".join(source_files) + env_variables["OBJECT_FILES"] = " ".join(object_files) + env_variables["LINKER_OPTIONS"] = linker_options for i, _ in enumerate(source_files): index = source_indexes[i] - env_variables['SOURCE_FILE_{}'.format(index)] = source_files[i] - env_variables['OBJECT_FILE_{}'.format(index)] = object_files[i] - env_variables['COMPILER_OPTIONS_{}'.format(index)] = compiler_options[i] + env_variables["SOURCE_FILE_{}".format(index)] = source_files[i] + env_variables["OBJECT_FILE_{}".format(index)] = object_files[i] + env_variables["COMPILER_OPTIONS_{}".format(index)] = compiler_options[i] - env_variables['EXECUTABLE_FILE'] = executable_file + env_variables["EXECUTABLE_FILE"] = executable_file return env_variables -def run_external_build_script(context, script_path, source_files, - compiler_options, linker_options, - executable_file): +def run_external_build_script( + context, + script_path, + source_files, + compiler_options, + linker_options, + executable_file, +): """Build an executable using a builder script. The executable is saved to `context.working_directory.path`. @@ -63,57 +67,61 @@ def run_external_build_script(context, script_path, source_files, cflags=compiler_options, ldflags=linker_options, ) - assert len(source_files) == len(compiler_options), (source_files, - compiler_options) + assert len(source_files) == len(compiler_options), (source_files, compiler_options) - script_environ = _get_script_environment(source_files, compiler_options, - linker_options, executable_file) + script_environ = _get_script_environment( + source_files, compiler_options, linker_options, executable_file + ) env = dict(os.environ) env.update(script_environ) try: - with Timer('running build script'): + with Timer("running build script"): process = subprocess.Popen( [script_path], cwd=context.working_directory.path, env=env, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stderr=subprocess.PIPE, + ) out, err = process.communicate() returncode = process.returncode - out = out.decode('utf-8') - err = err.decode('utf-8') + out = out.decode("utf-8") + err = err.decode("utf-8") if returncode != 0: raise BuildScriptException( - '{}: failed with returncode {}.\nstdout:\n{}\n\nstderr:\n{}\n'. - format(script_path, returncode, out, err), - script_error=err) + "{}: failed with returncode {}.\nstdout:\n{}\n\nstderr:\n{}\n".format( + script_path, returncode, out, err + ), + script_error=err, + ) return out, err, builderIR except OSError as e: - raise BuildScriptException('{}: {}'.format(e.strerror, script_path)) + raise BuildScriptException("{}: {}".format(e.strerror, script_path)) class TestBuilder(unittest.TestCase): def test_get_script_environment(self): - source_files = ['a.a', 'b.b'] - compiler_options = ['-option1 value1', '-option2 value2'] - linker_options = '-optionX valueX' - executable_file = 'exe.exe' - env = _get_script_environment(source_files, compiler_options, - linker_options, executable_file) + source_files = ["a.a", "b.b"] + compiler_options = ["-option1 value1", "-option2 value2"] + linker_options = "-optionX valueX" + executable_file = "exe.exe" + env = _get_script_environment( + source_files, compiler_options, linker_options, executable_file + ) - assert env['SOURCE_FILES'] == 'a.a b.b' - assert env['OBJECT_FILES'] == 'a.a.o b.b.o' + assert env["SOURCE_FILES"] == "a.a b.b" + assert env["OBJECT_FILES"] == "a.a.o b.b.o" - assert env['SOURCE_INDEXES'] == '01 02' - assert env['LINKER_OPTIONS'] == '-optionX valueX' + assert env["SOURCE_INDEXES"] == "01 02" + assert env["LINKER_OPTIONS"] == "-optionX valueX" - assert env['SOURCE_FILE_01'] == 'a.a' - assert env['SOURCE_FILE_02'] == 'b.b' + assert env["SOURCE_FILE_01"] == "a.a" + assert env["SOURCE_FILE_02"] == "b.b" - assert env['OBJECT_FILE_01'] == 'a.a.o' - assert env['OBJECT_FILE_02'] == 'b.b.o' + assert env["OBJECT_FILE_01"] == "a.a.o" + assert env["OBJECT_FILE_02"] == "b.b.o" - assert env['EXECUTABLE_FILE'] == 'exe.exe' + assert env["EXECUTABLE_FILE"] == "exe.exe" - assert env['COMPILER_OPTIONS_01'] == '-option1 value1' - assert env['COMPILER_OPTIONS_02'] == '-option2 value2' + assert env["COMPILER_OPTIONS_01"] == "-option1 value1" + assert env["COMPILER_OPTIONS_02"] == "-option2 value2" diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/builder/ParserOptions.py b/cross-project-tests/debuginfo-tests/dexter/dex/builder/ParserOptions.py index 27683f4..1416243 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/builder/ParserOptions.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/builder/ParserOptions.py @@ -21,17 +21,18 @@ def _find_build_scripts(): try: return _find_build_scripts.cached except AttributeError: - scripts_directory = os.path.join(os.path.dirname(__file__), 'scripts') + scripts_directory = os.path.join(os.path.dirname(__file__), "scripts") if is_native_windows(): - scripts_directory = os.path.join(scripts_directory, 'windows') + scripts_directory = os.path.join(scripts_directory, "windows") else: - scripts_directory = os.path.join(scripts_directory, 'posix') + scripts_directory = os.path.join(scripts_directory, "posix") assert os.path.isdir(scripts_directory), scripts_directory results = {} for f in os.listdir(scripts_directory): results[os.path.splitext(f)[0]] = os.path.abspath( - os.path.join(scripts_directory, f)) + os.path.join(scripts_directory, f) + ) _find_build_scripts.cached = results return results @@ -39,20 +40,23 @@ def _find_build_scripts(): def add_builder_tool_arguments(parser): build_group = parser.add_mutually_exclusive_group(required=True) - build_group.add_argument('--binary', - metavar="<file>", - help='provide binary file instead of --builder') + build_group.add_argument( + "--binary", metavar="<file>", help="provide binary file instead of --builder" + ) build_group.add_argument( - '--builder', + "--builder", type=str, choices=sorted(_find_build_scripts().keys()), - help='test builder to use') - build_group.add_argument('--vs-solution', metavar="<file>", - help='provide a path to an already existing visual studio solution.') - parser.add_argument( - '--cflags', type=str, default='', help='compiler flags') - parser.add_argument('--ldflags', type=str, default='', help='linker flags') + help="test builder to use", + ) + build_group.add_argument( + "--vs-solution", + metavar="<file>", + help="provide a path to an already existing visual studio solution.", + ) + parser.add_argument("--cflags", type=str, default="", help="compiler flags") + parser.add_argument("--ldflags", type=str, default="", help="linker flags") def handle_builder_tool_options(context: Context) -> str: diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/CommandBase.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/CommandBase.py index fdeb97f..b52544d 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/CommandBase.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/CommandBase.py @@ -13,13 +13,14 @@ import abc from collections import namedtuple from typing import List -StepExpectInfo = namedtuple('StepExpectInfo', 'expression, path, frame_idx, line_range') +StepExpectInfo = namedtuple("StepExpectInfo", "expression, path, frame_idx, line_range") + class CommandBase(object, metaclass=abc.ABCMeta): def __init__(self): self.path = None self.lineno = None - self.raw_text = '' + self.raw_text = "" def get_label_args(self): return list() diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py index 1027ba0..5afefb1 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py @@ -26,7 +26,10 @@ from dex.command.commands.DexExpectStepKind import DexExpectStepKind from dex.command.commands.DexExpectStepOrder import DexExpectStepOrder from dex.command.commands.DexExpectWatchType import DexExpectWatchType from dex.command.commands.DexExpectWatchValue import DexExpectWatchValue -from dex.command.commands.DexExpectWatchBase import AddressExpression, DexExpectWatchBase +from dex.command.commands.DexExpectWatchBase import ( + AddressExpression, + DexExpectWatchBase, +) from dex.command.commands.DexLabel import DexLabel from dex.command.commands.DexLimitSteps import DexLimitSteps from dex.command.commands.DexFinishTest import DexFinishTest @@ -35,6 +38,7 @@ from dex.command.commands.DexWatch import DexWatch from dex.utils import Timer from dex.utils.Exceptions import CommandParseError, DebuggerException + def _get_valid_commands(): """Return all top level DExTer test commands. @@ -42,19 +46,19 @@ def _get_valid_commands(): { name (str): command (class) } """ return { - DexCommandLine.get_name() : DexCommandLine, - DexDeclareAddress.get_name() : DexDeclareAddress, - DexDeclareFile.get_name() : DexDeclareFile, - DexExpectProgramState.get_name() : DexExpectProgramState, - DexExpectStepKind.get_name() : DexExpectStepKind, - DexExpectStepOrder.get_name() : DexExpectStepOrder, - DexExpectWatchType.get_name() : DexExpectWatchType, - DexExpectWatchValue.get_name() : DexExpectWatchValue, - DexLabel.get_name() : DexLabel, - DexLimitSteps.get_name() : DexLimitSteps, - DexFinishTest.get_name() : DexFinishTest, - DexUnreachable.get_name() : DexUnreachable, - DexWatch.get_name() : DexWatch + DexCommandLine.get_name(): DexCommandLine, + DexDeclareAddress.get_name(): DexDeclareAddress, + DexDeclareFile.get_name(): DexDeclareFile, + DexExpectProgramState.get_name(): DexExpectProgramState, + DexExpectStepKind.get_name(): DexExpectStepKind, + DexExpectStepOrder.get_name(): DexExpectStepOrder, + DexExpectWatchType.get_name(): DexExpectWatchType, + DexExpectWatchValue.get_name(): DexExpectWatchValue, + DexLabel.get_name(): DexLabel, + DexLimitSteps.get_name(): DexLimitSteps, + DexFinishTest.get_name(): DexFinishTest, + DexUnreachable.get_name(): DexUnreachable, + DexWatch.get_name(): DexWatch, } @@ -63,7 +67,7 @@ def _get_command_name(command_raw: str) -> str: command_raw on the first opening paranthesis and further stripping any potential leading or trailing whitespace. """ - return command_raw.split('(', 1)[0].rstrip() + return command_raw.split("(", 1)[0].rstrip() def _merge_subcommands(command_name: str, valid_commands: dict) -> dict: @@ -74,11 +78,13 @@ def _merge_subcommands(command_name: str, valid_commands: dict) -> dict: """ subcommands = valid_commands[command_name].get_subcommands() if subcommands: - return { **valid_commands, **subcommands } + return {**valid_commands, **subcommands} return valid_commands -def _build_command(command_type, labels, addresses, raw_text: str, path: str, lineno: str) -> CommandBase: +def _build_command( + command_type, labels, addresses, raw_text: str, path: str, lineno: str +) -> CommandBase: """Build a command object from raw text. This function will call eval(). @@ -89,23 +95,26 @@ def _build_command(command_type, labels, addresses, raw_text: str, path: str, li Returns: A dexter command object. """ + def label_to_line(label_name: str) -> int: line = labels.get(label_name, None) if line != None: return line raise format_unresolved_label_err(label_name, raw_text, path, lineno) - def get_address_object(address_name: str, offset: int=0): + def get_address_object(address_name: str, offset: int = 0): if address_name not in addresses: raise format_undeclared_address_err(address_name, raw_text, path, lineno) return AddressExpression(address_name, offset) valid_commands = _merge_subcommands( - command_type.get_name(), { - 'ref': label_to_line, - 'address': get_address_object, + command_type.get_name(), + { + "ref": label_to_line, + "address": get_address_object, command_type.get_name(): command_type, - }) + }, + ) # pylint: disable=eval-used command = eval(raw_text, valid_commands) @@ -130,7 +139,7 @@ def _search_line_for_cmd_start(line: str, start: int, valid_commands: dict) -> i idx = line.find(command, start) if idx != -1: # Ignore escaped '\' commands. - if idx > 0 and line[idx - 1] == '\\': + if idx > 0 and line[idx - 1] == "\\": continue return idx return -1 @@ -158,17 +167,17 @@ def _search_line_for_cmd_end(line: str, start: int, paren_balance: int) -> (int, """ for end in range(start, len(line)): ch = line[end] - if ch == '(': + if ch == "(": paren_balance += 1 - elif ch == ')': - paren_balance -=1 + elif ch == ")": + paren_balance -= 1 if paren_balance == 0: break end += 1 return (end, paren_balance) -class TextPoint(): +class TextPoint: def __init__(self, line, char): self.line = line self.char = char @@ -180,37 +189,45 @@ class TextPoint(): return self.char + 1 -def format_unresolved_label_err(label: str, src: str, filename: str, lineno) -> CommandParseError: +def format_unresolved_label_err( + label: str, src: str, filename: str, lineno +) -> CommandParseError: err = CommandParseError() err.src = src - err.caret = '' # Don't bother trying to point to the bad label. + err.caret = "" # Don't bother trying to point to the bad label. err.filename = filename err.lineno = lineno - err.info = f'Unresolved label: \'{label}\'' + err.info = f"Unresolved label: '{label}'" return err -def format_undeclared_address_err(address: str, src: str, filename: str, lineno) -> CommandParseError: + +def format_undeclared_address_err( + address: str, src: str, filename: str, lineno +) -> CommandParseError: err = CommandParseError() err.src = src - err.caret = '' # Don't bother trying to point to the bad address. + err.caret = "" # Don't bother trying to point to the bad address. err.filename = filename err.lineno = lineno - err.info = f'Undeclared address: \'{address}\'' + err.info = f"Undeclared address: '{address}'" return err -def format_parse_err(msg: str, path: str, lines: list, point: TextPoint) -> CommandParseError: + +def format_parse_err( + msg: str, path: str, lines: list, point: TextPoint +) -> CommandParseError: err = CommandParseError() err.filename = path err.src = lines[point.line].rstrip() err.lineno = point.get_lineno() err.info = msg - err.caret = '{}<r>^</>'.format(' ' * (point.char)) + err.caret = "{}<r>^</>".format(" " * (point.char)) return err def skip_horizontal_whitespace(line, point): - for idx, char in enumerate(line[point.char:]): - if char not in ' \t': + for idx, char in enumerate(line[point.char :]): + if char not in " \t": point.char += idx return @@ -219,34 +236,36 @@ def add_line_label(labels, label, cmd_path, cmd_lineno): # Enforce unique line labels. if label.eval() in labels: err = CommandParseError() - err.info = f'Found duplicate line label: \'{label.eval()}\'' + err.info = f"Found duplicate line label: '{label.eval()}'" err.lineno = cmd_lineno err.filename = cmd_path err.src = label.raw_text # Don't both trying to point to it since we're only printing the raw # command, which isn't much text. - err.caret = '' + err.caret = "" raise err labels[label.eval()] = label.get_line() + def add_address(addresses, address, cmd_path, cmd_lineno): # Enforce unique address variables. address_name = address.get_address_name() if address_name in addresses: err = CommandParseError() - err.info = f'Found duplicate address: \'{address_name}\'' + err.info = f"Found duplicate address: '{address_name}'" err.lineno = cmd_lineno err.filename = cmd_path err.src = address.raw_text # Don't both trying to point to it since we're only printing the raw # command, which isn't much text. - err.caret = '' + err.caret = "" raise err addresses.append(address_name) + def _find_all_commands_in_file(path, file_lines, valid_commands, source_root_dir): - labels = {} # dict of {name: line}. - addresses = [] # list of addresses. + labels = {} # dict of {name: line}. + addresses = [] # list of addresses. address_resolutions = {} cmd_path = path declared_files = set() @@ -262,23 +281,30 @@ def _find_all_commands_in_file(path, file_lines, valid_commands, source_root_dir while True: # If parens are currently balanced we can look for a new command. if paren_balance == 0: - region_start.char = _search_line_for_cmd_start(line, region_start.char, valid_commands) + region_start.char = _search_line_for_cmd_start( + line, region_start.char, valid_commands + ) if region_start.char == -1: - break # Read next line. + break # Read next line. - command_name = _get_command_name(line[region_start.char:]) + command_name = _get_command_name(line[region_start.char :]) cmd_point = copy(region_start) cmd_text_list = [command_name] - region_start.char += len(command_name) # Start searching for parens after cmd. + region_start.char += len( + command_name + ) # Start searching for parens after cmd. skip_horizontal_whitespace(line, region_start) - if region_start.char >= len(line) or line[region_start.char] != '(': + if region_start.char >= len(line) or line[region_start.char] != "(": raise format_parse_err( - "Missing open parenthesis", path, file_lines, region_start) + "Missing open parenthesis", path, file_lines, region_start + ) - end, paren_balance = _search_line_for_cmd_end(line, region_start.char, paren_balance) + end, paren_balance = _search_line_for_cmd_end( + line, region_start.char, paren_balance + ) # Add this text blob to the command. - cmd_text_list.append(line[region_start.char:end]) + cmd_text_list.append(line[region_start.char : end]) # Move parse ptr to end of line or parens. region_start.char = end @@ -302,8 +328,8 @@ def _find_all_commands_in_file(path, file_lines, valid_commands, source_root_dir # This err should point to the problem line. err_point = copy(cmd_point) # To e the command start is the absolute start, so use as offset. - err_point.line += e.lineno - 1 # e.lineno is a position, not index. - err_point.char += e.offset - 1 # e.offset is a position, not index. + err_point.line += e.lineno - 1 # e.lineno is a position, not index. + err_point.char += e.offset - 1 # e.offset is a position, not index. raise format_parse_err(e.msg, path, file_lines, err_point) except TypeError as e: # This err should always point to the end of the command name. @@ -322,18 +348,23 @@ def _find_all_commands_in_file(path, file_lines, valid_commands, source_root_dir elif type(command) is DexDeclareFile: cmd_path = command.declared_file if not os.path.isabs(cmd_path): - source_dir = (source_root_dir if source_root_dir else - os.path.dirname(path)) + source_dir = ( + source_root_dir + if source_root_dir + else os.path.dirname(path) + ) cmd_path = os.path.join(source_dir, cmd_path) # TODO: keep stored paths as PurePaths for 'longer'. cmd_path = str(PurePath(cmd_path)) declared_files.add(cmd_path) - elif type(command) is DexCommandLine and 'DexCommandLine' in commands: + elif type(command) is DexCommandLine and "DexCommandLine" in commands: msg = "More than one DexCommandLine in file" raise format_parse_err(msg, path, file_lines, err_point) assert (path, cmd_point) not in commands[command_name], ( - command_name, commands[command_name]) + command_name, + commands[command_name], + ) commands[command_name][path, cmd_point] = command if paren_balance != 0: @@ -344,6 +375,7 @@ def _find_all_commands_in_file(path, file_lines, valid_commands, source_root_dir raise format_parse_err(msg, path, file_lines, err_point) return dict(commands), declared_files + def _find_all_commands(test_files, source_root_dir): commands = defaultdict(dict) valid_commands = _get_valid_commands() @@ -352,28 +384,32 @@ def _find_all_commands(test_files, source_root_dir): with open(test_file) as fp: lines = fp.readlines() file_commands, declared_files = _find_all_commands_in_file( - test_file, lines, valid_commands, source_root_dir) + test_file, lines, valid_commands, source_root_dir + ) for command_name in file_commands: commands[command_name].update(file_commands[command_name]) new_source_files |= declared_files return dict(commands), new_source_files + def get_command_infos(test_files, source_root_dir): - with Timer('parsing commands'): - try: - commands, new_source_files = _find_all_commands(test_files, source_root_dir) - command_infos = OrderedDict() - for command_type in commands: - for command in commands[command_type].values(): - if command_type not in command_infos: - command_infos[command_type] = [] - command_infos[command_type].append(command) - return OrderedDict(command_infos), new_source_files - except CommandParseError as e: - msg = 'parser error: <d>{}({}):</> {}\n{}\n{}\n'.format( - e.filename, e.lineno, e.info, e.src, e.caret) - raise DebuggerException(msg) + with Timer("parsing commands"): + try: + commands, new_source_files = _find_all_commands(test_files, source_root_dir) + command_infos = OrderedDict() + for command_type in commands: + for command in commands[command_type].values(): + if command_type not in command_infos: + command_infos[command_type] = [] + command_infos[command_type].append(command) + return OrderedDict(command_infos), new_source_files + except CommandParseError as e: + msg = "parser error: <d>{}({}):</> {}\n{}\n{}\n".format( + e.filename, e.lineno, e.info, e.src, e.caret + ) + raise DebuggerException(msg) + class TestParseCommand(unittest.TestCase): class MockCmd(CommandBase): @@ -384,7 +420,7 @@ class TestParseCommand(unittest.TestCase): """ def __init__(self, *args): - self.value = args[0] + self.value = args[0] def get_name(): return __class__.__name__ @@ -392,25 +428,24 @@ class TestParseCommand(unittest.TestCase): def eval(this): pass - def __init__(self, *args): super().__init__(*args) self.valid_commands = { - TestParseCommand.MockCmd.get_name() : TestParseCommand.MockCmd + TestParseCommand.MockCmd.get_name(): TestParseCommand.MockCmd } - def _find_all_commands_in_lines(self, lines): """Use DExTer parsing methods to find all the mock commands in lines. Returns: { cmd_name: { (path, line): command_obj } } """ - cmds, declared_files = _find_all_commands_in_file(__file__, lines, self.valid_commands, None) + cmds, declared_files = _find_all_commands_in_file( + __file__, lines, self.valid_commands, None + ) return cmds - def _find_all_mock_values_in_lines(self, lines): """Use DExTer parsing methods to find all mock command values in lines. @@ -421,36 +456,34 @@ class TestParseCommand(unittest.TestCase): mocks = cmds.get(TestParseCommand.MockCmd.get_name(), None) return [v.value for v in mocks.values()] if mocks else [] - def test_parse_inline(self): """Commands can be embedded in other text.""" lines = [ 'MockCmd("START") Lorem ipsum dolor sit amet, consectetur\n', 'adipiscing elit, MockCmd("EMBEDDED") sed doeiusmod tempor,\n', - 'incididunt ut labore et dolore magna aliqua.\n' + "incididunt ut labore et dolore magna aliqua.\n", ] values = self._find_all_mock_values_in_lines(lines) - self.assertTrue('START' in values) - self.assertTrue('EMBEDDED' in values) - + self.assertTrue("START" in values) + self.assertTrue("EMBEDDED" in values) def test_parse_multi_line_comment(self): """Multi-line commands can embed comments.""" lines = [ - 'Lorem ipsum dolor sit amet, consectetur\n', - 'adipiscing elit, sed doeiusmod tempor,\n', - 'incididunt ut labore et MockCmd(\n', + "Lorem ipsum dolor sit amet, consectetur\n", + "adipiscing elit, sed doeiusmod tempor,\n", + "incididunt ut labore et MockCmd(\n", ' "WITH_COMMENT" # THIS IS A COMMENT\n', - ') dolore magna aliqua. Ut enim ad minim\n', + ") dolore magna aliqua. Ut enim ad minim\n", ] values = self._find_all_mock_values_in_lines(lines) - self.assertTrue('WITH_COMMENT' in values) + self.assertTrue("WITH_COMMENT" in values) def test_parse_empty(self): """Empty files are silently ignored.""" @@ -462,7 +495,7 @@ class TestParseCommand(unittest.TestCase): def test_parse_bad_whitespace(self): """Throw exception when parsing badly formed whitespace.""" lines = [ - 'MockCmd\n', + "MockCmd\n", '("XFAIL_CMD_LF_PAREN")\n', ] @@ -478,42 +511,38 @@ class TestParseCommand(unittest.TestCase): 'MockCmd\t\t("TABS")\n', 'MockCmd( "ARG_SPACE" )\n', 'MockCmd(\t\t"ARG_TABS"\t\t)\n', - 'MockCmd(\n', + "MockCmd(\n", '"CMD_PAREN_LF")\n', ] values = self._find_all_mock_values_in_lines(lines) - self.assertTrue('NONE' in values) - self.assertTrue('SPACE' in values) - self.assertTrue('TABS' in values) - self.assertTrue('ARG_SPACE' in values) - self.assertTrue('ARG_TABS' in values) - self.assertTrue('CMD_PAREN_LF' in values) - + self.assertTrue("NONE" in values) + self.assertTrue("SPACE" in values) + self.assertTrue("TABS" in values) + self.assertTrue("ARG_SPACE" in values) + self.assertTrue("ARG_TABS" in values) + self.assertTrue("CMD_PAREN_LF" in values) def test_parse_share_line(self): """More than one command can appear on one line.""" lines = [ 'MockCmd("START") MockCmd("CONSECUTIVE") words ' - 'MockCmd("EMBEDDED") more words\n' + 'MockCmd("EMBEDDED") more words\n' ] values = self._find_all_mock_values_in_lines(lines) - self.assertTrue('START' in values) - self.assertTrue('CONSECUTIVE' in values) - self.assertTrue('EMBEDDED' in values) - + self.assertTrue("START" in values) + self.assertTrue("CONSECUTIVE" in values) + self.assertTrue("EMBEDDED" in values) def test_parse_escaped(self): """Escaped commands are ignored.""" - lines = [ - 'words \MockCmd("IGNORED") words words words\n' - ] + lines = ['words \MockCmd("IGNORED") words words words\n'] values = self._find_all_mock_values_in_lines(lines) - self.assertFalse('IGNORED' in values) + self.assertFalse("IGNORED" in values) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/StepValueInfo.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/StepValueInfo.py index afcb9c5..92cd576 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/StepValueInfo.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/StepValueInfo.py @@ -13,11 +13,15 @@ class StepValueInfo(object): self.expected_value = expected_value def __str__(self): - return '{}:{}: expected value:{}'.format(self.step_index, self.watch_info, self.expected_value) + return "{}:{}: expected value:{}".format( + self.step_index, self.watch_info, self.expected_value + ) def __eq__(self, other): - return (self.watch_info.expression == other.watch_info.expression - and self.expected_value == other.expected_value) + return ( + self.watch_info.expression == other.watch_info.expression + and self.expected_value == other.expected_value + ) def __hash__(self): return hash(self.watch_info.expression, self.expected_value) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexCommandLine.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexCommandLine.py index 76b2ae5..77615e7 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexCommandLine.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexCommandLine.py @@ -9,18 +9,21 @@ from dex.command.CommandBase import CommandBase + class DexCommandLine(CommandBase): def __init__(self, the_cmdline): if type(the_cmdline) is not list: - raise TypeError('Expected list, got {}'.format(type(the_cmdline))) + raise TypeError("Expected list, got {}".format(type(the_cmdline))) for x in the_cmdline: - if type(x) is not str: - raise TypeError('Command line element "{}" has type {}'.format(x, type(x))) + if type(x) is not str: + raise TypeError( + 'Command line element "{}" has type {}'.format(x, type(x)) + ) self.the_cmdline = the_cmdline super(DexCommandLine, self).__init__() def eval(self): - raise NotImplementedError('DexCommandLine commands cannot be evaled.') + raise NotImplementedError("DexCommandLine commands cannot be evaled.") @staticmethod def get_name(): diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexDeclareAddress.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexDeclareAddress.py index 29d2dc7..dcf5aea 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexDeclareAddress.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexDeclareAddress.py @@ -12,16 +12,17 @@ from pathlib import PurePath from dex.command.CommandBase import CommandBase, StepExpectInfo + class DexDeclareAddress(CommandBase): def __init__(self, addr_name, expression, **kwargs): if not isinstance(addr_name, str): - raise TypeError('invalid argument type') + raise TypeError("invalid argument type") self.addr_name = addr_name self.expression = expression - self.on_line = kwargs.pop('on_line') - self.hit_count = kwargs.pop('hit_count', 0) + self.on_line = kwargs.pop("on_line") + self.hit_count = kwargs.pop("hit_count", 0) self.address_resolutions = None @@ -32,7 +33,11 @@ class DexDeclareAddress(CommandBase): return __class__.__name__ def get_watches(self): - return [StepExpectInfo(self.expression, self.path, 0, range(self.on_line, self.on_line + 1))] + return [ + StepExpectInfo( + self.expression, self.path, 0, range(self.on_line, self.on_line + 1) + ) + ] def get_address_name(self): return self.addr_name @@ -42,9 +47,12 @@ class DexDeclareAddress(CommandBase): for step in step_collection.steps: loc = step.current_location - if (loc.path and self.path and - PurePath(loc.path) == PurePath(self.path) and - loc.lineno == self.on_line): + if ( + loc.path + and self.path + and PurePath(loc.path) == PurePath(self.path) + and loc.lineno == self.on_line + ): if self.hit_count > 0: self.hit_count -= 1 continue diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexDeclareFile.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexDeclareFile.py index c40c854..1377408 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexDeclareFile.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexDeclareFile.py @@ -16,7 +16,7 @@ class DexDeclareFile(CommandBase): def __init__(self, declared_file): if not isinstance(declared_file, str): - raise TypeError('invalid argument type') + raise TypeError("invalid argument type") # Use PurePath to create a cannonical platform path. # TODO: keep paths as PurePath objects for 'longer' diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectProgramState.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectProgramState.py index 24b760a..54c62a1 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectProgramState.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectProgramState.py @@ -13,18 +13,21 @@ from itertools import chain from dex.command.CommandBase import CommandBase, StepExpectInfo from dex.dextIR import ProgramState, SourceLocation, StackFrame, DextIR + def frame_from_dict(source: dict) -> StackFrame: - if 'location' in source: - assert isinstance(source['location'], dict) - source['location'] = SourceLocation(**source['location']) + if "location" in source: + assert isinstance(source["location"], dict) + source["location"] = SourceLocation(**source["location"]) return StackFrame(**source) + def state_from_dict(source: dict) -> ProgramState: - if 'frames' in source: - assert isinstance(source['frames'], list) - source['frames'] = list(map(frame_from_dict, source['frames'])) + if "frames" in source: + assert isinstance(source["frames"], list) + source["frames"] = list(map(frame_from_dict, source["frames"])) return ProgramState(**source) + class DexExpectProgramState(CommandBase): """Expect to see a given program `state` a certain numer of `times`. @@ -35,16 +38,15 @@ class DexExpectProgramState(CommandBase): def __init__(self, *args, **kwargs): if len(args) != 1: - raise TypeError('expected exactly one unnamed arg') + raise TypeError("expected exactly one unnamed arg") self.program_state_text = str(args[0]) self.expected_program_state = state_from_dict(args[0]) - self.times = kwargs.pop('times', -1) + self.times = kwargs.pop("times", -1) if kwargs: - raise TypeError('unexpected named args: {}'.format( - ', '.join(kwargs))) + raise TypeError("unexpected named args: {}".format(", ".join(kwargs))) # Step indices at which the expected program state was encountered. self.encounters = [] @@ -58,18 +60,23 @@ class DexExpectProgramState(CommandBase): def get_watches(self): frame_expects = set() for idx, frame in enumerate(self.expected_program_state.frames): - path = (frame.location.path if - frame.location and frame.location.path else self.path) + path = ( + frame.location.path + if frame.location and frame.location.path + else self.path + ) line_range = ( range(frame.location.lineno, frame.location.lineno + 1) - if frame.location and frame.location.lineno else None) + if frame.location and frame.location.lineno + else None + ) for watch in frame.watches: frame_expects.add( StepExpectInfo( expression=watch, path=path, frame_idx=idx, - line_range=line_range + line_range=line_range, ) ) return frame_expects @@ -79,4 +86,6 @@ class DexExpectProgramState(CommandBase): if self.expected_program_state.match(step.program_state): self.encounters.append(step.step_index) - return self.times < 0 < len(self.encounters) or len(self.encounters) == self.times + return ( + self.times < 0 < len(self.encounters) or len(self.encounters) == self.times + ) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectStepKind.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectStepKind.py index 6370f5d..333b765 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectStepKind.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectStepKind.py @@ -21,13 +21,16 @@ class DexExpectStepKind(CommandBase): def __init__(self, *args): if len(args) != 2: - raise TypeError('expected two args') + raise TypeError("expected two args") try: step_kind = StepKind[args[0]] except KeyError: - raise TypeError('expected arg 0 to be one of {}'.format( - [kind for kind, _ in StepKind.__members__.items()])) + raise TypeError( + "expected arg 0 to be one of {}".format( + [kind for kind, _ in StepKind.__members__.items()] + ) + ) self.name = step_kind self.count = args[1] diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectStepOrder.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectStepOrder.py index d5cfc3c..cb5579b 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectStepOrder.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectStepOrder.py @@ -9,6 +9,7 @@ from dex.command.CommandBase import CommandBase from dex.dextIR import LocIR from dex.dextIR import ValueIR + class DexExpectStepOrder(CommandBase): """Expect the line every `DexExpectStepOrder` is found on to be stepped on in `order`. Each instance must have a set of unique ascending indicies. @@ -20,14 +21,16 @@ class DexExpectStepOrder(CommandBase): def __init__(self, *args, **kwargs): if not args: - raise TypeError('Need at least one order number') + raise TypeError("Need at least one order number") - if 'on_line' in kwargs: + if "on_line" in kwargs: try: - on_line = kwargs.pop('on_line') + on_line = kwargs.pop("on_line") self.on_line = int(on_line) except ValueError: - raise ValueError('on_line value \'{0}\' cannot be parsed to an integer'.format(on_line)) + raise ValueError( + "on_line value '{0}' cannot be parsed to an integer".format(on_line) + ) self.sequence = [int(x) for x in args] super(DexExpectStepOrder, self).__init__() @@ -36,12 +39,17 @@ class DexExpectStepOrder(CommandBase): return __class__.__name__ def get_line(self): - return self.on_line if hasattr(self, 'on_line') else self.lineno + return self.on_line if hasattr(self, "on_line") else self.lineno def eval(self, step_info): - return {'DexExpectStepOrder': ValueIR(expression=str(step_info.current_location.lineno), - value=str(step_info.step_index), type_name=None, - error_string=None, - could_evaluate=True, - is_optimized_away=True, - is_irretrievable=False)} + return { + "DexExpectStepOrder": ValueIR( + expression=str(step_info.current_location.lineno), + value=str(step_info.step_index), + type_name=None, + error_string=None, + could_evaluate=True, + is_optimized_away=True, + is_irretrievable=False, + ) + } diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py index e79b1bb..21f1b71 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py @@ -20,6 +20,7 @@ from dex.command.CommandBase import CommandBase, StepExpectInfo from dex.command.StepValueInfo import StepValueInfo from dex.utils.Exceptions import NonFloatValueInCommand + class AddressExpression(object): def __init__(self, name, offset=0): self.name = name @@ -36,37 +37,47 @@ class AddressExpression(object): # Technically we should fill(8) if we're debugging on a 32bit architecture? return format_address(resolutions[self.name] + self.offset) + def format_address(value, address_width=64): - return "0x" + hex(value)[2:].zfill(math.ceil(address_width/4)) + return "0x" + hex(value)[2:].zfill(math.ceil(address_width / 4)) + def resolved_value(value, resolutions): - return value.resolved_value(resolutions) if isinstance(value, AddressExpression) else value + return ( + value.resolved_value(resolutions) + if isinstance(value, AddressExpression) + else value + ) + class DexExpectWatchBase(CommandBase): def __init__(self, *args, **kwargs): if len(args) < 2: - raise TypeError('expected at least two args') + raise TypeError("expected at least two args") self.expression = args[0] - self.values = [arg if isinstance(arg, AddressExpression) else str(arg) for arg in args[1:]] + self.values = [ + arg if isinstance(arg, AddressExpression) else str(arg) for arg in args[1:] + ] try: - on_line = kwargs.pop('on_line') + on_line = kwargs.pop("on_line") self._from_line = on_line self._to_line = on_line except KeyError: - self._from_line = kwargs.pop('from_line', 1) - self._to_line = kwargs.pop('to_line', 999999) - self._require_in_order = kwargs.pop('require_in_order', True) - self.float_range = kwargs.pop('float_range', None) + self._from_line = kwargs.pop("from_line", 1) + self._to_line = kwargs.pop("to_line", 999999) + self._require_in_order = kwargs.pop("require_in_order", True) + self.float_range = kwargs.pop("float_range", None) if self.float_range is not None: for value in self.values: try: float(value) except ValueError: - raise NonFloatValueInCommand(f'Non-float value \'{value}\' when float_range arg provided') + raise NonFloatValueInCommand( + f"Non-float value '{value}' when float_range arg provided" + ) if kwargs: - raise TypeError('unexpected named args: {}'.format( - ', '.join(kwargs))) + raise TypeError("unexpected named args: {}".format(", ".join(kwargs))) # Number of times that this watch has been encountered. self.times_encountered = 0 @@ -108,7 +119,11 @@ class DexExpectWatchBase(CommandBase): super(DexExpectWatchBase, self).__init__() def resolve_value(self, value): - return value.resolved_value(self.address_resolutions) if isinstance(value, AddressExpression) else value + return ( + value.resolved_value(self.address_resolutions) + if isinstance(value, AddressExpression) + else value + ) def describe_value(self, value): if isinstance(value, AddressExpression): @@ -117,14 +132,18 @@ class DexExpectWatchBase(CommandBase): offset = f"+{value.offset}" elif value.offset < 0: offset = str(value.offset) - desc = f"address '{value.name}'{offset}" + desc = f"address '{value.name}'{offset}" if self.resolve_value(value) is not None: desc += f" ({self.resolve_value(value)})" return desc return value def get_watches(self): - return [StepExpectInfo(self.expression, self.path, 0, range(self._from_line, self._to_line + 1))] + return [ + StepExpectInfo( + self.expression, self.path, 0, range(self._from_line, self._to_line + 1) + ) + ] @property def line_range(self): @@ -136,12 +155,18 @@ class DexExpectWatchBase(CommandBase): @property def encountered_values(self): - return sorted(list(set(self.describe_value(v) for v in set(self.values) - self._missing_values))) + return sorted( + list( + set( + self.describe_value(v) + for v in set(self.values) - self._missing_values + ) + ) + ) @abc.abstractmethod def _get_expected_field(self, watch): - """Return a field from watch that this ExpectWatch command is checking. - """ + """Return a field from watch that this ExpectWatch command is checking.""" def _match_expected_floating_point(self, value): """Checks to see whether value is a float that falls within the @@ -155,13 +180,13 @@ class DexExpectWatchBase(CommandBase): possible_values = self.values for expected in possible_values: - try: - expected_as_float = float(expected) - difference = abs(value_as_float - expected_as_float) - if difference <= self.float_range: - return expected - except ValueError: - pass + try: + expected_as_float = float(expected) + difference = abs(value_as_float - expected_as_float) + if difference <= self.float_range: + return expected + except ValueError: + pass return value def _maybe_fix_float(self, value): @@ -190,9 +215,11 @@ class DexExpectWatchBase(CommandBase): # Check to see if this value matches with a resolved address. matching_address = None for v in self.values: - if (isinstance(v, AddressExpression) and - v.name in self.address_resolutions and - self.resolve_value(v) == expected_value): + if ( + isinstance(v, AddressExpression) + and v.name in self.address_resolutions + and self.resolve_value(v) == expected_value + ): matching_address = v break @@ -203,7 +230,9 @@ class DexExpectWatchBase(CommandBase): return self.expected_watches.append(step_info) - value_to_remove = matching_address if matching_address is not None else expected_value + value_to_remove = ( + matching_address if matching_address is not None else expected_value + ) try: self._missing_values.remove(value_to_remove) except KeyError: @@ -214,20 +243,23 @@ class DexExpectWatchBase(CommandBase): or not. """ differences = [] - actual_values = [self._maybe_fix_float(w.expected_value) for w in actual_watches] - value_differences = list(difflib.Differ().compare(actual_values, - expected_values)) + actual_values = [ + self._maybe_fix_float(w.expected_value) for w in actual_watches + ] + value_differences = list( + difflib.Differ().compare(actual_values, expected_values) + ) missing_value = False index = 0 for vd in value_differences: kind = vd[0] - if kind == '+': + if kind == "+": # A value that is encountered in the expected list but not in the # actual list. We'll keep a note that something is wrong and flag # the next value that matches as misordered. missing_value = True - elif kind == ' ': + elif kind == " ": # This value is as expected. It might still be wrong if we've # previously encountered a value that is in the expected list but # not the actual list. @@ -235,13 +267,13 @@ class DexExpectWatchBase(CommandBase): missing_value = False differences.append(actual_watches[index]) index += 1 - elif kind == '-': + elif kind == "-": # A value that is encountered in the actual list but not the # expected list. differences.append(actual_watches[index]) index += 1 else: - assert False, 'unexpected diff:{}'.format(vd) + assert False, "unexpected diff:{}".format(vd) return differences @@ -249,17 +281,19 @@ class DexExpectWatchBase(CommandBase): for step in step_collection.steps: loc = step.current_location - if (loc.path and self.path and - PurePath(loc.path) == PurePath(self.path) and - loc.lineno in self.line_range): + if ( + loc.path + and self.path + and PurePath(loc.path) == PurePath(self.path) + and loc.lineno in self.line_range + ): try: watch = step.program_state.frames[0].watches[self.expression] except KeyError: pass else: expected_field = self._get_expected_field(watch) - step_info = StepValueInfo(step.step_index, watch, - expected_field) + step_info = StepValueInfo(step.step_index, watch, expected_field) self._handle_watch(step_info) if self._require_in_order: @@ -276,6 +310,6 @@ class DexExpectWatchBase(CommandBase): resolved_values = [self.resolve_value(v) for v in self.values] self.misordered_watches = self._check_watch_order( - value_change_watches, [ - v for v in resolved_values if v in all_expected_values - ]) + value_change_watches, + [v for v in resolved_values if v in all_expected_values], + ) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchType.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchType.py index f2336de..0f0c65f 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchType.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchType.py @@ -9,6 +9,7 @@ from dex.command.commands.DexExpectWatchBase import DexExpectWatchBase + class DexExpectWatchType(DexExpectWatchBase): """Expect the expression `expr` to evaluate be evaluated and have each evaluation's type checked against the list of `types`. @@ -18,6 +19,7 @@ class DexExpectWatchType(DexExpectWatchBase): See Commands.md for more info. """ + @staticmethod def get_name(): return __class__.__name__ diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchValue.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchValue.py index d6da006..df5bb09 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchValue.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchValue.py @@ -9,6 +9,7 @@ from dex.command.commands.DexExpectWatchBase import DexExpectWatchBase + class DexExpectWatchValue(DexExpectWatchBase): """Expect the expression `expr` to evaluate to the list of `values` sequentially. diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexFinishTest.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexFinishTest.py index 5014317..7a28f1c 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexFinishTest.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexFinishTest.py @@ -10,6 +10,7 @@ breakpoint has been hit a number of times. from dex.command.CommandBase import CommandBase + class DexFinishTest(CommandBase): def __init__(self, *args, **kwargs): if len(args) == 0: @@ -20,15 +21,14 @@ class DexFinishTest(CommandBase): else: self.expression = args[0] self.values = [str(arg) for arg in args[1:]] - self.on_line = kwargs.pop('on_line') - self.hit_count = kwargs.pop('hit_count', 0) + self.on_line = kwargs.pop("on_line") + self.hit_count = kwargs.pop("hit_count", 0) if kwargs: - raise TypeError('unexpected named args: {}'.format( - ', '.join(kwargs))) + raise TypeError("unexpected named args: {}".format(", ".join(kwargs))) super(DexFinishTest, self).__init__() def eval(self): - raise NotImplementedError('DexFinishTest commands cannot be evaled.') + raise NotImplementedError("DexFinishTest commands cannot be evaled.") @staticmethod def get_name(): diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexLabel.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexLabel.py index 9f42d42..575e5ea 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexLabel.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexLabel.py @@ -15,10 +15,10 @@ class DexLabel(CommandBase): def __init__(self, label, **kwargs): if not isinstance(label, str): - raise TypeError('invalid argument type') + raise TypeError("invalid argument type") try: - self.on_line = kwargs.pop('on_line') + self.on_line = kwargs.pop("on_line") except KeyError: # We cannot use self.lineno because it hasn't been set yet. pass @@ -29,7 +29,7 @@ class DexLabel(CommandBase): super(DexLabel, self).__init__() def get_line(self): - return getattr(self, 'on_line', self.lineno) + return getattr(self, "on_line", self.lineno) def get_as_pair(self): return (self._label, self.get_line()) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py index d779539..1c6aee1 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexLimitSteps.py @@ -10,6 +10,7 @@ points using an start condition and range. from dex.command.CommandBase import CommandBase + class DexLimitSteps(CommandBase): def __init__(self, *args, **kwargs): if len(args) == 0: @@ -21,20 +22,19 @@ class DexLimitSteps(CommandBase): self.expression = args[0] self.values = [str(arg) for arg in args[1:]] try: - on_line = kwargs.pop('on_line') + on_line = kwargs.pop("on_line") self.from_line = on_line self.to_line = on_line except KeyError: - self.from_line = kwargs.pop('from_line', 1) - self.to_line = kwargs.pop('to_line', 999999) - self.hit_count = kwargs.pop('hit_count', None) + self.from_line = kwargs.pop("from_line", 1) + self.to_line = kwargs.pop("to_line", 999999) + self.hit_count = kwargs.pop("hit_count", None) if kwargs: - raise TypeError('unexpected named args: {}'.format( - ', '.join(kwargs))) + raise TypeError("unexpected named args: {}".format(", ".join(kwargs))) super(DexLimitSteps, self).__init__() def eval(self): - raise NotImplementedError('DexLimitSteps commands cannot be evaled.') + raise NotImplementedError("DexLimitSteps commands cannot be evaled.") @staticmethod def get_name(): diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexUnreachable.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexUnreachable.py index 5b40ba0..8356ed7 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexUnreachable.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexUnreachable.py @@ -21,14 +21,14 @@ class DexUnreachable(CommandBase): def __init__(self, *args, **kwargs): if len(args) != 0: raise TypeError("DexUnreachable takes no positional arguments") - if 'on_line' in kwargs: - on_line = kwargs.pop('on_line') + if "on_line" in kwargs: + on_line = kwargs.pop("on_line") self._from_line = on_line self._to_line = on_line - elif 'from_line' in kwargs and 'to_line' in kwargs: - self._from_line = kwargs.pop('from_line') - self._to_line = kwargs.pop('to_line') - elif 'from_line' in kwargs or 'to_line' in kwargs: + elif "from_line" in kwargs and "to_line" in kwargs: + self._from_line = kwargs.pop("from_line") + self._to_line = kwargs.pop("to_line") + elif "from_line" in kwargs or "to_line" in kwargs: raise TypeError("Must provide both from_line and to_line to DexUnreachable") if len(kwargs) > 0: @@ -43,10 +43,13 @@ class DexUnreachable(CommandBase): def eval(self, step_info): # If we're ever called, at all, then we're evaluating a line that has # been marked as unreachable. Which means a failure. - vir = ValueIR(expression="Unreachable", - value="True", type_name=None, - error_string=None, - could_evaluate=True, - is_optimized_away=True, - is_irretrievable=False) - return {'DexUnreachable' : vir} + vir = ValueIR( + expression="Unreachable", + value="True", + type_name=None, + error_string=None, + could_evaluate=True, + is_optimized_away=True, + is_irretrievable=False, + ) + return {"DexUnreachable": vir} diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexWatch.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexWatch.py index 2dfa3a3..62f12c3 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexWatch.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexWatch.py @@ -22,11 +22,11 @@ class DexWatch(CommandBase): def __init__(self, *args): if not args: - raise TypeError('expected some arguments') + raise TypeError("expected some arguments") for arg in args: if not isinstance(arg, str): - raise TypeError('invalid argument type') + raise TypeError("invalid argument type") self._args = args super(DexWatch, self).__init__() diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py index 8a18ee0..d4a5550 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerBase.py @@ -18,14 +18,14 @@ from dex.dextIR import DebuggerIR, FrameIR, LocIR, StepIR, ValueIR from dex.utils.Exceptions import DebuggerException from dex.utils.ReturnCode import ReturnCode + def watch_is_active(watch_info: StepExpectInfo, path, frame_idx, line_no): _, watch_path, watch_frame_idx, watch_line_range = watch_info # If this watch should only be active for a specific file... if watch_path and os.path.isfile(watch_path): # If the current path does not match the expected file, this watch is # not active. - if not (path and os.path.isfile(path) and - os.path.samefile(path, watch_path)): + if not (path and os.path.isfile(path) and os.path.samefile(path, watch_path)): return False if watch_frame_idx != frame_idx: return False @@ -33,6 +33,7 @@ def watch_is_active(watch_info: StepExpectInfo, path, frame_idx, line_no): return False return True + class DebuggerBase(object, metaclass=abc.ABCMeta): def __init__(self, context): self.context = context @@ -77,8 +78,7 @@ class DebuggerBase(object, metaclass=abc.ABCMeta): @property def loading_error(self): - return (str(self._loading_error[1]) - if self._loading_error is not None else None) + return str(self._loading_error[1]) if self._loading_error is not None else None @property def loading_error_trace(self): @@ -89,13 +89,14 @@ class DebuggerBase(object, metaclass=abc.ABCMeta): if self._loading_error[1].orig_exception is not None: orig_exception = traceback.format_exception( - *self._loading_error[1].orig_exception) + *self._loading_error[1].orig_exception + ) - if ''.join(orig_exception) not in ''.join(tb): - tb.extend(['\n']) + if "".join(orig_exception) not in "".join(tb): + tb.extend(["\n"]) tb.extend(orig_exception) - tb = ''.join(tb).splitlines(True) + tb = "".join(tb).splitlines(True) return tb def _sanitize_function_name(self, name): # pylint: disable=no-self-use @@ -147,8 +148,7 @@ class DebuggerBase(object, metaclass=abc.ABCMeta): @abc.abstractmethod def _add_breakpoint(self, file_, line): - """Returns a unique opaque breakpoint id. - """ + """Returns a unique opaque breakpoint id.""" pass def add_conditional_breakpoint(self, file_, line, condition): @@ -158,12 +158,12 @@ class DebuggerBase(object, metaclass=abc.ABCMeta): an int. """ return self._add_conditional_breakpoint( - self._external_to_debug_path(file_), line, condition) + self._external_to_debug_path(file_), line, condition + ) @abc.abstractmethod def _add_conditional_breakpoint(self, file_, line, condition): - """Returns a unique opaque breakpoint id. - """ + """Returns a unique opaque breakpoint id.""" pass @abc.abstractmethod @@ -176,8 +176,7 @@ class DebuggerBase(object, metaclass=abc.ABCMeta): @abc.abstractmethod def get_triggered_breakpoint_ids(self): - """Returns a set of opaque ids for just-triggered breakpoints. - """ + """Returns a set of opaque ids for just-triggered breakpoints.""" pass @abc.abstractmethod @@ -225,7 +224,7 @@ class DebuggerBase(object, metaclass=abc.ABCMeta): if not root_dir or not path: return path assert path.startswith(root_dir) - return path[len(root_dir):].lstrip(os.path.sep) + return path[len(root_dir) :].lstrip(os.path.sep) def _debug_to_external_path(self, path): if not self.options.debugger_use_relative_paths: @@ -237,10 +236,9 @@ class DebuggerBase(object, metaclass=abc.ABCMeta): return file return path -class TestDebuggerBase(unittest.TestCase): +class TestDebuggerBase(unittest.TestCase): class MockDebugger(DebuggerBase): - def __init__(self, context, *args): super().__init__(context, *args) self.step_info = None @@ -255,8 +253,8 @@ class TestDebuggerBase(unittest.TestCase): def __init__(self, *args): super().__init__(*args) TestDebuggerBase.MockDebugger.__abstractmethods__ = set() - self.options = SimpleNamespace(source_root_dir = '', source_files = []) - context = SimpleNamespace(options = self.options) + self.options = SimpleNamespace(source_root_dir="", source_files=[]) + context = SimpleNamespace(options=self.options) self.dbg = TestDebuggerBase.MockDebugger(context) def _new_step(self, paths): @@ -264,7 +262,9 @@ class TestDebuggerBase(unittest.TestCase): FrameIR( function=None, is_inlined=False, - loc=LocIR(path=path, lineno=0, column=0)) for path in paths + loc=LocIR(path=path, lineno=0, column=0), + ) + for path in paths ] return StepIR(step_index=0, stop_reason=None, frames=frames) @@ -273,47 +273,45 @@ class TestDebuggerBase(unittest.TestCase): def test_add_breakpoint_no_source_root_dir(self): self.options.debugger_use_relative_paths = True - self.options.source_root_dir = '' - path = os.path.join(os.path.sep + 'root', 'some_file') + self.options.source_root_dir = "" + path = os.path.join(os.path.sep + "root", "some_file") self.dbg.add_breakpoint(path, 12) self.assertEqual(path, self.dbg.breakpoint_file) def test_add_breakpoint_with_source_root_dir(self): self.options.debugger_use_relative_paths = True - self.options.source_root_dir = os.path.sep + 'my_root' - path = os.path.join(self.options.source_root_dir, 'some_file') + self.options.source_root_dir = os.path.sep + "my_root" + path = os.path.join(self.options.source_root_dir, "some_file") self.dbg.add_breakpoint(path, 12) - self.assertEqual('some_file', self.dbg.breakpoint_file) + self.assertEqual("some_file", self.dbg.breakpoint_file) def test_add_breakpoint_with_source_root_dir_slash_suffix(self): self.options.debugger_use_relative_paths = True - self.options.source_root_dir = os.path.sep + 'my_root' + os.path.sep - path = os.path.join(self.options.source_root_dir, 'some_file') + self.options.source_root_dir = os.path.sep + "my_root" + os.path.sep + path = os.path.join(self.options.source_root_dir, "some_file") self.dbg.add_breakpoint(path, 12) - self.assertEqual('some_file', self.dbg.breakpoint_file) + self.assertEqual("some_file", self.dbg.breakpoint_file) def test_get_step_info_no_source_root_dir(self): self.options.debugger_use_relative_paths = True - path = os.path.join(os.path.sep + 'root', 'some_file') + path = os.path.join(os.path.sep + "root", "some_file") self.dbg.step_info = self._new_step([path]) - self.assertEqual([path], - self._step_paths(self.dbg.get_step_info([], 0))) + self.assertEqual([path], self._step_paths(self.dbg.get_step_info([], 0))) def test_get_step_info_no_frames(self): self.options.debugger_use_relative_paths = True - self.options.source_root_dir = os.path.sep + 'my_root' + self.options.source_root_dir = os.path.sep + "my_root" self.dbg.step_info = self._new_step([]) - self.assertEqual([], - self._step_paths(self.dbg.get_step_info([], 0))) + self.assertEqual([], self._step_paths(self.dbg.get_step_info([], 0))) def test_get_step_info(self): self.options.debugger_use_relative_paths = True - self.options.source_root_dir = os.path.sep + 'my_root' - path = os.path.join(self.options.source_root_dir, 'some_file') + self.options.source_root_dir = os.path.sep + "my_root" + path = os.path.join(self.options.source_root_dir, "some_file") self.options.source_files = [path] - other_path = os.path.join(os.path.sep + 'other', 'file') - dbg_path = os.path.join(os.path.sep + 'dbg', 'some_file') - self.dbg.step_info = self._new_step( - [None, other_path, dbg_path]) - self.assertEqual([None, other_path, path], - self._step_paths(self.dbg.get_step_info([], 0))) + other_path = os.path.join(os.path.sep + "other", "file") + dbg_path = os.path.join(os.path.sep + "dbg", "some_file") + self.dbg.step_info = self._new_step([None, other_path, dbg_path]) + self.assertEqual( + [None, other_path, path], self._step_paths(self.dbg.get_step_info([], 0)) + ) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py index c2e97e3..fb5536d 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ConditionalController.py @@ -12,8 +12,13 @@ import time from collections import defaultdict from itertools import chain -from dex.debugger.DebuggerControllers.ControllerHelpers import in_source_file, update_step_watches -from dex.debugger.DebuggerControllers.DebuggerControllerBase import DebuggerControllerBase +from dex.debugger.DebuggerControllers.ControllerHelpers import ( + in_source_file, + update_step_watches, +) +from dex.debugger.DebuggerControllers.DebuggerControllerBase import ( + DebuggerControllerBase, +) from dex.debugger.DebuggerBase import DebuggerBase from dex.utils.Exceptions import DebuggerException from dex.utils.Timeout import Timeout @@ -37,8 +42,16 @@ class BreakpointRange: leading breakpoint is triggered before it is removed. """ - def __init__(self, expression: str, path: str, range_from: int, range_to: int, - values: list, hit_count: int, finish_on_remove: bool): + def __init__( + self, + expression: str, + path: str, + range_from: int, + range_to: int, + values: list, + hit_count: int, + finish_on_remove: bool, + ): self.expression = expression self.path = path self.range_from = range_from @@ -55,7 +68,7 @@ class BreakpointRange: conditional_list = [] for value in self.conditional_values: # (<expression>) == (<value>) - conditional_expression = '({}) == ({})'.format(self.expression, value) + conditional_expression = "({}) == ({})".format(self.expression, value) conditional_list.append(conditional_expression) return conditional_list @@ -70,44 +83,48 @@ class BreakpointRange: class ConditionalController(DebuggerControllerBase): def __init__(self, context, step_collection): - self._bp_ranges = None - self._watches = set() - self._step_index = 0 - self._pause_between_steps = context.options.pause_between_steps - self._max_steps = context.options.max_steps - # Map {id: BreakpointRange} - self._leading_bp_handles = {} - super(ConditionalController, self).__init__(context, step_collection) - self._build_bp_ranges() + self._bp_ranges = None + self._watches = set() + self._step_index = 0 + self._pause_between_steps = context.options.pause_between_steps + self._max_steps = context.options.max_steps + # Map {id: BreakpointRange} + self._leading_bp_handles = {} + super(ConditionalController, self).__init__(context, step_collection) + self._build_bp_ranges() def _build_bp_ranges(self): commands = self.step_collection.commands self._bp_ranges = [] try: - limit_commands = commands['DexLimitSteps'] + limit_commands = commands["DexLimitSteps"] for lc in limit_commands: bpr = BreakpointRange( - lc.expression, - lc.path, - lc.from_line, - lc.to_line, - lc.values, - lc.hit_count, - False) + lc.expression, + lc.path, + lc.from_line, + lc.to_line, + lc.values, + lc.hit_count, + False, + ) self._bp_ranges.append(bpr) except KeyError: - raise DebuggerException('Missing DexLimitSteps commands, cannot conditionally step.') - if 'DexFinishTest' in commands: - finish_commands = commands['DexFinishTest'] + raise DebuggerException( + "Missing DexLimitSteps commands, cannot conditionally step." + ) + if "DexFinishTest" in commands: + finish_commands = commands["DexFinishTest"] for ic in finish_commands: bpr = BreakpointRange( - ic.expression, - ic.path, - ic.on_line, - ic.on_line, - ic.values, - ic.hit_count + 1, - True) + ic.expression, + ic.path, + ic.on_line, + ic.on_line, + ic.values, + ic.hit_count + 1, + True, + ) self._bp_ranges.append(bpr) def _set_leading_bps(self): @@ -117,9 +134,9 @@ class ConditionalController(DebuggerControllerBase): if bpr.has_conditions(): # Add a conditional breakpoint for each condition. for cond_expr in bpr.get_conditional_expression_list(): - id = self.debugger.add_conditional_breakpoint(bpr.path, - bpr.range_from, - cond_expr) + id = self.debugger.add_conditional_breakpoint( + bpr.path, bpr.range_from, cond_expr + ) self._leading_bp_handles[id] = bpr else: # Add an unconditional breakpoint. @@ -128,8 +145,10 @@ class ConditionalController(DebuggerControllerBase): def _run_debugger_custom(self, cmdline): # TODO: Add conditional and unconditional breakpoint support to dbgeng. - if self.debugger.get_name() == 'dbgeng': - raise DebuggerException('DexLimitSteps commands are not supported by dbgeng') + if self.debugger.get_name() == "dbgeng": + raise DebuggerException( + "DexLimitSteps commands are not supported by dbgeng" + ) self.step_collection.clear_steps() self._set_leading_bps() @@ -150,13 +169,17 @@ class ConditionalController(DebuggerControllerBase): while self.debugger.is_running and not timed_out: # Check to see whether we've timed out while we're waiting. if total_timeout.timed_out(): - self.context.logger.error('Debugger session has been ' - f'running for {total_timeout.elapsed}s, timeout reached!') + self.context.logger.error( + "Debugger session has been " + f"running for {total_timeout.elapsed}s, timeout reached!" + ) timed_out = True if breakpoint_timeout.timed_out(): - self.context.logger.error(f'Debugger session has not ' - f'hit a breakpoint for {breakpoint_timeout.elapsed}s, timeout ' - 'reached!') + self.context.logger.error( + f"Debugger session has not " + f"hit a breakpoint for {breakpoint_timeout.elapsed}s, timeout " + "reached!" + ) timed_out = True if timed_out: @@ -165,7 +188,9 @@ class ConditionalController(DebuggerControllerBase): step_info = self.debugger.get_step_info(self._watches, self._step_index) if step_info.current_frame: self._step_index += 1 - update_step_watches(step_info, self._watches, self.step_collection.commands) + update_step_watches( + step_info, self._watches, self.step_collection.commands + ) self.step_collection.new_step(self.context, step_info) bp_to_delete = [] diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ControllerHelpers.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ControllerHelpers.py index 8044f39..3e5a7b9 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ControllerHelpers.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/ControllerHelpers.py @@ -8,6 +8,7 @@ import os from itertools import chain + def in_source_file(source_files, step_info): if not step_info.current_frame: return False @@ -15,31 +16,34 @@ def in_source_file(source_files, step_info): return False if not os.path.exists(step_info.current_location.path): return False - return any(os.path.samefile(step_info.current_location.path, f) \ - for f in source_files) + return any( + os.path.samefile(step_info.current_location.path, f) for f in source_files + ) + def have_hit_line(watch, loc): - if hasattr(watch, 'on_line'): - return watch.on_line == loc.lineno - elif hasattr(watch, '_from_line'): - return watch._from_line <= loc.lineno and watch._to_line >= loc.lineno - elif watch.lineno == loc.lineno: - return True - return False + if hasattr(watch, "on_line"): + return watch.on_line == loc.lineno + elif hasattr(watch, "_from_line"): + return watch._from_line <= loc.lineno and watch._to_line >= loc.lineno + elif watch.lineno == loc.lineno: + return True + return False + def update_step_watches(step_info, watches, commands): - watch_cmds = ['DexUnreachable', 'DexExpectStepOrder'] - towatch = chain.from_iterable(commands[x] - for x in watch_cmds - if x in commands) + watch_cmds = ["DexUnreachable", "DexExpectStepOrder"] + towatch = chain.from_iterable(commands[x] for x in watch_cmds if x in commands) try: # Iterate over all watches of the types named in watch_cmds for watch in towatch: loc = step_info.current_location - if (loc.path != None - and os.path.exists(loc.path) - and os.path.samefile(watch.path, loc.path) - and have_hit_line(watch, loc)): + if ( + loc.path != None + and os.path.exists(loc.path) + and os.path.samefile(watch.path, loc.path) + and have_hit_line(watch, loc) + ): result = watch.eval(step_info) step_info.watches.update(result) break diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py index 8673453..4cd44bb 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DebuggerControllerBase.py @@ -8,6 +8,7 @@ import abc + class DebuggerControllerBase(object, metaclass=abc.ABCMeta): def __init__(self, context, step_collection): self.context = context @@ -21,15 +22,14 @@ class DebuggerControllerBase(object, metaclass=abc.ABCMeta): pass def run_debugger(self, debugger): - """Responsible for correctly launching and tearing down the debugger. - """ + """Responsible for correctly launching and tearing down the debugger.""" self.debugger = debugger # Fetch command line options, if any. the_cmdline = [] commands = self.step_collection.commands - if 'DexCommandLine' in commands: - cmd_line_objs = commands['DexCommandLine'] + if "DexCommandLine" in commands: + cmd_line_objs = commands["DexCommandLine"] assert len(cmd_line_objs) == 1 cmd_line_obj = cmd_line_objs[0] the_cmdline = cmd_line_obj.the_cmdline diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py index 1cc2c44..9b0a6ac 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/DebuggerControllers/DefaultController.py @@ -10,11 +10,17 @@ from itertools import chain import os import time -from dex.debugger.DebuggerControllers.DebuggerControllerBase import DebuggerControllerBase -from dex.debugger.DebuggerControllers.ControllerHelpers import in_source_file, update_step_watches +from dex.debugger.DebuggerControllers.DebuggerControllerBase import ( + DebuggerControllerBase, +) +from dex.debugger.DebuggerControllers.ControllerHelpers import ( + in_source_file, + update_step_watches, +) from dex.utils.Exceptions import DebuggerException, LoadDebuggerException from dex.utils.Timeout import Timeout + class EarlyExitCondition(object): def __init__(self, on_line, hit_count, expression, values): self.on_line = on_line @@ -22,6 +28,7 @@ class EarlyExitCondition(object): self.expression = expression self.values = values + class DefaultController(DebuggerControllerBase): def __init__(self, context, step_collection): self.source_files = context.options.source_files @@ -31,24 +38,26 @@ class DefaultController(DebuggerControllerBase): def _break_point_all_lines(self): for s in self.context.options.source_files: - with open(s, 'r') as fp: + with open(s, "r") as fp: num_lines = len(fp.readlines()) for line in range(1, num_lines + 1): try: - self.debugger.add_breakpoint(s, line) + self.debugger.add_breakpoint(s, line) except DebuggerException: - raise LoadDebuggerException(DebuggerException.msg) + raise LoadDebuggerException(DebuggerException.msg) def _get_early_exit_conditions(self): commands = self.step_collection.commands early_exit_conditions = [] - if 'DexFinishTest' in commands: - finish_commands = commands['DexFinishTest'] + if "DexFinishTest" in commands: + finish_commands = commands["DexFinishTest"] for fc in finish_commands: - condition = EarlyExitCondition(on_line=fc.on_line, - hit_count=fc.hit_count, - expression=fc.expression, - values=fc.values) + condition = EarlyExitCondition( + on_line=fc.on_line, + hit_count=fc.hit_count, + expression=fc.expression, + values=fc.values, + ) early_exit_conditions.append(condition) return early_exit_conditions @@ -61,8 +70,10 @@ class DefaultController(DebuggerControllerBase): # Conditional Controller, check equality in the debugger # rather than in python (as the two can differ). for value in condition.values: - expr_val = self.debugger.evaluate_expression(f'({condition.expression}) == ({value})') - if expr_val.value == 'true': + expr_val = self.debugger.evaluate_expression( + f"({condition.expression}) == ({value})" + ) + if expr_val.value == "true": exit_condition_hit = True break if exit_condition_hit: @@ -72,7 +83,6 @@ class DefaultController(DebuggerControllerBase): condition.hit_count -= 1 return False - def _run_debugger_custom(self, cmdline): self.step_collection.debugger = self.debugger.debugger_info self._break_point_all_lines() @@ -91,13 +101,17 @@ class DefaultController(DebuggerControllerBase): while self.debugger.is_running and not timed_out: # Check to see whether we've timed out while we're waiting. if total_timeout.timed_out(): - self.context.logger.error('Debugger session has been ' - f'running for {total_timeout.elapsed}s, timeout reached!') + self.context.logger.error( + "Debugger session has been " + f"running for {total_timeout.elapsed}s, timeout reached!" + ) timed_out = True if breakpoint_timeout.timed_out(): - self.context.logger.error(f'Debugger session has not ' - f'hit a breakpoint for {breakpoint_timeout.elapsed}s, timeout ' - 'reached!') + self.context.logger.error( + f"Debugger session has not " + f"hit a breakpoint for {breakpoint_timeout.elapsed}s, timeout " + "reached!" + ) timed_out = True if timed_out or self.debugger.is_finished: @@ -107,9 +121,13 @@ class DefaultController(DebuggerControllerBase): step_info = self.debugger.get_step_info(self.watches, self.step_index) if step_info.current_frame: - update_step_watches(step_info, self.watches, self.step_collection.commands) + update_step_watches( + step_info, self.watches, self.step_collection.commands + ) self.step_collection.new_step(self.context, step_info) - if self._should_exit(early_exit_conditions, step_info.current_frame.loc.lineno): + if self._should_exit( + early_exit_conditions, step_info.current_frame.loc.lineno + ): break if in_source_file(self.source_files, step_info): @@ -120,4 +138,5 @@ class DefaultController(DebuggerControllerBase): time.sleep(self.context.options.pause_between_steps) else: raise DebuggerException( - 'maximum number of steps reached ({})'.format(max_steps)) + "maximum number of steps reached ({})".format(max_steps) + ) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/Debuggers.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/Debuggers.py index c85310b..bee62c7 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/Debuggers.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/Debuggers.py @@ -39,29 +39,31 @@ def _get_potential_debuggers(): # noqa LLDB.get_option_name(): LLDB, VisualStudio2015.get_option_name(): VisualStudio2015, VisualStudio2017.get_option_name(): VisualStudio2017, - VisualStudio2019.get_option_name(): VisualStudio2019 + VisualStudio2019.get_option_name(): VisualStudio2019, } def _warn_meaningless_option(context, option): - if hasattr(context.options, 'list_debuggers'): + if hasattr(context.options, "list_debuggers"): return context.logger.warning( - f'option "{option}" is meaningless with this debugger', - enable_prefix=True, - flag=f'--debugger={context.options.debugger}') + f'option "{option}" is meaningless with this debugger', + enable_prefix=True, + flag=f"--debugger={context.options.debugger}", + ) def add_debugger_tool_base_arguments(parser, defaults): - defaults.lldb_executable = 'lldb.exe' if is_native_windows() else 'lldb' + defaults.lldb_executable = "lldb.exe" if is_native_windows() else "lldb" parser.add_argument( - '--lldb-executable', + "--lldb-executable", type=str, - metavar='<file>', + metavar="<file>", default=None, display_default=defaults.lldb_executable, - help='location of LLDB executable') + help="location of LLDB executable", + ) def add_debugger_tool_arguments(parser, context, defaults): @@ -71,71 +73,79 @@ def add_debugger_tool_arguments(parser, context, defaults): add_debugger_tool_base_arguments(parser, defaults) parser.add_argument( - '--debugger', + "--debugger", type=str, choices=potential_debuggers, required=True, - help='debugger to use') + help="debugger to use", + ) parser.add_argument( - '--max-steps', - metavar='<int>', + "--max-steps", + metavar="<int>", type=int, default=1000, - help='maximum number of program steps allowed') + help="maximum number of program steps allowed", + ) parser.add_argument( - '--pause-between-steps', - metavar='<seconds>', + "--pause-between-steps", + metavar="<seconds>", type=float, default=0.0, - help='number of seconds to pause between steps') + help="number of seconds to pause between steps", + ) defaults.show_debugger = False parser.add_argument( - '--show-debugger', - action='store_true', - default=None, - help='show the debugger') - defaults.arch = 'x86_64' + "--show-debugger", action="store_true", default=None, help="show the debugger" + ) + defaults.arch = "x86_64" parser.add_argument( - '--arch', + "--arch", type=str, - metavar='<architecture>', + metavar="<architecture>", default=None, display_default=defaults.arch, - help='target architecture') - defaults.source_root_dir = '' + help="target architecture", + ) + defaults.source_root_dir = "" parser.add_argument( - '--source-root-dir', + "--source-root-dir", type=str, - metavar='<directory>', + metavar="<directory>", default=None, - help='source root directory') + help="source root directory", + ) parser.add_argument( - '--debugger-use-relative-paths', - action='store_true', + "--debugger-use-relative-paths", + action="store_true", default=False, - help='pass the debugger paths relative to --source-root-dir') + help="pass the debugger paths relative to --source-root-dir", + ) parser.add_argument( - '--target-run-args', + "--target-run-args", type=str, - metavar='<flags>', - default='', - help='command line arguments for the test program, in addition to any ' - 'provided by DexCommandLine') + metavar="<flags>", + default="", + help="command line arguments for the test program, in addition to any " + "provided by DexCommandLine", + ) parser.add_argument( - '--timeout-total', - metavar='<seconds>', + "--timeout-total", + metavar="<seconds>", type=float, default=0.0, - help='if >0, debugger session will automatically exit after ' - 'running for <timeout-total> seconds') + help="if >0, debugger session will automatically exit after " + "running for <timeout-total> seconds", + ) parser.add_argument( - '--timeout-breakpoint', - metavar='<seconds>', + "--timeout-breakpoint", + metavar="<seconds>", type=float, default=0.0, - help='if >0, debugger session will automatically exit after ' - 'waiting <timeout-breakpoint> seconds without hitting a ' - 'breakpoint') + help="if >0, debugger session will automatically exit after " + "waiting <timeout-breakpoint> seconds without hitting a " + "breakpoint", + ) + def handle_debugger_tool_base_options(context, defaults): # noqa options = context.options @@ -143,13 +153,14 @@ def handle_debugger_tool_base_options(context, defaults): # noqa if options.lldb_executable is None: options.lldb_executable = defaults.lldb_executable else: - if getattr(options, 'debugger', 'lldb') != 'lldb': - _warn_meaningless_option(context, '--lldb-executable') + if getattr(options, "debugger", "lldb") != "lldb": + _warn_meaningless_option(context, "--lldb-executable") options.lldb_executable = os.path.abspath(options.lldb_executable) if not os.path.isfile(options.lldb_executable): - raise ToolArgumentError('<d>could not find</> <r>"{}"</>'.format( - options.lldb_executable)) + raise ToolArgumentError( + '<d>could not find</> <r>"{}"</>'.format(options.lldb_executable) + ) def handle_debugger_tool_options(context, defaults): # noqa @@ -160,53 +171,59 @@ def handle_debugger_tool_options(context, defaults): # noqa if options.arch is None: options.arch = defaults.arch else: - if options.debugger != 'lldb': - _warn_meaningless_option(context, '--arch') + if options.debugger != "lldb": + _warn_meaningless_option(context, "--arch") if options.show_debugger is None: options.show_debugger = defaults.show_debugger else: - if options.debugger == 'lldb': - _warn_meaningless_option(context, '--show-debugger') + if options.debugger == "lldb": + _warn_meaningless_option(context, "--show-debugger") if options.source_root_dir != None: if not os.path.isabs(options.source_root_dir): - raise ToolArgumentError(f'<d>--source-root-dir: expected absolute path, got</> <r>"{options.source_root_dir}"</>') + raise ToolArgumentError( + f'<d>--source-root-dir: expected absolute path, got</> <r>"{options.source_root_dir}"</>' + ) if not os.path.isdir(options.source_root_dir): - raise ToolArgumentError(f'<d>--source-root-dir: could not find directory</> <r>"{options.source_root_dir}"</>') + raise ToolArgumentError( + f'<d>--source-root-dir: could not find directory</> <r>"{options.source_root_dir}"</>' + ) if options.debugger_use_relative_paths: if not options.source_root_dir: - raise ToolArgumentError(f'<d>--debugger-relative-paths</> <r>requires --source-root-dir</>') + raise ToolArgumentError( + f"<d>--debugger-relative-paths</> <r>requires --source-root-dir</>" + ) + def run_debugger_subprocess(debugger_controller, working_dir_path): - with NamedTemporaryFile( - dir=working_dir_path, delete=False, mode='wb') as fp: + with NamedTemporaryFile(dir=working_dir_path, delete=False, mode="wb") as fp: pickle.dump(debugger_controller, fp, protocol=pickle.HIGHEST_PROTOCOL) controller_path = fp.name dexter_py = os.path.basename(sys.argv[0]) if not os.path.isfile(dexter_py): - dexter_py = os.path.join(get_root_directory(), '..', dexter_py) + dexter_py = os.path.join(get_root_directory(), "..", dexter_py) assert os.path.isfile(dexter_py) with NamedTemporaryFile(dir=working_dir_path) as fp: args = [ sys.executable, dexter_py, - 'run-debugger-internal-', + "run-debugger-internal-", controller_path, - '--working-directory={}'.format(working_dir_path), - '--unittest=off', - '--indent-timer-level={}'.format(Timer.indent + 2) + "--working-directory={}".format(working_dir_path), + "--unittest=off", + "--indent-timer-level={}".format(Timer.indent + 2), ] try: - with Timer('running external debugger process'): + with Timer("running external debugger process"): subprocess.check_call(args) except subprocess.CalledProcessError as e: raise DebuggerException(e) - with open(controller_path, 'rb') as fp: + with open(controller_path, "rb") as fp: debugger_controller = pickle.load(fp) return debugger_controller @@ -225,7 +242,7 @@ class Debuggers(object): self.context = context def load(self, key): - with Timer('load {}'.format(key)): + with Timer("load {}".format(key)): return Debuggers.potential_debuggers()[key](self.context) def _populate_debugger_cache(self): @@ -237,19 +254,19 @@ class Debuggers(object): pass LoadedDebugger.option_name = key - LoadedDebugger.full_name = '[{}]'.format(debugger.name) + LoadedDebugger.full_name = "[{}]".format(debugger.name) LoadedDebugger.is_available = debugger.is_available if LoadedDebugger.is_available: try: LoadedDebugger.version = debugger.version.splitlines() except AttributeError: - LoadedDebugger.version = [''] + LoadedDebugger.version = [""] else: try: LoadedDebugger.error = debugger.loading_error.splitlines() except AttributeError: - LoadedDebugger.error = [''] + LoadedDebugger.error = [""] try: LoadedDebugger.error_trace = debugger.loading_error_trace @@ -269,29 +286,27 @@ class Debuggers(object): for d in debuggers: # Option name, right padded with spaces for alignment - option_name = ( - '{{name: <{}}}'.format(max_o_len).format(name=d.option_name)) + option_name = "{{name: <{}}}".format(max_o_len).format(name=d.option_name) # Full name, right padded with spaces for alignment - full_name = ('{{name: <{}}}'.format(max_n_len) - .format(name=d.full_name)) + full_name = "{{name: <{}}}".format(max_n_len).format(name=d.full_name) if d.is_available: - name = '<b>{} {}</>'.format(option_name, full_name) + name = "<b>{} {}</>".format(option_name, full_name) # If the debugger is available, show the first line of the # version info. - available = '<g>YES</>' - info = '<b>({})</>'.format(d.version[0]) + available = "<g>YES</>" + info = "<b>({})</>".format(d.version[0]) else: - name = '<y>{} {}</>'.format(option_name, full_name) + name = "<y>{} {}</>".format(option_name, full_name) # If the debugger is not available, show the first line of the # error reason. - available = '<r>NO</> ' - info = '<y>({})</>'.format(d.error[0]) + available = "<r>NO</> " + info = "<y>({})</>".format(d.error[0]) - msg = '{} {} {}'.format(name, available, info) + msg = "{} {} {}".format(name, available, info) if self.context.options.verbose: # If verbose mode and there was more version or error output @@ -300,16 +315,18 @@ class Debuggers(object): verbose_info = None if d.is_available: if d.version[1:]: - verbose_info = d.version + ['\n'] + verbose_info = d.version + ["\n"] else: # Some of list elems may contain multiple lines, so make # sure each elem is a line of its own. verbose_info = d.error_trace if verbose_info: - verbose_info = '\n'.join(' {}'.format(l.rstrip()) - for l in verbose_info) + '\n' - msg = '{}\n\n{}'.format(msg, verbose_info) + verbose_info = ( + "\n".join(" {}".format(l.rstrip()) for l in verbose_info) + + "\n" + ) + msg = "{}\n\n{}".format(msg, verbose_info) msgs.append(msg) - self.context.o.auto('\n{}\n\n'.format('\n'.join(msgs))) + self.context.o.auto("\n{}\n\n".format("\n".join(msgs))) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/__init__.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/__init__.py index 394f9f0..6bd389a 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/__init__.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/__init__.py @@ -6,5 +6,7 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception from dex.debugger.Debuggers import Debuggers -from dex.debugger.DebuggerControllers.DebuggerControllerBase import DebuggerControllerBase +from dex.debugger.DebuggerControllers.DebuggerControllerBase import ( + DebuggerControllerBase, +) from dex.debugger.DebuggerControllers.DefaultController import DefaultController diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/__init__.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/__init__.py index 3c458f9..78e599d 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/__init__.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/__init__.py @@ -8,12 +8,13 @@ from . import dbgeng import platform -if platform.system() == 'Windows': - from . import breakpoint - from . import control - from . import probe_process - from . import setup - from . import symbols - from . import symgroup - from . import sysobjs - from . import utils + +if platform.system() == "Windows": + from . import breakpoint + from . import control + from . import probe_process + from . import setup + from . import symbols + from . import symgroup + from . import sysobjs + from . import utils diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/breakpoint.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/breakpoint.py index c966d8c..0bac873 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/breakpoint.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/breakpoint.py @@ -11,78 +11,90 @@ from functools import partial from .utils import * + class BreakpointTypes(IntEnum): - DEBUG_BREAKPOINT_CODE = 0 - DEBUG_BREAKPOINT_DATA = 1 - DEBUG_BREAKPOINT_TIME = 2 - DEBUG_BREAKPOINT_INLINE = 3 + DEBUG_BREAKPOINT_CODE = 0 + DEBUG_BREAKPOINT_DATA = 1 + DEBUG_BREAKPOINT_TIME = 2 + DEBUG_BREAKPOINT_INLINE = 3 + class BreakpointFlags(IntFlag): - DEBUG_BREAKPOINT_GO_ONLY = 0x00000001 - DEBUG_BREAKPOINT_DEFERRED = 0x00000002 - DEBUG_BREAKPOINT_ENABLED = 0x00000004 - DEBUG_BREAKPOINT_ADDER_ONLY = 0x00000008 - DEBUG_BREAKPOINT_ONE_SHOT = 0x00000010 + DEBUG_BREAKPOINT_GO_ONLY = 0x00000001 + DEBUG_BREAKPOINT_DEFERRED = 0x00000002 + DEBUG_BREAKPOINT_ENABLED = 0x00000004 + DEBUG_BREAKPOINT_ADDER_ONLY = 0x00000008 + DEBUG_BREAKPOINT_ONE_SHOT = 0x00000010 + + +DebugBreakpoint2IID = IID( + 0x1B278D20, + 0x79F2, + 0x426E, + IID_Data4_Type(0xA3, 0xF9, 0xC1, 0xDD, 0xF3, 0x75, 0xD4, 0x8E), +) -DebugBreakpoint2IID = IID(0x1b278d20, 0x79f2, 0x426e, IID_Data4_Type(0xa3, 0xf9, 0xc1, 0xdd, 0xf3, 0x75, 0xd4, 0x8e)) class DebugBreakpoint2(Structure): - pass + pass + class DebugBreakpoint2Vtbl(Structure): - wrp = partial(WINFUNCTYPE, c_long, POINTER(DebugBreakpoint2)) - idb_setoffset = wrp(c_ulonglong) - idb_setflags = wrp(c_ulong) - _fields_ = [ - ("QueryInterface", c_void_p), - ("AddRef", c_void_p), - ("Release", c_void_p), - ("GetId", c_void_p), - ("GetType", c_void_p), - ("GetAdder", c_void_p), - ("GetFlags", c_void_p), - ("AddFlags", c_void_p), - ("RemoveFlags", c_void_p), - ("SetFlags", idb_setflags), - ("GetOffset", c_void_p), - ("SetOffset", idb_setoffset), - ("GetDataParameters", c_void_p), - ("SetDataParameters", c_void_p), - ("GetPassCount", c_void_p), - ("SetPassCount", c_void_p), - ("GetCurrentPassCount", c_void_p), - ("GetMatchThreadId", c_void_p), - ("SetMatchThreadId", c_void_p), - ("GetCommand", c_void_p), - ("SetCommand", c_void_p), - ("GetOffsetExpression", c_void_p), - ("SetOffsetExpression", c_void_p), - ("GetParameters", c_void_p), - ("GetCommandWide", c_void_p), - ("SetCommandWide", c_void_p), - ("GetOffsetExpressionWide", c_void_p), - ("SetOffsetExpressionWide", c_void_p) + wrp = partial(WINFUNCTYPE, c_long, POINTER(DebugBreakpoint2)) + idb_setoffset = wrp(c_ulonglong) + idb_setflags = wrp(c_ulong) + _fields_ = [ + ("QueryInterface", c_void_p), + ("AddRef", c_void_p), + ("Release", c_void_p), + ("GetId", c_void_p), + ("GetType", c_void_p), + ("GetAdder", c_void_p), + ("GetFlags", c_void_p), + ("AddFlags", c_void_p), + ("RemoveFlags", c_void_p), + ("SetFlags", idb_setflags), + ("GetOffset", c_void_p), + ("SetOffset", idb_setoffset), + ("GetDataParameters", c_void_p), + ("SetDataParameters", c_void_p), + ("GetPassCount", c_void_p), + ("SetPassCount", c_void_p), + ("GetCurrentPassCount", c_void_p), + ("GetMatchThreadId", c_void_p), + ("SetMatchThreadId", c_void_p), + ("GetCommand", c_void_p), + ("SetCommand", c_void_p), + ("GetOffsetExpression", c_void_p), + ("SetOffsetExpression", c_void_p), + ("GetParameters", c_void_p), + ("GetCommandWide", c_void_p), + ("SetCommandWide", c_void_p), + ("GetOffsetExpressionWide", c_void_p), + ("SetOffsetExpressionWide", c_void_p), ] + DebugBreakpoint2._fields_ = [("lpVtbl", POINTER(DebugBreakpoint2Vtbl))] + class Breakpoint(object): - def __init__(self, breakpoint): - self.breakpoint = breakpoint.contents - self.vt = self.breakpoint.lpVtbl.contents + def __init__(self, breakpoint): + self.breakpoint = breakpoint.contents + self.vt = self.breakpoint.lpVtbl.contents - def SetFlags(self, flags): - res = self.vt.SetFlags(self.breakpoint, flags) - aborter(res, "Breakpoint SetFlags") + def SetFlags(self, flags): + res = self.vt.SetFlags(self.breakpoint, flags) + aborter(res, "Breakpoint SetFlags") - def SetOffset(self, offs): - res = self.vt.SetOffset(self.breakpoint, offs) - aborter(res, "Breakpoint SetOffset") + def SetOffset(self, offs): + res = self.vt.SetOffset(self.breakpoint, offs) + aborter(res, "Breakpoint SetOffset") - def RemoveFlags(self, flags): - res = self.vt.RemoveFlags(self.breakpoint, flags) - aborter(res, "Breakpoint RemoveFlags") + def RemoveFlags(self, flags): + res = self.vt.RemoveFlags(self.breakpoint, flags) + aborter(res, "Breakpoint RemoveFlags") - def die(self): - self.breakpoint = None - self.vt = None + def die(self): + self.breakpoint = None + self.vt = None diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/client.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/client.py index 22d4652..d5fb504 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/client.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/client.py @@ -14,198 +14,224 @@ from . import control from . import symbols from . import sysobjs + class DebugAttach(IntFlag): - DEBUG_ATTACH_DEFAULT = 0 - DEBUG_ATTACH_NONINVASIVE = 1 - DEBUG_ATTACH_EXISTING = 2 - DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND = 4 - DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK = 8 - DEBUG_ATTACH_INVASIVE_RESUME_PROCESS = 0x10 - DEBUG_ATTACH_NONINVASIVE_ALLOW_PARTIAL = 0x20 + DEBUG_ATTACH_DEFAULT = 0 + DEBUG_ATTACH_NONINVASIVE = 1 + DEBUG_ATTACH_EXISTING = 2 + DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND = 4 + DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK = 8 + DEBUG_ATTACH_INVASIVE_RESUME_PROCESS = 0x10 + DEBUG_ATTACH_NONINVASIVE_ALLOW_PARTIAL = 0x20 + # UUID for DebugClient7 interface. -DebugClient7IID = IID(0x13586be3, 0x542e, 0x481e, IID_Data4_Type(0xb1, 0xf2, 0x84, 0x97, 0xba, 0x74, 0xf9, 0xa9 )) +DebugClient7IID = IID( + 0x13586BE3, + 0x542E, + 0x481E, + IID_Data4_Type(0xB1, 0xF2, 0x84, 0x97, 0xBA, 0x74, 0xF9, 0xA9), +) + class DEBUG_CREATE_PROCESS_OPTIONS(Structure): - _fields_ = [ - ("CreateFlags", c_ulong), - ("EngCreateFlags", c_ulong), - ("VerifierFlags", c_ulong), - ("Reserved", c_ulong) - ] + _fields_ = [ + ("CreateFlags", c_ulong), + ("EngCreateFlags", c_ulong), + ("VerifierFlags", c_ulong), + ("Reserved", c_ulong), + ] + class IDebugClient7(Structure): - pass + pass + class IDebugClient7Vtbl(Structure): - wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugClient7)) - idc_queryinterface = wrp(POINTER(IID), POINTER(c_void_p)) - idc_attachprocess = wrp(c_longlong, c_long, c_long) - idc_detachprocesses = wrp() - idc_terminateprocesses = wrp() - idc_createprocessandattach2 = wrp(c_ulonglong, c_char_p, c_void_p, c_ulong, c_char_p, c_char_p, c_ulong, c_ulong) - _fields_ = [ - ("QueryInterface", idc_queryinterface), - ("AddRef", c_void_p), - ("Release", c_void_p), - ("AttachKernel", c_void_p), - ("GetKernelConnectionOptions", c_void_p), - ("SetKernelConnectionOptions", c_void_p), - ("StartProcessServer", c_void_p), - ("ConnectProcessServer", c_void_p), - ("DisconnectProcessServer", c_void_p), - ("GetRunningProcessSystemIds", c_void_p), - ("GetRunningProcessSystemIdsByExecutableName", c_void_p), - ("GetRunningProcessDescription", c_void_p), - ("AttachProcess", idc_attachprocess), - ("CreateProcess", c_void_p), - ("CreateProcessAndAttach", c_void_p), - ("GetProcessOptions", c_void_p), - ("AddProcessOptions", c_void_p), - ("RemoveProcessOptions", c_void_p), - ("SetProcessOptions", c_void_p), - ("OpenDumpFile", c_void_p), - ("WriteDumpFile", c_void_p), - ("ConnectSession", c_void_p), - ("StartServer", c_void_p), - ("OutputServers", c_void_p), - ("TerminateProcesses", idc_terminateprocesses), - ("DetachProcesses", idc_detachprocesses), - ("EndSession", c_void_p), - ("GetExitCode", c_void_p), - ("DispatchCallbacks", c_void_p), - ("ExitDispatch", c_void_p), - ("CreateClient", c_void_p), - ("GetInputCallbacks", c_void_p), - ("SetInputCallbacks", c_void_p), - ("GetOutputCallbacks", c_void_p), - ("SetOutputCallbacks", c_void_p), - ("GetOutputMask", c_void_p), - ("SetOutputMask", c_void_p), - ("GetOtherOutputMask", c_void_p), - ("SetOtherOutputMask", c_void_p), - ("GetOutputWidth", c_void_p), - ("SetOutputWidth", c_void_p), - ("GetOutputLinePrefix", c_void_p), - ("SetOutputLinePrefix", c_void_p), - ("GetIdentity", c_void_p), - ("OutputIdentity", c_void_p), - ("GetEventCallbacks", c_void_p), - ("SetEventCallbacks", c_void_p), - ("FlushCallbacks", c_void_p), - ("WriteDumpFile2", c_void_p), - ("AddDumpInformationFile", c_void_p), - ("EndProcessServer", c_void_p), - ("WaitForProcessServerEnd", c_void_p), - ("IsKernelDebuggerEnabled", c_void_p), - ("TerminateCurrentProcess", c_void_p), - ("DetachCurrentProcess", c_void_p), - ("AbandonCurrentProcess", c_void_p), - ("GetRunningProcessSystemIdByExecutableNameWide", c_void_p), - ("GetRunningProcessDescriptionWide", c_void_p), - ("CreateProcessWide", c_void_p), - ("CreateProcessAndAttachWide", c_void_p), - ("OpenDumpFileWide", c_void_p), - ("WriteDumpFileWide", c_void_p), - ("AddDumpInformationFileWide", c_void_p), - ("GetNumberDumpFiles", c_void_p), - ("GetDumpFile", c_void_p), - ("GetDumpFileWide", c_void_p), - ("AttachKernelWide", c_void_p), - ("GetKernelConnectionOptionsWide", c_void_p), - ("SetKernelConnectionOptionsWide", c_void_p), - ("StartProcessServerWide", c_void_p), - ("ConnectProcessServerWide", c_void_p), - ("StartServerWide", c_void_p), - ("OutputServerWide", c_void_p), - ("GetOutputCallbacksWide", c_void_p), - ("SetOutputCallbacksWide", c_void_p), - ("GetOutputLinePrefixWide", c_void_p), - ("SetOutputLinePrefixWide", c_void_p), - ("GetIdentityWide", c_void_p), - ("OutputIdentityWide", c_void_p), - ("GetEventCallbacksWide", c_void_p), - ("SetEventCallbacksWide", c_void_p), - ("CreateProcess2", c_void_p), - ("CreateProcess2Wide", c_void_p), - ("CreateProcessAndAttach2", idc_createprocessandattach2), - ("CreateProcessAndAttach2Wide", c_void_p), - ("PushOutputLinePrefix", c_void_p), - ("PushOutputLinePrefixWide", c_void_p), - ("PopOutputLinePrefix", c_void_p), - ("GetNumberInputCallbacks", c_void_p), - ("GetNumberOutputCallbacks", c_void_p), - ("GetNumberEventCallbacks", c_void_p), - ("GetQuitLockString", c_void_p), - ("SetQuitLockString", c_void_p), - ("GetQuitLockStringWide", c_void_p), - ("SetQuitLockStringWide", c_void_p), - ("SetEventContextCallbacks", c_void_p), - ("SetClientContext", c_void_p), + wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugClient7)) + idc_queryinterface = wrp(POINTER(IID), POINTER(c_void_p)) + idc_attachprocess = wrp(c_longlong, c_long, c_long) + idc_detachprocesses = wrp() + idc_terminateprocesses = wrp() + idc_createprocessandattach2 = wrp( + c_ulonglong, c_char_p, c_void_p, c_ulong, c_char_p, c_char_p, c_ulong, c_ulong + ) + _fields_ = [ + ("QueryInterface", idc_queryinterface), + ("AddRef", c_void_p), + ("Release", c_void_p), + ("AttachKernel", c_void_p), + ("GetKernelConnectionOptions", c_void_p), + ("SetKernelConnectionOptions", c_void_p), + ("StartProcessServer", c_void_p), + ("ConnectProcessServer", c_void_p), + ("DisconnectProcessServer", c_void_p), + ("GetRunningProcessSystemIds", c_void_p), + ("GetRunningProcessSystemIdsByExecutableName", c_void_p), + ("GetRunningProcessDescription", c_void_p), + ("AttachProcess", idc_attachprocess), + ("CreateProcess", c_void_p), + ("CreateProcessAndAttach", c_void_p), + ("GetProcessOptions", c_void_p), + ("AddProcessOptions", c_void_p), + ("RemoveProcessOptions", c_void_p), + ("SetProcessOptions", c_void_p), + ("OpenDumpFile", c_void_p), + ("WriteDumpFile", c_void_p), + ("ConnectSession", c_void_p), + ("StartServer", c_void_p), + ("OutputServers", c_void_p), + ("TerminateProcesses", idc_terminateprocesses), + ("DetachProcesses", idc_detachprocesses), + ("EndSession", c_void_p), + ("GetExitCode", c_void_p), + ("DispatchCallbacks", c_void_p), + ("ExitDispatch", c_void_p), + ("CreateClient", c_void_p), + ("GetInputCallbacks", c_void_p), + ("SetInputCallbacks", c_void_p), + ("GetOutputCallbacks", c_void_p), + ("SetOutputCallbacks", c_void_p), + ("GetOutputMask", c_void_p), + ("SetOutputMask", c_void_p), + ("GetOtherOutputMask", c_void_p), + ("SetOtherOutputMask", c_void_p), + ("GetOutputWidth", c_void_p), + ("SetOutputWidth", c_void_p), + ("GetOutputLinePrefix", c_void_p), + ("SetOutputLinePrefix", c_void_p), + ("GetIdentity", c_void_p), + ("OutputIdentity", c_void_p), + ("GetEventCallbacks", c_void_p), + ("SetEventCallbacks", c_void_p), + ("FlushCallbacks", c_void_p), + ("WriteDumpFile2", c_void_p), + ("AddDumpInformationFile", c_void_p), + ("EndProcessServer", c_void_p), + ("WaitForProcessServerEnd", c_void_p), + ("IsKernelDebuggerEnabled", c_void_p), + ("TerminateCurrentProcess", c_void_p), + ("DetachCurrentProcess", c_void_p), + ("AbandonCurrentProcess", c_void_p), + ("GetRunningProcessSystemIdByExecutableNameWide", c_void_p), + ("GetRunningProcessDescriptionWide", c_void_p), + ("CreateProcessWide", c_void_p), + ("CreateProcessAndAttachWide", c_void_p), + ("OpenDumpFileWide", c_void_p), + ("WriteDumpFileWide", c_void_p), + ("AddDumpInformationFileWide", c_void_p), + ("GetNumberDumpFiles", c_void_p), + ("GetDumpFile", c_void_p), + ("GetDumpFileWide", c_void_p), + ("AttachKernelWide", c_void_p), + ("GetKernelConnectionOptionsWide", c_void_p), + ("SetKernelConnectionOptionsWide", c_void_p), + ("StartProcessServerWide", c_void_p), + ("ConnectProcessServerWide", c_void_p), + ("StartServerWide", c_void_p), + ("OutputServerWide", c_void_p), + ("GetOutputCallbacksWide", c_void_p), + ("SetOutputCallbacksWide", c_void_p), + ("GetOutputLinePrefixWide", c_void_p), + ("SetOutputLinePrefixWide", c_void_p), + ("GetIdentityWide", c_void_p), + ("OutputIdentityWide", c_void_p), + ("GetEventCallbacksWide", c_void_p), + ("SetEventCallbacksWide", c_void_p), + ("CreateProcess2", c_void_p), + ("CreateProcess2Wide", c_void_p), + ("CreateProcessAndAttach2", idc_createprocessandattach2), + ("CreateProcessAndAttach2Wide", c_void_p), + ("PushOutputLinePrefix", c_void_p), + ("PushOutputLinePrefixWide", c_void_p), + ("PopOutputLinePrefix", c_void_p), + ("GetNumberInputCallbacks", c_void_p), + ("GetNumberOutputCallbacks", c_void_p), + ("GetNumberEventCallbacks", c_void_p), + ("GetQuitLockString", c_void_p), + ("SetQuitLockString", c_void_p), + ("GetQuitLockStringWide", c_void_p), + ("SetQuitLockStringWide", c_void_p), + ("SetEventContextCallbacks", c_void_p), + ("SetClientContext", c_void_p), ] + IDebugClient7._fields_ = [("lpVtbl", POINTER(IDebugClient7Vtbl))] + class Client(object): - def __init__(self): - DbgEng = WinDLL("DbgEng") - DbgEng.DebugCreate.argtypes = [POINTER(IID), POINTER(POINTER(IDebugClient7))] - DbgEng.DebugCreate.restype = c_ulong - - # Call DebugCreate to create a new debug client - ptr = POINTER(IDebugClient7)() - res = DbgEng.DebugCreate(byref(DebugClient7IID), ptr) - aborter(res, "DebugCreate") - self.client = ptr.contents - self.vt = vt = self.client.lpVtbl.contents - - def QI(iface, ptr): - return vt.QueryInterface(self.client, byref(iface), byref(ptr)) - - # Query for a control object - ptr = c_void_p() - res = QI(control.DebugControl7IID, ptr) - aborter(res, "QueryInterface control") - self.control_ptr = cast(ptr, POINTER(control.IDebugControl7)) - self.Control = control.Control(self.control_ptr) - - # Query for a SystemObjects object - ptr = c_void_p() - res = QI(sysobjs.DebugSystemObjects4IID, ptr) - aborter(res, "QueryInterface sysobjects") - self.sysobjects_ptr = cast(ptr, POINTER(sysobjs.IDebugSystemObjects4)) - self.SysObjects = sysobjs.SysObjects(self.sysobjects_ptr) - - # Query for a Symbols object - ptr = c_void_p() - res = QI(symbols.DebugSymbols5IID, ptr) - aborter(res, "QueryInterface debugsymbosl5") - self.symbols_ptr = cast(ptr, POINTER(symbols.IDebugSymbols5)) - self.Symbols = symbols.Symbols(self.symbols_ptr) - - def AttachProcess(self, pid): - # Zero process-server id means no process-server. - res = self.vt.AttachProcess(self.client, 0, pid, DebugAttach.DEBUG_ATTACH_DEFAULT) - aborter(res, "AttachProcess") - return - - def DetachProcesses(self): - res = self.vt.DetachProcesses(self.client) - aborter(res, "DetachProcesses") - return - - def TerminateProcesses(self): - res = self.vt.TerminateProcesses(self.client) - aborter(res, "TerminateProcesses") - return - - def CreateProcessAndAttach2(self, cmdline): - options = DEBUG_CREATE_PROCESS_OPTIONS() - options.CreateFlags = 0x2 # DEBUG_ONLY_THIS_PROCESS - options.EngCreateFlags = 0 - options.VerifierFlags = 0 - options.Reserved = 0 - attach_flags = 0 - res = self.vt.CreateProcessAndAttach2(self.client, 0, cmdline.encode("ascii"), byref(options), sizeof(options), None, None, 0, attach_flags) - aborter(res, "CreateProcessAndAttach2") - return + def __init__(self): + DbgEng = WinDLL("DbgEng") + DbgEng.DebugCreate.argtypes = [POINTER(IID), POINTER(POINTER(IDebugClient7))] + DbgEng.DebugCreate.restype = c_ulong + + # Call DebugCreate to create a new debug client + ptr = POINTER(IDebugClient7)() + res = DbgEng.DebugCreate(byref(DebugClient7IID), ptr) + aborter(res, "DebugCreate") + self.client = ptr.contents + self.vt = vt = self.client.lpVtbl.contents + + def QI(iface, ptr): + return vt.QueryInterface(self.client, byref(iface), byref(ptr)) + + # Query for a control object + ptr = c_void_p() + res = QI(control.DebugControl7IID, ptr) + aborter(res, "QueryInterface control") + self.control_ptr = cast(ptr, POINTER(control.IDebugControl7)) + self.Control = control.Control(self.control_ptr) + + # Query for a SystemObjects object + ptr = c_void_p() + res = QI(sysobjs.DebugSystemObjects4IID, ptr) + aborter(res, "QueryInterface sysobjects") + self.sysobjects_ptr = cast(ptr, POINTER(sysobjs.IDebugSystemObjects4)) + self.SysObjects = sysobjs.SysObjects(self.sysobjects_ptr) + + # Query for a Symbols object + ptr = c_void_p() + res = QI(symbols.DebugSymbols5IID, ptr) + aborter(res, "QueryInterface debugsymbosl5") + self.symbols_ptr = cast(ptr, POINTER(symbols.IDebugSymbols5)) + self.Symbols = symbols.Symbols(self.symbols_ptr) + + def AttachProcess(self, pid): + # Zero process-server id means no process-server. + res = self.vt.AttachProcess( + self.client, 0, pid, DebugAttach.DEBUG_ATTACH_DEFAULT + ) + aborter(res, "AttachProcess") + return + + def DetachProcesses(self): + res = self.vt.DetachProcesses(self.client) + aborter(res, "DetachProcesses") + return + + def TerminateProcesses(self): + res = self.vt.TerminateProcesses(self.client) + aborter(res, "TerminateProcesses") + return + + def CreateProcessAndAttach2(self, cmdline): + options = DEBUG_CREATE_PROCESS_OPTIONS() + options.CreateFlags = 0x2 # DEBUG_ONLY_THIS_PROCESS + options.EngCreateFlags = 0 + options.VerifierFlags = 0 + options.Reserved = 0 + attach_flags = 0 + res = self.vt.CreateProcessAndAttach2( + self.client, + 0, + cmdline.encode("ascii"), + byref(options), + sizeof(options), + None, + None, + 0, + attach_flags, + ) + aborter(res, "CreateProcessAndAttach2") + return diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/control.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/control.py index 5f23e2d..e370738 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/control.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/control.py @@ -11,401 +11,440 @@ from functools import partial from .utils import * from .breakpoint import * + class DEBUG_STACK_FRAME_EX(Structure): - _fields_ = [ - ("InstructionOffset", c_ulonglong), - ("ReturnOffset", c_ulonglong), - ("FrameOffset", c_ulonglong), - ("StackOffset", c_ulonglong), - ("FuncTableEntry", c_ulonglong), - ("Params", c_ulonglong * 4), - ("Reserved", c_ulonglong * 6), - ("Virtual", c_bool), - ("FrameNumber", c_ulong), - ("InlineFrameContext", c_ulong), - ("Reserved1", c_ulong) + _fields_ = [ + ("InstructionOffset", c_ulonglong), + ("ReturnOffset", c_ulonglong), + ("FrameOffset", c_ulonglong), + ("StackOffset", c_ulonglong), + ("FuncTableEntry", c_ulonglong), + ("Params", c_ulonglong * 4), + ("Reserved", c_ulonglong * 6), + ("Virtual", c_bool), + ("FrameNumber", c_ulong), + ("InlineFrameContext", c_ulong), + ("Reserved1", c_ulong), ] + + PDEBUG_STACK_FRAME_EX = POINTER(DEBUG_STACK_FRAME_EX) + class DEBUG_VALUE_U(Union): - _fields_ = [ - ("I8", c_byte), - ("I16", c_short), - ("I32", c_int), - ("I64", c_long), - ("F32", c_float), - ("F64", c_double), - ("RawBytes", c_ubyte * 24) # Force length to 24b. + _fields_ = [ + ("I8", c_byte), + ("I16", c_short), + ("I32", c_int), + ("I64", c_long), + ("F32", c_float), + ("F64", c_double), + ("RawBytes", c_ubyte * 24), # Force length to 24b. ] + class DEBUG_VALUE(Structure): - _fields_ = [ - ("U", DEBUG_VALUE_U), - ("TailOfRawBytes", c_ulong), - ("Type", c_ulong) - ] + _fields_ = [("U", DEBUG_VALUE_U), ("TailOfRawBytes", c_ulong), ("Type", c_ulong)] + + PDEBUG_VALUE = POINTER(DEBUG_VALUE) + class DebugValueType(IntEnum): - DEBUG_VALUE_INVALID = 0 - DEBUG_VALUE_INT8 = 1 - DEBUG_VALUE_INT16 = 2 - DEBUG_VALUE_INT32 = 3 - DEBUG_VALUE_INT64 = 4 - DEBUG_VALUE_FLOAT32 = 5 - DEBUG_VALUE_FLOAT64 = 6 - DEBUG_VALUE_FLOAT80 = 7 - DEBUG_VALUE_FLOAT82 = 8 - DEBUG_VALUE_FLOAT128 = 9 - DEBUG_VALUE_VECTOR64 = 10 - DEBUG_VALUE_VECTOR128 = 11 - DEBUG_VALUE_TYPES = 12 + DEBUG_VALUE_INVALID = 0 + DEBUG_VALUE_INT8 = 1 + DEBUG_VALUE_INT16 = 2 + DEBUG_VALUE_INT32 = 3 + DEBUG_VALUE_INT64 = 4 + DEBUG_VALUE_FLOAT32 = 5 + DEBUG_VALUE_FLOAT64 = 6 + DEBUG_VALUE_FLOAT80 = 7 + DEBUG_VALUE_FLOAT82 = 8 + DEBUG_VALUE_FLOAT128 = 9 + DEBUG_VALUE_VECTOR64 = 10 + DEBUG_VALUE_VECTOR128 = 11 + DEBUG_VALUE_TYPES = 12 + # UUID for DebugControl7 interface. -DebugControl7IID = IID(0xb86fb3b1, 0x80d4, 0x475b, IID_Data4_Type(0xae, 0xa3, 0xcf, 0x06, 0x53, 0x9c, 0xf6, 0x3a)) +DebugControl7IID = IID( + 0xB86FB3B1, + 0x80D4, + 0x475B, + IID_Data4_Type(0xAE, 0xA3, 0xCF, 0x06, 0x53, 0x9C, 0xF6, 0x3A), +) + class IDebugControl7(Structure): - pass + pass + class IDebugControl7Vtbl(Structure): - wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugControl7)) - idc_getnumbereventfilters = wrp(c_ulong_p, c_ulong_p, c_ulong_p) - idc_setexceptionfiltersecondcommand = wrp(c_ulong, c_char_p) - idc_waitforevent = wrp(c_long, c_long) - idc_execute = wrp(c_long, c_char_p, c_long) - idc_setexpressionsyntax = wrp(c_ulong) - idc_addbreakpoint2 = wrp(c_ulong, c_ulong, POINTER(POINTER(DebugBreakpoint2))) - idc_setexecutionstatus = wrp(c_ulong) - idc_getexecutionstatus = wrp(c_ulong_p) - idc_getstacktraceex = wrp(c_ulonglong, c_ulonglong, c_ulonglong, PDEBUG_STACK_FRAME_EX, c_ulong, c_ulong_p) - idc_evaluate = wrp(c_char_p, c_ulong, PDEBUG_VALUE, c_ulong_p) - idc_setengineoptions = wrp(c_ulong) - _fields_ = [ - ("QueryInterface", c_void_p), - ("AddRef", c_void_p), - ("Release", c_void_p), - ("GetInterrupt", c_void_p), - ("SetInterrupt", c_void_p), - ("GetInterruptTimeout", c_void_p), - ("SetInterruptTimeout", c_void_p), - ("GetLogFile", c_void_p), - ("OpenLogFile", c_void_p), - ("CloseLogFile", c_void_p), - ("GetLogMask", c_void_p), - ("SetLogMask", c_void_p), - ("Input", c_void_p), - ("ReturnInput", c_void_p), - ("Output", c_void_p), - ("OutputVaList", c_void_p), - ("ControlledOutput", c_void_p), - ("ControlledOutputVaList", c_void_p), - ("OutputPrompt", c_void_p), - ("OutputPromptVaList", c_void_p), - ("GetPromptText", c_void_p), - ("OutputCurrentState", c_void_p), - ("OutputVersionInformation", c_void_p), - ("GetNotifyEventHandle", c_void_p), - ("SetNotifyEventHandle", c_void_p), - ("Assemble", c_void_p), - ("Disassemble", c_void_p), - ("GetDisassembleEffectiveOffset", c_void_p), - ("OutputDisassembly", c_void_p), - ("OutputDisassemblyLines", c_void_p), - ("GetNearInstruction", c_void_p), - ("GetStackTrace", c_void_p), - ("GetReturnOffset", c_void_p), - ("OutputStackTrace", c_void_p), - ("GetDebuggeeType", c_void_p), - ("GetActualProcessorType", c_void_p), - ("GetExecutingProcessorType", c_void_p), - ("GetNumberPossibleExecutingProcessorTypes", c_void_p), - ("GetPossibleExecutingProcessorTypes", c_void_p), - ("GetNumberProcessors", c_void_p), - ("GetSystemVersion", c_void_p), - ("GetPageSize", c_void_p), - ("IsPointer64Bit", c_void_p), - ("ReadBugCheckData", c_void_p), - ("GetNumberSupportedProcessorTypes", c_void_p), - ("GetSupportedProcessorTypes", c_void_p), - ("GetProcessorTypeNames", c_void_p), - ("GetEffectiveProcessorType", c_void_p), - ("SetEffectiveProcessorType", c_void_p), - ("GetExecutionStatus", idc_getexecutionstatus), - ("SetExecutionStatus", idc_setexecutionstatus), - ("GetCodeLevel", c_void_p), - ("SetCodeLevel", c_void_p), - ("GetEngineOptions", c_void_p), - ("AddEngineOptions", c_void_p), - ("RemoveEngineOptions", c_void_p), - ("SetEngineOptions", idc_setengineoptions), - ("GetSystemErrorControl", c_void_p), - ("SetSystemErrorControl", c_void_p), - ("GetTextMacro", c_void_p), - ("SetTextMacro", c_void_p), - ("GetRadix", c_void_p), - ("SetRadix", c_void_p), - ("Evaluate", idc_evaluate), - ("CoerceValue", c_void_p), - ("CoerceValues", c_void_p), - ("Execute", idc_execute), - ("ExecuteCommandFile", c_void_p), - ("GetNumberBreakpoints", c_void_p), - ("GetBreakpointByIndex", c_void_p), - ("GetBreakpointById", c_void_p), - ("GetBreakpointParameters", c_void_p), - ("AddBreakpoint", c_void_p), - ("RemoveBreakpoint", c_void_p), - ("AddExtension", c_void_p), - ("RemoveExtension", c_void_p), - ("GetExtensionByPath", c_void_p), - ("CallExtension", c_void_p), - ("GetExtensionFunction", c_void_p), - ("GetWindbgExtensionApis32", c_void_p), - ("GetWindbgExtensionApis64", c_void_p), - ("GetNumberEventFilters", idc_getnumbereventfilters), - ("GetEventFilterText", c_void_p), - ("GetEventFilterCommand", c_void_p), - ("SetEventFilterCommand", c_void_p), - ("GetSpecificFilterParameters", c_void_p), - ("SetSpecificFilterParameters", c_void_p), - ("GetSpecificFilterArgument", c_void_p), - ("SetSpecificFilterArgument", c_void_p), - ("GetExceptionFilterParameters", c_void_p), - ("SetExceptionFilterParameters", c_void_p), - ("GetExceptionFilterSecondCommand", c_void_p), - ("SetExceptionFilterSecondCommand", idc_setexceptionfiltersecondcommand), - ("WaitForEvent", idc_waitforevent), - ("GetLastEventInformation", c_void_p), - ("GetCurrentTimeDate", c_void_p), - ("GetCurrentSystemUpTime", c_void_p), - ("GetDumpFormatFlags", c_void_p), - ("GetNumberTextReplacements", c_void_p), - ("GetTextReplacement", c_void_p), - ("SetTextReplacement", c_void_p), - ("RemoveTextReplacements", c_void_p), - ("OutputTextReplacements", c_void_p), - ("GetAssemblyOptions", c_void_p), - ("AddAssemblyOptions", c_void_p), - ("RemoveAssemblyOptions", c_void_p), - ("SetAssemblyOptions", c_void_p), - ("GetExpressionSyntax", c_void_p), - ("SetExpressionSyntax", idc_setexpressionsyntax), - ("SetExpressionSyntaxByName", c_void_p), - ("GetNumberExpressionSyntaxes", c_void_p), - ("GetExpressionSyntaxNames", c_void_p), - ("GetNumberEvents", c_void_p), - ("GetEventIndexDescription", c_void_p), - ("GetCurrentEventIndex", c_void_p), - ("SetNextEventIndex", c_void_p), - ("GetLogFileWide", c_void_p), - ("OpenLogFileWide", c_void_p), - ("InputWide", c_void_p), - ("ReturnInputWide", c_void_p), - ("OutputWide", c_void_p), - ("OutputVaListWide", c_void_p), - ("ControlledOutputWide", c_void_p), - ("ControlledOutputVaListWide", c_void_p), - ("OutputPromptWide", c_void_p), - ("OutputPromptVaListWide", c_void_p), - ("GetPromptTextWide", c_void_p), - ("AssembleWide", c_void_p), - ("DisassembleWide", c_void_p), - ("GetProcessrTypeNamesWide", c_void_p), - ("GetTextMacroWide", c_void_p), - ("SetTextMacroWide", c_void_p), - ("EvaluateWide", c_void_p), - ("ExecuteWide", c_void_p), - ("ExecuteCommandFileWide", c_void_p), - ("GetBreakpointByIndex2", c_void_p), - ("GetBreakpointById2", c_void_p), - ("AddBreakpoint2", idc_addbreakpoint2), - ("RemoveBreakpoint2", c_void_p), - ("AddExtensionWide", c_void_p), - ("GetExtensionByPathWide", c_void_p), - ("CallExtensionWide", c_void_p), - ("GetExtensionFunctionWide", c_void_p), - ("GetEventFilterTextWide", c_void_p), - ("GetEventfilterCommandWide", c_void_p), - ("SetEventFilterCommandWide", c_void_p), - ("GetSpecificFilterArgumentWide", c_void_p), - ("SetSpecificFilterArgumentWide", c_void_p), - ("GetExceptionFilterSecondCommandWide", c_void_p), - ("SetExceptionFilterSecondCommandWider", c_void_p), - ("GetLastEventInformationWide", c_void_p), - ("GetTextReplacementWide", c_void_p), - ("SetTextReplacementWide", c_void_p), - ("SetExpressionSyntaxByNameWide", c_void_p), - ("GetExpressionSyntaxNamesWide", c_void_p), - ("GetEventIndexDescriptionWide", c_void_p), - ("GetLogFile2", c_void_p), - ("OpenLogFile2", c_void_p), - ("GetLogFile2Wide", c_void_p), - ("OpenLogFile2Wide", c_void_p), - ("GetSystemVersionValues", c_void_p), - ("GetSystemVersionString", c_void_p), - ("GetSystemVersionStringWide", c_void_p), - ("GetContextStackTrace", c_void_p), - ("OutputContextStackTrace", c_void_p), - ("GetStoredEventInformation", c_void_p), - ("GetManagedStatus", c_void_p), - ("GetManagedStatusWide", c_void_p), - ("ResetManagedStatus", c_void_p), - ("GetStackTraceEx", idc_getstacktraceex), - ("OutputStackTraceEx", c_void_p), - ("GetContextStackTraceEx", c_void_p), - ("OutputContextStackTraceEx", c_void_p), - ("GetBreakpointByGuid", c_void_p), - ("GetExecutionStatusEx", c_void_p), - ("GetSynchronizationStatus", c_void_p), - ("GetDebuggeeType2", c_void_p) + wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugControl7)) + idc_getnumbereventfilters = wrp(c_ulong_p, c_ulong_p, c_ulong_p) + idc_setexceptionfiltersecondcommand = wrp(c_ulong, c_char_p) + idc_waitforevent = wrp(c_long, c_long) + idc_execute = wrp(c_long, c_char_p, c_long) + idc_setexpressionsyntax = wrp(c_ulong) + idc_addbreakpoint2 = wrp(c_ulong, c_ulong, POINTER(POINTER(DebugBreakpoint2))) + idc_setexecutionstatus = wrp(c_ulong) + idc_getexecutionstatus = wrp(c_ulong_p) + idc_getstacktraceex = wrp( + c_ulonglong, c_ulonglong, c_ulonglong, PDEBUG_STACK_FRAME_EX, c_ulong, c_ulong_p + ) + idc_evaluate = wrp(c_char_p, c_ulong, PDEBUG_VALUE, c_ulong_p) + idc_setengineoptions = wrp(c_ulong) + _fields_ = [ + ("QueryInterface", c_void_p), + ("AddRef", c_void_p), + ("Release", c_void_p), + ("GetInterrupt", c_void_p), + ("SetInterrupt", c_void_p), + ("GetInterruptTimeout", c_void_p), + ("SetInterruptTimeout", c_void_p), + ("GetLogFile", c_void_p), + ("OpenLogFile", c_void_p), + ("CloseLogFile", c_void_p), + ("GetLogMask", c_void_p), + ("SetLogMask", c_void_p), + ("Input", c_void_p), + ("ReturnInput", c_void_p), + ("Output", c_void_p), + ("OutputVaList", c_void_p), + ("ControlledOutput", c_void_p), + ("ControlledOutputVaList", c_void_p), + ("OutputPrompt", c_void_p), + ("OutputPromptVaList", c_void_p), + ("GetPromptText", c_void_p), + ("OutputCurrentState", c_void_p), + ("OutputVersionInformation", c_void_p), + ("GetNotifyEventHandle", c_void_p), + ("SetNotifyEventHandle", c_void_p), + ("Assemble", c_void_p), + ("Disassemble", c_void_p), + ("GetDisassembleEffectiveOffset", c_void_p), + ("OutputDisassembly", c_void_p), + ("OutputDisassemblyLines", c_void_p), + ("GetNearInstruction", c_void_p), + ("GetStackTrace", c_void_p), + ("GetReturnOffset", c_void_p), + ("OutputStackTrace", c_void_p), + ("GetDebuggeeType", c_void_p), + ("GetActualProcessorType", c_void_p), + ("GetExecutingProcessorType", c_void_p), + ("GetNumberPossibleExecutingProcessorTypes", c_void_p), + ("GetPossibleExecutingProcessorTypes", c_void_p), + ("GetNumberProcessors", c_void_p), + ("GetSystemVersion", c_void_p), + ("GetPageSize", c_void_p), + ("IsPointer64Bit", c_void_p), + ("ReadBugCheckData", c_void_p), + ("GetNumberSupportedProcessorTypes", c_void_p), + ("GetSupportedProcessorTypes", c_void_p), + ("GetProcessorTypeNames", c_void_p), + ("GetEffectiveProcessorType", c_void_p), + ("SetEffectiveProcessorType", c_void_p), + ("GetExecutionStatus", idc_getexecutionstatus), + ("SetExecutionStatus", idc_setexecutionstatus), + ("GetCodeLevel", c_void_p), + ("SetCodeLevel", c_void_p), + ("GetEngineOptions", c_void_p), + ("AddEngineOptions", c_void_p), + ("RemoveEngineOptions", c_void_p), + ("SetEngineOptions", idc_setengineoptions), + ("GetSystemErrorControl", c_void_p), + ("SetSystemErrorControl", c_void_p), + ("GetTextMacro", c_void_p), + ("SetTextMacro", c_void_p), + ("GetRadix", c_void_p), + ("SetRadix", c_void_p), + ("Evaluate", idc_evaluate), + ("CoerceValue", c_void_p), + ("CoerceValues", c_void_p), + ("Execute", idc_execute), + ("ExecuteCommandFile", c_void_p), + ("GetNumberBreakpoints", c_void_p), + ("GetBreakpointByIndex", c_void_p), + ("GetBreakpointById", c_void_p), + ("GetBreakpointParameters", c_void_p), + ("AddBreakpoint", c_void_p), + ("RemoveBreakpoint", c_void_p), + ("AddExtension", c_void_p), + ("RemoveExtension", c_void_p), + ("GetExtensionByPath", c_void_p), + ("CallExtension", c_void_p), + ("GetExtensionFunction", c_void_p), + ("GetWindbgExtensionApis32", c_void_p), + ("GetWindbgExtensionApis64", c_void_p), + ("GetNumberEventFilters", idc_getnumbereventfilters), + ("GetEventFilterText", c_void_p), + ("GetEventFilterCommand", c_void_p), + ("SetEventFilterCommand", c_void_p), + ("GetSpecificFilterParameters", c_void_p), + ("SetSpecificFilterParameters", c_void_p), + ("GetSpecificFilterArgument", c_void_p), + ("SetSpecificFilterArgument", c_void_p), + ("GetExceptionFilterParameters", c_void_p), + ("SetExceptionFilterParameters", c_void_p), + ("GetExceptionFilterSecondCommand", c_void_p), + ("SetExceptionFilterSecondCommand", idc_setexceptionfiltersecondcommand), + ("WaitForEvent", idc_waitforevent), + ("GetLastEventInformation", c_void_p), + ("GetCurrentTimeDate", c_void_p), + ("GetCurrentSystemUpTime", c_void_p), + ("GetDumpFormatFlags", c_void_p), + ("GetNumberTextReplacements", c_void_p), + ("GetTextReplacement", c_void_p), + ("SetTextReplacement", c_void_p), + ("RemoveTextReplacements", c_void_p), + ("OutputTextReplacements", c_void_p), + ("GetAssemblyOptions", c_void_p), + ("AddAssemblyOptions", c_void_p), + ("RemoveAssemblyOptions", c_void_p), + ("SetAssemblyOptions", c_void_p), + ("GetExpressionSyntax", c_void_p), + ("SetExpressionSyntax", idc_setexpressionsyntax), + ("SetExpressionSyntaxByName", c_void_p), + ("GetNumberExpressionSyntaxes", c_void_p), + ("GetExpressionSyntaxNames", c_void_p), + ("GetNumberEvents", c_void_p), + ("GetEventIndexDescription", c_void_p), + ("GetCurrentEventIndex", c_void_p), + ("SetNextEventIndex", c_void_p), + ("GetLogFileWide", c_void_p), + ("OpenLogFileWide", c_void_p), + ("InputWide", c_void_p), + ("ReturnInputWide", c_void_p), + ("OutputWide", c_void_p), + ("OutputVaListWide", c_void_p), + ("ControlledOutputWide", c_void_p), + ("ControlledOutputVaListWide", c_void_p), + ("OutputPromptWide", c_void_p), + ("OutputPromptVaListWide", c_void_p), + ("GetPromptTextWide", c_void_p), + ("AssembleWide", c_void_p), + ("DisassembleWide", c_void_p), + ("GetProcessrTypeNamesWide", c_void_p), + ("GetTextMacroWide", c_void_p), + ("SetTextMacroWide", c_void_p), + ("EvaluateWide", c_void_p), + ("ExecuteWide", c_void_p), + ("ExecuteCommandFileWide", c_void_p), + ("GetBreakpointByIndex2", c_void_p), + ("GetBreakpointById2", c_void_p), + ("AddBreakpoint2", idc_addbreakpoint2), + ("RemoveBreakpoint2", c_void_p), + ("AddExtensionWide", c_void_p), + ("GetExtensionByPathWide", c_void_p), + ("CallExtensionWide", c_void_p), + ("GetExtensionFunctionWide", c_void_p), + ("GetEventFilterTextWide", c_void_p), + ("GetEventfilterCommandWide", c_void_p), + ("SetEventFilterCommandWide", c_void_p), + ("GetSpecificFilterArgumentWide", c_void_p), + ("SetSpecificFilterArgumentWide", c_void_p), + ("GetExceptionFilterSecondCommandWide", c_void_p), + ("SetExceptionFilterSecondCommandWider", c_void_p), + ("GetLastEventInformationWide", c_void_p), + ("GetTextReplacementWide", c_void_p), + ("SetTextReplacementWide", c_void_p), + ("SetExpressionSyntaxByNameWide", c_void_p), + ("GetExpressionSyntaxNamesWide", c_void_p), + ("GetEventIndexDescriptionWide", c_void_p), + ("GetLogFile2", c_void_p), + ("OpenLogFile2", c_void_p), + ("GetLogFile2Wide", c_void_p), + ("OpenLogFile2Wide", c_void_p), + ("GetSystemVersionValues", c_void_p), + ("GetSystemVersionString", c_void_p), + ("GetSystemVersionStringWide", c_void_p), + ("GetContextStackTrace", c_void_p), + ("OutputContextStackTrace", c_void_p), + ("GetStoredEventInformation", c_void_p), + ("GetManagedStatus", c_void_p), + ("GetManagedStatusWide", c_void_p), + ("ResetManagedStatus", c_void_p), + ("GetStackTraceEx", idc_getstacktraceex), + ("OutputStackTraceEx", c_void_p), + ("GetContextStackTraceEx", c_void_p), + ("OutputContextStackTraceEx", c_void_p), + ("GetBreakpointByGuid", c_void_p), + ("GetExecutionStatusEx", c_void_p), + ("GetSynchronizationStatus", c_void_p), + ("GetDebuggeeType2", c_void_p), ] + IDebugControl7._fields_ = [("lpVtbl", POINTER(IDebugControl7Vtbl))] + class DebugStatus(IntEnum): - DEBUG_STATUS_NO_CHANGE = 0 - DEBUG_STATUS_GO = 1 - DEBUG_STATUS_GO_HANDLED = 2 - DEBUG_STATUS_GO_NOT_HANDLED = 3 - DEBUG_STATUS_STEP_OVER = 4 - DEBUG_STATUS_STEP_INTO = 5 - DEBUG_STATUS_BREAK = 6 - DEBUG_STATUS_NO_DEBUGGEE = 7 - DEBUG_STATUS_STEP_BRANCH = 8 - DEBUG_STATUS_IGNORE_EVENT = 9 - DEBUG_STATUS_RESTART_REQUESTED = 10 - DEBUG_STATUS_REVERSE_GO = 11 - DEBUG_STATUS_REVERSE_STEP_BRANCH = 12 - DEBUG_STATUS_REVERSE_STEP_OVER = 13 - DEBUG_STATUS_REVERSE_STEP_INTO = 14 - DEBUG_STATUS_OUT_OF_SYNC = 15 - DEBUG_STATUS_WAIT_INPUT = 16 - DEBUG_STATUS_TIMEOUT = 17 + DEBUG_STATUS_NO_CHANGE = 0 + DEBUG_STATUS_GO = 1 + DEBUG_STATUS_GO_HANDLED = 2 + DEBUG_STATUS_GO_NOT_HANDLED = 3 + DEBUG_STATUS_STEP_OVER = 4 + DEBUG_STATUS_STEP_INTO = 5 + DEBUG_STATUS_BREAK = 6 + DEBUG_STATUS_NO_DEBUGGEE = 7 + DEBUG_STATUS_STEP_BRANCH = 8 + DEBUG_STATUS_IGNORE_EVENT = 9 + DEBUG_STATUS_RESTART_REQUESTED = 10 + DEBUG_STATUS_REVERSE_GO = 11 + DEBUG_STATUS_REVERSE_STEP_BRANCH = 12 + DEBUG_STATUS_REVERSE_STEP_OVER = 13 + DEBUG_STATUS_REVERSE_STEP_INTO = 14 + DEBUG_STATUS_OUT_OF_SYNC = 15 + DEBUG_STATUS_WAIT_INPUT = 16 + DEBUG_STATUS_TIMEOUT = 17 + class DebugSyntax(IntEnum): - DEBUG_EXPR_MASM = 0 - DEBUG_EXPR_CPLUSPLUS = 1 + DEBUG_EXPR_MASM = 0 + DEBUG_EXPR_CPLUSPLUS = 1 + class Control(object): - def __init__(self, control): - self.ptr = control - self.control = control.contents - self.vt = self.control.lpVtbl.contents - # Keep a handy ulong for passing into C methods. - self.ulong = c_ulong() - - def GetExecutionStatus(self, doprint=False): - ret = self.vt.GetExecutionStatus(self.control, byref(self.ulong)) - aborter(ret, "GetExecutionStatus") - status = DebugStatus(self.ulong.value) - if doprint: - print("Execution status: {}".format(status)) - return status - - def SetExecutionStatus(self, status): - assert isinstance(status, DebugStatus) - res = self.vt.SetExecutionStatus(self.control, status.value) - aborter(res, "SetExecutionStatus") - - def WaitForEvent(self, timeout=100): - # No flags are taken by WaitForEvent, hence 0 - ret = self.vt.WaitForEvent(self.control, 0, timeout) - aborter(ret, "WaitforEvent", ignore=[S_FALSE]) - return ret - - def GetNumberEventFilters(self): - specific_events = c_ulong() - specific_exceptions = c_ulong() - arbitrary_exceptions = c_ulong() - res = self.vt.GetNumberEventFilters(self.control, byref(specific_events), - byref(specific_exceptions), - byref(arbitrary_exceptions)) - aborter(res, "GetNumberEventFilters") - return (specific_events.value, specific_exceptions.value, - arbitrary_exceptions.value) - - def SetExceptionFilterSecondCommand(self, index, command): - buf = create_string_buffer(command.encode('ascii')) - res = self.vt.SetExceptionFilterSecondCommand(self.control, index, buf) - aborter(res, "SetExceptionFilterSecondCommand") - return - - def AddBreakpoint2(self, offset=None, enabled=None): - breakpoint = POINTER(DebugBreakpoint2)() - res = self.vt.AddBreakpoint2(self.control, BreakpointTypes.DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, byref(breakpoint)) - aborter(res, "Add breakpoint 2") - bp = Breakpoint(breakpoint) - - if offset is not None: - bp.SetOffset(offset) - if enabled is not None and enabled: - bp.SetFlags(BreakpointFlags.DEBUG_BREAKPOINT_ENABLED) - - return bp - - def RemoveBreakpoint(self, bp): - res = self.vt.RemoveBreakpoint2(self.control, bp.breakpoint) - aborter(res, "RemoveBreakpoint2") - bp.die() - - def GetStackTraceEx(self): - # XXX -- I can't find a way to query for how many stack frames there _are_ - # in advance. Guess 128 for now. - num_frames_buffer = 128 - - frames = (DEBUG_STACK_FRAME_EX * num_frames_buffer)() - numframes = c_ulong() - - # First three args are frame/stack/IP offsets -- leave them as zero to - # default to the current instruction. - res = self.vt.GetStackTraceEx(self.control, 0, 0, 0, frames, num_frames_buffer, byref(numframes)) - aborter(res, "GetStackTraceEx") - return frames, numframes.value - - def Execute(self, command): - # First zero is DEBUG_OUTCTL_*, which we leave as a default, second - # zero is DEBUG_EXECUTE_* flags, of which we set none. - res = self.vt.Execute(self.control, 0, command.encode('ascii'), 0) - aborter(res, "Client execute") - - def SetExpressionSyntax(self, cpp=True): - if cpp: - syntax = DebugSyntax.DEBUG_EXPR_CPLUSPLUS - else: - syntax = DebugSyntax.DEBUG_EXPR_MASM - - res = self.vt.SetExpressionSyntax(self.control, syntax) - aborter(res, "SetExpressionSyntax") - - def Evaluate(self, expr): - ptr = DEBUG_VALUE() - res = self.vt.Evaluate(self.control, expr.encode("ascii"), DebugValueType.DEBUG_VALUE_INVALID, byref(ptr), None) - aborter(res, "Evaluate", ignore=[E_INTERNALEXCEPTION, E_FAIL]) - if res != 0: - return None - - val_type = DebugValueType(ptr.Type) - - # Here's a map from debug value types to fields. Unclear what happens - # with unsigned values, as DbgEng doesn't present any unsigned fields. - - extract_map = { - DebugValueType.DEBUG_VALUE_INT8 : ("I8", "char"), - DebugValueType.DEBUG_VALUE_INT16 : ("I16", "short"), - DebugValueType.DEBUG_VALUE_INT32 : ("I32", "int"), - DebugValueType.DEBUG_VALUE_INT64 : ("I64", "long"), - DebugValueType.DEBUG_VALUE_FLOAT32 : ("F32", "float"), - DebugValueType.DEBUG_VALUE_FLOAT64 : ("F64", "double") - } # And everything else is invalid. - - if val_type not in extract_map: - raise Exception("Unexpected debug value type {} when evalutaing".format(val_type)) - - # Also produce a type name... - - return getattr(ptr.U, extract_map[val_type][0]), extract_map[val_type][1] - - def SetEngineOptions(self, opt): - res = self.vt.SetEngineOptions(self.control, opt) - aborter(res, "SetEngineOptions") - return + def __init__(self, control): + self.ptr = control + self.control = control.contents + self.vt = self.control.lpVtbl.contents + # Keep a handy ulong for passing into C methods. + self.ulong = c_ulong() + + def GetExecutionStatus(self, doprint=False): + ret = self.vt.GetExecutionStatus(self.control, byref(self.ulong)) + aborter(ret, "GetExecutionStatus") + status = DebugStatus(self.ulong.value) + if doprint: + print("Execution status: {}".format(status)) + return status + + def SetExecutionStatus(self, status): + assert isinstance(status, DebugStatus) + res = self.vt.SetExecutionStatus(self.control, status.value) + aborter(res, "SetExecutionStatus") + + def WaitForEvent(self, timeout=100): + # No flags are taken by WaitForEvent, hence 0 + ret = self.vt.WaitForEvent(self.control, 0, timeout) + aborter(ret, "WaitforEvent", ignore=[S_FALSE]) + return ret + + def GetNumberEventFilters(self): + specific_events = c_ulong() + specific_exceptions = c_ulong() + arbitrary_exceptions = c_ulong() + res = self.vt.GetNumberEventFilters( + self.control, + byref(specific_events), + byref(specific_exceptions), + byref(arbitrary_exceptions), + ) + aborter(res, "GetNumberEventFilters") + return ( + specific_events.value, + specific_exceptions.value, + arbitrary_exceptions.value, + ) + + def SetExceptionFilterSecondCommand(self, index, command): + buf = create_string_buffer(command.encode("ascii")) + res = self.vt.SetExceptionFilterSecondCommand(self.control, index, buf) + aborter(res, "SetExceptionFilterSecondCommand") + return + + def AddBreakpoint2(self, offset=None, enabled=None): + breakpoint = POINTER(DebugBreakpoint2)() + res = self.vt.AddBreakpoint2( + self.control, + BreakpointTypes.DEBUG_BREAKPOINT_CODE, + DEBUG_ANY_ID, + byref(breakpoint), + ) + aborter(res, "Add breakpoint 2") + bp = Breakpoint(breakpoint) + + if offset is not None: + bp.SetOffset(offset) + if enabled is not None and enabled: + bp.SetFlags(BreakpointFlags.DEBUG_BREAKPOINT_ENABLED) + + return bp + + def RemoveBreakpoint(self, bp): + res = self.vt.RemoveBreakpoint2(self.control, bp.breakpoint) + aborter(res, "RemoveBreakpoint2") + bp.die() + + def GetStackTraceEx(self): + # XXX -- I can't find a way to query for how many stack frames there _are_ + # in advance. Guess 128 for now. + num_frames_buffer = 128 + + frames = (DEBUG_STACK_FRAME_EX * num_frames_buffer)() + numframes = c_ulong() + + # First three args are frame/stack/IP offsets -- leave them as zero to + # default to the current instruction. + res = self.vt.GetStackTraceEx( + self.control, 0, 0, 0, frames, num_frames_buffer, byref(numframes) + ) + aborter(res, "GetStackTraceEx") + return frames, numframes.value + + def Execute(self, command): + # First zero is DEBUG_OUTCTL_*, which we leave as a default, second + # zero is DEBUG_EXECUTE_* flags, of which we set none. + res = self.vt.Execute(self.control, 0, command.encode("ascii"), 0) + aborter(res, "Client execute") + + def SetExpressionSyntax(self, cpp=True): + if cpp: + syntax = DebugSyntax.DEBUG_EXPR_CPLUSPLUS + else: + syntax = DebugSyntax.DEBUG_EXPR_MASM + + res = self.vt.SetExpressionSyntax(self.control, syntax) + aborter(res, "SetExpressionSyntax") + + def Evaluate(self, expr): + ptr = DEBUG_VALUE() + res = self.vt.Evaluate( + self.control, + expr.encode("ascii"), + DebugValueType.DEBUG_VALUE_INVALID, + byref(ptr), + None, + ) + aborter(res, "Evaluate", ignore=[E_INTERNALEXCEPTION, E_FAIL]) + if res != 0: + return None + + val_type = DebugValueType(ptr.Type) + + # Here's a map from debug value types to fields. Unclear what happens + # with unsigned values, as DbgEng doesn't present any unsigned fields. + + extract_map = { + DebugValueType.DEBUG_VALUE_INT8: ("I8", "char"), + DebugValueType.DEBUG_VALUE_INT16: ("I16", "short"), + DebugValueType.DEBUG_VALUE_INT32: ("I32", "int"), + DebugValueType.DEBUG_VALUE_INT64: ("I64", "long"), + DebugValueType.DEBUG_VALUE_FLOAT32: ("F32", "float"), + DebugValueType.DEBUG_VALUE_FLOAT64: ("F64", "double"), + } # And everything else is invalid. + + if val_type not in extract_map: + raise Exception( + "Unexpected debug value type {} when evalutaing".format(val_type) + ) + + # Also produce a type name... + + return getattr(ptr.U, extract_map[val_type][0]), extract_map[val_type][1] + + def SetEngineOptions(self, opt): + res = self.vt.SetEngineOptions(self.control, opt) + aborter(res, "SetEngineOptions") + return diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py index 8e01566..03aa548 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/dbgeng.py @@ -16,10 +16,11 @@ from dex.utils.Exceptions import DebuggerException, LoadDebuggerException from dex.utils.ReturnCode import ReturnCode if platform.system() == "Windows": - # Don't load on linux; _load_interface will croak before any names are used. - from . import setup - from . import probe_process - from . import breakpoint + # Don't load on linux; _load_interface will croak before any names are used. + from . import setup + from . import probe_process + from . import breakpoint + class DbgEng(DebuggerBase): def __init__(self, context, *args): @@ -31,11 +32,11 @@ class DbgEng(DebuggerBase): def _custom_init(self): try: - res = setup.setup_everything(self.context.options.executable) - self.client = res - self.running = True + res = setup.setup_everything(self.context.options.executable) + self.client = res + self.running = True except Exception as e: - raise Exception('Failed to start debuggee: {}'.format(e)) + raise Exception("Failed to start debuggee: {}".format(e)) def _custom_exit(self): setup.cleanup(self.client) @@ -43,23 +44,25 @@ class DbgEng(DebuggerBase): def _load_interface(self): arch = platform.architecture()[0] machine = platform.machine() - if arch == '32bit' and machine == 'AMD64': - # This python process is 32 bits, but is sitting on a 64 bit machine. - # Bad things may happen, don't support it. - raise LoadDebuggerException('Can\'t run Dexter dbgeng on 32 bit python in a 64 bit environment') + if arch == "32bit" and machine == "AMD64": + # This python process is 32 bits, but is sitting on a 64 bit machine. + # Bad things may happen, don't support it. + raise LoadDebuggerException( + "Can't run Dexter dbgeng on 32 bit python in a 64 bit environment" + ) - if platform.system() != 'Windows': - raise LoadDebuggerException('DbgEng supports Windows only') + if platform.system() != "Windows": + raise LoadDebuggerException("DbgEng supports Windows only") # Otherwise, everything was imported earlier @classmethod def get_name(cls): - return 'dbgeng' + return "dbgeng" @classmethod def get_option_name(cls): - return 'dbgeng' + return "dbgeng" @property def frames_below_main(self): @@ -85,25 +88,33 @@ class DbgEng(DebuggerBase): def _add_conditional_breakpoint(self, file_, line, condition): # breakpoint setting/deleting is not supported by dbgeng at this moment # but is something that should be considered in the future. - raise NotImplementedError('add_conditional_breakpoint is not yet implemented by dbgeng') + raise NotImplementedError( + "add_conditional_breakpoint is not yet implemented by dbgeng" + ) def get_triggered_breakpoint_ids(self): - raise NotImplementedError('get_triggered_breakpoint_ids is not yet implemented by dbgeng') + raise NotImplementedError( + "get_triggered_breakpoint_ids is not yet implemented by dbgeng" + ) def delete_breakpoints(self, ids): # breakpoint setting/deleting is not supported by dbgeng at this moment # but is something that should be considered in the future. - raise NotImplementedError('delete_conditional_breakpoint is not yet implemented by dbgeng') + raise NotImplementedError( + "delete_conditional_breakpoint is not yet implemented by dbgeng" + ) def launch(self, cmdline): - assert len(cmdline) == 0 and not self.context.options.target_run_args, "Command lines unimplemented for dbgeng right now" + assert ( + len(cmdline) == 0 and not self.context.options.target_run_args + ), "Command lines unimplemented for dbgeng right now" # We are, by this point, already launched. self.step_info = probe_process.probe_state(self.client) def step(self): res = setup.step_once(self.client) if not res: - self.finished = True + self.finished = True self.step_info = res def go(self): @@ -121,38 +132,44 @@ class DbgEng(DebuggerBase): # inlining. dex_frames = [] for i, x in enumerate(frames): - # XXX Might be able to get columns out through - # GetSourceEntriesByOffset, not a priority now - loc = LocIR(path=x.source_file, lineno=x.line_no, column=0) - new_frame = FrameIR(function=x.function_name, is_inlined=False, loc=loc) - dex_frames.append(new_frame) - - state_frame = StackFrame(function=new_frame.function, - is_inlined=new_frame.is_inlined, - location=SourceLocation(path=x.source_file, - lineno=x.line_no, - column=0), - watches={}) - for expr in map( - # Filter out watches that are not active in the current frame, - # and then evaluate all the active watches. - lambda watch_info, idx=i: - self.evaluate_expression(watch_info.expression, idx), - filter( - lambda watch_info, idx=i, line_no=loc.lineno, path=loc.path: - watch_is_active(watch_info, path, idx, line_no), - watches)): - state_frame.watches[expr.expression] = expr - state_frames.append(state_frame) + # XXX Might be able to get columns out through + # GetSourceEntriesByOffset, not a priority now + loc = LocIR(path=x.source_file, lineno=x.line_no, column=0) + new_frame = FrameIR(function=x.function_name, is_inlined=False, loc=loc) + dex_frames.append(new_frame) + + state_frame = StackFrame( + function=new_frame.function, + is_inlined=new_frame.is_inlined, + location=SourceLocation(path=x.source_file, lineno=x.line_no, column=0), + watches={}, + ) + for expr in map( + # Filter out watches that are not active in the current frame, + # and then evaluate all the active watches. + lambda watch_info, idx=i: self.evaluate_expression( + watch_info.expression, idx + ), + filter( + lambda watch_info, idx=i, line_no=loc.lineno, path=loc.path: watch_is_active( + watch_info, path, idx, line_no + ), + watches, + ), + ): + state_frame.watches[expr.expression] = expr + state_frames.append(state_frame) return StepIR( - step_index=step_index, frames=dex_frames, + step_index=step_index, + frames=dex_frames, stop_reason=StopReason.STEP, - program_state=ProgramState(state_frames)) + program_state=ProgramState(state_frames), + ) @property def is_running(self): - return False # We're never free-running + return False # We're never free-running @property def is_finished(self): @@ -161,18 +178,18 @@ class DbgEng(DebuggerBase): def evaluate_expression(self, expression, frame_idx=0): # XXX: cdb insists on using '->' to examine fields of structures, # as it appears to reserve '.' for other purposes. - fixed_expr = expression.replace('.', '->') + fixed_expr = expression.replace(".", "->") orig_scope_idx = self.client.Symbols.GetCurrentScopeFrameIndex() self.client.Symbols.SetScopeFrameByIndex(frame_idx) res = self.client.Control.Evaluate(fixed_expr) if res is not None: - result, typename = self.client.Control.Evaluate(fixed_expr) - could_eval = True + result, typename = self.client.Control.Evaluate(fixed_expr) + could_eval = True else: - result, typename = (None, None) - could_eval = False + result, typename = (None, None) + could_eval = False self.client.Symbols.SetScopeFrameByIndex(orig_scope_idx) @@ -183,4 +200,5 @@ class DbgEng(DebuggerBase): error_string="", could_evaluate=could_eval, is_optimized_away=False, - is_irretrievable=not could_eval) + is_irretrievable=not could_eval, + ) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/probe_process.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/probe_process.py index 8bd7f60..b47bb388 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/probe_process.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/probe_process.py @@ -9,72 +9,75 @@ import os from .utils import * + class Frame(object): - def __init__(self, frame, idx, Symbols): - # Store some base information about the frame - self.ip = frame.InstructionOffset - self.scope_idx = idx - self.virtual = frame.Virtual - self.inline_frame_context = frame.InlineFrameContext - self.func_tbl_entry = frame.FuncTableEntry - - # Fetch the module/symbol we're in, with displacement. Useful for debugging. - self.descr = Symbols.GetNearNameByOffset(self.ip) - split = self.descr.split('!')[0] - self.module = split[0] - self.symbol = split[1] - - # Fetch symbol group for this scope. - prevscope = Symbols.GetCurrentScopeFrameIndex() - if Symbols.SetScopeFrameByIndex(idx): - symgroup = Symbols.GetScopeSymbolGroup2() - Symbols.SetScopeFrameByIndex(prevscope) - self.symgroup = symgroup - else: - self.symgroup = None - - # Fetch the name according to the line-table, using inlining context. - name = Symbols.GetNameByInlineContext(self.ip, self.inline_frame_context) - self.function_name = name.split('!')[-1] - - try: - tup = Symbols.GetLineByInlineContext(self.ip, self.inline_frame_context) - self.source_file, self.line_no = tup - except WinError as e: - # Fall back to trying to use a non-inlining-aware line number - # XXX - this is not inlining aware - sym = Symbols.GetLineByOffset(self.ip) - if sym is not None: - self.source_file, self.line_no = sym - else: - self.source_file = None - self.line_no = None - self.basename = None - - if self.source_file is not None: - self.basename = os.path.basename(self.source_file) - else: - self.basename = None - - - - def __str__(self): - return '{}:{}({}) {}'.format(self.basename, self.line, self.descr, self.function_name) + def __init__(self, frame, idx, Symbols): + # Store some base information about the frame + self.ip = frame.InstructionOffset + self.scope_idx = idx + self.virtual = frame.Virtual + self.inline_frame_context = frame.InlineFrameContext + self.func_tbl_entry = frame.FuncTableEntry + + # Fetch the module/symbol we're in, with displacement. Useful for debugging. + self.descr = Symbols.GetNearNameByOffset(self.ip) + split = self.descr.split("!")[0] + self.module = split[0] + self.symbol = split[1] + + # Fetch symbol group for this scope. + prevscope = Symbols.GetCurrentScopeFrameIndex() + if Symbols.SetScopeFrameByIndex(idx): + symgroup = Symbols.GetScopeSymbolGroup2() + Symbols.SetScopeFrameByIndex(prevscope) + self.symgroup = symgroup + else: + self.symgroup = None + + # Fetch the name according to the line-table, using inlining context. + name = Symbols.GetNameByInlineContext(self.ip, self.inline_frame_context) + self.function_name = name.split("!")[-1] + + try: + tup = Symbols.GetLineByInlineContext(self.ip, self.inline_frame_context) + self.source_file, self.line_no = tup + except WinError as e: + # Fall back to trying to use a non-inlining-aware line number + # XXX - this is not inlining aware + sym = Symbols.GetLineByOffset(self.ip) + if sym is not None: + self.source_file, self.line_no = sym + else: + self.source_file = None + self.line_no = None + self.basename = None + + if self.source_file is not None: + self.basename = os.path.basename(self.source_file) + else: + self.basename = None + + def __str__(self): + return "{}:{}({}) {}".format( + self.basename, self.line, self.descr, self.function_name + ) + def main_on_stack(Symbols, frames): - module_name = Symbols.get_exefile_module_name() - main_name = "{}!main".format(module_name) - for x in frames: - if main_name in x.descr: # Could be less hard coded... - return True - return False + module_name = Symbols.get_exefile_module_name() + main_name = "{}!main".format(module_name) + for x in frames: + if main_name in x.descr: # Could be less hard coded... + return True + return False + def probe_state(Client): - # Fetch the state of the program -- represented by the stack frames. - frames, numframes = Client.Control.GetStackTraceEx() + # Fetch the state of the program -- represented by the stack frames. + frames, numframes = Client.Control.GetStackTraceEx() - the_frames = [Frame(frames[x], x, Client.Symbols) for x in range(numframes)] - if not main_on_stack(Client.Symbols, the_frames): - return None + the_frames = [Frame(frames[x], x, Client.Symbols) for x in range(numframes)] + if not main_on_stack(Client.Symbols, the_frames): + return None - return the_frames + return the_frames diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/setup.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/setup.py index 26360f6..fab423c 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/setup.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/setup.py @@ -13,133 +13,145 @@ from . import symbols from .probe_process import probe_state from .utils import * + class STARTUPINFOA(Structure): - _fields_ = [ - ('cb', c_ulong), - ('lpReserved', c_char_p), - ('lpDesktop', c_char_p), - ('lpTitle', c_char_p), - ('dwX', c_ulong), - ('dwY', c_ulong), - ('dwXSize', c_ulong), - ('dwYSize', c_ulong), - ('dwXCountChars', c_ulong), - ('dwYCountChars', c_ulong), - ('dwFillAttribute', c_ulong), - ('wShowWindow', c_ushort), - ('cbReserved2', c_ushort), - ('lpReserved2', c_char_p), - ('hStdInput', c_void_p), - ('hStdOutput', c_void_p), - ('hStdError', c_void_p) + _fields_ = [ + ("cb", c_ulong), + ("lpReserved", c_char_p), + ("lpDesktop", c_char_p), + ("lpTitle", c_char_p), + ("dwX", c_ulong), + ("dwY", c_ulong), + ("dwXSize", c_ulong), + ("dwYSize", c_ulong), + ("dwXCountChars", c_ulong), + ("dwYCountChars", c_ulong), + ("dwFillAttribute", c_ulong), + ("wShowWindow", c_ushort), + ("cbReserved2", c_ushort), + ("lpReserved2", c_char_p), + ("hStdInput", c_void_p), + ("hStdOutput", c_void_p), + ("hStdError", c_void_p), ] + class PROCESS_INFORMATION(Structure): - _fields_ = [ - ('hProcess', c_void_p), - ('hThread', c_void_p), - ('dwProcessId', c_ulong), - ('dwThreadId', c_ulong) + _fields_ = [ + ("hProcess", c_void_p), + ("hThread", c_void_p), + ("dwProcessId", c_ulong), + ("dwThreadId", c_ulong), ] -def fetch_local_function_syms(Symbols, prefix): - syms = Symbols.get_all_functions() - - def is_sym_in_src_dir(sym): - name, data = sym - symdata = Symbols.GetLineByOffset(data.Offset) - if symdata is not None: - srcfile, line = symdata - if prefix in srcfile: - return True - return False - - syms = [x for x in syms if is_sym_in_src_dir(x)] - return syms -def break_on_all_but_main(Control, Symbols, main_offset): - mainfile, _ = Symbols.GetLineByOffset(main_offset) - prefix = '\\'.join(mainfile.split('\\')[:-1]) - - for name, rec in fetch_local_function_syms(Symbols, prefix): - if name == "main": - continue - bp = Control.AddBreakpoint2(offset=rec.Offset, enabled=True) +def fetch_local_function_syms(Symbols, prefix): + syms = Symbols.get_all_functions() - # All breakpoints are currently discarded: we just sys.exit for cleanup - return + def is_sym_in_src_dir(sym): + name, data = sym + symdata = Symbols.GetLineByOffset(data.Offset) + if symdata is not None: + srcfile, line = symdata + if prefix in srcfile: + return True + return False -def setup_everything(binfile): - from . import client - from . import symbols - Client = client.Client() + syms = [x for x in syms if is_sym_in_src_dir(x)] + return syms - Client.Control.SetEngineOptions(0x20) # DEBUG_ENGOPT_INITIAL_BREAK - Client.CreateProcessAndAttach2(binfile) +def break_on_all_but_main(Control, Symbols, main_offset): + mainfile, _ = Symbols.GetLineByOffset(main_offset) + prefix = "\\".join(mainfile.split("\\")[:-1]) - # Load lines as well as general symbols - sym_opts = Client.Symbols.GetSymbolOptions() - sym_opts |= symbols.SymbolOptionFlags.SYMOPT_LOAD_LINES - Client.Symbols.SetSymbolOptions(sym_opts) + for name, rec in fetch_local_function_syms(Symbols, prefix): + if name == "main": + continue + bp = Control.AddBreakpoint2(offset=rec.Offset, enabled=True) - # Need to enter the debugger engine to let it attach properly. - res = Client.Control.WaitForEvent(timeout=1000) - if res == S_FALSE: - # The debugee apparently didn't do anything at all. Rather than risk - # hanging, bail out at this point. - client.TerminateProcesses() - raise Exception("Debuggee did not start in a timely manner") - - # Enable line stepping. - Client.Control.Execute("l+t") - # Enable C++ expression interpretation. - Client.Control.SetExpressionSyntax(cpp=True) - - # We've requested to break into the process at the earliest opportunity, - # and WaitForEvent'ing means we should have reached that break state. - # Now set a breakpoint on the main symbol, and "go" until we reach it. - module_name = Client.Symbols.get_exefile_module_name() - offset = Client.Symbols.GetOffsetByName("{}!main".format(module_name)) - breakpoint = Client.Control.AddBreakpoint2(offset=offset, enabled=True) - Client.Control.SetExecutionStatus(control.DebugStatus.DEBUG_STATUS_GO) - - # Problem: there is no guarantee that the client will ever reach main, - # something else exciting could happen in that time, the host system may - # be very loaded, and similar. Wait for some period, say, five seconds, and - # abort afterwards: this is a trade-off between spurious timeouts and - # completely hanging in the case of a environmental/programming error. - res = Client.Control.WaitForEvent(timeout=5000) - if res == S_FALSE: - client.TerminateProcesses() - raise Exception("Debuggee did not reach main function in a timely manner") + # All breakpoints are currently discarded: we just sys.exit for cleanup + return - break_on_all_but_main(Client.Control, Client.Symbols, offset) - # Set the default action on all exceptions to be "quit and detach". If we - # don't, dbgeng will merrily spin at the exception site forever. - filts = Client.Control.GetNumberEventFilters() - for x in range(filts[0], filts[0] + filts[1]): - Client.Control.SetExceptionFilterSecondCommand(x, "qd") +def setup_everything(binfile): + from . import client + from . import symbols + + Client = client.Client() + + Client.Control.SetEngineOptions(0x20) # DEBUG_ENGOPT_INITIAL_BREAK + + Client.CreateProcessAndAttach2(binfile) + + # Load lines as well as general symbols + sym_opts = Client.Symbols.GetSymbolOptions() + sym_opts |= symbols.SymbolOptionFlags.SYMOPT_LOAD_LINES + Client.Symbols.SetSymbolOptions(sym_opts) + + # Need to enter the debugger engine to let it attach properly. + res = Client.Control.WaitForEvent(timeout=1000) + if res == S_FALSE: + # The debugee apparently didn't do anything at all. Rather than risk + # hanging, bail out at this point. + client.TerminateProcesses() + raise Exception("Debuggee did not start in a timely manner") + + # Enable line stepping. + Client.Control.Execute("l+t") + # Enable C++ expression interpretation. + Client.Control.SetExpressionSyntax(cpp=True) + + # We've requested to break into the process at the earliest opportunity, + # and WaitForEvent'ing means we should have reached that break state. + # Now set a breakpoint on the main symbol, and "go" until we reach it. + module_name = Client.Symbols.get_exefile_module_name() + offset = Client.Symbols.GetOffsetByName("{}!main".format(module_name)) + breakpoint = Client.Control.AddBreakpoint2(offset=offset, enabled=True) + Client.Control.SetExecutionStatus(control.DebugStatus.DEBUG_STATUS_GO) + + # Problem: there is no guarantee that the client will ever reach main, + # something else exciting could happen in that time, the host system may + # be very loaded, and similar. Wait for some period, say, five seconds, and + # abort afterwards: this is a trade-off between spurious timeouts and + # completely hanging in the case of a environmental/programming error. + res = Client.Control.WaitForEvent(timeout=5000) + if res == S_FALSE: + client.TerminateProcesses() + raise Exception("Debuggee did not reach main function in a timely manner") + + break_on_all_but_main(Client.Control, Client.Symbols, offset) + + # Set the default action on all exceptions to be "quit and detach". If we + # don't, dbgeng will merrily spin at the exception site forever. + filts = Client.Control.GetNumberEventFilters() + for x in range(filts[0], filts[0] + filts[1]): + Client.Control.SetExceptionFilterSecondCommand(x, "qd") + + return Client - return Client def step_once(client): - client.Control.Execute("p") - try: - client.Control.WaitForEvent() - except Exception as e: - if client.Control.GetExecutionStatus() == control.DebugStatus.DEBUG_STATUS_NO_DEBUGGEE: - return None # Debuggee has gone away, likely due to an exception. - raise e - # Could assert here that we're in the "break" state - client.Control.GetExecutionStatus() - return probe_state(client) + client.Control.Execute("p") + try: + client.Control.WaitForEvent() + except Exception as e: + if ( + client.Control.GetExecutionStatus() + == control.DebugStatus.DEBUG_STATUS_NO_DEBUGGEE + ): + return None # Debuggee has gone away, likely due to an exception. + raise e + # Could assert here that we're in the "break" state + client.Control.GetExecutionStatus() + return probe_state(client) + def main_loop(client): - res = True - while res is not None: - res = step_once(client) + res = True + while res is not None: + res = step_once(client) + def cleanup(client): - client.TerminateProcesses() + client.TerminateProcesses() diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/symbols.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/symbols.py index bc998fa..8ba8fad 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/symbols.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/symbols.py @@ -13,487 +13,594 @@ from functools import reduce, partial from .symgroup import SymbolGroup, IDebugSymbolGroup2 from .utils import * + class SymbolOptionFlags(IntFlag): - SYMOPT_CASE_INSENSITIVE = 0x00000001 - SYMOPT_UNDNAME = 0x00000002 - SYMOPT_DEFERRED_LOADS = 0x00000004 - SYMOPT_NO_CPP = 0x00000008 - SYMOPT_LOAD_LINES = 0x00000010 - SYMOPT_OMAP_FIND_NEAREST = 0x00000020 - SYMOPT_LOAD_ANYTHING = 0x00000040 - SYMOPT_IGNORE_CVREC = 0x00000080 - SYMOPT_NO_UNQUALIFIED_LOADS = 0x00000100 - SYMOPT_FAIL_CRITICAL_ERRORS = 0x00000200 - SYMOPT_EXACT_SYMBOLS = 0x00000400 - SYMOPT_ALLOW_ABSOLUTE_SYMBOLS = 0x00000800 - SYMOPT_IGNORE_NT_SYMPATH = 0x00001000 - SYMOPT_INCLUDE_32BIT_MODULES = 0x00002000 - SYMOPT_PUBLICS_ONLY = 0x00004000 - SYMOPT_NO_PUBLICS = 0x00008000 - SYMOPT_AUTO_PUBLICS = 0x00010000 - SYMOPT_NO_IMAGE_SEARCH = 0x00020000 - SYMOPT_SECURE = 0x00040000 - SYMOPT_NO_PROMPTS = 0x00080000 - SYMOPT_DEBUG = 0x80000000 + SYMOPT_CASE_INSENSITIVE = 0x00000001 + SYMOPT_UNDNAME = 0x00000002 + SYMOPT_DEFERRED_LOADS = 0x00000004 + SYMOPT_NO_CPP = 0x00000008 + SYMOPT_LOAD_LINES = 0x00000010 + SYMOPT_OMAP_FIND_NEAREST = 0x00000020 + SYMOPT_LOAD_ANYTHING = 0x00000040 + SYMOPT_IGNORE_CVREC = 0x00000080 + SYMOPT_NO_UNQUALIFIED_LOADS = 0x00000100 + SYMOPT_FAIL_CRITICAL_ERRORS = 0x00000200 + SYMOPT_EXACT_SYMBOLS = 0x00000400 + SYMOPT_ALLOW_ABSOLUTE_SYMBOLS = 0x00000800 + SYMOPT_IGNORE_NT_SYMPATH = 0x00001000 + SYMOPT_INCLUDE_32BIT_MODULES = 0x00002000 + SYMOPT_PUBLICS_ONLY = 0x00004000 + SYMOPT_NO_PUBLICS = 0x00008000 + SYMOPT_AUTO_PUBLICS = 0x00010000 + SYMOPT_NO_IMAGE_SEARCH = 0x00020000 + SYMOPT_SECURE = 0x00040000 + SYMOPT_NO_PROMPTS = 0x00080000 + SYMOPT_DEBUG = 0x80000000 + class ScopeGroupFlags(IntFlag): - DEBUG_SCOPE_GROUP_ARGUMENTS = 0x00000001 - DEBUG_SCOPE_GROUP_LOCALS = 0x00000002 - DEBUG_SCOPE_GROUP_ALL = 0x00000003 - DEBUG_SCOPE_GROUP_BY_DATAMODEL = 0x00000004 + DEBUG_SCOPE_GROUP_ARGUMENTS = 0x00000001 + DEBUG_SCOPE_GROUP_LOCALS = 0x00000002 + DEBUG_SCOPE_GROUP_ALL = 0x00000003 + DEBUG_SCOPE_GROUP_BY_DATAMODEL = 0x00000004 + class DebugModuleNames(IntEnum): - DEBUG_MODNAME_IMAGE = 0x00000000 - DEBUG_MODNAME_MODULE = 0x00000001 - DEBUG_MODNAME_LOADED_IMAGE = 0x00000002 - DEBUG_MODNAME_SYMBOL_FILE = 0x00000003 - DEBUG_MODNAME_MAPPED_IMAGE = 0x00000004 + DEBUG_MODNAME_IMAGE = 0x00000000 + DEBUG_MODNAME_MODULE = 0x00000001 + DEBUG_MODNAME_LOADED_IMAGE = 0x00000002 + DEBUG_MODNAME_SYMBOL_FILE = 0x00000003 + DEBUG_MODNAME_MAPPED_IMAGE = 0x00000004 + class DebugModuleFlags(IntFlag): - DEBUG_MODULE_LOADED = 0x00000000 - DEBUG_MODULE_UNLOADED = 0x00000001 - DEBUG_MODULE_USER_MODE = 0x00000002 - DEBUG_MODULE_EXE_MODULE = 0x00000004 - DEBUG_MODULE_EXPLICIT = 0x00000008 - DEBUG_MODULE_SECONDARY = 0x00000010 - DEBUG_MODULE_SYNTHETIC = 0x00000020 - DEBUG_MODULE_SYM_BAD_CHECKSUM = 0x00010000 + DEBUG_MODULE_LOADED = 0x00000000 + DEBUG_MODULE_UNLOADED = 0x00000001 + DEBUG_MODULE_USER_MODE = 0x00000002 + DEBUG_MODULE_EXE_MODULE = 0x00000004 + DEBUG_MODULE_EXPLICIT = 0x00000008 + DEBUG_MODULE_SECONDARY = 0x00000010 + DEBUG_MODULE_SYNTHETIC = 0x00000020 + DEBUG_MODULE_SYM_BAD_CHECKSUM = 0x00010000 + class DEBUG_MODULE_PARAMETERS(Structure): - _fields_ = [ - ("Base", c_ulonglong), - ("Size", c_ulong), - ("TimeDateStamp", c_ulong), - ("Checksum", c_ulong), - ("Flags", c_ulong), - ("SymbolType", c_ulong), - ("ImageNameSize", c_ulong), - ("ModuleNameSize", c_ulong), - ("LoadedImageNameSize", c_ulong), - ("SymbolFileNameSize", c_ulong), - ("MappedImageNameSize", c_ulong), - ("Reserved", c_ulonglong * 2) + _fields_ = [ + ("Base", c_ulonglong), + ("Size", c_ulong), + ("TimeDateStamp", c_ulong), + ("Checksum", c_ulong), + ("Flags", c_ulong), + ("SymbolType", c_ulong), + ("ImageNameSize", c_ulong), + ("ModuleNameSize", c_ulong), + ("LoadedImageNameSize", c_ulong), + ("SymbolFileNameSize", c_ulong), + ("MappedImageNameSize", c_ulong), + ("Reserved", c_ulonglong * 2), ] + + PDEBUG_MODULE_PARAMETERS = POINTER(DEBUG_MODULE_PARAMETERS) + class DEBUG_MODULE_AND_ID(Structure): - _fields_ = [ - ("ModuleBase", c_ulonglong), - ("Id", c_ulonglong) - ] + _fields_ = [("ModuleBase", c_ulonglong), ("Id", c_ulonglong)] + + PDEBUG_MODULE_AND_ID = POINTER(DEBUG_MODULE_AND_ID) + class DEBUG_SYMBOL_ENTRY(Structure): - _fields_ = [ - ("ModuleBase", c_ulonglong), - ("Offset", c_ulonglong), - ("Id", c_ulonglong), - ("Arg64", c_ulonglong), - ("Size", c_ulong), - ("Flags", c_ulong), - ("TypeId", c_ulong), - ("NameSize", c_ulong), - ("Token", c_ulong), - ("Tag", c_ulong), - ("Arg32", c_ulong), - ("Reserved", c_ulong) + _fields_ = [ + ("ModuleBase", c_ulonglong), + ("Offset", c_ulonglong), + ("Id", c_ulonglong), + ("Arg64", c_ulonglong), + ("Size", c_ulong), + ("Flags", c_ulong), + ("TypeId", c_ulong), + ("NameSize", c_ulong), + ("Token", c_ulong), + ("Tag", c_ulong), + ("Arg32", c_ulong), + ("Reserved", c_ulong), ] + + PDEBUG_SYMBOL_ENTRY = POINTER(DEBUG_SYMBOL_ENTRY) # UUID for DebugSymbols5 interface. -DebugSymbols5IID = IID(0xc65fa83e, 0x1e69, 0x475e, IID_Data4_Type(0x8e, 0x0e, 0xb5, 0xd7, 0x9e, 0x9c, 0xc1, 0x7e)) +DebugSymbols5IID = IID( + 0xC65FA83E, + 0x1E69, + 0x475E, + IID_Data4_Type(0x8E, 0x0E, 0xB5, 0xD7, 0x9E, 0x9C, 0xC1, 0x7E), +) + class IDebugSymbols5(Structure): - pass + pass + class IDebugSymbols5Vtbl(Structure): - wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugSymbols5)) - ids_getsymboloptions = wrp(c_ulong_p) - ids_setsymboloptions = wrp(c_ulong) - ids_getmoduleparameters = wrp(c_ulong, c_ulong64_p, c_ulong, PDEBUG_MODULE_PARAMETERS) - ids_getmodulenamestring = wrp(c_ulong, c_ulong, c_ulonglong, c_char_p, c_ulong, c_ulong_p) - ids_getoffsetbyname = wrp(c_char_p, c_ulong64_p) - ids_getlinebyoffset = wrp(c_ulonglong, c_ulong_p, c_char_p, c_ulong, c_ulong_p, c_ulong64_p) - ids_getsymbolentriesbyname = wrp(c_char_p, c_ulong, PDEBUG_MODULE_AND_ID, c_ulong, c_ulong_p) - ids_getsymbolentrystring = wrp(PDEBUG_MODULE_AND_ID, c_ulong, c_char_p, c_ulong, c_ulong_p) - ids_getsymbolentryinformation = wrp(PDEBUG_MODULE_AND_ID, PDEBUG_SYMBOL_ENTRY) - ids_getcurrentscopeframeindex = wrp(c_ulong_p) - ids_getnearnamebyoffset = wrp(c_ulonglong, c_long, c_char_p, c_ulong, c_ulong_p, c_ulong64_p) - ids_setscopeframebyindex = wrp(c_ulong) - ids_getscopesymbolgroup2 = wrp(c_ulong, POINTER(IDebugSymbolGroup2), POINTER(POINTER(IDebugSymbolGroup2))) - ids_getnamebyinlinecontext = wrp(c_ulonglong, c_ulong, c_char_p, c_ulong, c_ulong_p, c_ulong64_p) - ids_getlinebyinlinecontext = wrp(c_ulonglong, c_ulong, c_ulong_p, c_char_p, c_ulong, c_ulong_p, c_ulong64_p) - _fields_ = [ - ("QueryInterface", c_void_p), - ("AddRef", c_void_p), - ("Release", c_void_p), - ("GetSymbolOptions", ids_getsymboloptions), - ("AddSymbolOptions", c_void_p), - ("RemoveSymbolOptions", c_void_p), - ("SetSymbolOptions", ids_setsymboloptions), - ("GetNameByOffset", c_void_p), - ("GetOffsetByName", ids_getoffsetbyname), - ("GetNearNameByOffset", ids_getnearnamebyoffset), - ("GetLineByOffset", ids_getlinebyoffset), - ("GetOffsetByLine", c_void_p), - ("GetNumberModules", c_void_p), - ("GetModuleByIndex", c_void_p), - ("GetModuleByModuleName", c_void_p), - ("GetModuleByOffset", c_void_p), - ("GetModuleNames", c_void_p), - ("GetModuleParameters", ids_getmoduleparameters), - ("GetSymbolModule", c_void_p), - ("GetTypeName", c_void_p), - ("GetTypeId", c_void_p), - ("GetTypeSize", c_void_p), - ("GetFieldOffset", c_void_p), - ("GetSymbolTypeId", c_void_p), - ("GetOffsetTypeId", c_void_p), - ("ReadTypedDataVirtual", c_void_p), - ("WriteTypedDataVirtual", c_void_p), - ("OutputTypedDataVirtual", c_void_p), - ("ReadTypedDataPhysical", c_void_p), - ("WriteTypedDataPhysical", c_void_p), - ("OutputTypedDataPhysical", c_void_p), - ("GetScope", c_void_p), - ("SetScope", c_void_p), - ("ResetScope", c_void_p), - ("GetScopeSymbolGroup", c_void_p), - ("CreateSymbolGroup", c_void_p), - ("StartSymbolMatch", c_void_p), - ("GetNextSymbolMatch", c_void_p), - ("EndSymbolMatch", c_void_p), - ("Reload", c_void_p), - ("GetSymbolPath", c_void_p), - ("SetSymbolPath", c_void_p), - ("AppendSymbolPath", c_void_p), - ("GetImagePath", c_void_p), - ("SetImagePath", c_void_p), - ("AppendImagePath", c_void_p), - ("GetSourcePath", c_void_p), - ("GetSourcePathElement", c_void_p), - ("SetSourcePath", c_void_p), - ("AppendSourcePath", c_void_p), - ("FindSourceFile", c_void_p), - ("GetSourceFileLineOffsets", c_void_p), - ("GetModuleVersionInformation", c_void_p), - ("GetModuleNameString", ids_getmodulenamestring), - ("GetConstantName", c_void_p), - ("GetFieldName", c_void_p), - ("GetTypeOptions", c_void_p), - ("AddTypeOptions", c_void_p), - ("RemoveTypeOptions", c_void_p), - ("SetTypeOptions", c_void_p), - ("GetNameByOffsetWide", c_void_p), - ("GetOffsetByNameWide", c_void_p), - ("GetNearNameByOffsetWide", c_void_p), - ("GetLineByOffsetWide", c_void_p), - ("GetOffsetByLineWide", c_void_p), - ("GetModuleByModuleNameWide", c_void_p), - ("GetSymbolModuleWide", c_void_p), - ("GetTypeNameWide", c_void_p), - ("GetTypeIdWide", c_void_p), - ("GetFieldOffsetWide", c_void_p), - ("GetSymbolTypeIdWide", c_void_p), - ("GetScopeSymbolGroup2", ids_getscopesymbolgroup2), - ("CreateSymbolGroup2", c_void_p), - ("StartSymbolMatchWide", c_void_p), - ("GetNextSymbolMatchWide", c_void_p), - ("ReloadWide", c_void_p), - ("GetSymbolPathWide", c_void_p), - ("SetSymbolPathWide", c_void_p), - ("AppendSymbolPathWide", c_void_p), - ("GetImagePathWide", c_void_p), - ("SetImagePathWide", c_void_p), - ("AppendImagePathWide", c_void_p), - ("GetSourcePathWide", c_void_p), - ("GetSourcePathElementWide", c_void_p), - ("SetSourcePathWide", c_void_p), - ("AppendSourcePathWide", c_void_p), - ("FindSourceFileWide", c_void_p), - ("GetSourceFileLineOffsetsWide", c_void_p), - ("GetModuleVersionInformationWide", c_void_p), - ("GetModuleNameStringWide", c_void_p), - ("GetConstantNameWide", c_void_p), - ("GetFieldNameWide", c_void_p), - ("IsManagedModule", c_void_p), - ("GetModuleByModuleName2", c_void_p), - ("GetModuleByModuleName2Wide", c_void_p), - ("GetModuleByOffset2", c_void_p), - ("AddSyntheticModule", c_void_p), - ("AddSyntheticModuleWide", c_void_p), - ("RemoveSyntheticModule", c_void_p), - ("GetCurrentScopeFrameIndex", ids_getcurrentscopeframeindex), - ("SetScopeFrameByIndex", ids_setscopeframebyindex), - ("SetScopeFromJitDebugInfo", c_void_p), - ("SetScopeFromStoredEvent", c_void_p), - ("OutputSymbolByOffset", c_void_p), - ("GetFunctionEntryByOffset", c_void_p), - ("GetFieldTypeAndOffset", c_void_p), - ("GetFieldTypeAndOffsetWide", c_void_p), - ("AddSyntheticSymbol", c_void_p), - ("AddSyntheticSymbolWide", c_void_p), - ("RemoveSyntheticSymbol", c_void_p), - ("GetSymbolEntriesByOffset", c_void_p), - ("GetSymbolEntriesByName", ids_getsymbolentriesbyname), - ("GetSymbolEntriesByNameWide", c_void_p), - ("GetSymbolEntryByToken", c_void_p), - ("GetSymbolEntryInformation", ids_getsymbolentryinformation), - ("GetSymbolEntryString", ids_getsymbolentrystring), - ("GetSymbolEntryStringWide", c_void_p), - ("GetSymbolEntryOffsetRegions", c_void_p), - ("GetSymbolEntryBySymbolEntry", c_void_p), - ("GetSourceEntriesByOffset", c_void_p), - ("GetSourceEntriesByLine", c_void_p), - ("GetSourceEntriesByLineWide", c_void_p), - ("GetSourceEntryString", c_void_p), - ("GetSourceEntryStringWide", c_void_p), - ("GetSourceEntryOffsetRegions", c_void_p), - ("GetsourceEntryBySourceEntry", c_void_p), - ("GetScopeEx", c_void_p), - ("SetScopeEx", c_void_p), - ("GetNameByInlineContext", ids_getnamebyinlinecontext), - ("GetNameByInlineContextWide", c_void_p), - ("GetLineByInlineContext", ids_getlinebyinlinecontext), - ("GetLineByInlineContextWide", c_void_p), - ("OutputSymbolByInlineContext", c_void_p), - ("GetCurrentScopeFrameIndexEx", c_void_p), - ("SetScopeFrameByIndexEx", c_void_p) + wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugSymbols5)) + ids_getsymboloptions = wrp(c_ulong_p) + ids_setsymboloptions = wrp(c_ulong) + ids_getmoduleparameters = wrp( + c_ulong, c_ulong64_p, c_ulong, PDEBUG_MODULE_PARAMETERS + ) + ids_getmodulenamestring = wrp( + c_ulong, c_ulong, c_ulonglong, c_char_p, c_ulong, c_ulong_p + ) + ids_getoffsetbyname = wrp(c_char_p, c_ulong64_p) + ids_getlinebyoffset = wrp( + c_ulonglong, c_ulong_p, c_char_p, c_ulong, c_ulong_p, c_ulong64_p + ) + ids_getsymbolentriesbyname = wrp( + c_char_p, c_ulong, PDEBUG_MODULE_AND_ID, c_ulong, c_ulong_p + ) + ids_getsymbolentrystring = wrp( + PDEBUG_MODULE_AND_ID, c_ulong, c_char_p, c_ulong, c_ulong_p + ) + ids_getsymbolentryinformation = wrp(PDEBUG_MODULE_AND_ID, PDEBUG_SYMBOL_ENTRY) + ids_getcurrentscopeframeindex = wrp(c_ulong_p) + ids_getnearnamebyoffset = wrp( + c_ulonglong, c_long, c_char_p, c_ulong, c_ulong_p, c_ulong64_p + ) + ids_setscopeframebyindex = wrp(c_ulong) + ids_getscopesymbolgroup2 = wrp( + c_ulong, POINTER(IDebugSymbolGroup2), POINTER(POINTER(IDebugSymbolGroup2)) + ) + ids_getnamebyinlinecontext = wrp( + c_ulonglong, c_ulong, c_char_p, c_ulong, c_ulong_p, c_ulong64_p + ) + ids_getlinebyinlinecontext = wrp( + c_ulonglong, c_ulong, c_ulong_p, c_char_p, c_ulong, c_ulong_p, c_ulong64_p + ) + _fields_ = [ + ("QueryInterface", c_void_p), + ("AddRef", c_void_p), + ("Release", c_void_p), + ("GetSymbolOptions", ids_getsymboloptions), + ("AddSymbolOptions", c_void_p), + ("RemoveSymbolOptions", c_void_p), + ("SetSymbolOptions", ids_setsymboloptions), + ("GetNameByOffset", c_void_p), + ("GetOffsetByName", ids_getoffsetbyname), + ("GetNearNameByOffset", ids_getnearnamebyoffset), + ("GetLineByOffset", ids_getlinebyoffset), + ("GetOffsetByLine", c_void_p), + ("GetNumberModules", c_void_p), + ("GetModuleByIndex", c_void_p), + ("GetModuleByModuleName", c_void_p), + ("GetModuleByOffset", c_void_p), + ("GetModuleNames", c_void_p), + ("GetModuleParameters", ids_getmoduleparameters), + ("GetSymbolModule", c_void_p), + ("GetTypeName", c_void_p), + ("GetTypeId", c_void_p), + ("GetTypeSize", c_void_p), + ("GetFieldOffset", c_void_p), + ("GetSymbolTypeId", c_void_p), + ("GetOffsetTypeId", c_void_p), + ("ReadTypedDataVirtual", c_void_p), + ("WriteTypedDataVirtual", c_void_p), + ("OutputTypedDataVirtual", c_void_p), + ("ReadTypedDataPhysical", c_void_p), + ("WriteTypedDataPhysical", c_void_p), + ("OutputTypedDataPhysical", c_void_p), + ("GetScope", c_void_p), + ("SetScope", c_void_p), + ("ResetScope", c_void_p), + ("GetScopeSymbolGroup", c_void_p), + ("CreateSymbolGroup", c_void_p), + ("StartSymbolMatch", c_void_p), + ("GetNextSymbolMatch", c_void_p), + ("EndSymbolMatch", c_void_p), + ("Reload", c_void_p), + ("GetSymbolPath", c_void_p), + ("SetSymbolPath", c_void_p), + ("AppendSymbolPath", c_void_p), + ("GetImagePath", c_void_p), + ("SetImagePath", c_void_p), + ("AppendImagePath", c_void_p), + ("GetSourcePath", c_void_p), + ("GetSourcePathElement", c_void_p), + ("SetSourcePath", c_void_p), + ("AppendSourcePath", c_void_p), + ("FindSourceFile", c_void_p), + ("GetSourceFileLineOffsets", c_void_p), + ("GetModuleVersionInformation", c_void_p), + ("GetModuleNameString", ids_getmodulenamestring), + ("GetConstantName", c_void_p), + ("GetFieldName", c_void_p), + ("GetTypeOptions", c_void_p), + ("AddTypeOptions", c_void_p), + ("RemoveTypeOptions", c_void_p), + ("SetTypeOptions", c_void_p), + ("GetNameByOffsetWide", c_void_p), + ("GetOffsetByNameWide", c_void_p), + ("GetNearNameByOffsetWide", c_void_p), + ("GetLineByOffsetWide", c_void_p), + ("GetOffsetByLineWide", c_void_p), + ("GetModuleByModuleNameWide", c_void_p), + ("GetSymbolModuleWide", c_void_p), + ("GetTypeNameWide", c_void_p), + ("GetTypeIdWide", c_void_p), + ("GetFieldOffsetWide", c_void_p), + ("GetSymbolTypeIdWide", c_void_p), + ("GetScopeSymbolGroup2", ids_getscopesymbolgroup2), + ("CreateSymbolGroup2", c_void_p), + ("StartSymbolMatchWide", c_void_p), + ("GetNextSymbolMatchWide", c_void_p), + ("ReloadWide", c_void_p), + ("GetSymbolPathWide", c_void_p), + ("SetSymbolPathWide", c_void_p), + ("AppendSymbolPathWide", c_void_p), + ("GetImagePathWide", c_void_p), + ("SetImagePathWide", c_void_p), + ("AppendImagePathWide", c_void_p), + ("GetSourcePathWide", c_void_p), + ("GetSourcePathElementWide", c_void_p), + ("SetSourcePathWide", c_void_p), + ("AppendSourcePathWide", c_void_p), + ("FindSourceFileWide", c_void_p), + ("GetSourceFileLineOffsetsWide", c_void_p), + ("GetModuleVersionInformationWide", c_void_p), + ("GetModuleNameStringWide", c_void_p), + ("GetConstantNameWide", c_void_p), + ("GetFieldNameWide", c_void_p), + ("IsManagedModule", c_void_p), + ("GetModuleByModuleName2", c_void_p), + ("GetModuleByModuleName2Wide", c_void_p), + ("GetModuleByOffset2", c_void_p), + ("AddSyntheticModule", c_void_p), + ("AddSyntheticModuleWide", c_void_p), + ("RemoveSyntheticModule", c_void_p), + ("GetCurrentScopeFrameIndex", ids_getcurrentscopeframeindex), + ("SetScopeFrameByIndex", ids_setscopeframebyindex), + ("SetScopeFromJitDebugInfo", c_void_p), + ("SetScopeFromStoredEvent", c_void_p), + ("OutputSymbolByOffset", c_void_p), + ("GetFunctionEntryByOffset", c_void_p), + ("GetFieldTypeAndOffset", c_void_p), + ("GetFieldTypeAndOffsetWide", c_void_p), + ("AddSyntheticSymbol", c_void_p), + ("AddSyntheticSymbolWide", c_void_p), + ("RemoveSyntheticSymbol", c_void_p), + ("GetSymbolEntriesByOffset", c_void_p), + ("GetSymbolEntriesByName", ids_getsymbolentriesbyname), + ("GetSymbolEntriesByNameWide", c_void_p), + ("GetSymbolEntryByToken", c_void_p), + ("GetSymbolEntryInformation", ids_getsymbolentryinformation), + ("GetSymbolEntryString", ids_getsymbolentrystring), + ("GetSymbolEntryStringWide", c_void_p), + ("GetSymbolEntryOffsetRegions", c_void_p), + ("GetSymbolEntryBySymbolEntry", c_void_p), + ("GetSourceEntriesByOffset", c_void_p), + ("GetSourceEntriesByLine", c_void_p), + ("GetSourceEntriesByLineWide", c_void_p), + ("GetSourceEntryString", c_void_p), + ("GetSourceEntryStringWide", c_void_p), + ("GetSourceEntryOffsetRegions", c_void_p), + ("GetsourceEntryBySourceEntry", c_void_p), + ("GetScopeEx", c_void_p), + ("SetScopeEx", c_void_p), + ("GetNameByInlineContext", ids_getnamebyinlinecontext), + ("GetNameByInlineContextWide", c_void_p), + ("GetLineByInlineContext", ids_getlinebyinlinecontext), + ("GetLineByInlineContextWide", c_void_p), + ("OutputSymbolByInlineContext", c_void_p), + ("GetCurrentScopeFrameIndexEx", c_void_p), + ("SetScopeFrameByIndexEx", c_void_p), ] + IDebugSymbols5._fields_ = [("lpVtbl", POINTER(IDebugSymbols5Vtbl))] SymbolId = namedtuple("SymbolId", ["ModuleBase", "Id"]) -SymbolEntry = namedtuple("SymbolEntry", ["ModuleBase", "Offset", "Id", "Arg64", "Size", "Flags", "TypeId", "NameSize", "Token", "Tag", "Arg32"]) -DebugModuleParams = namedtuple("DebugModuleParams", ["Base", "Size", "TimeDateStamp", "Checksum", "Flags", "SymbolType", "ImageNameSize", "ModuleNameSize", "LoadedImageNameSize", "SymbolFileNameSize", "MappedImageNameSize"]) +SymbolEntry = namedtuple( + "SymbolEntry", + [ + "ModuleBase", + "Offset", + "Id", + "Arg64", + "Size", + "Flags", + "TypeId", + "NameSize", + "Token", + "Tag", + "Arg32", + ], +) +DebugModuleParams = namedtuple( + "DebugModuleParams", + [ + "Base", + "Size", + "TimeDateStamp", + "Checksum", + "Flags", + "SymbolType", + "ImageNameSize", + "ModuleNameSize", + "LoadedImageNameSize", + "SymbolFileNameSize", + "MappedImageNameSize", + ], +) + class SymTags(IntEnum): - Null = 0 - Exe = 1 - SymTagFunction = 5 + Null = 0 + Exe = 1 + SymTagFunction = 5 + def make_debug_module_params(cdata): - fieldvalues = map(lambda y: getattr(cdata, y), DebugModuleParams._fields) - return DebugModuleParams(*fieldvalues) + fieldvalues = map(lambda y: getattr(cdata, y), DebugModuleParams._fields) + return DebugModuleParams(*fieldvalues) + class Symbols(object): - def __init__(self, symbols): - self.ptr = symbols - self.symbols = symbols.contents - self.vt = self.symbols.lpVtbl.contents - # Keep some handy ulongs for passing into C methods. - self.ulong = c_ulong() - self.ulong64 = c_ulonglong() - - def GetCurrentScopeFrameIndex(self): - res = self.vt.GetCurrentScopeFrameIndex(self.symbols, byref(self.ulong)) - aborter(res, "GetCurrentScopeFrameIndex") - return self.ulong.value - - def SetScopeFrameByIndex(self, idx): - res = self.vt.SetScopeFrameByIndex(self.symbols, idx) - aborter(res, "SetScopeFrameByIndex", ignore=[E_EINVAL]) - return res != E_EINVAL - - def GetOffsetByName(self, name): - res = self.vt.GetOffsetByName(self.symbols, name.encode("ascii"), byref(self.ulong64)) - aborter(res, "GetOffsetByName {}".format(name)) - return self.ulong64.value - - def GetNearNameByOffset(self, addr): - ptr = create_string_buffer(256) - pulong = c_ulong() - disp = c_ulonglong() - # Zero arg -> "delta" indicating how many symbols to skip - res = self.vt.GetNearNameByOffset(self.symbols, addr, 0, ptr, 255, byref(pulong), byref(disp)) - if res == E_NOINTERFACE: - return "{noname}" - aborter(res, "GetNearNameByOffset") - ptr[255] = '\0'.encode("ascii") - return '{}+{}'.format(string_at(ptr).decode("ascii"), disp.value) - - def GetModuleByModuleName2(self, name): - # First zero arg -> module index to search from, second zero arg -> - # DEBUG_GETMOD_* flags, none of which we use. - res = self.vt.GetModuleByModuleName2(self.symbols, name, 0, 0, None, byref(self.ulong64)) - aborter(res, "GetModuleByModuleName2") - return self.ulong64.value - - def GetScopeSymbolGroup2(self): - retptr = POINTER(IDebugSymbolGroup2)() - res = self.vt.GetScopeSymbolGroup2(self.symbols, ScopeGroupFlags.DEBUG_SCOPE_GROUP_ALL, None, retptr) - aborter(res, "GetScopeSymbolGroup2") - return SymbolGroup(retptr) - - def GetSymbolEntryString(self, idx, module): - symid = DEBUG_MODULE_AND_ID() - symid.ModuleBase = module - symid.Id = idx - ptr = create_string_buffer(1024) - # Zero arg is the string index -- symbols can have multiple names, for now - # only support the first one. - res = self.vt.GetSymbolEntryString(self.symbols, symid, 0, ptr, 1023, byref(self.ulong)) - aborter(res, "GetSymbolEntryString") - return string_at(ptr).decode("ascii") - - def GetSymbolEntryInformation(self, module, theid): - symid = DEBUG_MODULE_AND_ID() - symentry = DEBUG_SYMBOL_ENTRY() - symid.ModuleBase = module - symid.Id = theid - res = self.vt.GetSymbolEntryInformation(self.symbols, symid, symentry) - aborter(res, "GetSymbolEntryInformation") - # Fetch fields into SymbolEntry object - fields = map(lambda x: getattr(symentry, x), SymbolEntry._fields) - return SymbolEntry(*fields) - - def GetSymbolEntriesByName(self, symstr): - # Initial query to find number of symbol entries - res = self.vt.GetSymbolEntriesByName(self.symbols, symstr.encode("ascii"), 0, None, 0, byref(self.ulong)) - aborter(res, "GetSymbolEntriesByName") - - # Build a buffer and query for 'length' entries - length = self.ulong.value - symrecs = (DEBUG_MODULE_AND_ID * length)() - # Zero arg -> flags, of which there are none defined. - res = self.vt.GetSymbolEntriesByName(self.symbols, symstr.encode("ascii"), 0, symrecs, length, byref(self.ulong)) - aborter(res, "GetSymbolEntriesByName") - - # Extract 'length' number of SymbolIds - length = self.ulong.value - def extract(x): - sym = symrecs[x] - return SymbolId(sym.ModuleBase, sym.Id) - return [extract(x) for x in range(length)] - - def GetSymbolPath(self): - # Query for length of buffer to allocate - res = self.vt.GetSymbolPath(self.symbols, None, 0, byref(self.ulong)) - aborter(res, "GetSymbolPath", ignore=[S_FALSE]) - - # Fetch 'length' length symbol path string - length = self.ulong.value - arr = create_string_buffer(length) - res = self.vt.GetSymbolPath(self.symbols, arr, length, byref(self.ulong)) - aborter(res, "GetSymbolPath") - - return string_at(arr).decode("ascii") - - def GetSourcePath(self): - # Query for length of buffer to allocate - res = self.vt.GetSourcePath(self.symbols, None, 0, byref(self.ulong)) - aborter(res, "GetSourcePath", ignore=[S_FALSE]) - - # Fetch a string of len 'length' - length = self.ulong.value - arr = create_string_buffer(length) - res = self.vt.GetSourcePath(self.symbols, arr, length, byref(self.ulong)) - aborter(res, "GetSourcePath") - - return string_at(arr).decode("ascii") - - def SetSourcePath(self, string): - res = self.vt.SetSourcePath(self.symbols, string.encode("ascii")) - aborter(res, "SetSourcePath") - return - - def GetModuleParameters(self, base): - self.ulong64.value = base - params = DEBUG_MODULE_PARAMETERS() - # Fetch one module params struct, starting at idx zero - res = self.vt.GetModuleParameters(self.symbols, 1, byref(self.ulong64), 0, byref(params)) - aborter(res, "GetModuleParameters") - return make_debug_module_params(params) - - def GetSymbolOptions(self): - res = self.vt.GetSymbolOptions(self.symbols, byref(self.ulong)) - aborter(res, "GetSymbolOptions") - return SymbolOptionFlags(self.ulong.value) - - def SetSymbolOptions(self, opts): - assert isinstance(opts, SymbolOptionFlags) - res = self.vt.SetSymbolOptions(self.symbols, opts.value) - aborter(res, "SetSymbolOptions") - return - - def GetLineByOffset(self, offs): - # Initial query for filename buffer size - res = self.vt.GetLineByOffset(self.symbols, offs, None, None, 0, byref(self.ulong), None) - if res == E_FAIL: - return None # Sometimes we just can't get line numbers, of course - aborter(res, "GetLineByOffset", ignore=[S_FALSE]) - - # Allocate filename buffer and query for line number too - filenamelen = self.ulong.value - text = create_string_buffer(filenamelen) - line = c_ulong() - res = self.vt.GetLineByOffset(self.symbols, offs, byref(line), text, filenamelen, byref(self.ulong), None) - aborter(res, "GetLineByOffset") - - return string_at(text).decode("ascii"), line.value - - def GetModuleNameString(self, whichname, base): - # Initial query for name string length - res = self.vt.GetModuleNameString(self.symbols, whichname, DEBUG_ANY_ID, base, None, 0, byref(self.ulong)) - aborter(res, "GetModuleNameString", ignore=[S_FALSE]) - - module_name_len = self.ulong.value - module_name = (c_char * module_name_len)() - res = self.vt.GetModuleNameString(self.symbols, whichname, DEBUG_ANY_ID, base, module_name, module_name_len, None) - aborter(res, "GetModuleNameString") - - return string_at(module_name).decode("ascii") - - def GetNameByInlineContext(self, pc, ctx): - # None args -> ignore output name size and displacement - buf = create_string_buffer(256) - res = self.vt.GetNameByInlineContext(self.symbols, pc, ctx, buf, 255, None, None) - aborter(res, "GetNameByInlineContext") - return string_at(buf).decode("ascii") - - def GetLineByInlineContext(self, pc, ctx): - # None args -> ignore output filename size and displacement - buf = create_string_buffer(256) - res = self.vt.GetLineByInlineContext(self.symbols, pc, ctx, byref(self.ulong), buf, 255, None, None) - aborter(res, "GetLineByInlineContext") - return string_at(buf).decode("ascii"), self.ulong.value - - def get_all_symbols(self): - main_module_name = self.get_exefile_module_name() - idnumbers = self.GetSymbolEntriesByName("{}!*".format(main_module_name)) - lst = [] - for symid in idnumbers: - s = self.GetSymbolEntryString(symid.Id, symid.ModuleBase) - symentry = self.GetSymbolEntryInformation(symid.ModuleBase, symid.Id) - lst.append((s, symentry)) - return lst - - def get_all_functions(self): - syms = self.get_all_symbols() - return [x for x in syms if x[1].Tag == SymTags.SymTagFunction] - - def get_all_modules(self): - params = DEBUG_MODULE_PARAMETERS() - idx = 0 - res = 0 - all_modules = [] - while res != E_EINVAL: - res = self.vt.GetModuleParameters(self.symbols, 1, None, idx, byref(params)) - aborter(res, "GetModuleParameters", ignore=[E_EINVAL]) - all_modules.append(make_debug_module_params(params)) - idx += 1 - return all_modules - - def get_exefile_module(self): - all_modules = self.get_all_modules() - reduce_func = lambda x, y: y if y.Flags & DebugModuleFlags.DEBUG_MODULE_EXE_MODULE else x - main_module = reduce(reduce_func, all_modules, None) - if main_module is None: - raise Exception("Couldn't find the exefile module") - return main_module - - def get_module_name(self, base): - return self.GetModuleNameString(DebugModuleNames.DEBUG_MODNAME_MODULE, base) - - def get_exefile_module_name(self): - return self.get_module_name(self.get_exefile_module().Base) + def __init__(self, symbols): + self.ptr = symbols + self.symbols = symbols.contents + self.vt = self.symbols.lpVtbl.contents + # Keep some handy ulongs for passing into C methods. + self.ulong = c_ulong() + self.ulong64 = c_ulonglong() + + def GetCurrentScopeFrameIndex(self): + res = self.vt.GetCurrentScopeFrameIndex(self.symbols, byref(self.ulong)) + aborter(res, "GetCurrentScopeFrameIndex") + return self.ulong.value + + def SetScopeFrameByIndex(self, idx): + res = self.vt.SetScopeFrameByIndex(self.symbols, idx) + aborter(res, "SetScopeFrameByIndex", ignore=[E_EINVAL]) + return res != E_EINVAL + + def GetOffsetByName(self, name): + res = self.vt.GetOffsetByName( + self.symbols, name.encode("ascii"), byref(self.ulong64) + ) + aborter(res, "GetOffsetByName {}".format(name)) + return self.ulong64.value + + def GetNearNameByOffset(self, addr): + ptr = create_string_buffer(256) + pulong = c_ulong() + disp = c_ulonglong() + # Zero arg -> "delta" indicating how many symbols to skip + res = self.vt.GetNearNameByOffset( + self.symbols, addr, 0, ptr, 255, byref(pulong), byref(disp) + ) + if res == E_NOINTERFACE: + return "{noname}" + aborter(res, "GetNearNameByOffset") + ptr[255] = "\0".encode("ascii") + return "{}+{}".format(string_at(ptr).decode("ascii"), disp.value) + + def GetModuleByModuleName2(self, name): + # First zero arg -> module index to search from, second zero arg -> + # DEBUG_GETMOD_* flags, none of which we use. + res = self.vt.GetModuleByModuleName2( + self.symbols, name, 0, 0, None, byref(self.ulong64) + ) + aborter(res, "GetModuleByModuleName2") + return self.ulong64.value + + def GetScopeSymbolGroup2(self): + retptr = POINTER(IDebugSymbolGroup2)() + res = self.vt.GetScopeSymbolGroup2( + self.symbols, ScopeGroupFlags.DEBUG_SCOPE_GROUP_ALL, None, retptr + ) + aborter(res, "GetScopeSymbolGroup2") + return SymbolGroup(retptr) + + def GetSymbolEntryString(self, idx, module): + symid = DEBUG_MODULE_AND_ID() + symid.ModuleBase = module + symid.Id = idx + ptr = create_string_buffer(1024) + # Zero arg is the string index -- symbols can have multiple names, for now + # only support the first one. + res = self.vt.GetSymbolEntryString( + self.symbols, symid, 0, ptr, 1023, byref(self.ulong) + ) + aborter(res, "GetSymbolEntryString") + return string_at(ptr).decode("ascii") + + def GetSymbolEntryInformation(self, module, theid): + symid = DEBUG_MODULE_AND_ID() + symentry = DEBUG_SYMBOL_ENTRY() + symid.ModuleBase = module + symid.Id = theid + res = self.vt.GetSymbolEntryInformation(self.symbols, symid, symentry) + aborter(res, "GetSymbolEntryInformation") + # Fetch fields into SymbolEntry object + fields = map(lambda x: getattr(symentry, x), SymbolEntry._fields) + return SymbolEntry(*fields) + + def GetSymbolEntriesByName(self, symstr): + # Initial query to find number of symbol entries + res = self.vt.GetSymbolEntriesByName( + self.symbols, symstr.encode("ascii"), 0, None, 0, byref(self.ulong) + ) + aborter(res, "GetSymbolEntriesByName") + + # Build a buffer and query for 'length' entries + length = self.ulong.value + symrecs = (DEBUG_MODULE_AND_ID * length)() + # Zero arg -> flags, of which there are none defined. + res = self.vt.GetSymbolEntriesByName( + self.symbols, symstr.encode("ascii"), 0, symrecs, length, byref(self.ulong) + ) + aborter(res, "GetSymbolEntriesByName") + + # Extract 'length' number of SymbolIds + length = self.ulong.value + + def extract(x): + sym = symrecs[x] + return SymbolId(sym.ModuleBase, sym.Id) + + return [extract(x) for x in range(length)] + + def GetSymbolPath(self): + # Query for length of buffer to allocate + res = self.vt.GetSymbolPath(self.symbols, None, 0, byref(self.ulong)) + aborter(res, "GetSymbolPath", ignore=[S_FALSE]) + + # Fetch 'length' length symbol path string + length = self.ulong.value + arr = create_string_buffer(length) + res = self.vt.GetSymbolPath(self.symbols, arr, length, byref(self.ulong)) + aborter(res, "GetSymbolPath") + + return string_at(arr).decode("ascii") + + def GetSourcePath(self): + # Query for length of buffer to allocate + res = self.vt.GetSourcePath(self.symbols, None, 0, byref(self.ulong)) + aborter(res, "GetSourcePath", ignore=[S_FALSE]) + + # Fetch a string of len 'length' + length = self.ulong.value + arr = create_string_buffer(length) + res = self.vt.GetSourcePath(self.symbols, arr, length, byref(self.ulong)) + aborter(res, "GetSourcePath") + + return string_at(arr).decode("ascii") + + def SetSourcePath(self, string): + res = self.vt.SetSourcePath(self.symbols, string.encode("ascii")) + aborter(res, "SetSourcePath") + return + + def GetModuleParameters(self, base): + self.ulong64.value = base + params = DEBUG_MODULE_PARAMETERS() + # Fetch one module params struct, starting at idx zero + res = self.vt.GetModuleParameters( + self.symbols, 1, byref(self.ulong64), 0, byref(params) + ) + aborter(res, "GetModuleParameters") + return make_debug_module_params(params) + + def GetSymbolOptions(self): + res = self.vt.GetSymbolOptions(self.symbols, byref(self.ulong)) + aborter(res, "GetSymbolOptions") + return SymbolOptionFlags(self.ulong.value) + + def SetSymbolOptions(self, opts): + assert isinstance(opts, SymbolOptionFlags) + res = self.vt.SetSymbolOptions(self.symbols, opts.value) + aborter(res, "SetSymbolOptions") + return + + def GetLineByOffset(self, offs): + # Initial query for filename buffer size + res = self.vt.GetLineByOffset( + self.symbols, offs, None, None, 0, byref(self.ulong), None + ) + if res == E_FAIL: + return None # Sometimes we just can't get line numbers, of course + aborter(res, "GetLineByOffset", ignore=[S_FALSE]) + + # Allocate filename buffer and query for line number too + filenamelen = self.ulong.value + text = create_string_buffer(filenamelen) + line = c_ulong() + res = self.vt.GetLineByOffset( + self.symbols, offs, byref(line), text, filenamelen, byref(self.ulong), None + ) + aborter(res, "GetLineByOffset") + + return string_at(text).decode("ascii"), line.value + + def GetModuleNameString(self, whichname, base): + # Initial query for name string length + res = self.vt.GetModuleNameString( + self.symbols, whichname, DEBUG_ANY_ID, base, None, 0, byref(self.ulong) + ) + aborter(res, "GetModuleNameString", ignore=[S_FALSE]) + + module_name_len = self.ulong.value + module_name = (c_char * module_name_len)() + res = self.vt.GetModuleNameString( + self.symbols, + whichname, + DEBUG_ANY_ID, + base, + module_name, + module_name_len, + None, + ) + aborter(res, "GetModuleNameString") + + return string_at(module_name).decode("ascii") + + def GetNameByInlineContext(self, pc, ctx): + # None args -> ignore output name size and displacement + buf = create_string_buffer(256) + res = self.vt.GetNameByInlineContext( + self.symbols, pc, ctx, buf, 255, None, None + ) + aborter(res, "GetNameByInlineContext") + return string_at(buf).decode("ascii") + + def GetLineByInlineContext(self, pc, ctx): + # None args -> ignore output filename size and displacement + buf = create_string_buffer(256) + res = self.vt.GetLineByInlineContext( + self.symbols, pc, ctx, byref(self.ulong), buf, 255, None, None + ) + aborter(res, "GetLineByInlineContext") + return string_at(buf).decode("ascii"), self.ulong.value + + def get_all_symbols(self): + main_module_name = self.get_exefile_module_name() + idnumbers = self.GetSymbolEntriesByName("{}!*".format(main_module_name)) + lst = [] + for symid in idnumbers: + s = self.GetSymbolEntryString(symid.Id, symid.ModuleBase) + symentry = self.GetSymbolEntryInformation(symid.ModuleBase, symid.Id) + lst.append((s, symentry)) + return lst + + def get_all_functions(self): + syms = self.get_all_symbols() + return [x for x in syms if x[1].Tag == SymTags.SymTagFunction] + + def get_all_modules(self): + params = DEBUG_MODULE_PARAMETERS() + idx = 0 + res = 0 + all_modules = [] + while res != E_EINVAL: + res = self.vt.GetModuleParameters(self.symbols, 1, None, idx, byref(params)) + aborter(res, "GetModuleParameters", ignore=[E_EINVAL]) + all_modules.append(make_debug_module_params(params)) + idx += 1 + return all_modules + + def get_exefile_module(self): + all_modules = self.get_all_modules() + reduce_func = ( + lambda x, y: y if y.Flags & DebugModuleFlags.DEBUG_MODULE_EXE_MODULE else x + ) + main_module = reduce(reduce_func, all_modules, None) + if main_module is None: + raise Exception("Couldn't find the exefile module") + return main_module + + def get_module_name(self, base): + return self.GetModuleNameString(DebugModuleNames.DEBUG_MODNAME_MODULE, base) + + def get_exefile_module_name(self): + return self.get_module_name(self.get_exefile_module().Base) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/symgroup.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/symgroup.py index 2775af3..abe7143 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/symgroup.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/symgroup.py @@ -13,86 +13,92 @@ from .utils import * Symbol = namedtuple("Symbol", ["num", "name", "type", "value"]) + class IDebugSymbolGroup2(Structure): - pass + pass + class IDebugSymbolGroup2Vtbl(Structure): - wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugSymbolGroup2)) - ids_getnumbersymbols = wrp(c_ulong_p) - ids_getsymbolname = wrp(c_ulong, c_char_p, c_ulong, c_ulong_p) - ids_getsymboltypename = wrp(c_ulong, c_char_p, c_ulong, c_ulong_p) - ids_getsymbolvaluetext = wrp(c_ulong, c_char_p, c_ulong, c_ulong_p) - _fields_ = [ - ("QueryInterface", c_void_p), - ("AddRef", c_void_p), - ("Release", c_void_p), - ("GetNumberSymbols", ids_getnumbersymbols), - ("AddSymbol", c_void_p), - ("RemoveSymbolByName", c_void_p), - ("RemoveSymbolByIndex", c_void_p), - ("GetSymbolName", ids_getsymbolname), - ("GetSymbolParameters", c_void_p), - ("ExpandSymbol", c_void_p), - ("OutputSymbols", c_void_p), - ("WriteSymbol", c_void_p), - ("OutputAsType", c_void_p), - ("AddSymbolWide", c_void_p), - ("RemoveSymbolByNameWide", c_void_p), - ("GetSymbolNameWide", c_void_p), - ("WritesymbolWide", c_void_p), - ("OutputAsTypeWide", c_void_p), - ("GetSymbolTypeName", ids_getsymboltypename), - ("GetSymbolTypeNameWide", c_void_p), - ("GetSymbolSize", c_void_p), - ("GetSymbolOffset", c_void_p), - ("GetSymbolRegister", c_void_p), - ("GetSymbolValueText", ids_getsymbolvaluetext), - ("GetSymbolValueTextWide", c_void_p), - ("GetSymbolEntryInformation", c_void_p) + wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugSymbolGroup2)) + ids_getnumbersymbols = wrp(c_ulong_p) + ids_getsymbolname = wrp(c_ulong, c_char_p, c_ulong, c_ulong_p) + ids_getsymboltypename = wrp(c_ulong, c_char_p, c_ulong, c_ulong_p) + ids_getsymbolvaluetext = wrp(c_ulong, c_char_p, c_ulong, c_ulong_p) + _fields_ = [ + ("QueryInterface", c_void_p), + ("AddRef", c_void_p), + ("Release", c_void_p), + ("GetNumberSymbols", ids_getnumbersymbols), + ("AddSymbol", c_void_p), + ("RemoveSymbolByName", c_void_p), + ("RemoveSymbolByIndex", c_void_p), + ("GetSymbolName", ids_getsymbolname), + ("GetSymbolParameters", c_void_p), + ("ExpandSymbol", c_void_p), + ("OutputSymbols", c_void_p), + ("WriteSymbol", c_void_p), + ("OutputAsType", c_void_p), + ("AddSymbolWide", c_void_p), + ("RemoveSymbolByNameWide", c_void_p), + ("GetSymbolNameWide", c_void_p), + ("WritesymbolWide", c_void_p), + ("OutputAsTypeWide", c_void_p), + ("GetSymbolTypeName", ids_getsymboltypename), + ("GetSymbolTypeNameWide", c_void_p), + ("GetSymbolSize", c_void_p), + ("GetSymbolOffset", c_void_p), + ("GetSymbolRegister", c_void_p), + ("GetSymbolValueText", ids_getsymbolvaluetext), + ("GetSymbolValueTextWide", c_void_p), + ("GetSymbolEntryInformation", c_void_p), ] + IDebugSymbolGroup2._fields_ = [("lpVtbl", POINTER(IDebugSymbolGroup2Vtbl))] + class SymbolGroup(object): - def __init__(self, symgroup): - self.symgroup = symgroup.contents - self.vt = self.symgroup.lpVtbl.contents - self.ulong = c_ulong() - - def GetNumberSymbols(self): - res = self.vt.GetNumberSymbols(self.symgroup, byref(self.ulong)) - aborter(res, "GetNumberSymbols") - return self.ulong.value - - def GetSymbolName(self, idx): - buf = create_string_buffer(256) - res = self.vt.GetSymbolName(self.symgroup, idx, buf, 255, byref(self.ulong)) - aborter(res, "GetSymbolName") - thelen = self.ulong.value - return string_at(buf).decode("ascii") - - def GetSymbolTypeName(self, idx): - buf = create_string_buffer(256) - res = self.vt.GetSymbolTypeName(self.symgroup, idx, buf, 255, byref(self.ulong)) - aborter(res, "GetSymbolTypeName") - thelen = self.ulong.value - return string_at(buf).decode("ascii") - - def GetSymbolValueText(self, idx, handleserror=False): - buf = create_string_buffer(256) - res = self.vt.GetSymbolValueText(self.symgroup, idx, buf, 255, byref(self.ulong)) - if res != 0 and handleserror: - return None - aborter(res, "GetSymbolTypeName") - thelen = self.ulong.value - return string_at(buf).decode("ascii") - - def get_symbol(self, idx): - name = self.GetSymbolName(idx) - thetype = self.GetSymbolTypeName(idx) - value = self.GetSymbolValueText(idx) - return Symbol(idx, name, thetype, value) - - def get_all_symbols(self): - num_syms = self.GetNumberSymbols() - return list(map(self.get_symbol, list(range(num_syms)))) + def __init__(self, symgroup): + self.symgroup = symgroup.contents + self.vt = self.symgroup.lpVtbl.contents + self.ulong = c_ulong() + + def GetNumberSymbols(self): + res = self.vt.GetNumberSymbols(self.symgroup, byref(self.ulong)) + aborter(res, "GetNumberSymbols") + return self.ulong.value + + def GetSymbolName(self, idx): + buf = create_string_buffer(256) + res = self.vt.GetSymbolName(self.symgroup, idx, buf, 255, byref(self.ulong)) + aborter(res, "GetSymbolName") + thelen = self.ulong.value + return string_at(buf).decode("ascii") + + def GetSymbolTypeName(self, idx): + buf = create_string_buffer(256) + res = self.vt.GetSymbolTypeName(self.symgroup, idx, buf, 255, byref(self.ulong)) + aborter(res, "GetSymbolTypeName") + thelen = self.ulong.value + return string_at(buf).decode("ascii") + + def GetSymbolValueText(self, idx, handleserror=False): + buf = create_string_buffer(256) + res = self.vt.GetSymbolValueText( + self.symgroup, idx, buf, 255, byref(self.ulong) + ) + if res != 0 and handleserror: + return None + aborter(res, "GetSymbolTypeName") + thelen = self.ulong.value + return string_at(buf).decode("ascii") + + def get_symbol(self, idx): + name = self.GetSymbolName(idx) + thetype = self.GetSymbolTypeName(idx) + value = self.GetSymbolValueText(idx) + return Symbol(idx, name, thetype, value) + + def get_all_symbols(self): + num_syms = self.GetNumberSymbols() + return list(map(self.get_symbol, list(range(num_syms)))) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/sysobjs.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/sysobjs.py index 0e9844a..5e1fe92 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/sysobjs.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/sysobjs.py @@ -11,190 +11,210 @@ from functools import partial from .utils import * # UUID For SystemObjects4 interface. -DebugSystemObjects4IID = IID(0x489468e6, 0x7d0f, 0x4af5, IID_Data4_Type(0x87, 0xab, 0x25, 0x20, 0x74, 0x54, 0xd5, 0x53)) +DebugSystemObjects4IID = IID( + 0x489468E6, + 0x7D0F, + 0x4AF5, + IID_Data4_Type(0x87, 0xAB, 0x25, 0x20, 0x74, 0x54, 0xD5, 0x53), +) + class IDebugSystemObjects4(Structure): - pass + pass + class IDebugSystemObjects4Vtbl(Structure): - wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugSystemObjects4)) - ids_getnumberprocesses = wrp(POINTER(c_ulong)) - ids_getprocessidsbyindex = wrp(c_ulong, c_ulong, c_ulong_p, c_ulong_p) - ids_setcurrentprocessid = wrp(c_ulong) - ids_getnumberthreads = wrp(c_ulong_p) - ids_getthreadidsbyindex = wrp(c_ulong, c_ulong, c_ulong_p, c_ulong_p) - ids_setcurrentthreadid = wrp(c_ulong) - _fields_ = [ - ("QueryInterface", c_void_p), - ("AddRef", c_void_p), - ("Release", c_void_p), - ("GetEventThread", c_void_p), - ("GetEventProcess", c_void_p), - ("GetCurrentThreadId", c_void_p), - ("SetCurrentThreadId", ids_setcurrentthreadid), - ("GetCurrentProcessId", c_void_p), - ("SetCurrentProcessId", ids_setcurrentprocessid), - ("GetNumberThreads", ids_getnumberthreads), - ("GetTotalNumberThreads", c_void_p), - ("GetThreadIdsByIndex", ids_getthreadidsbyindex), - ("GetThreadIdByProcessor", c_void_p), - ("GetCurrentThreadDataOffset", c_void_p), - ("GetThreadIdByDataOffset", c_void_p), - ("GetCurrentThreadTeb", c_void_p), - ("GetThreadIdByTeb", c_void_p), - ("GetCurrentThreadSystemId", c_void_p), - ("GetThreadIdBySystemId", c_void_p), - ("GetCurrentThreadHandle", c_void_p), - ("GetThreadIdByHandle", c_void_p), - ("GetNumberProcesses", ids_getnumberprocesses), - ("GetProcessIdsByIndex", ids_getprocessidsbyindex), - ("GetCurrentProcessDataOffset", c_void_p), - ("GetProcessIdByDataOffset", c_void_p), - ("GetCurrentProcessPeb", c_void_p), - ("GetProcessIdByPeb", c_void_p), - ("GetCurrentProcessSystemId", c_void_p), - ("GetProcessIdBySystemId", c_void_p), - ("GetCurrentProcessHandle", c_void_p), - ("GetProcessIdByHandle", c_void_p), - ("GetCurrentProcessExecutableName", c_void_p), - ("GetCurrentProcessUpTime", c_void_p), - ("GetImplicitThreadDataOffset", c_void_p), - ("SetImplicitThreadDataOffset", c_void_p), - ("GetImplicitProcessDataOffset", c_void_p), - ("SetImplicitProcessDataOffset", c_void_p), - ("GetEventSystem", c_void_p), - ("GetCurrentSystemId", c_void_p), - ("SetCurrentSystemId", c_void_p), - ("GetNumberSystems", c_void_p), - ("GetSystemIdsByIndex", c_void_p), - ("GetTotalNumberThreadsAndProcesses", c_void_p), - ("GetCurrentSystemServer", c_void_p), - ("GetSystemByServer", c_void_p), - ("GetCurrentSystemServerName", c_void_p), - ("GetCurrentProcessExecutableNameWide", c_void_p), - ("GetCurrentSystemServerNameWide", c_void_p) + wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugSystemObjects4)) + ids_getnumberprocesses = wrp(POINTER(c_ulong)) + ids_getprocessidsbyindex = wrp(c_ulong, c_ulong, c_ulong_p, c_ulong_p) + ids_setcurrentprocessid = wrp(c_ulong) + ids_getnumberthreads = wrp(c_ulong_p) + ids_getthreadidsbyindex = wrp(c_ulong, c_ulong, c_ulong_p, c_ulong_p) + ids_setcurrentthreadid = wrp(c_ulong) + _fields_ = [ + ("QueryInterface", c_void_p), + ("AddRef", c_void_p), + ("Release", c_void_p), + ("GetEventThread", c_void_p), + ("GetEventProcess", c_void_p), + ("GetCurrentThreadId", c_void_p), + ("SetCurrentThreadId", ids_setcurrentthreadid), + ("GetCurrentProcessId", c_void_p), + ("SetCurrentProcessId", ids_setcurrentprocessid), + ("GetNumberThreads", ids_getnumberthreads), + ("GetTotalNumberThreads", c_void_p), + ("GetThreadIdsByIndex", ids_getthreadidsbyindex), + ("GetThreadIdByProcessor", c_void_p), + ("GetCurrentThreadDataOffset", c_void_p), + ("GetThreadIdByDataOffset", c_void_p), + ("GetCurrentThreadTeb", c_void_p), + ("GetThreadIdByTeb", c_void_p), + ("GetCurrentThreadSystemId", c_void_p), + ("GetThreadIdBySystemId", c_void_p), + ("GetCurrentThreadHandle", c_void_p), + ("GetThreadIdByHandle", c_void_p), + ("GetNumberProcesses", ids_getnumberprocesses), + ("GetProcessIdsByIndex", ids_getprocessidsbyindex), + ("GetCurrentProcessDataOffset", c_void_p), + ("GetProcessIdByDataOffset", c_void_p), + ("GetCurrentProcessPeb", c_void_p), + ("GetProcessIdByPeb", c_void_p), + ("GetCurrentProcessSystemId", c_void_p), + ("GetProcessIdBySystemId", c_void_p), + ("GetCurrentProcessHandle", c_void_p), + ("GetProcessIdByHandle", c_void_p), + ("GetCurrentProcessExecutableName", c_void_p), + ("GetCurrentProcessUpTime", c_void_p), + ("GetImplicitThreadDataOffset", c_void_p), + ("SetImplicitThreadDataOffset", c_void_p), + ("GetImplicitProcessDataOffset", c_void_p), + ("SetImplicitProcessDataOffset", c_void_p), + ("GetEventSystem", c_void_p), + ("GetCurrentSystemId", c_void_p), + ("SetCurrentSystemId", c_void_p), + ("GetNumberSystems", c_void_p), + ("GetSystemIdsByIndex", c_void_p), + ("GetTotalNumberThreadsAndProcesses", c_void_p), + ("GetCurrentSystemServer", c_void_p), + ("GetSystemByServer", c_void_p), + ("GetCurrentSystemServerName", c_void_p), + ("GetCurrentProcessExecutableNameWide", c_void_p), + ("GetCurrentSystemServerNameWide", c_void_p), ] + IDebugSystemObjects4._fields_ = [("lpVtbl", POINTER(IDebugSystemObjects4Vtbl))] + class SysObjects(object): - def __init__(self, sysobjects): - self.ptr = sysobjects - self.sysobjects = sysobjects.contents - self.vt = self.sysobjects.lpVtbl.contents - # Keep a handy ulong for passing into C methods. - self.ulong = c_ulong() - - def GetNumberSystems(self): - res = self.vt.GetNumberSystems(self.sysobjects, byref(self.ulong)) - aborter(res, "GetNumberSystems") - return self.ulong.value - - def GetNumberProcesses(self): - res = self.vt.GetNumberProcesses(self.sysobjects, byref(self.ulong)) - aborter(res, "GetNumberProcesses") - return self.ulong.value - - def GetNumberThreads(self): - res = self.vt.GetNumberThreads(self.sysobjects, byref(self.ulong)) - aborter(res, "GetNumberThreads") - return self.ulong.value - - def GetTotalNumberThreadsAndProcesses(self): - tthreads = c_ulong() - tprocs = c_ulong() - pulong3 = c_ulong() - res = self.vt.GetTotalNumberThreadsAndProcesses(self.sysobjects, byref(tthreads), byref(tprocs), byref(pulong3), byref(pulong3), byref(pulong3)) - aborter(res, "GettotalNumberThreadsAndProcesses") - return tthreads.value, tprocs.value - - def GetCurrentProcessId(self): - res = self.vt.GetCurrentProcessId(self.sysobjects, byref(self.ulong)) - aborter(res, "GetCurrentProcessId") - return self.ulong.value - - def SetCurrentProcessId(self, sysid): - res = self.vt.SetCurrentProcessId(self.sysobjects, sysid) - aborter(res, "SetCurrentProcessId") - return - - def GetCurrentThreadId(self): - res = self.vt.GetCurrentThreadId(self.sysobjects, byref(self.ulong)) - aborter(res, "GetCurrentThreadId") - return self.ulong.value - - def SetCurrentThreadId(self, sysid): - res = self.vt.SetCurrentThreadId(self.sysobjects, sysid) - aborter(res, "SetCurrentThreadId") - return - - def GetProcessIdsByIndex(self): - num_processes = self.GetNumberProcesses() - if num_processes == 0: - return [] - engineids = (c_ulong * num_processes)() - pids = (c_ulong * num_processes)() - for x in range(num_processes): - engineids[x] = DEBUG_ANY_ID - pids[x] = DEBUG_ANY_ID - res = self.vt.GetProcessIdsByIndex(self.sysobjects, 0, num_processes, engineids, pids) - aborter(res, "GetProcessIdsByIndex") - return list(zip(engineids, pids)) - - def GetThreadIdsByIndex(self): - num_threads = self.GetNumberThreads() - if num_threads == 0: - return [] - engineids = (c_ulong * num_threads)() - tids = (c_ulong * num_threads)() - for x in range(num_threads): - engineids[x] = DEBUG_ANY_ID - tids[x] = DEBUG_ANY_ID - # Zero -> start index - res = self.vt.GetThreadIdsByIndex(self.sysobjects, 0, num_threads, engineids, tids) - aborter(res, "GetThreadIdsByIndex") - return list(zip(engineids, tids)) - - def GetCurThreadHandle(self): - pulong64 = c_ulonglong() - res = self.vt.GetCurrentThreadHandle(self.sysobjects, byref(pulong64)) - aborter(res, "GetCurrentThreadHandle") - return pulong64.value - - def set_current_thread(self, pid, tid): - proc_sys_id = -1 - for x in self.GetProcessIdsByIndex(): - sysid, procid = x - if procid == pid: - proc_sys_id = sysid - - if proc_sys_id == -1: - raise Exception("Couldn't find designated PID {}".format(pid)) - - self.SetCurrentProcessId(proc_sys_id) - - thread_sys_id = -1 - for x in self.GetThreadIdsByIndex(): - sysid, threadid = x - if threadid == tid: - thread_sys_id = sysid - - if thread_sys_id == -1: - raise Exception("Couldn't find designated TID {}".format(tid)) - - self.SetCurrentThreadId(thread_sys_id) - return - - def print_current_procs_threads(self): - procs = [] - for x in self.GetProcessIdsByIndex(): - sysid, procid = x - procs.append(procid) - - threads = [] - for x in self.GetThreadIdsByIndex(): - sysid, threadid = x - threads.append(threadid) - - print("Current processes: {}".format(procs)) - print("Current threads: {}".format(threads)) + def __init__(self, sysobjects): + self.ptr = sysobjects + self.sysobjects = sysobjects.contents + self.vt = self.sysobjects.lpVtbl.contents + # Keep a handy ulong for passing into C methods. + self.ulong = c_ulong() + + def GetNumberSystems(self): + res = self.vt.GetNumberSystems(self.sysobjects, byref(self.ulong)) + aborter(res, "GetNumberSystems") + return self.ulong.value + + def GetNumberProcesses(self): + res = self.vt.GetNumberProcesses(self.sysobjects, byref(self.ulong)) + aborter(res, "GetNumberProcesses") + return self.ulong.value + + def GetNumberThreads(self): + res = self.vt.GetNumberThreads(self.sysobjects, byref(self.ulong)) + aborter(res, "GetNumberThreads") + return self.ulong.value + + def GetTotalNumberThreadsAndProcesses(self): + tthreads = c_ulong() + tprocs = c_ulong() + pulong3 = c_ulong() + res = self.vt.GetTotalNumberThreadsAndProcesses( + self.sysobjects, + byref(tthreads), + byref(tprocs), + byref(pulong3), + byref(pulong3), + byref(pulong3), + ) + aborter(res, "GettotalNumberThreadsAndProcesses") + return tthreads.value, tprocs.value + + def GetCurrentProcessId(self): + res = self.vt.GetCurrentProcessId(self.sysobjects, byref(self.ulong)) + aborter(res, "GetCurrentProcessId") + return self.ulong.value + + def SetCurrentProcessId(self, sysid): + res = self.vt.SetCurrentProcessId(self.sysobjects, sysid) + aborter(res, "SetCurrentProcessId") + return + + def GetCurrentThreadId(self): + res = self.vt.GetCurrentThreadId(self.sysobjects, byref(self.ulong)) + aborter(res, "GetCurrentThreadId") + return self.ulong.value + + def SetCurrentThreadId(self, sysid): + res = self.vt.SetCurrentThreadId(self.sysobjects, sysid) + aborter(res, "SetCurrentThreadId") + return + + def GetProcessIdsByIndex(self): + num_processes = self.GetNumberProcesses() + if num_processes == 0: + return [] + engineids = (c_ulong * num_processes)() + pids = (c_ulong * num_processes)() + for x in range(num_processes): + engineids[x] = DEBUG_ANY_ID + pids[x] = DEBUG_ANY_ID + res = self.vt.GetProcessIdsByIndex( + self.sysobjects, 0, num_processes, engineids, pids + ) + aborter(res, "GetProcessIdsByIndex") + return list(zip(engineids, pids)) + + def GetThreadIdsByIndex(self): + num_threads = self.GetNumberThreads() + if num_threads == 0: + return [] + engineids = (c_ulong * num_threads)() + tids = (c_ulong * num_threads)() + for x in range(num_threads): + engineids[x] = DEBUG_ANY_ID + tids[x] = DEBUG_ANY_ID + # Zero -> start index + res = self.vt.GetThreadIdsByIndex( + self.sysobjects, 0, num_threads, engineids, tids + ) + aborter(res, "GetThreadIdsByIndex") + return list(zip(engineids, tids)) + + def GetCurThreadHandle(self): + pulong64 = c_ulonglong() + res = self.vt.GetCurrentThreadHandle(self.sysobjects, byref(pulong64)) + aborter(res, "GetCurrentThreadHandle") + return pulong64.value + + def set_current_thread(self, pid, tid): + proc_sys_id = -1 + for x in self.GetProcessIdsByIndex(): + sysid, procid = x + if procid == pid: + proc_sys_id = sysid + + if proc_sys_id == -1: + raise Exception("Couldn't find designated PID {}".format(pid)) + + self.SetCurrentProcessId(proc_sys_id) + + thread_sys_id = -1 + for x in self.GetThreadIdsByIndex(): + sysid, threadid = x + if threadid == tid: + thread_sys_id = sysid + + if thread_sys_id == -1: + raise Exception("Couldn't find designated TID {}".format(tid)) + + self.SetCurrentThreadId(thread_sys_id) + return + + def print_current_procs_threads(self): + procs = [] + for x in self.GetProcessIdsByIndex(): + sysid, procid = x + procs.append(procid) + + threads = [] + for x in self.GetThreadIdsByIndex(): + sysid, threadid = x + threads.append(threadid) + + print("Current processes: {}".format(procs)) + print("Current threads: {}".format(threads)) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/utils.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/utils.py index 0c9197a..8bea3da 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/utils.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/dbgeng/utils.py @@ -19,29 +19,34 @@ S_FALSE = 1 # This doesn't fit into any convenient category DEBUG_ANY_ID = 0xFFFFFFFF + class WinError(Exception): - def __init__(self, msg, hstatus): - self.hstatus = hstatus - super(WinError, self).__init__(msg) + def __init__(self, msg, hstatus): + self.hstatus = hstatus + super(WinError, self).__init__(msg) + def aborter(res, msg, ignore=[]): - if res != 0 and res not in ignore: - # Convert a negative error code to a positive unsigned one, which is - # now NTSTATUSes appear in documentation. - if res < 0: - res += 0x100000000 - msg = '{:08X} : {}'.format(res, msg) - raise WinError(msg, res) + if res != 0 and res not in ignore: + # Convert a negative error code to a positive unsigned one, which is + # now NTSTATUSes appear in documentation. + if res < 0: + res += 0x100000000 + msg = "{:08X} : {}".format(res, msg) + raise WinError(msg, res) + IID_Data4_Type = c_ubyte * 8 + class IID(Structure): - _fields_ = [ - ("Data1", c_uint), - ("Data2", c_ushort), - ("Data3", c_ushort), - ("Data4", IID_Data4_Type) - ] + _fields_ = [ + ("Data1", c_uint), + ("Data2", c_ushort), + ("Data3", c_ushort), + ("Data4", IID_Data4_Type), + ] + c_ulong_p = POINTER(c_ulong) c_ulong64_p = POINTER(c_ulonglong) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py index 4ab693c..b13e743 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/lldb/LLDB.py @@ -36,17 +36,19 @@ class LLDB(DebuggerBase): self._debugger = self._interface.SBDebugger.Create() self._debugger.SetAsync(False) self._target = self._debugger.CreateTargetWithFileAndArch( - self.context.options.executable, self.context.options.arch) + self.context.options.executable, self.context.options.arch + ) if not self._target: raise LoadDebuggerException( - 'could not create target for executable "{}" with arch:{}'. - format(self.context.options.executable, - self.context.options.arch)) + 'could not create target for executable "{}" with arch:{}'.format( + self.context.options.executable, self.context.options.arch + ) + ) def _custom_exit(self): - if getattr(self, '_process', None): + if getattr(self, "_process", None): self._process.Kill() - if getattr(self, '_debugger', None) and getattr(self, '_target', None): + if getattr(self, "_debugger", None) and getattr(self, "_target", None): self._debugger.DeleteTarget(self._target) def _translate_stop_reason(self, reason): @@ -64,38 +66,37 @@ class LLDB(DebuggerBase): def _load_interface(self): try: - args = [self.lldb_executable, '-P'] - pythonpath = check_output( - args, stderr=STDOUT).rstrip().decode('utf-8') + args = [self.lldb_executable, "-P"] + pythonpath = check_output(args, stderr=STDOUT).rstrip().decode("utf-8") except CalledProcessError as e: raise LoadDebuggerException(str(e), sys.exc_info()) except OSError as e: raise LoadDebuggerException( - '{} ["{}"]'.format(e.strerror, self.lldb_executable), - sys.exc_info()) + '{} ["{}"]'.format(e.strerror, self.lldb_executable), sys.exc_info() + ) if not os.path.isdir(pythonpath): raise LoadDebuggerException( - 'path "{}" does not exist [result of {}]'.format( - pythonpath, args), sys.exc_info()) + 'path "{}" does not exist [result of {}]'.format(pythonpath, args), + sys.exc_info(), + ) try: - module_info = imp.find_module('lldb', [pythonpath]) - return imp.load_module('lldb', *module_info) + module_info = imp.find_module("lldb", [pythonpath]) + return imp.load_module("lldb", *module_info) except ImportError as e: msg = str(e) - if msg.endswith('not a valid Win32 application.'): - msg = '{} [Are you mixing 32-bit and 64-bit binaries?]'.format( - msg) + if msg.endswith("not a valid Win32 application."): + msg = "{} [Are you mixing 32-bit and 64-bit binaries?]".format(msg) raise LoadDebuggerException(msg, sys.exc_info()) @classmethod def get_name(cls): - return 'lldb' + return "lldb" @classmethod def get_option_name(cls): - return 'lldb' + return "lldb" @property def version(self): @@ -114,7 +115,8 @@ class LLDB(DebuggerBase): bp = self._target.BreakpointCreateByLocation(file_, line) if not bp: raise DebuggerException( - 'could not add breakpoint [{}:{}]'.format(file_, line)) + "could not add breakpoint [{}:{}]".format(file_, line) + ) id = bp.GetID() if condition: bp.SetCondition(condition) @@ -134,7 +136,7 @@ class LLDB(DebuggerBase): # This must be an unconditional breakpoint. return True valueIR = self.evaluate_expression(condition) - return valueIR.type_name == 'bool' and valueIR.value == 'true' + return valueIR.type_name == "bool" and valueIR.value == "true" def get_triggered_breakpoint_ids(self): # Breakpoints can only have been triggered if we've hit one. @@ -173,12 +175,12 @@ class LLDB(DebuggerBase): def launch(self, cmdline): if self.context.options.target_run_args: - cmdline += shlex.split(self.context.options.target_run_args) + cmdline += shlex.split(self.context.options.target_run_args) self._process = self._target.LaunchSimple(cmdline, None, os.getcwd()) if not self._process or self._process.GetNumThreads() == 0: - raise DebuggerException('could not launch process') + raise DebuggerException("could not launch process") if self._process.GetNumThreads() != 1: - raise DebuggerException('multiple threads not supported') + raise DebuggerException("multiple threads not supported") self._thread = self._process.GetThreadAtIndex(0) assert self._thread, (self._process, self._thread) @@ -199,45 +201,52 @@ class LLDB(DebuggerBase): sb_filespec = sb_line.GetFileSpec() try: - path = os.path.join(sb_filespec.GetDirectory(), - sb_filespec.GetFilename()) + path = os.path.join( + sb_filespec.GetDirectory(), sb_filespec.GetFilename() + ) except (AttributeError, TypeError): path = None function = self._sanitize_function_name(sb_frame.GetFunctionName()) loc_dict = { - 'path': path, - 'lineno': sb_line.GetLine(), - 'column': sb_line.GetColumn() + "path": path, + "lineno": sb_line.GetLine(), + "column": sb_line.GetColumn(), } loc = LocIR(**loc_dict) valid_loc_for_watch = loc.path and os.path.exists(loc.path) - frame = FrameIR( - function=function, is_inlined=sb_frame.IsInlined(), loc=loc) + frame = FrameIR(function=function, is_inlined=sb_frame.IsInlined(), loc=loc) if any( - name in (frame.function or '') # pylint: disable=no-member - for name in self.frames_below_main): + name in (frame.function or "") # pylint: disable=no-member + for name in self.frames_below_main + ): break frames.append(frame) - state_frame = StackFrame(function=frame.function, - is_inlined=frame.is_inlined, - location=SourceLocation(**loc_dict), - watches={}) + state_frame = StackFrame( + function=frame.function, + is_inlined=frame.is_inlined, + location=SourceLocation(**loc_dict), + watches={}, + ) if valid_loc_for_watch: for expr in map( # Filter out watches that are not active in the current frame, # and then evaluate all the active watches. - lambda watch_info, idx=i: - self.evaluate_expression(watch_info.expression, idx), + lambda watch_info, idx=i: self.evaluate_expression( + watch_info.expression, idx + ), filter( - lambda watch_info, idx=i, line_no=loc.lineno, loc_path=loc.path: - watch_is_active(watch_info, loc_path, idx, line_no), - watches)): + lambda watch_info, idx=i, line_no=loc.lineno, loc_path=loc.path: watch_is_active( + watch_info, loc_path, idx, line_no + ), + watches, + ), + ): state_frame.watches[expr.expression] = expr state_frames.append(state_frame) @@ -248,8 +257,11 @@ class LLDB(DebuggerBase): reason = self._translate_stop_reason(self._thread.GetStopReason()) return StepIR( - step_index=step_index, frames=frames, stop_reason=reason, - program_state=ProgramState(state_frames)) + step_index=step_index, + frames=frames, + stop_reason=reason, + program_state=ProgramState(state_frames), + ) @property def is_running(self): @@ -262,46 +274,54 @@ class LLDB(DebuggerBase): @property def frames_below_main(self): - return ['__scrt_common_main_seh', '__libc_start_main', '__libc_start_call_main'] + return ["__scrt_common_main_seh", "__libc_start_main", "__libc_start_call_main"] def evaluate_expression(self, expression, frame_idx=0) -> ValueIR: - result = self._thread.GetFrameAtIndex(frame_idx - ).EvaluateExpression(expression) + result = self._thread.GetFrameAtIndex(frame_idx).EvaluateExpression(expression) error_string = str(result.error) value = result.value - could_evaluate = not any(s in error_string for s in [ - "Can't run the expression locally", - "use of undeclared identifier", - "no member named", - "Couldn't lookup symbols", - "reference to local variable", - "invalid use of 'this' outside of a non-static member function", - ]) - - is_optimized_away = any(s in error_string for s in [ - 'value may have been optimized out', - ]) - - is_irretrievable = any(s in error_string for s in [ - "couldn't get the value of variable", - "couldn't read its memory", - "couldn't read from memory", - "Cannot access memory at address", - "invalid address (fault address:", - ]) + could_evaluate = not any( + s in error_string + for s in [ + "Can't run the expression locally", + "use of undeclared identifier", + "no member named", + "Couldn't lookup symbols", + "reference to local variable", + "invalid use of 'this' outside of a non-static member function", + ] + ) + + is_optimized_away = any( + s in error_string + for s in [ + "value may have been optimized out", + ] + ) + + is_irretrievable = any( + s in error_string + for s in [ + "couldn't get the value of variable", + "couldn't read its memory", + "couldn't read from memory", + "Cannot access memory at address", + "invalid address (fault address:", + ] + ) if could_evaluate and not is_irretrievable and not is_optimized_away: - assert error_string == 'success', (error_string, expression, value) + assert error_string == "success", (error_string, expression, value) # assert result.value is not None, (result.value, expression) - if error_string == 'success': + if error_string == "success": error_string = None # attempt to find expression as a variable, if found, take the variable # obj's type information as it's 'usually' more accurate. var_result = self._thread.GetFrameAtIndex(frame_idx).FindVariable(expression) - if str(var_result.error) == 'success': + if str(var_result.error) == "success": type_name = var_result.type.GetDisplayTypeName() else: type_name = result.type.GetDisplayTypeName() diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py index 1db20c7..ca71d8b 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio.py @@ -24,9 +24,9 @@ from dex.utils.ReturnCode import ReturnCode def _load_com_module(): try: module_info = imp.find_module( - 'ComInterface', - [os.path.join(os.path.dirname(__file__), 'windows')]) - return imp.load_module('ComInterface', *module_info) + "ComInterface", [os.path.join(os.path.dirname(__file__), "windows")] + ) + return imp.load_module("ComInterface", *module_info) except ImportError as e: raise LoadDebuggerException(e, sys.exc_info()) @@ -34,9 +34,12 @@ def _load_com_module(): # VSBreakpoint(path: PurePath, line: int, col: int, cond: str). This is enough # info to identify breakpoint equivalence in visual studio based on the # properties we set through dexter currently. -VSBreakpoint = namedtuple('VSBreakpoint', 'path, line, col, cond') +VSBreakpoint = namedtuple("VSBreakpoint", "path, line, col, cond") + -class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abstract-method +class VisualStudio( + DebuggerBase, metaclass=abc.ABCMeta +): # pylint: disable=abstract-method # Constants for results of Debugger.CurrentMode # (https://msdn.microsoft.com/en-us/library/envdte.debugger.currentmode.aspx) @@ -68,29 +71,31 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst super(VisualStudio, self).__init__(*args) def _create_solution(self): - self._solution.Create(self.context.working_directory.path, - 'DexterSolution') + self._solution.Create(self.context.working_directory.path, "DexterSolution") try: self._solution.AddFromFile(self._project_file) except OSError: raise LoadDebuggerException( - 'could not debug the specified executable', sys.exc_info()) + "could not debug the specified executable", sys.exc_info() + ) def _load_solution(self): try: self._solution.Open(self.context.options.vs_solution) except: raise LoadDebuggerException( - 'could not load specified vs solution at {}'. - format(self.context.options.vs_solution), sys.exc_info()) + "could not load specified vs solution at {}".format( + self.context.options.vs_solution + ), + sys.exc_info(), + ) def _custom_init(self): try: self._debugger = self._interface.Debugger self._debugger.HexDisplayMode = False - self._interface.MainWindow.Visible = ( - self.context.options.show_debugger) + self._interface.MainWindow.Visible = self.context.options.show_debugger self._solution = self._interface.Solution if self.context.options.vs_solution is None: @@ -118,14 +123,14 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst @property def _location(self): - #TODO: Find a better way of determining path, line and column info + # TODO: Find a better way of determining path, line and column info # that doesn't require reading break points. This method requires # all lines to have a break point on them. bp = self._debugger.BreakpointLastHit return { - 'path': getattr(bp, 'File', None), - 'lineno': getattr(bp, 'FileLine', None), - 'column': getattr(bp, 'FileColumn', None) + "path": getattr(bp, "File", None), + "lineno": getattr(bp, "FileLine", None), + "column": getattr(bp, "FileColumn", None), } @property @@ -150,7 +155,7 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst self._dex_id_to_vs.clear() def _add_breakpoint(self, file_, line): - return self._add_conditional_breakpoint(file_, line, '') + return self._add_conditional_breakpoint(file_, line, "") def _get_next_id(self): # "Generate" a new unique id for the breakpoint. @@ -171,7 +176,7 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst # Breakpoint doesn't exist already. Add it now. count_before = self._debugger.Breakpoints.Count - self._debugger.Breakpoints.Add('', file_, line, col, condition) + self._debugger.Breakpoints.Add("", file_, line, col, condition) # Our internal representation of VS says that the breakpoint doesn't # already exist so we do not expect this operation to fail here. assert count_before < self._debugger.Breakpoints.Count @@ -181,8 +186,7 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst return new_id def get_triggered_breakpoint_ids(self): - """Returns a set of opaque ids for just-triggered breakpoints. - """ + """Returns a set of opaque ids for just-triggered breakpoints.""" bps_hit = self._debugger.AllBreakpointsLastHit bp_id_list = [] # Intuitively, AllBreakpointsLastHit breakpoints are the last hit @@ -194,8 +198,12 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst # All bound breakpoints should have the user-defined breakpoint as # a parent. assert bp.Parent - vsbp = VSBreakpoint(PurePath(bp.Parent.File), bp.Parent.FileLine, - bp.Parent.FileColumn, bp.Parent.Condition) + vsbp = VSBreakpoint( + PurePath(bp.Parent.File), + bp.Parent.FileLine, + bp.Parent.FileColumn, + bp.Parent.Condition, + ) try: ids = self._vs_to_dex_ids[vsbp] except KeyError: @@ -229,20 +237,21 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst # We're looking at the user-set breakpoints so there should be no # Parent. assert bp.Parent == None - this_vsbp = VSBreakpoint(PurePath(bp.File), bp.FileLine, - bp.FileColumn, bp.Condition) + this_vsbp = VSBreakpoint( + PurePath(bp.File), bp.FileLine, bp.FileColumn, bp.Condition + ) if this_vsbp in vsbp_set: bp.Delete() vsbp_to_del_count -= 1 if vsbp_to_del_count == 0: break if vsbp_to_del_count: - raise KeyError('did not find breakpoint to be deleted') + raise KeyError("did not find breakpoint to be deleted") def _fetch_property(self, props, name): num_props = props.Count result = None - for x in range(1, num_props+1): + for x in range(1, num_props + 1): item = props.Item(x) if item.Name == name: return item @@ -251,18 +260,22 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst def launch(self, cmdline): exe_path = Path(self.context.options.executable) self.context.logger.note(f"VS: Using executable: '{exe_path}'") - cmdline_str = ' '.join(cmdline) + cmdline_str = " ".join(cmdline) if self.context.options.target_run_args: - cmdline_str += f" {self.context.options.target_run_args}" + cmdline_str += f" {self.context.options.target_run_args}" if cmdline_str: - self.context.logger.note(f"VS: Using executable args: '{cmdline_str}'") + self.context.logger.note(f"VS: Using executable args: '{cmdline_str}'") # In a slightly baroque manner, lookup the VS project that runs when # you click "run", and set its command line options to the desired # command line options. - startup_proj_name = str(self._fetch_property(self._interface.Solution.Properties, 'StartupProject')) + startup_proj_name = str( + self._fetch_property(self._interface.Solution.Properties, "StartupProject") + ) project = self._fetch_property(self._interface.Solution, startup_proj_name) - ActiveConfiguration = self._fetch_property(project.Properties, 'ActiveConfiguration').Object + ActiveConfiguration = self._fetch_property( + project.Properties, "ActiveConfiguration" + ).Object ActiveConfiguration.DebugSettings.CommandArguments = cmdline_str self.context.logger.note("Launching VS debugger...") @@ -282,8 +295,11 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst stack_frame = stack_frames[idx] self._debugger.CurrentStackFrame = stack_frame.raw except IndexError: - raise Error('attempted to access stack frame {} out of {}' - .format(idx, len(stack_frames))) + raise Error( + "attempted to access stack frame {} out of {}".format( + idx, len(stack_frames) + ) + ) def _get_step_info(self, watches, step_index): thread = self._debugger.CurrentThread @@ -292,30 +308,31 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst frames = [] state_frames = [] - loc = LocIR(**self._location) valid_loc_for_watch = loc.path and os.path.exists(loc.path) for idx, sf in enumerate(stackframes): frame = FrameIR( function=self._sanitize_function_name(sf.FunctionName), - is_inlined=sf.FunctionName.startswith('[Inline Frame]'), - loc=LocIR(path=None, lineno=None, column=None)) + is_inlined=sf.FunctionName.startswith("[Inline Frame]"), + loc=LocIR(path=None, lineno=None, column=None), + ) - fname = frame.function or '' # pylint: disable=no-member + fname = frame.function or "" # pylint: disable=no-member if any(name in fname for name in self.frames_below_main): break - state_frame = StackFrame(function=frame.function, - is_inlined=frame.is_inlined, - watches={}) + state_frame = StackFrame( + function=frame.function, is_inlined=frame.is_inlined, watches={} + ) if valid_loc_for_watch and idx == 0: for watch_info in watches: if watch_is_active(watch_info, loc.path, idx, loc.lineno): watch_expr = watch_info.expression - state_frame.watches[watch_expr] = self.evaluate_expression(watch_expr, idx) - + state_frame.watches[watch_expr] = self.evaluate_expression( + watch_expr, idx + ) state_frames.append(state_frame) frames.append(frame) @@ -331,8 +348,11 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst program_state = ProgramState(frames=state_frames) return StepIR( - step_index=step_index, frames=frames, stop_reason=reason, - program_state=program_state) + step_index=step_index, + frames=frames, + stop_reason=reason, + program_state=program_state, + ) @property def is_running(self): @@ -345,8 +365,10 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst @property def frames_below_main(self): return [ - '[Inline Frame] invoke_main', '__scrt_common_main_seh', - '__tmainCRTStartup', 'mainCRTStartup' + "[Inline Frame] invoke_main", + "__scrt_common_main_seh", + "__tmainCRTStartup", + "mainCRTStartup", ] def evaluate_expression(self, expression, frame_idx=0) -> ValueIR: @@ -357,20 +379,25 @@ class VisualStudio(DebuggerBase, metaclass=abc.ABCMeta): # pylint: disable=abst self.set_current_stack_frame(0) value = result.Value - is_optimized_away = any(s in value for s in [ - 'Variable is optimized away and not available', - 'Value is not available, possibly due to optimization', - ]) + is_optimized_away = any( + s in value + for s in [ + "Variable is optimized away and not available", + "Value is not available, possibly due to optimization", + ] + ) - is_irretrievable = any(s in value for s in [ - '???', - '<Unable to read memory>', - ]) + is_irretrievable = any( + s in value + for s in [ + "???", + "<Unable to read memory>", + ] + ) # an optimized away value is still counted as being able to be # evaluated. - could_evaluate = (result.IsValidValue or is_optimized_away - or is_irretrievable) + could_evaluate = result.IsValidValue or is_optimized_away or is_irretrievable return ValueIR( expression=expression, diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2015.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2015.py index af6edcd..3c5c5ea01 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2015.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2015.py @@ -12,12 +12,12 @@ from dex.debugger.visualstudio.VisualStudio import VisualStudio class VisualStudio2015(VisualStudio): @classmethod def get_name(cls): - return 'Visual Studio 2015' + return "Visual Studio 2015" @classmethod def get_option_name(cls): - return 'vs2015' + return "vs2015" @property def _dte_version(self): - return 'VisualStudio.DTE.14.0' + return "VisualStudio.DTE.14.0" diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2017.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2017.py index f2f7575..fa28df7 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2017.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2017.py @@ -12,12 +12,12 @@ from dex.debugger.visualstudio.VisualStudio import VisualStudio class VisualStudio2017(VisualStudio): @classmethod def get_name(cls): - return 'Visual Studio 2017' + return "Visual Studio 2017" @classmethod def get_option_name(cls): - return 'vs2017' + return "vs2017" @property def _dte_version(self): - return 'VisualStudio.DTE.15.0' + return "VisualStudio.DTE.15.0" diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2019.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2019.py index 9ebe0a0..7a9ba18 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2019.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/VisualStudio2019.py @@ -12,12 +12,12 @@ from dex.debugger.visualstudio.VisualStudio import VisualStudio class VisualStudio2019(VisualStudio): @classmethod def get_name(cls): - return 'Visual Studio 2019' + return "Visual Studio 2019" @classmethod def get_option_name(cls): - return 'vs2019' + return "vs2019" @property def _dte_version(self): - return 'VisualStudio.DTE.16.0' + return "VisualStudio.DTE.16.0" diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/windows/ComInterface.py b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/windows/ComInterface.py index 0bce5b5..84f4336 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/windows/ComInterface.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/debugger/visualstudio/windows/ComInterface.py @@ -13,6 +13,7 @@ import sys # pylint: disable=import-error import win32com.client as com import win32api + # pylint: enable=import-error from dex.utils.Exceptions import LoadDebuggerException @@ -22,25 +23,27 @@ _com_error = com.pywintypes.com_error # pylint: disable=no-member def get_file_version(file_): try: - info = win32api.GetFileVersionInfo(file_, '\\') - ms = info['FileVersionMS'] - ls = info['FileVersionLS'] - return '.'.join( - str(s) for s in [ + info = win32api.GetFileVersionInfo(file_, "\\") + ms = info["FileVersionMS"] + ls = info["FileVersionLS"] + return ".".join( + str(s) + for s in [ win32api.HIWORD(ms), win32api.LOWORD(ms), win32api.HIWORD(ls), - win32api.LOWORD(ls) - ]) + win32api.LOWORD(ls), + ] + ) except com.pywintypes.error: # pylint: disable=no-member - return 'no versioninfo present' + return "no versioninfo present" def _handle_com_error(e): exc = sys.exc_info() msg = win32api.FormatMessage(e.hresult) try: - msg = msg.decode('CP1251') + msg = msg.decode("CP1251") except AttributeError: pass msg = msg.strip() @@ -54,7 +57,7 @@ class ComObject(object): def __init__(self, raw): assert not isinstance(raw, ComObject), raw - self.__dict__['raw'] = raw + self.__dict__["raw"] = raw def __str__(self): return self._call(self.raw.__str__) @@ -88,15 +91,15 @@ class ComObject(object): """ ex = AssertionError("this should never be raised!") - assert (inspect.isfunction(fn) or inspect.ismethod(fn) - or inspect.isbuiltin(fn)), (fn, type(fn)) + assert ( + inspect.isfunction(fn) or inspect.ismethod(fn) or inspect.isbuiltin(fn) + ), (fn, type(fn)) retries = ([0] * 50) + ([1] * 5) for r in retries: try: try: result = fn(*args) - if inspect.ismethod(result) or 'win32com' in str( - result.__class__): + if inspect.ismethod(result) or "win32com" in str(result.__class__): result = ComObject(result) return result except _com_error as e: @@ -116,4 +119,5 @@ class DTE(ComObject): except _com_error as e: msg, exc = _handle_com_error(e) raise LoadDebuggerException( - '{} [{}]'.format(msg, class_string), orig_exception=exc) + "{} [{}]".format(msg, class_string), orig_exception=exc + ) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/BuilderIR.py b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/BuilderIR.py index b94a1fb..953b1da 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/BuilderIR.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/BuilderIR.py @@ -7,8 +7,7 @@ class BuilderIR: - """Data class which represents the compiler related options passed to Dexter - """ + """Data class which represents the compiler related options passed to Dexter""" def __init__(self, name: str, cflags: str, ldflags: str): self.name = name diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/DextIR.py b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/DextIR.py index b82c2ba..be3299c 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/DextIR.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/DextIR.py @@ -14,12 +14,15 @@ from dex.dextIR.StepIR import StepIR, StepKind def _step_kind_func(context, step): - if (step.current_location.path is None or - not os.path.exists(step.current_location.path)): + if step.current_location.path is None or not os.path.exists( + step.current_location.path + ): return StepKind.FUNC_UNKNOWN - if any(os.path.samefile(step.current_location.path, f) - for f in context.options.source_files): + if any( + os.path.samefile(step.current_location.path, f) + for f in context.options.source_files + ): return StepKind.FUNC return StepKind.FUNC_EXTERNAL @@ -39,13 +42,15 @@ class DextIR: commands: { name (str), commands (list[CommandIR]) """ - def __init__(self, - dexter_version: str, - executable_path: str, - source_paths: List[str], - builder: BuilderIR = None, - debugger: DebuggerIR = None, - commands: OrderedDict = None): + def __init__( + self, + dexter_version: str, + executable_path: str, + source_paths: List[str], + builder: BuilderIR = None, + debugger: DebuggerIR = None, + commands: OrderedDict = None, + ): self.dexter_version = dexter_version self.executable_path = executable_path self.source_paths = source_paths @@ -55,18 +60,22 @@ class DextIR: self.steps: List[StepIR] = [] def __str__(self): - colors = 'rgby' - st = '## BEGIN ##\n' + colors = "rgby" + st = "## BEGIN ##\n" color_idx = 0 for step in self.steps: - if step.step_kind in (StepKind.FUNC, StepKind.FUNC_EXTERNAL, - StepKind.FUNC_UNKNOWN): + if step.step_kind in ( + StepKind.FUNC, + StepKind.FUNC_EXTERNAL, + StepKind.FUNC_UNKNOWN, + ): color_idx += 1 color = colors[color_idx % len(colors)] - st += '<{}>{}</>\n'.format(color, step) - st += '## END ({} step{}) ##\n'.format( - self.num_steps, '' if self.num_steps == 1 else 's') + st += "<{}>{}</>\n".format(color, step) + st += "## END ({} step{}) ##\n".format( + self.num_steps, "" if self.num_steps == 1 else "s" + ) return st @property @@ -79,9 +88,15 @@ class DextIR: Returns: StepIR or None if there is no previous step in this frame. """ - return next((s for s in reversed(self.steps) - if s.current_function == step.current_function - and s.num_frames == step.num_frames), None) + return next( + ( + s + for s in reversed(self.steps) + if s.current_function == step.current_function + and s.num_frames == step.num_frames + ), + None, + ) def _get_new_step_kind(self, context, step): if step.current_function is None: @@ -103,7 +118,10 @@ class DextIR: prev_step = frame_step if frame_step is not None else prev_step # If we're missing line numbers to compare then the step kind has to be UNKNOWN. - if prev_step.current_location.lineno is None or step.current_location.lineno is None: + if ( + prev_step.current_location.lineno is None + or step.current_location.lineno is None + ): return StepKind.UNKNOWN # We're in the same func as prev step, check lineo. diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/LocIR.py b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/LocIR.py index 52a56a8..9f98a67 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/LocIR.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/LocIR.py @@ -18,13 +18,16 @@ class LocIR: self.column = column def __str__(self): - return '{}({}:{})'.format(self.path, self.lineno, self.column) + return "{}({}:{})".format(self.path, self.lineno, self.column) def __eq__(self, rhs): - return (os.path.exists(self.path) and os.path.exists(rhs.path) - and os.path.samefile(self.path, rhs.path) - and self.lineno == rhs.lineno - and self.column == rhs.column) + return ( + os.path.exists(self.path) + and os.path.exists(rhs.path) + and os.path.samefile(self.path, rhs.path) + and self.lineno == rhs.lineno + and self.column == rhs.column + ) def __lt__(self, rhs): if self.path != rhs.path: diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/ProgramState.py b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/ProgramState.py index a1de5fb..a3b6b3a 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/ProgramState.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/ProgramState.py @@ -14,6 +14,7 @@ from collections import OrderedDict from pathlib import PurePath from typing import List + class SourceLocation: def __init__(self, path: str = None, lineno: int = None, column: int = None): if path: @@ -23,7 +24,7 @@ class SourceLocation: self.column = column def __str__(self): - return '{}({}:{})'.format(self.path, self.lineno, self.column) + return "{}({}:{})".format(self.path, self.lineno, self.column) def match(self, other) -> bool: """Returns true iff all the properties that appear in `self` have the @@ -32,7 +33,9 @@ class SourceLocation: if not other or not isinstance(other, SourceLocation): return False - if self.path and (other.path is None or (PurePath(self.path) != PurePath(other.path))): + if self.path and ( + other.path is None or (PurePath(self.path) != PurePath(other.path)) + ): return False if self.lineno and (self.lineno != other.lineno): @@ -45,11 +48,13 @@ class SourceLocation: class StackFrame: - def __init__(self, - function: str = None, - is_inlined: bool = None, - location: SourceLocation = None, - watches: OrderedDict = None): + def __init__( + self, + function: str = None, + is_inlined: bool = None, + location: SourceLocation = None, + watches: OrderedDict = None, + ): if watches is None: watches = {} @@ -59,11 +64,12 @@ class StackFrame: self.watches = watches def __str__(self): - return '{}{}: {} | {}'.format( + return "{}{}: {} | {}".format( self.function, - ' (inlined)' if self.is_inlined else '', + " (inlined)" if self.is_inlined else "", self.location, - {k: str(self.watches[k]) for k in self.watches}) + {k: str(self.watches[k]) for k in self.watches}, + ) def match(self, other) -> bool: """Returns true iff all the properties that appear in `self` have the @@ -80,8 +86,10 @@ class StackFrame: try: if isinstance(self.watches[name], dict): for attr in iter(self.watches[name]): - if (getattr(other.watches[name], attr, None) != - self.watches[name][attr]): + if ( + getattr(other.watches[name], attr, None) + != self.watches[name][attr] + ): return False else: if other.watches[name].value != self.watches[name]: @@ -91,14 +99,18 @@ class StackFrame: return True + class ProgramState: def __init__(self, frames: List[StackFrame] = None): self.frames = frames def __str__(self): - return '\n'.join(map( - lambda enum: 'Frame {}: {}'.format(enum[0], enum[1]), - enumerate(self.frames))) + return "\n".join( + map( + lambda enum: "Frame {}: {}".format(enum[0], enum[1]), + enumerate(self.frames), + ) + ) def match(self, other) -> bool: """Returns true iff all the properties that appear in `self` have the diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/StepIR.py b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/StepIR.py index 8111968..b6c965e5e 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/StepIR.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/StepIR.py @@ -43,13 +43,15 @@ class StepIR: watches (OrderedDict): { expression (str), result (ValueIR) } """ - def __init__(self, - step_index: int, - stop_reason: StopReason, - frames: List[FrameIR], - step_kind: StepKind = None, - watches: OrderedDict = None, - program_state: ProgramState = None): + def __init__( + self, + step_index: int, + stop_reason: StopReason, + frames: List[FrameIR], + step_kind: StepKind = None, + watches: OrderedDict = None, + program_state: ProgramState = None, + ): self.step_index = step_index self.step_kind = step_kind self.stop_reason = stop_reason @@ -66,17 +68,22 @@ class StepIR: def __str__(self): try: frame = self.current_frame - frame_info = (frame.function, frame.loc.path, frame.loc.lineno, - frame.loc.column) + frame_info = ( + frame.function, + frame.loc.path, + frame.loc.lineno, + frame.loc.column, + ) except AttributeError: frame_info = (None, None, None, None) - step_info = (self.step_index, ) + frame_info + ( - str(self.stop_reason), str(self.step_kind), - [w for w in self.watches]) + step_info = ( + (self.step_index,) + + frame_info + + (str(self.stop_reason), str(self.step_kind), [w for w in self.watches]) + ) - return '{}{}'.format('. ' * (self.num_frames - 1), - json.dumps(step_info)) + return "{}{}".format(". " * (self.num_frames - 1), json.dumps(step_info)) @property def num_frames(self): diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/ValueIR.py b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/ValueIR.py index 9d532ac..770f646 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/ValueIR.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/dextIR/ValueIR.py @@ -9,14 +9,16 @@ class ValueIR: """Data class to store the result of an expression evaluation.""" - def __init__(self, - expression: str, - value: str, - type_name: str, - could_evaluate: bool, - error_string: str = None, - is_optimized_away: bool = False, - is_irretrievable: bool = False): + def __init__( + self, + expression: str, + value: str, + type_name: str, + could_evaluate: bool, + error_string: str = None, + is_optimized_away: bool = False, + is_irretrievable: bool = False, + ): self.expression = expression self.value = value self.type_name = type_name @@ -30,9 +32,10 @@ class ValueIR: if self.error_string is not None: return prefix + self.error_string if self.value is not None: - return prefix + '({}) {}'.format(self.type_name, self.value) - return (prefix + - 'could_evaluate: {}; irretrievable: {}; optimized_away: {};' - .format(self.could_evaluate, self.is_irretrievable, - self.is_optimized_away)) - + return prefix + "({}) {}".format(self.type_name, self.value) + return ( + prefix + + "could_evaluate: {}; irretrievable: {}; optimized_away: {};".format( + self.could_evaluate, self.is_irretrievable, self.is_optimized_away + ) + ) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/heuristic/Heuristic.py b/cross-project-tests/debuginfo-tests/dexter/dex/heuristic/Heuristic.py index 52ba7e1..5d1c5a7 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/heuristic/Heuristic.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/heuristic/Heuristic.py @@ -18,84 +18,92 @@ from dex.command.StepValueInfo import StepValueInfo from dex.command.commands.DexExpectWatchBase import format_address -PenaltyCommand = namedtuple('PenaltyCommand', ['pen_dict', 'max_penalty']) +PenaltyCommand = namedtuple("PenaltyCommand", ["pen_dict", "max_penalty"]) # 'meta' field used in different ways by different things -PenaltyInstance = namedtuple('PenaltyInstance', ['meta', 'the_penalty']) +PenaltyInstance = namedtuple("PenaltyInstance", ["meta", "the_penalty"]) def add_heuristic_tool_arguments(parser): parser.add_argument( - '--penalty-variable-optimized', + "--penalty-variable-optimized", type=int, default=3, - help='set the penalty multiplier for each' - ' occurrence of a variable that was optimized' - ' away', - metavar='<int>') + help="set the penalty multiplier for each" + " occurrence of a variable that was optimized" + " away", + metavar="<int>", + ) parser.add_argument( - '--penalty-misordered-values', + "--penalty-misordered-values", type=int, default=3, - help='set the penalty multiplier for each' - ' occurrence of a misordered value.', - metavar='<int>') + help="set the penalty multiplier for each" " occurrence of a misordered value.", + metavar="<int>", + ) parser.add_argument( - '--penalty-irretrievable', + "--penalty-irretrievable", type=int, default=4, - help='set the penalty multiplier for each' + help="set the penalty multiplier for each" " occurrence of a variable that couldn't" - ' be retrieved', - metavar='<int>') + " be retrieved", + metavar="<int>", + ) parser.add_argument( - '--penalty-not-evaluatable', + "--penalty-not-evaluatable", type=int, default=5, - help='set the penalty multiplier for each' + help="set the penalty multiplier for each" " occurrence of a variable that couldn't" - ' be evaluated', - metavar='<int>') + " be evaluated", + metavar="<int>", + ) parser.add_argument( - '--penalty-missing-values', + "--penalty-missing-values", type=int, default=6, - help='set the penalty multiplier for each missing' - ' value', - metavar='<int>') + help="set the penalty multiplier for each missing" " value", + metavar="<int>", + ) parser.add_argument( - '--penalty-incorrect-values', + "--penalty-incorrect-values", type=int, default=7, - help='set the penalty multiplier for each' - ' occurrence of an unexpected value.', - metavar='<int>') + help="set the penalty multiplier for each" + " occurrence of an unexpected value.", + metavar="<int>", + ) parser.add_argument( - '--penalty-unreachable', + "--penalty-unreachable", type=int, default=4, # XXX XXX XXX selected by random - help='set the penalty for each line stepped onto that should' - ' have been unreachable.', - metavar='<int>') + help="set the penalty for each line stepped onto that should" + " have been unreachable.", + metavar="<int>", + ) parser.add_argument( - '--penalty-misordered-steps', + "--penalty-misordered-steps", type=int, default=2, # XXX XXX XXX selected by random - help='set the penalty for differences in the order of steps' - ' the program was expected to observe.', - metavar='<int>') + help="set the penalty for differences in the order of steps" + " the program was expected to observe.", + metavar="<int>", + ) parser.add_argument( - '--penalty-missing-step', + "--penalty-missing-step", type=int, default=4, # XXX XXX XXX selected by random - help='set the penalty for the program skipping over a step.', - metavar='<int>') + help="set the penalty for the program skipping over a step.", + metavar="<int>", + ) parser.add_argument( - '--penalty-incorrect-program-state', + "--penalty-incorrect-program-state", type=int, default=4, # XXX XXX XXX selected by random - help='set the penalty for the program never entering an expected state' - ' or entering an unexpected state.', - metavar='<int>') + help="set the penalty for the program never entering an expected state" + " or entering an unexpected state.", + metavar="<int>", + ) class PenaltyLineRanges: @@ -106,21 +114,21 @@ class PenaltyLineRanges: def add_step(self, next_step, penalty): last_range = self.ranges[-1] last_step = last_range[1] - if (next_step == last_step + 1): + if next_step == last_step + 1: self.ranges[-1] = (last_range[0], next_step) else: self.ranges.append((next_step, next_step)) self.penalty += penalty def __str__(self): - range_to_str = lambda r: str(r[0]) if r[0] == r[1] else f'{r[0]}-{r[1]}' + range_to_str = lambda r: str(r[0]) if r[0] == r[1] else f"{r[0]}-{r[1]}" if self.ranges[0][0] == self.ranges[-1][1]: - text = f'step {self.ranges[0][0]}' + text = f"step {self.ranges[0][0]}" else: - step_list = ', '.join([range_to_str(r) for r in self.ranges]) - text = f'steps [{step_list}]' + step_list = ", ".join([range_to_str(r) for r in self.ranges]) + text = f"steps [{step_list}]" if self.penalty: - text += ' <r>[-{}]</>'.format(self.penalty) + text += " <r>[-{}]</>".format(self.penalty) return text @@ -130,16 +138,22 @@ class Heuristic(object): self.penalties = {} self.address_resolutions = {} - worst_penalty = max([ - self.penalty_variable_optimized, self.penalty_irretrievable, - self.penalty_not_evaluatable, self.penalty_incorrect_values, - self.penalty_missing_values, self.penalty_unreachable, - self.penalty_missing_step, self.penalty_misordered_steps - ]) + worst_penalty = max( + [ + self.penalty_variable_optimized, + self.penalty_irretrievable, + self.penalty_not_evaluatable, + self.penalty_incorrect_values, + self.penalty_missing_values, + self.penalty_unreachable, + self.penalty_missing_step, + self.penalty_misordered_steps, + ] + ) # Before evaluating scoring commands, evaluate address values. try: - for command in steps.commands['DexDeclareAddress']: + for command in steps.commands["DexDeclareAddress"]: command.address_resolutions = self.address_resolutions command.eval(steps) except KeyError: @@ -147,90 +161,94 @@ class Heuristic(object): # Get DexExpectWatchType results. try: - for command in steps.commands['DexExpectWatchType']: + for command in steps.commands["DexExpectWatchType"]: command.eval(steps) - maximum_possible_penalty = min(3, len( - command.values)) * worst_penalty + maximum_possible_penalty = min(3, len(command.values)) * worst_penalty name, p = self._calculate_expect_watch_penalties( - command, maximum_possible_penalty) - name = name + ' ExpectType' - self.penalties[name] = PenaltyCommand(p, - maximum_possible_penalty) + command, maximum_possible_penalty + ) + name = name + " ExpectType" + self.penalties[name] = PenaltyCommand(p, maximum_possible_penalty) except KeyError: pass # Get DexExpectWatchValue results. try: - for command in steps.commands['DexExpectWatchValue']: + for command in steps.commands["DexExpectWatchValue"]: command.address_resolutions = self.address_resolutions command.eval(steps) - maximum_possible_penalty = min(3, len( - command.values)) * worst_penalty + maximum_possible_penalty = min(3, len(command.values)) * worst_penalty name, p = self._calculate_expect_watch_penalties( - command, maximum_possible_penalty) - name = name + ' ExpectValue' - self.penalties[name] = PenaltyCommand(p, - maximum_possible_penalty) + command, maximum_possible_penalty + ) + name = name + " ExpectValue" + self.penalties[name] = PenaltyCommand(p, maximum_possible_penalty) except KeyError: pass try: penalties = defaultdict(list) maximum_possible_penalty_all = 0 - for expect_state in steps.commands['DexExpectProgramState']: + for expect_state in steps.commands["DexExpectProgramState"]: success = expect_state.eval(steps) p = 0 if success else self.penalty_incorrect_program_state - meta = 'expected {}: {}'.format( - '{} times'.format(expect_state.times) - if expect_state.times >= 0 else 'at least once', - expect_state.program_state_text) + meta = "expected {}: {}".format( + "{} times".format(expect_state.times) + if expect_state.times >= 0 + else "at least once", + expect_state.program_state_text, + ) if success: - meta = '<g>{}</>'.format(meta) + meta = "<g>{}</>".format(meta) maximum_possible_penalty = self.penalty_incorrect_program_state maximum_possible_penalty_all += maximum_possible_penalty name = expect_state.program_state_text - penalties[meta] = [PenaltyInstance('{} times'.format( - len(expect_state.encounters)), p)] - self.penalties['expected program states'] = PenaltyCommand( - penalties, maximum_possible_penalty_all) + penalties[meta] = [ + PenaltyInstance("{} times".format(len(expect_state.encounters)), p) + ] + self.penalties["expected program states"] = PenaltyCommand( + penalties, maximum_possible_penalty_all + ) except KeyError: pass # Get the total number of each step kind. step_kind_counts = defaultdict(int) - for step in getattr(steps, 'steps'): + for step in getattr(steps, "steps"): step_kind_counts[step.step_kind] += 1 # Get DexExpectStepKind results. penalties = defaultdict(list) maximum_possible_penalty_all = 0 try: - for command in steps.commands['DexExpectStepKind']: + for command in steps.commands["DexExpectStepKind"]: command.eval() # Cap the penalty at 2 * expected count or else 1 maximum_possible_penalty = max(command.count * 2, 1) p = abs(command.count - step_kind_counts[command.name]) actual_penalty = min(p, maximum_possible_penalty) - key = ('{}'.format(command.name) - if actual_penalty else '<g>{}</>'.format(command.name)) + key = ( + "{}".format(command.name) + if actual_penalty + else "<g>{}</>".format(command.name) + ) penalties[key] = [PenaltyInstance(p, actual_penalty)] maximum_possible_penalty_all += maximum_possible_penalty - self.penalties['step kind differences'] = PenaltyCommand( - penalties, maximum_possible_penalty_all) + self.penalties["step kind differences"] = PenaltyCommand( + penalties, maximum_possible_penalty_all + ) except KeyError: pass - if 'DexUnreachable' in steps.commands: - cmds = steps.commands['DexUnreachable'] + if "DexUnreachable" in steps.commands: + cmds = steps.commands["DexUnreachable"] unreach_count = 0 # Find steps with unreachable in them - ureachs = [ - s for s in steps.steps if 'DexUnreachable' in s.watches.keys() - ] + ureachs = [s for s in steps.steps if "DexUnreachable" in s.watches.keys()] # There's no need to match up cmds with the actual watches upen = self.penalty_unreachable @@ -239,22 +257,19 @@ class Heuristic(object): if count != 0: d = dict() for x in ureachs: - msg = 'line {} reached'.format(x.current_location.lineno) + msg = "line {} reached".format(x.current_location.lineno) d[msg] = [PenaltyInstance(upen, upen)] else: - d = { - '<g>No unreachable lines seen</>': [PenaltyInstance(0, 0)] - } + d = {"<g>No unreachable lines seen</>": [PenaltyInstance(0, 0)]} total = PenaltyCommand(d, len(cmds) * upen) - self.penalties['unreachable lines'] = total + self.penalties["unreachable lines"] = total - if 'DexExpectStepOrder' in steps.commands: - cmds = steps.commands['DexExpectStepOrder'] + if "DexExpectStepOrder" in steps.commands: + cmds = steps.commands["DexExpectStepOrder"] # Form a list of which line/cmd we _should_ have seen - cmd_num_lst = [(x, c.get_line()) for c in cmds - for x in c.sequence] + cmd_num_lst = [(x, c.get_line()) for c in cmds for x in c.sequence] # Order them by the sequence number cmd_num_lst.sort(key=lambda t: t[0]) # Strip out sequence key @@ -262,8 +277,8 @@ class Heuristic(object): # Now do the same, but for the actually observed lines/cmds ss = steps.steps - deso = [s for s in ss if 'DexExpectStepOrder' in s.watches.keys()] - deso = [s.watches['DexExpectStepOrder'] for s in deso] + deso = [s for s in ss if "DexExpectStepOrder" in s.watches.keys()] + deso = [s.watches["DexExpectStepOrder"] for s in deso] # We rely on the steps remaining in order here order_list = [int(x.expression) for x in deso] @@ -279,11 +294,11 @@ class Heuristic(object): num_repeats = 0 for k, v in expected.items(): if k not in seen: - msg = 'Line {} not seen'.format(k) + msg = "Line {} not seen".format(k) unseen_line_dict[msg] = [PenaltyInstance(mispen, mispen)] num_missing += v elif v > seen[k]: - msg = 'Line {} skipped at least once'.format(k) + msg = "Line {} skipped at least once".format(k) skipped_line_dict[msg] = [PenaltyInstance(mispen, mispen)] num_missing += v - seen[k] elif v < seen[k]: @@ -294,16 +309,16 @@ class Heuristic(object): if len(unseen_line_dict) == 0: pi = PenaltyInstance(0, 0) - unseen_line_dict['<g>All lines were seen</>'] = [pi] + unseen_line_dict["<g>All lines were seen</>"] = [pi] if len(skipped_line_dict) == 0: pi = PenaltyInstance(0, 0) - skipped_line_dict['<g>No lines were skipped</>'] = [pi] + skipped_line_dict["<g>No lines were skipped</>"] = [pi] total = PenaltyCommand(unseen_line_dict, len(expected) * mispen) - self.penalties['Unseen lines'] = total + self.penalties["Unseen lines"] = total total = PenaltyCommand(skipped_line_dict, len(expected) * mispen) - self.penalties['Skipped lines'] = total + self.penalties["Skipped lines"] = total ordpen = self.penalty_misordered_steps cmd_num_lst = [str(x) for x in cmd_num_lst] @@ -311,7 +326,7 @@ class Heuristic(object): lst = list(difflib.Differ().compare(cmd_num_lst, order_list)) diff_detail = Counter(l[0] for l in lst) - assert '?' not in diff_detail + assert "?" not in diff_detail # Diffs are hard to interpret; there are many algorithms for # condensing them. Ignore all that, and just print out the changed @@ -328,9 +343,9 @@ class Heuristic(object): diff_msgs = dict() def reportdiff(start_idx, segment, end_idx): - msg = 'Order mismatch, expected linenos {}, saw {}' - expected_linenos = filt_lines(start_idx, segment, end_idx, '-') - seen_linenos = filt_lines(start_idx, segment, end_idx, '+') + msg = "Order mismatch, expected linenos {}, saw {}" + expected_linenos = filt_lines(start_idx, segment, end_idx, "-") + seen_linenos = filt_lines(start_idx, segment, end_idx, "+") msg = msg.format(expected_linenos, seen_linenos) diff_msgs[msg] = [PenaltyInstance(ordpen, ordpen)] @@ -338,13 +353,12 @@ class Heuristic(object): start_expt_step = 0 end_expt_step = 0 to_print_lst = [] - for k, subit in groupby(lst, lambda x: x[0] == ' '): + for k, subit in groupby(lst, lambda x: x[0] == " "): if k: # Whitespace group nochanged = [x for x in subit] end_expt_step = int(nochanged[0][2:]) if len(to_print_lst) > 0: - reportdiff(start_expt_step, to_print_lst, - end_expt_step) + reportdiff(start_expt_step, to_print_lst, end_expt_step) start_expt_step = int(nochanged[-1][2:]) to_print_lst = [] else: # Diff group, save for printing @@ -352,14 +366,12 @@ class Heuristic(object): # If there was a dangling different step, print that too. if len(to_print_lst) > 0: - reportdiff(start_expt_step, to_print_lst, '[End]') + reportdiff(start_expt_step, to_print_lst, "[End]") if len(diff_msgs) == 0: - diff_msgs['<g>No lines misordered</>'] = [ - PenaltyInstance(0, 0) - ] + diff_msgs["<g>No lines misordered</>"] = [PenaltyInstance(0, 0)] total = PenaltyCommand(diff_msgs, len(cmd_num_lst) * ordpen) - self.penalties['Misordered lines'] = total + self.penalties["Misordered lines"] = total return @@ -369,13 +381,11 @@ class Heuristic(object): if c.line_range[0] == c.line_range[-1]: line_range = str(c.line_range[0]) else: - line_range = '{}-{}'.format(c.line_range[0], c.line_range[-1]) + line_range = "{}-{}".format(c.line_range[0], c.line_range[-1]) - name = '{}:{} [{}]'.format( - os.path.basename(c.path), line_range, c.expression) + name = "{}:{} [{}]".format(os.path.basename(c.path), line_range, c.expression) - num_actual_watches = len(c.expected_watches) + len( - c.unexpected_watches) + num_actual_watches = len(c.expected_watches) + len(c.unexpected_watches) penalty_available = maximum_possible_penalty @@ -384,27 +394,29 @@ class Heuristic(object): # encountered the value at all. if num_actual_watches or c.times_encountered == 0: for v in c.missing_values: - current_penalty = min(penalty_available, - self.penalty_missing_values) + current_penalty = min(penalty_available, self.penalty_missing_values) penalty_available -= current_penalty - penalties['missing values'].append( - PenaltyInstance(v, current_penalty)) + penalties["missing values"].append(PenaltyInstance(v, current_penalty)) for v in c.encountered_values: - penalties['<g>expected encountered watches</>'].append( - PenaltyInstance(v, 0)) + penalties["<g>expected encountered watches</>"].append( + PenaltyInstance(v, 0) + ) penalty_descriptions = [ - (self.penalty_not_evaluatable, c.invalid_watches, - 'could not evaluate'), - (self.penalty_variable_optimized, c.optimized_out_watches, - 'result optimized away'), - (self.penalty_misordered_values, c.misordered_watches, - 'misordered result'), - (self.penalty_irretrievable, c.irretrievable_watches, - 'result could not be retrieved'), - (self.penalty_incorrect_values, c.unexpected_watches, - 'unexpected result'), + (self.penalty_not_evaluatable, c.invalid_watches, "could not evaluate"), + ( + self.penalty_variable_optimized, + c.optimized_out_watches, + "result optimized away", + ), + (self.penalty_misordered_values, c.misordered_watches, "misordered result"), + ( + self.penalty_irretrievable, + c.irretrievable_watches, + "result could not be retrieved", + ), + (self.penalty_incorrect_values, c.unexpected_watches, "unexpected result"), ] for penalty_score, watches, description in penalty_descriptions: @@ -417,8 +429,7 @@ class Heuristic(object): times_to_penalize -= 1 penalty_score = min(penalty_available, penalty_score) penalty_available -= penalty_score - penalties[description].append( - PenaltyInstance(w, penalty_score)) + penalties[description].append(PenaltyInstance(w, penalty_score)) if not times_to_penalize: penalty_score = 0 @@ -445,56 +456,60 @@ class Heuristic(object): try: return 1.0 - (self.penalty / float(self.max_penalty)) except ZeroDivisionError: - return float('nan') + return float("nan") @property def summary_string(self): score = self.score isnan = score != score # pylint: disable=comparison-with-itself - color = 'g' + color = "g" if score < 0.25 or isnan: - color = 'r' + color = "r" elif score < 0.75: - color = 'y' + color = "y" - return '<{}>({:.4f})</>'.format(color, score) + return "<{}>({:.4f})</>".format(color, score) @property def verbose_output(self): # noqa - string = '' + string = "" # Add address resolutions if present. if self.address_resolutions: if self.resolved_addresses: - string += '\nResolved Addresses:\n' + string += "\nResolved Addresses:\n" for addr, res in self.resolved_addresses.items(): string += f" '{addr}': {res}\n" if self.unresolved_addresses: - string += '\n' - string += f'Unresolved Addresses:\n {self.unresolved_addresses}\n' + string += "\n" + string += f"Unresolved Addresses:\n {self.unresolved_addresses}\n" - string += ('\n') + string += "\n" for command in sorted(self.penalties): pen_cmd = self.penalties[command] maximum_possible_penalty = pen_cmd.max_penalty total_penalty = 0 lines = [] for category in sorted(pen_cmd.pen_dict): - lines.append(' <r>{}</>:\n'.format(category)) + lines.append(" <r>{}</>:\n".format(category)) step_value_results = {} for result, penalty in pen_cmd.pen_dict[category]: if not isinstance(result, StepValueInfo): continue if result.expected_value not in step_value_results: - step_value_results[result.expected_value] = PenaltyLineRanges(result.step_index, penalty) + step_value_results[result.expected_value] = PenaltyLineRanges( + result.step_index, penalty + ) else: - step_value_results[result.expected_value].add_step(result.step_index, penalty) + step_value_results[result.expected_value].add_step( + result.step_index, penalty + ) for value, penalty_line_range in step_value_results.items(): - text = f'({value}): {penalty_line_range}' + text = f"({value}): {penalty_line_range}" total_penalty += penalty_line_range.penalty - lines.append(' {}\n'.format(text)) + lines.append(" {}\n".format(text)) for result, penalty in pen_cmd.pen_dict[category]: if isinstance(result, StepValueInfo): @@ -504,21 +519,26 @@ class Heuristic(object): if penalty: assert penalty > 0, penalty total_penalty += penalty - text += ' <r>[-{}]</>'.format(penalty) - lines.append(' {}\n'.format(text)) + text += " <r>[-{}]</>".format(penalty) + lines.append(" {}\n".format(text)) - lines.append('\n') + lines.append("\n") - string += (' <b>{}</> <y>[{}/{}]</>\n'.format( - command, total_penalty, maximum_possible_penalty)) + string += " <b>{}</> <y>[{}/{}]</>\n".format( + command, total_penalty, maximum_possible_penalty + ) for line in lines: - string += (line) - string += ('\n') + string += line + string += "\n" return string @property def resolved_addresses(self): - return {addr: format_address(res) for addr, res in self.address_resolutions.items() if res is not None} + return { + addr: format_address(res) + for addr, res in self.address_resolutions.items() + if res is not None + } @property def unresolved_addresses(self): diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/Main.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/Main.py index c69ffab..d9efe32 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/Main.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/Main.py @@ -26,106 +26,114 @@ from dex.utils.ReturnCode import ReturnCode def _output_bug_report_message(context): - """ In the event of a catastrophic failure, print bug report request to the - user. + """In the event of a catastrophic failure, print bug report request to the + user. """ context.o.red( - '\n\n' - '<g>****************************************</>\n' - '<b>****************************************</>\n' - '****************************************\n' - '** **\n' - '** <y>This is a bug in <a>DExTer</>.</> **\n' - '** **\n' - '** <y>Please report it.</> **\n' - '** **\n' - '****************************************\n' - '<b>****************************************</>\n' - '<g>****************************************</>\n' - '\n' - '<b>system:</>\n' - '<d>{}</>\n\n' - '<b>version:</>\n' - '<d>{}</>\n\n' - '<b>args:</>\n' - '<d>{}</>\n' - '\n'.format(sys.platform, version('DExTer'), - [sys.executable] + sys.argv), - stream=PrettyOutput.stderr) + "\n\n" + "<g>****************************************</>\n" + "<b>****************************************</>\n" + "****************************************\n" + "** **\n" + "** <y>This is a bug in <a>DExTer</>.</> **\n" + "** **\n" + "** <y>Please report it.</> **\n" + "** **\n" + "****************************************\n" + "<b>****************************************</>\n" + "<g>****************************************</>\n" + "\n" + "<b>system:</>\n" + "<d>{}</>\n\n" + "<b>version:</>\n" + "<d>{}</>\n\n" + "<b>args:</>\n" + "<d>{}</>\n" + "\n".format(sys.platform, version("DExTer"), [sys.executable] + sys.argv), + stream=PrettyOutput.stderr, + ) def get_tools_directory(): - """ Returns directory path where DExTer tool imports can be - found. + """Returns directory path where DExTer tool imports can be + found. """ - tools_directory = os.path.join(get_root_directory(), 'tools') + tools_directory = os.path.join(get_root_directory(), "tools") assert os.path.isdir(tools_directory), tools_directory return tools_directory def get_tool_names(): - """ Returns a list of expected DExTer Tools - """ + """Returns a list of expected DExTer Tools""" return [ - 'clang-opt-bisect', 'help', 'list-debuggers', 'no-tool-', - 'run-debugger-internal-', 'test', 'view' + "clang-opt-bisect", + "help", + "list-debuggers", + "no-tool-", + "run-debugger-internal-", + "test", + "view", ] def _set_auto_highlights(context): - """Flag some strings for auto-highlighting. - """ - context.o.auto_reds.extend([ - r'[Ee]rror\:', - r'[Ee]xception\:', - r'un(expected|recognized) argument', - ]) - context.o.auto_yellows.extend([ - r'[Ww]arning\:', - r'\(did you mean ', - r'During handling of the above exception, another exception', - ]) + """Flag some strings for auto-highlighting.""" + context.o.auto_reds.extend( + [ + r"[Ee]rror\:", + r"[Ee]xception\:", + r"un(expected|recognized) argument", + ] + ) + context.o.auto_yellows.extend( + [ + r"[Ww]arning\:", + r"\(did you mean ", + r"During handling of the above exception, another exception", + ] + ) def _get_options_and_args(context): - """ get the options and arguments from the commandline - """ + """get the options and arguments from the commandline""" parser = argparse.ExtArgumentParser(context, add_help=False) - parser.add_argument('tool', default=None, nargs='?') + parser.add_argument("tool", default=None, nargs="?") options, args = parser.parse_known_args(sys.argv[1:]) return options, args def _get_tool_name(options): - """ get the name of the dexter tool (if passed) specified on the command - line, otherwise return 'no_tool_'. + """get the name of the dexter tool (if passed) specified on the command + line, otherwise return 'no_tool_'. """ tool_name = options.tool if tool_name is None: - tool_name = 'no_tool_' + tool_name = "no_tool_" else: _is_valid_tool_name(tool_name) return tool_name def _is_valid_tool_name(tool_name): - """ check tool name matches a tool directory within the dexter tools - directory. + """check tool name matches a tool directory within the dexter tools + directory. """ valid_tools = get_tool_names() if tool_name not in valid_tools: - raise Error('invalid tool "{}" (choose from {})'.format( - tool_name, - ', '.join([t for t in valid_tools if not t.endswith('-')]))) + raise Error( + 'invalid tool "{}" (choose from {})'.format( + tool_name, ", ".join([t for t in valid_tools if not t.endswith("-")]) + ) + ) def _import_tool_module(tool_name): - """ Imports the python module at the tool directory specificed by - tool_name. + """Imports the python module at the tool directory specificed by + tool_name. """ # format tool argument to reflect tool directory form. - tool_name = tool_name.replace('-', '_') + tool_name = tool_name.replace("-", "_") tools_directory = get_tools_directory() module_info = imp.find_module(tool_name, [tools_directory]) @@ -143,7 +151,7 @@ def tool_main(context, tool, args): context.version = version(tool.name) if options.version: - context.o.green('{}\n'.format(context.version)) + context.o.green("{}\n".format(context.version)) return ReturnCode.OK if options.verbose: @@ -151,8 +159,8 @@ def tool_main(context, tool, args): elif options.no_warnings: context.logger.verbosity = 0 - if (options.unittest != 'off' and not unit_tests_ok(context)): - raise Error('<d>unit test failures</>') + if options.unittest != "off" and not unit_tests_ok(context): + raise Error("<d>unit test failures</>") if options.colortest: context.o.colortest() diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/TestToolBase.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/TestToolBase.py index 349a6f0..dba245f 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/TestToolBase.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/TestToolBase.py @@ -34,39 +34,45 @@ class TestToolBase(ToolBase): add_heuristic_tool_arguments(parser) parser.add_argument( - 'test_path', + "test_path", type=str, - metavar='<test-path>', - nargs='?', - default=os.path.abspath( - os.path.join(get_root_directory(), '..', 'tests')), - help='directory containing test(s)') + metavar="<test-path>", + nargs="?", + default=os.path.abspath(os.path.join(get_root_directory(), "..", "tests")), + help="directory containing test(s)", + ) parser.add_argument( - '--results-directory', + "--results-directory", type=str, - metavar='<directory>', + metavar="<directory>", default=None, - help='directory to save results (default: none)') + help="directory to save results (default: none)", + ) def handle_options(self, defaults): options = self.context.options if not options.builder and (options.cflags or options.ldflags): self.context.logger.warning( - '--cflags and --ldflags will be ignored when not using --builder', - enable_prefix=True) + "--cflags and --ldflags will be ignored when not using --builder", + enable_prefix=True, + ) if options.vs_solution: options.vs_solution = os.path.abspath(options.vs_solution) if not os.path.isfile(options.vs_solution): - raise Error('<d>could not find VS solution file</> <r>"{}"</>' - .format(options.vs_solution)) + raise Error( + '<d>could not find VS solution file</> <r>"{}"</>'.format( + options.vs_solution + ) + ) elif options.binary: options.binary = os.path.abspath(options.binary) if not os.path.isfile(options.binary): - raise Error('<d>could not find binary file</> <r>"{}"</>' - .format(options.binary)) + raise Error( + '<d>could not find binary file</> <r>"{}"</>'.format(options.binary) + ) else: try: self.build_script = handle_builder_tool_options(self.context) @@ -80,10 +86,12 @@ class TestToolBase(ToolBase): options.test_path = os.path.abspath(options.test_path) options.test_path = os.path.normcase(options.test_path) - if not os.path.isfile(options.test_path) and not os.path.isdir(options.test_path): + if not os.path.isfile(options.test_path) and not os.path.isdir( + options.test_path + ): raise Error( - '<d>could not find test path</> <r>"{}"</>'.format( - options.test_path)) + '<d>could not find test path</> <r>"{}"</>'.format(options.test_path) + ) if options.results_directory: options.results_directory = os.path.abspath(options.results_directory) @@ -92,14 +100,17 @@ class TestToolBase(ToolBase): os.makedirs(options.results_directory, exist_ok=True) except OSError as e: raise Error( - '<d>could not create directory</> <r>"{}"</> <y>({})</>'. - format(options.results_directory, e.strerror)) + '<d>could not create directory</> <r>"{}"</> <y>({})</>'.format( + options.results_directory, e.strerror + ) + ) def go(self) -> ReturnCode: # noqa options = self.context.options options.executable = os.path.join( - self.context.working_directory.path, 'tmp.exe') + self.context.working_directory.path, "tmp.exe" + ) # Test files contain dexter commands. options.test_files = [] @@ -107,21 +118,20 @@ class TestToolBase(ToolBase): # contains dexter commands. options.source_files = [] if os.path.isdir(options.test_path): - subdirs = sorted([ - r for r, _, f in os.walk(options.test_path) - if 'test.cfg' in f - ]) + subdirs = sorted( + [r for r, _, f in os.walk(options.test_path) if "test.cfg" in f] + ) for subdir in subdirs: for f in os.listdir(subdir): # TODO: read file extensions from the test.cfg file instead so # that this isn't just limited to C and C++. file_path = os.path.normcase(os.path.join(subdir, f)) - if f.endswith('.cpp'): + if f.endswith(".cpp"): options.source_files.append(file_path) - elif f.endswith('.c'): + elif f.endswith(".c"): options.source_files.append(file_path) - elif f.endswith('.dex'): + elif f.endswith(".dex"): options.test_files.append(file_path) # Source files can contain dexter commands too. options.test_files = options.test_files + options.source_files @@ -130,7 +140,7 @@ class TestToolBase(ToolBase): else: # We're dealing with a direct file path to a test file. If the file is non # .dex, then it must be a source file. - if not options.test_path.endswith('.dex'): + if not options.test_path.endswith(".dex"): options.source_files = [options.test_path] options.test_files = [options.test_path] self._run_test(self._get_test_name(options.test_path)) @@ -139,7 +149,7 @@ class TestToolBase(ToolBase): @staticmethod def _is_current_directory(test_directory): - return test_directory == '.' + return test_directory == "." def _get_test_name(self, test_path): """Get the test name from either the test file, or the sub directory @@ -147,8 +157,7 @@ class TestToolBase(ToolBase): """ # test names are distinguished by their relative path from the # specified test path. - test_name = os.path.relpath(test_path, - self.context.options.test_path) + test_name = os.path.relpath(test_path, self.context.options.test_path) if self._is_current_directory(test_name): test_name = os.path.basename(test_path) return test_name diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/ToolBase.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/ToolBase.py index 53274d3..4b09c13 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/ToolBase.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/ToolBase.py @@ -40,74 +40,86 @@ class ToolBase(object, metaclass=abc.ABCMeta): pass pparser = ExtArgParse.ExtArgumentParser( - self.context, add_help=False, prog=self.name) + self.context, add_help=False, prog=self.name + ) pparser.add_argument( - '--no-color-output', - action='store_true', + "--no-color-output", + action="store_true", default=False, - help='do not use colored output on stdout/stderr') + help="do not use colored output on stdout/stderr", + ) pparser.add_argument( - '--time-report', - action='store_true', + "--time-report", + action="store_true", default=False, - help='display timing statistics') + help="display timing statistics", + ) self.parser = ExtArgParse.ExtArgumentParser( - self.context, parents=[pparser], prog=self.name) + self.context, parents=[pparser], prog=self.name + ) self.parser.add_argument( - '-v', - '--verbose', - action='store_true', + "-v", + "--verbose", + action="store_true", default=False, - help='enable verbose output (overrides --no-warnings)') + help="enable verbose output (overrides --no-warnings)", + ) self.parser.add_argument( - '-V', - '--version', - action='store_true', + "-V", + "--version", + action="store_true", default=False, - help='display the DExTer version and exit') + help="display the DExTer version and exit", + ) self.parser.add_argument( - '-w', - '--no-warnings', - action='store_true', + "-w", + "--no-warnings", + action="store_true", default=False, - help='suppress warning output') + help="suppress warning output", + ) self.parser.add_argument( - '--unittest', + "--unittest", type=str, - choices=['off', 'show-failures', 'show-all'], - default='off', - help='run the DExTer codebase unit tests') + choices=["off", "show-failures", "show-all"], + default="off", + help="run the DExTer codebase unit tests", + ) suppress = ExtArgParse.SUPPRESS # pylint: disable=no-member self.parser.add_argument( - '--colortest', action='store_true', default=False, help=suppress) + "--colortest", action="store_true", default=False, help=suppress + ) self.parser.add_argument( - '--error-debug', action='store_true', default=False, help=suppress) - defaults.working_directory = os.path.join(tempfile.gettempdir(), - 'dexter') + "--error-debug", action="store_true", default=False, help=suppress + ) + defaults.working_directory = os.path.join(tempfile.gettempdir(), "dexter") self.parser.add_argument( - '--indent-timer-level', type=int, default=1, help=suppress) + "--indent-timer-level", type=int, default=1, help=suppress + ) self.parser.add_argument( - '--working-directory', + "--working-directory", type=str, - metavar='<file>', + metavar="<file>", default=None, display_default=defaults.working_directory, - help='location of working directory') + help="location of working directory", + ) self.parser.add_argument( - '--save-temps', - action='store_true', + "--save-temps", + action="store_true", default=False, - help='save temporary files') + help="save temporary files", + ) self.add_tool_arguments(self.parser, defaults) # If an error is encountered during pparser, show the full usage text # including self.parser options. Strip the preceding 'usage: ' to avoid # having it appear twice. - pparser.usage = self.parser.format_usage().lstrip('usage: ') + pparser.usage = self.parser.format_usage().lstrip("usage: ") options, args = pparser.parse_known_args(args) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/clang_opt_bisect/Tool.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/clang_opt_bisect/Tool.py index adabc06..2902eee 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/clang_opt_bisect/Tool.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/clang_opt_bisect/Tool.py @@ -44,8 +44,7 @@ class Tool(TestToolBase): debugging experience at each value. """ - _re_running_pass = re.compile( - r'^BISECT\: running pass \((\d+)\) (.+?)( \(.+\))?$') + _re_running_pass = re.compile(r"^BISECT\: running pass \((\d+)\) (.+?)( \(.+\))?$") def __init__(self, *args, **kwargs): super(Tool, self).__init__(*args, **kwargs) @@ -53,7 +52,7 @@ class Tool(TestToolBase): @property def name(self): - return 'DExTer clang opt bisect' + return "DExTer clang opt bisect" def _get_bisect_limits(self): options = self.context.options @@ -61,40 +60,45 @@ class Tool(TestToolBase): max_limit = 999999 limits = [max_limit for _ in options.source_files] all_passes = [ - l for l in self._clang_opt_bisect_build(limits)[1].splitlines() - if l.startswith('BISECT: running pass (') + l + for l in self._clang_opt_bisect_build(limits)[1].splitlines() + if l.startswith("BISECT: running pass (") ] results = [] for i, pass_ in enumerate(all_passes[1:]): - if pass_.startswith('BISECT: running pass (1)'): + if pass_.startswith("BISECT: running pass (1)"): results.append(all_passes[i]) results.append(all_passes[-1]) - assert len(results) == len( - options.source_files), (results, options.source_files) + assert len(results) == len(options.source_files), ( + results, + options.source_files, + ) - limits = [ - int(Tool._re_running_pass.match(r).group(1)) for r in results - ] + limits = [int(Tool._re_running_pass.match(r).group(1)) for r in results] return limits def handle_options(self, defaults): options = self.context.options if "clang" not in options.builder.lower(): - raise Error("--builder %s is not supported by the clang-opt-bisect tool - only 'clang' is " - "supported " % options.builder) + raise Error( + "--builder %s is not supported by the clang-opt-bisect tool - only 'clang' is " + "supported " % options.builder + ) super(Tool, self).handle_options(defaults) def _init_debugger_controller(self): step_collection = DextIR( executable_path=self.context.options.executable, source_paths=self.context.options.source_files, - dexter_version=self.context.version) + dexter_version=self.context.version, + ) step_collection.commands, new_source_files = get_command_infos( - self.context.options.source_files, self.context.options.source_root_dir) + self.context.options.source_files, self.context.options.source_root_dir + ) self.context.options.source_files.extend(list(new_source_files)) debugger_controller = DefaultController(self.context, step_collection) @@ -139,15 +143,17 @@ class Tool(TestToolBase): pass_info = (0, None, None) try: - debugger_controller =self._init_debugger_controller() + debugger_controller = self._init_debugger_controller() debugger_controller = run_debugger_subprocess( - debugger_controller, self.context.working_directory.path) + debugger_controller, self.context.working_directory.path + ) steps = debugger_controller.step_collection except DebuggerException: - steps = DextIR( + steps = DextIR( executable_path=self.context.options.executable, source_paths=self.context.options.source_files, - dexter_version=self.context.version) + dexter_version=self.context.version, + ) steps.builder = builderIR @@ -161,25 +167,25 @@ class Tool(TestToolBase): isnan = heuristic.score != heuristic.score if isnan or score_difference < 0: - color1 = 'r' - color2 = 'r' + color1 = "r" + color2 = "r" elif score_difference > 0: - color1 = 'g' - color2 = 'g' + color1 = "g" + color2 = "g" else: - color1 = 'y' - color2 = 'd' + color1 = "y" + color2 = "d" summary = '<{}>running pass {}/{} on "{}"'.format( - color2, pass_info[0], max_limits[current_file_index], - test_name) + color2, pass_info[0], max_limits[current_file_index], test_name + ) if len(options.source_files) > 1: - summary += ' [{}/{}]'.format(current_limit, overall_limit) + summary += " [{}/{}]".format(current_limit, overall_limit) - pass_text = ''.join(p for p in pass_info[1:] if p) - summary += ': {} <{}>{:+.4f}</> <{}>{}</></>\n'.format( - heuristic.summary_string, color1, score_difference, color2, - pass_text) + pass_text = "".join(p for p in pass_info[1:] if p) + summary += ": {} <{}>{:+.4f}</> <{}>{}</></>\n".format( + heuristic.summary_string, color1, score_difference, color2, pass_text + ) self.context.o.auto(summary) @@ -195,79 +201,86 @@ class Tool(TestToolBase): # If a results directory has been specified and this is the first # pass or something has changed, write a text file containing # verbose information on the current status. - if options.results_directory and (current_limit == 0 or - score_difference or - steps_changed): - file_name = '-'.join( - str(s) for s in [ - 'status', test_name, '{{:0>{}}}'.format( - len(str(overall_limit))).format(current_limit), - '{:.4f}'.format(heuristic.score).replace( - '.', '_'), pass_info[1] - ] if s is not None) - - file_name = ''.join( - c for c in file_name - if c.isalnum() or c in '()-_./ ').strip().replace( - ' ', '_').replace('/', '_') - - output_text_path = os.path.join(options.results_directory, - '{}.txt'.format(file_name)) - with open(output_text_path, 'w') as fp: - self.context.o.auto(summary + '\n', stream=Stream(fp)) - self.context.o.auto(str(steps) + '\n', stream=Stream(fp)) + if options.results_directory and ( + current_limit == 0 or score_difference or steps_changed + ): + file_name = "-".join( + str(s) + for s in [ + "status", + test_name, + "{{:0>{}}}".format(len(str(overall_limit))).format( + current_limit + ), + "{:.4f}".format(heuristic.score).replace(".", "_"), + pass_info[1], + ] + if s is not None + ) + + file_name = ( + "".join(c for c in file_name if c.isalnum() or c in "()-_./ ") + .strip() + .replace(" ", "_") + .replace("/", "_") + ) + + output_text_path = os.path.join( + options.results_directory, "{}.txt".format(file_name) + ) + with open(output_text_path, "w") as fp: + self.context.o.auto(summary + "\n", stream=Stream(fp)) + self.context.o.auto(str(steps) + "\n", stream=Stream(fp)) self.context.o.auto( - heuristic_verbose_output + '\n', stream=Stream(fp)) + heuristic_verbose_output + "\n", stream=Stream(fp) + ) - output_dextIR_path = os.path.join(options.results_directory, - '{}.dextIR'.format(file_name)) - with open(output_dextIR_path, 'wb') as fp: + output_dextIR_path = os.path.join( + options.results_directory, "{}.dextIR".format(file_name) + ) + with open(output_dextIR_path, "wb") as fp: pickle.dump(steps, fp, protocol=pickle.HIGHEST_PROTOCOL) - per_pass_score.append((test_name, pass_text, - heuristic.score)) + per_pass_score.append((test_name, pass_text, heuristic.score)) if pass_info[1]: - self._all_bisect_pass_summary[pass_info[1]].append( - score_difference) + self._all_bisect_pass_summary[pass_info[1]].append(score_difference) - current_bisect_pass_summary[pass_info[1]].append( - score_difference) + current_bisect_pass_summary[pass_info[1]].append(score_difference) if options.results_directory: per_pass_score_path = os.path.join( - options.results_directory, - '{}-per_pass_score.csv'.format(test_name)) + options.results_directory, "{}-per_pass_score.csv".format(test_name) + ) - with open(per_pass_score_path, mode='w', newline='') as fp: - writer = csv.writer(fp, delimiter=',') - writer.writerow(['Source File', 'Pass', 'Score']) + with open(per_pass_score_path, mode="w", newline="") as fp: + writer = csv.writer(fp, delimiter=",") + writer.writerow(["Source File", "Pass", "Score"]) for path, pass_, score in per_pass_score: writer.writerow([path, pass_, score]) self.context.o.blue('wrote "{}"\n'.format(per_pass_score_path)) pass_summary_path = os.path.join( - options.results_directory, '{}-pass-summary.csv'.format(test_name)) + options.results_directory, "{}-pass-summary.csv".format(test_name) + ) - self._write_pass_summary(pass_summary_path, - current_bisect_pass_summary) + self._write_pass_summary(pass_summary_path, current_bisect_pass_summary) def _handle_results(self) -> ReturnCode: options = self.context.options if options.results_directory: - pass_summary_path = os.path.join(options.results_directory, - 'overall-pass-summary.csv') + pass_summary_path = os.path.join( + options.results_directory, "overall-pass-summary.csv" + ) - self._write_pass_summary(pass_summary_path, - self._all_bisect_pass_summary) + self._write_pass_summary(pass_summary_path, self._all_bisect_pass_summary) return ReturnCode.OK def _clang_opt_bisect_build(self, opt_bisect_limits): options = self.context.options compiler_options = [ - '{} -mllvm -opt-bisect-limit={}'.format(options.cflags, - opt_bisect_limit) + "{} -mllvm -opt-bisect-limit={}".format(options.cflags, opt_bisect_limit) for opt_bisect_limit in opt_bisect_limits ] linker_options = options.ldflags @@ -279,7 +292,8 @@ class Tool(TestToolBase): compiler_options=compiler_options, linker_options=linker_options, script_path=self.build_script, - executable_file=options.executable) + executable_file=options.executable, + ) except BuildScriptException as e: raise Error(e) @@ -291,11 +305,15 @@ class Tool(TestToolBase): # Add elems for the sum, min, and max of the values, as well as # 'interestingness' which is whether any of these values are # non-zero. - pass_summary_list[i] += (sum(item[1]), min(item[1]), max(item[1]), - any(item[1])) + pass_summary_list[i] += ( + sum(item[1]), + min(item[1]), + max(item[1]), + any(item[1]), + ) # Split the pass name into the basic name and kind. - pass_summary_list[i] += tuple(item[0].rsplit(' on ', 1)) + pass_summary_list[i] += tuple(item[0].rsplit(" on ", 1)) # Sort the list by the following columns in order of precedence: # - Is interesting (True first) @@ -304,16 +322,23 @@ class Tool(TestToolBase): # - Kind (alphabetically) # - Name (alphabetically) pass_summary_list.sort( - key=lambda tup: (not tup[5], tup[2], -len(tup[1]), tup[7], tup[6])) - - with open(path, mode='w', newline='') as fp: - writer = csv.writer(fp, delimiter=',') - writer.writerow( - ['Pass', 'Kind', 'Sum', 'Min', 'Max', 'Interesting']) - - for (_, vals, sum_, min_, max_, interesting, name, - kind) in pass_summary_list: - writer.writerow([name, kind, sum_, min_, max_, interesting] + - vals) + key=lambda tup: (not tup[5], tup[2], -len(tup[1]), tup[7], tup[6]) + ) + + with open(path, mode="w", newline="") as fp: + writer = csv.writer(fp, delimiter=",") + writer.writerow(["Pass", "Kind", "Sum", "Min", "Max", "Interesting"]) + + for ( + _, + vals, + sum_, + min_, + max_, + interesting, + name, + kind, + ) in pass_summary_list: + writer.writerow([name, kind, sum_, min_, max_, interesting] + vals) self.context.o.blue('wrote "{}"\n'.format(path)) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/help/Tool.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/help/Tool.py index 2b35af4..520bf9f 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/help/Tool.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/help/Tool.py @@ -18,35 +18,32 @@ class Tool(ToolBase): @property def name(self): - return 'DExTer help' + return "DExTer help" @property def _visible_tool_names(self): - return [t for t in get_tool_names() if not t.endswith('-')] + return [t for t in get_tool_names() if not t.endswith("-")] def add_tool_arguments(self, parser, defaults): parser.description = Tool.__doc__ parser.add_argument( - 'tool', - choices=self._visible_tool_names, - nargs='?', - help='name of subtool') + "tool", choices=self._visible_tool_names, nargs="?", help="name of subtool" + ) def handle_options(self, defaults): pass @property def _default_text(self): - s = '\n<b>The following subtools are available:</>\n\n' + s = "\n<b>The following subtools are available:</>\n\n" tools_directory = get_tools_directory() for tool_name in sorted(self._visible_tool_names): - internal_name = tool_name.replace('-', '_') + internal_name = tool_name.replace("-", "_") module_info = imp.find_module(internal_name, [tools_directory]) - tool_doc = imp.load_module(internal_name, - *module_info).Tool.__doc__ - tool_doc = tool_doc.strip() if tool_doc else '' - tool_doc = textwrap.fill(' '.join(tool_doc.split()), 80) - s += '<g>{}</>\n{}\n\n'.format(tool_name, tool_doc) + tool_doc = imp.load_module(internal_name, *module_info).Tool.__doc__ + tool_doc = tool_doc.strip() if tool_doc else "" + tool_doc = textwrap.fill(" ".join(tool_doc.split()), 80) + s += "<g>{}</>\n{}\n\n".format(tool_name, tool_doc) return s def go(self) -> ReturnCode: @@ -54,8 +51,8 @@ class Tool(ToolBase): self.context.o.auto(self._default_text) return ReturnCode.OK - tool_name = self.context.options.tool.replace('-', '_') + tool_name = self.context.options.tool.replace("-", "_") tools_directory = get_tools_directory() module_info = imp.find_module(tool_name, [tools_directory]) module = imp.load_module(tool_name, *module_info) - return tool_main(self.context, module.Tool(self.context), ['--help']) + return tool_main(self.context, module.Tool(self.context), ["--help"]) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/list_debuggers/Tool.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/list_debuggers/Tool.py index 5ef5d65..75e6dcb 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/list_debuggers/Tool.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/list_debuggers/Tool.py @@ -22,7 +22,7 @@ class Tool(ToolBase): @property def name(self): - return 'DExTer list debuggers' + return "DExTer list debuggers" def add_tool_arguments(self, parser, defaults): parser.description = Tool.__doc__ @@ -32,7 +32,7 @@ class Tool(ToolBase): handle_debugger_tool_base_options(self.context, defaults) def go(self) -> ReturnCode: - with Timer('list debuggers'): + with Timer("list debuggers"): try: Debuggers(self.context).list() except DebuggerException as e: diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/no_tool_/Tool.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/no_tool_/Tool.py index 3d73189..4e25fdb 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/no_tool_/Tool.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/no_tool_/Tool.py @@ -23,25 +23,28 @@ class Tool(ToolBase): @property def name(self): - return 'DExTer' + return "DExTer" def add_tool_arguments(self, parser, defaults): parser.description = Tool.__doc__ parser.add_argument( - 'subtool', - choices=[t for t in get_tool_names() if not t.endswith('-')], - nargs='?', - help='name of subtool') + "subtool", + choices=[t for t in get_tool_names() if not t.endswith("-")], + nargs="?", + help="name of subtool", + ) parser.add_argument( - 'subtool_options', - metavar='subtool-options', - nargs='*', - help='subtool specific options') + "subtool_options", + metavar="subtool-options", + nargs="*", + help="subtool specific options", + ) def handle_options(self, defaults): if not self.context.options.subtool: - raise Error('<d>no subtool specified</>\n\n{}\n'.format( - self.parser.format_help())) + raise Error( + "<d>no subtool specified</>\n\n{}\n".format(self.parser.format_help()) + ) def go(self) -> ReturnCode: # This fn is never called because not specifying a subtool raises an diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/run_debugger_internal_/Tool.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/run_debugger_internal_/Tool.py index 5091e60..844a3ef 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/run_debugger_internal_/Tool.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/run_debugger_internal_/Tool.py @@ -17,6 +17,7 @@ from dex.utils import Timer from dex.utils.Exceptions import DebuggerException, Error from dex.utils.ReturnCode import ReturnCode + class Tool(ToolBase): def __init__(self, *args, **kwargs): self.controller_path = None @@ -26,43 +27,42 @@ class Tool(ToolBase): @property def name(self): - return 'DExTer run debugger internal' + return "DExTer run debugger internal" def add_tool_arguments(self, parser, defaults): parser.add_argument( - 'controller_path', - type=str, - help='pickled debugger controller file') + "controller_path", type=str, help="pickled debugger controller file" + ) def handle_options(self, defaults): - with open(self.context.options.controller_path, 'rb') as fp: + with open(self.context.options.controller_path, "rb") as fp: self.debugger_controller = pickle.load(fp) - self.controller_path = self.context.options.controller_path + self.controller_path = self.context.options.controller_path self.context = self.debugger_controller.context self.options = self.context.options Timer.display = self.options.time_report def raise_debugger_error(self, action, debugger): - msg = '<d>could not {} {}</> ({})\n'.format( - action, debugger.name, debugger.loading_error) + msg = "<d>could not {} {}</> ({})\n".format( + action, debugger.name, debugger.loading_error + ) if self.options.verbose: - msg = '{}\n {}'.format( - msg, ' '.join(debugger.loading_error_trace)) + msg = "{}\n {}".format(msg, " ".join(debugger.loading_error_trace)) raise Error(msg) def go(self) -> ReturnCode: - with Timer('loading debugger'): + with Timer("loading debugger"): debugger = Debuggers(self.context).load(self.options.debugger) - with Timer('running debugger'): + with Timer("running debugger"): if not debugger.is_available: - self.raise_debugger_error('load', debugger) + self.raise_debugger_error("load", debugger) self.debugger_controller.run_debugger(debugger) if debugger.loading_error: - self.raise_debugger_error('run', debugger) + self.raise_debugger_error("run", debugger) - with open(self.controller_path, 'wb') as fp: + with open(self.controller_path, "wb") as fp: pickle.dump(self.debugger_controller, fp) return ReturnCode.OK diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/test/Tool.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/test/Tool.py index dcf4838..9c73b56 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/test/Tool.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/test/Tool.py @@ -39,43 +39,44 @@ class TestCase(object): try: return self.heuristic.penalty except AttributeError: - return float('nan') + return float("nan") @property def max_penalty(self): try: return self.heuristic.max_penalty except AttributeError: - return float('nan') + return float("nan") @property def score(self): try: return self.heuristic.score except AttributeError: - return float('nan') + return float("nan") def __str__(self): if self.error and self.context.options.verbose: verbose_error = str(self.error) else: - verbose_error = '' + verbose_error = "" if self.error: - script_error = (' : {}'.format( - self.error.script_error.splitlines()[0]) if getattr( - self.error, 'script_error', None) else '') + script_error = ( + " : {}".format(self.error.script_error.splitlines()[0]) + if getattr(self.error, "script_error", None) + else "" + ) - error = ' [{}{}]'.format( - str(self.error).splitlines()[0], script_error) + error = " [{}{}]".format(str(self.error).splitlines()[0], script_error) else: - error = '' + error = "" try: summary = self.heuristic.summary_string except AttributeError: - summary = '<r>nan/nan (nan)</>' - return '{}: {}{}\n{}'.format(self.name, summary, error, verbose_error) + summary = "<r>nan/nan (nan)</>" + return "{}: {}{}\n{}".format(self.name, summary, error, verbose_error) class Tool(TestToolBase): @@ -90,18 +91,22 @@ class Tool(TestToolBase): @property def name(self): - return 'DExTer test' + return "DExTer test" def add_tool_arguments(self, parser, defaults): - parser.add_argument('--fail-lt', - type=float, - default=0.0, # By default TEST always succeeds. - help='exit with status FAIL(2) if the test result' - ' is less than this value.', - metavar='<float>') - parser.add_argument('--calculate-average', - action="store_true", - help='calculate the average score of every test run') + parser.add_argument( + "--fail-lt", + type=float, + default=0.0, # By default TEST always succeeds. + help="exit with status FAIL(2) if the test result" + " is less than this value.", + metavar="<float>", + ) + parser.add_argument( + "--calculate-average", + action="store_true", + help="calculate the average score of every test run", + ) super(Tool, self).add_tool_arguments(parser, defaults) def _build_test_case(self): @@ -119,10 +124,7 @@ class Tool(TestToolBase): if options.binary: # Copy user's binary into the tmp working directory shutil.copy(options.binary, options.executable) - builderIR = BuilderIR( - name='binary', - cflags=[options.binary], - ldflags='') + builderIR = BuilderIR(name="binary", cflags=[options.binary], ldflags="") else: options = self.context.options compiler_options = [options.cflags for _ in options.source_files] @@ -133,21 +135,24 @@ class Tool(TestToolBase): source_files=options.source_files, compiler_options=compiler_options, linker_options=linker_options, - executable_file=options.executable) + executable_file=options.executable, + ) return builderIR def _init_debugger_controller(self): step_collection = DextIR( executable_path=self.context.options.executable, source_paths=self.context.options.source_files, - dexter_version=self.context.version) + dexter_version=self.context.version, + ) step_collection.commands, new_source_files = get_command_infos( - self.context.options.test_files, self.context.options.source_root_dir) + self.context.options.test_files, self.context.options.source_root_dir + ) self.context.options.source_files.extend(list(new_source_files)) - if 'DexLimitSteps' in step_collection.commands: + if "DexLimitSteps" in step_collection.commands: debugger_controller = ConditionalController(self.context, step_collection) else: debugger_controller = DefaultController(self.context, step_collection) @@ -155,11 +160,11 @@ class Tool(TestToolBase): return debugger_controller def _get_steps(self, builderIR): - """Generate a list of debugger steps from a test case. - """ + """Generate a list of debugger steps from a test case.""" debugger_controller = self._init_debugger_controller() debugger_controller = run_debugger_subprocess( - debugger_controller, self.context.working_directory.path) + debugger_controller, self.context.working_directory.path + ) steps = debugger_controller.step_collection steps.builder = builderIR return steps @@ -167,30 +172,31 @@ class Tool(TestToolBase): def _get_results_basename(self, test_name): def splitall(x): while len(x) > 0: - x, y = os.path.split(x) - yield y + x, y = os.path.split(x) + yield y + all_components = reversed([x for x in splitall(test_name)]) - return '_'.join(all_components) + return "_".join(all_components) def _get_results_path(self, test_name): """Returns the path to the test results directory for the test denoted by test_name. """ assert self.context.options.results_directory != None - return os.path.join(self.context.options.results_directory, - self._get_results_basename(test_name)) + return os.path.join( + self.context.options.results_directory, + self._get_results_basename(test_name), + ) def _get_results_text_path(self, test_name): - """Returns path results .txt file for test denoted by test_name. - """ + """Returns path results .txt file for test denoted by test_name.""" test_results_path = self._get_results_path(test_name) - return '{}.txt'.format(test_results_path) + return "{}.txt".format(test_results_path) def _get_results_pickle_path(self, test_name): - """Returns path results .dextIR file for test denoted by test_name. - """ + """Returns path results .dextIR file for test denoted by test_name.""" test_results_path = self._get_results_path(test_name) - return '{}.dextIR'.format(test_results_path) + return "{}.dextIR".format(test_results_path) def _record_steps(self, test_name, steps): """Write out the set of steps out to the test's .txt and .json @@ -198,11 +204,11 @@ class Tool(TestToolBase): """ if self.context.options.results_directory: output_text_path = self._get_results_text_path(test_name) - with open(output_text_path, 'w') as fp: + with open(output_text_path, "w") as fp: self.context.o.auto(str(steps), stream=Stream(fp)) output_dextIR_path = self._get_results_pickle_path(test_name) - with open(output_dextIR_path, 'wb') as fp: + with open(output_dextIR_path, "wb") as fp: pickle.dump(steps, fp, protocol=pickle.HIGHEST_PROTOCOL) def _record_score(self, test_name, heuristic): @@ -211,7 +217,7 @@ class Tool(TestToolBase): """ if self.context.options.results_directory: output_text_path = self._get_results_text_path(test_name) - with open(output_text_path, 'a') as fp: + with open(output_text_path, "a") as fp: self.context.o.auto(heuristic.verbose_output, stream=Stream(fp)) def _record_test_and_display(self, test_case): @@ -235,7 +241,7 @@ class Tool(TestToolBase): test_case = TestCase(self.context, test_name, heuristic, None) self._record_test_and_display(test_case) if self.context.options.verbose: - self.context.o.auto('\n{}\n'.format(steps)) + self.context.o.auto("\n{}\n".format(steps)) self.context.o.auto(heuristic.verbose_output) def _run_test(self, test_name): @@ -248,8 +254,7 @@ class Tool(TestToolBase): self._record_steps(test_name, steps) heuristic_score = Heuristic(self.context, steps) self._record_score(test_name, heuristic_score) - except (BuildScriptException, DebuggerException, - HeuristicException) as e: + except (BuildScriptException, DebuggerException, HeuristicException) as e: self._record_failed_test(test_name, e) return @@ -261,7 +266,7 @@ class Tool(TestToolBase): options = self.context.options if not options.verbose: - self.context.o.auto('\n') + self.context.o.auto("\n") if options.calculate_average: # Calculate and print the average score @@ -274,22 +279,25 @@ class Tool(TestToolBase): num_tests += 1 if num_tests != 0: - print("@avg: ({:.4f})".format(score_sum/num_tests)) + print("@avg: ({:.4f})".format(score_sum / num_tests)) has_failed = lambda test: test.score < options.fail_lt or test.error if any(map(has_failed, self._test_cases)): return_code = ReturnCode.FAIL if options.results_directory: - summary_path = os.path.join(options.results_directory, 'summary.csv') - with open(summary_path, mode='w', newline='') as fp: - writer = csv.writer(fp, delimiter=',') - writer.writerow(['Test Case', 'Score', 'Error']) + summary_path = os.path.join(options.results_directory, "summary.csv") + with open(summary_path, mode="w", newline="") as fp: + writer = csv.writer(fp, delimiter=",") + writer.writerow(["Test Case", "Score", "Error"]) for test_case in self._test_cases: - writer.writerow([ - test_case.name, '{:.4f}'.format(test_case.score), - test_case.error - ]) + writer.writerow( + [ + test_case.name, + "{:.4f}".format(test_case.score), + test_case.error, + ] + ) return return_code diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/tools/view/Tool.py b/cross-project-tests/debuginfo-tests/dexter/dex/tools/view/Tool.py index ad7d530..9ddb6a2 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/tools/view/Tool.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/tools/view/Tool.py @@ -17,21 +17,21 @@ from dex.utils.ReturnCode import ReturnCode class Tool(ToolBase): - """Given a dextIR file, display the information in a human-readable form. - """ + """Given a dextIR file, display the information in a human-readable form.""" @property def name(self): - return 'DExTer view' + return "DExTer view" def add_tool_arguments(self, parser, defaults): add_heuristic_tool_arguments(parser) parser.add_argument( - 'input_path', - metavar='dextIR-file', + "input_path", + metavar="dextIR-file", type=str, default=None, - help='dexter dextIR file to view') + help="dexter dextIR file to view", + ) parser.description = Tool.__doc__ def handle_options(self, defaults): @@ -39,21 +39,25 @@ class Tool(ToolBase): options.input_path = os.path.abspath(options.input_path) if not os.path.isfile(options.input_path): - raise Error('<d>could not find dextIR file</> <r>"{}"</>'.format( - options.input_path)) + raise Error( + '<d>could not find dextIR file</> <r>"{}"</>'.format(options.input_path) + ) def go(self) -> ReturnCode: options = self.context.options - with open(options.input_path, 'rb') as fp: + with open(options.input_path, "rb") as fp: steps = pickle.load(fp) try: heuristic = Heuristic(self.context, steps) except HeuristicException as e: - raise Error('could not apply heuristic: {}'.format(e)) + raise Error("could not apply heuristic: {}".format(e)) - self.context.o.auto('{}\n\n{}\n\n{}\n\n'.format( - heuristic.summary_string, steps, heuristic.verbose_output)) + self.context.o.auto( + "{}\n\n{}\n\n{}\n\n".format( + heuristic.summary_string, steps, heuristic.verbose_output + ) + ) return ReturnCode.OK diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Environment.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Environment.py index d2df252..f6fd601 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Environment.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Environment.py @@ -10,13 +10,14 @@ import os def is_native_windows(): - return os.name == 'nt' + return os.name == "nt" def has_pywin32(): try: import win32com.client # pylint:disable=unused-variable import win32api # pylint:disable=unused-variable + return True except ImportError: return False diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Exceptions.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Exceptions.py index b636acb..3c00752 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Exceptions.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Exceptions.py @@ -9,11 +9,13 @@ class Dexception(Exception): """All dexter-specific exceptions derive from this.""" + pass class Error(Dexception): """Error. Prints 'error: <message>' without a traceback.""" + pass @@ -31,6 +33,7 @@ class DebuggerException(Dexception): class LoadDebuggerException(DebuggerException): """If specified debugger cannot be loaded.""" + pass @@ -38,8 +41,9 @@ class NotYetLoadedDebuggerException(LoadDebuggerException): """If specified debugger has not yet been attempted to load.""" def __init__(self): - super(NotYetLoadedDebuggerException, - self).__init__('not loaded', orig_exception=None) + super(NotYetLoadedDebuggerException, self).__init__( + "not loaded", orig_exception=None + ) class CommandParseError(Dexception): @@ -62,8 +66,10 @@ class NonFloatValueInCommand(CommandParseError): super(NonFloatValueInCommand, self).__init__(*args, **kwargs) self.value = None + class ToolArgumentError(Dexception): """If a tool argument is invalid.""" + pass @@ -71,10 +77,11 @@ class BuildScriptException(Dexception): """If there is an error in a build script file.""" def __init__(self, *args, **kwargs): - self.script_error = kwargs.pop('script_error', None) + self.script_error = kwargs.pop("script_error", None) super(BuildScriptException, self).__init__(*args, **kwargs) class HeuristicException(Dexception): """If there was a problem with the heuristic.""" + pass diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/ExtArgParse.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/ExtArgParse.py index 9fa08fb..8d968a7 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/ExtArgParse.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/ExtArgParse.py @@ -22,32 +22,32 @@ for argitem in argparse.__all__: def _did_you_mean(val, possibles): close_matches = difflib.get_close_matches(val, possibles) - did_you_mean = '' + did_you_mean = "" if close_matches: - did_you_mean = 'did you mean {}?'.format(' or '.join( - "<y>'{}'</>".format(c) for c in close_matches[:2])) + did_you_mean = "did you mean {}?".format( + " or ".join("<y>'{}'</>".format(c) for c in close_matches[:2]) + ) return did_you_mean def _colorize(message): lines = message.splitlines() for i, line in enumerate(lines): - lines[i] = lines[i].replace('usage:', '<g>usage:</>') - if line.endswith(':'): - lines[i] = '<g>{}</>'.format(line) - return '\n'.join(lines) + lines[i] = lines[i].replace("usage:", "<g>usage:</>") + if line.endswith(":"): + lines[i] = "<g>{}</>".format(line) + return "\n".join(lines) class ExtArgumentParser(argparse.ArgumentParser): def error(self, message): - """Use the Dexception Error mechanism (including auto-colored output). - """ - raise Error('{}\n\n{}'.format(message, self.format_usage())) + """Use the Dexception Error mechanism (including auto-colored output).""" + raise Error("{}\n\n{}".format(message, self.format_usage())) # pylint: disable=redefined-builtin def _print_message(self, message, file=None): if message: - if file and file.name == '<stdout>': + if file and file.name == "<stdout>": file = PrettyOutput.stdout else: file = PrettyOutput.stderr @@ -60,13 +60,14 @@ class ExtArgumentParser(argparse.ArgumentParser): return _colorize(super(ExtArgumentParser, self).format_usage()) def format_help(self): - return _colorize(super(ExtArgumentParser, self).format_help() + '\n\n') + return _colorize(super(ExtArgumentParser, self).format_help() + "\n\n") @property def _valid_visible_options(self): """A list of all non-suppressed command line flags.""" return [ - item for sublist in vars(self)['_actions'] + item + for sublist in vars(self)["_actions"] for item in sublist.option_strings if sublist.help != argparse.SUPPRESS ] @@ -83,29 +84,32 @@ class ExtArgumentParser(argparse.ArgumentParser): error = "unrecognized argument: <y>'{}'</>".format(arg) dym = _did_you_mean(arg, self._valid_visible_options) if dym: - error += ' ({})'.format(dym) + error += " ({})".format(dym) errors.append(error) - self.error('\n '.join(errors)) + self.error("\n ".join(errors)) return args def add_argument(self, *args, **kwargs): """Automatically add the default value to help text.""" - if 'default' in kwargs: - default = kwargs['default'] + if "default" in kwargs: + default = kwargs["default"] if default is None: - default = kwargs.pop('display_default', None) + default = kwargs.pop("display_default", None) - if (default and isinstance(default, (str, int, float)) - and default != argparse.SUPPRESS): + if ( + default + and isinstance(default, (str, int, float)) + and default != argparse.SUPPRESS + ): assert ( - 'choices' not in kwargs or default in kwargs['choices']), ( - "default value '{}' is not one of allowed choices: {}". - format(default, kwargs['choices'])) - if 'help' in kwargs and kwargs['help'] != argparse.SUPPRESS: - assert isinstance(kwargs['help'], str), type(kwargs['help']) - kwargs['help'] = ('{} (default:{})'.format( - kwargs['help'], default)) + "choices" not in kwargs or default in kwargs["choices"] + ), "default value '{}' is not one of allowed choices: {}".format( + default, kwargs["choices"] + ) + if "help" in kwargs and kwargs["help"] != argparse.SUPPRESS: + assert isinstance(kwargs["help"], str), type(kwargs["help"]) + kwargs["help"] = "{} (default:{})".format(kwargs["help"], default) super(ExtArgumentParser, self).add_argument(*args, **kwargs) @@ -117,32 +121,34 @@ class ExtArgumentParser(argparse.ArgumentParser): class TestExtArgumentParser(unittest.TestCase): def test_did_you_mean(self): parser = ExtArgumentParser(None) - parser.add_argument('--foo') - parser.add_argument('--qoo', help=argparse.SUPPRESS) - parser.add_argument('jam', nargs='?') + parser.add_argument("--foo") + parser.add_argument("--qoo", help=argparse.SUPPRESS) + parser.add_argument("jam", nargs="?") - parser.parse_args(['--foo', '0']) + parser.parse_args(["--foo", "0"]) - expected = (r"^unrecognized argument\: <y>'\-\-doo'</>\s+" - r"\(did you mean <y>'\-\-foo'</>\?\)\n" - r"\s*<g>usage:</>") + expected = ( + r"^unrecognized argument\: <y>'\-\-doo'</>\s+" + r"\(did you mean <y>'\-\-foo'</>\?\)\n" + r"\s*<g>usage:</>" + ) with self.assertRaisesRegex(Error, expected): - parser.parse_args(['--doo']) + parser.parse_args(["--doo"]) - parser.add_argument('--noo') + parser.add_argument("--noo") - expected = (r"^unrecognized argument\: <y>'\-\-doo'</>\s+" - r"\(did you mean <y>'\-\-noo'</> or <y>'\-\-foo'</>\?\)\n" - r"\s*<g>usage:</>") + expected = ( + r"^unrecognized argument\: <y>'\-\-doo'</>\s+" + r"\(did you mean <y>'\-\-noo'</> or <y>'\-\-foo'</>\?\)\n" + r"\s*<g>usage:</>" + ) with self.assertRaisesRegex(Error, expected): - parser.parse_args(['--doo']) + parser.parse_args(["--doo"]) - expected = (r"^unrecognized argument\: <y>'\-\-bar'</>\n" - r"\s*<g>usage:</>") + expected = r"^unrecognized argument\: <y>'\-\-bar'</>\n" r"\s*<g>usage:</>" with self.assertRaisesRegex(Error, expected): - parser.parse_args(['--bar']) + parser.parse_args(["--bar"]) - expected = (r"^unexpected argument\: <y>'\-\-foo'</>\n" - r"\s*<g>usage:</>") + expected = r"^unexpected argument\: <y>'\-\-foo'</>\n" r"\s*<g>usage:</>" with self.assertRaisesRegex(Error, expected): - parser.parse_args(['--', 'x', '--foo']) + parser.parse_args(["--", "x", "--foo"]) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Logging.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Logging.py index 11386b4..89fe50f 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Logging.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Logging.py @@ -8,6 +8,7 @@ from dex.utils import PrettyOutput + class Logger(object): def __init__(self, pretty_output: PrettyOutput): self.o = pretty_output @@ -20,25 +21,25 @@ class Logger(object): if self.verbosity < 0: return if enable_prefix: - msg = f'error: {msg}' + msg = f"error: {msg}" if flag: - msg = f'{msg} <y>[{flag}]</>' - self.error_color('{}\n'.format(msg), stream=PrettyOutput.stderr) + msg = f"{msg} <y>[{flag}]</>" + self.error_color("{}\n".format(msg), stream=PrettyOutput.stderr) def warning(self, msg, enable_prefix=True, flag=None): if self.verbosity < 1: return if enable_prefix: - msg = f'warning: {msg}' + msg = f"warning: {msg}" if flag: - msg = f'{msg} <y>[{flag}]</>' - self.warning_color('{}\n'.format(msg), stream=PrettyOutput.stderr) + msg = f"{msg} <y>[{flag}]</>" + self.warning_color("{}\n".format(msg), stream=PrettyOutput.stderr) def note(self, msg, enable_prefix=True, flag=None): if self.verbosity < 2: return if enable_prefix: - msg = f'note: {msg}' + msg = f"note: {msg}" if flag: - msg = f'{msg} <y>[{flag}]</>' - self.note_color('{}\n'.format(msg), stream=PrettyOutput.stderr) + msg = f"{msg} <y>[{flag}]</>" + self.note_color("{}\n".format(msg), stream=PrettyOutput.stderr) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/PrettyOutputBase.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/PrettyOutputBase.py index d21db89..0b4a47d 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/PrettyOutputBase.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/PrettyOutputBase.py @@ -37,9 +37,7 @@ class PreserveAutoColors(object): def __init__(self, pretty_output): self.pretty_output = pretty_output self.orig_values = {} - self.properties = [ - 'auto_reds', 'auto_yellows', 'auto_greens', 'auto_blues' - ] + self.properties = ["auto_reds", "auto_yellows", "auto_greens", "auto_blues"] def __enter__(self): for p in self.properties: @@ -90,18 +88,16 @@ class PrettyOutputBase(object, metaclass=abc.ABCMeta): # This function parses these tags using a very simple recursive # descent. colors = { - 'r': self.red, - 'y': self.yellow, - 'g': self.green, - 'b': self.blue, - 'd': self.default, - 'a': self.auto, + "r": self.red, + "y": self.yellow, + "g": self.green, + "b": self.blue, + "d": self.default, + "a": self.auto, } # Find all tags (whether open or close) - tags = [ - t for t in re.finditer('<([{}/])>'.format(''.join(colors)), text) - ] + tags = [t for t in re.finditer("<([{}/])>".format("".join(colors)), text)] if not tags: # No tags. Just write the text to the current stream and return. @@ -109,17 +105,20 @@ class PrettyOutputBase(object, metaclass=abc.ABCMeta): # render as colors (for example in error output from this # function). stream = self._set_valid_stream(stream) - stream.py.write(text.replace(r'\>', '>')) + stream.py.write(text.replace(r"\>", ">")) return - open_tags = [i for i in tags if i.group(1) != '/'] - close_tags = [i for i in tags if i.group(1) == '/'] + open_tags = [i for i in tags if i.group(1) != "/"] + close_tags = [i for i in tags if i.group(1) == "/"] - if (len(open_tags) != len(close_tags) - or any(o.start() >= c.start() - for (o, c) in zip(open_tags, close_tags))): - raise Error('open/close tag mismatch in "{}"'.format( - text.rstrip()).replace('>', r'\>')) + if len(open_tags) != len(close_tags) or any( + o.start() >= c.start() for (o, c) in zip(open_tags, close_tags) + ): + raise Error( + 'open/close tag mismatch in "{}"'.format(text.rstrip()).replace( + ">", r"\>" + ) + ) open_tag = open_tags.pop(0) @@ -128,7 +127,7 @@ class PrettyOutputBase(object, metaclass=abc.ABCMeta): tag_nesting = 1 close_tag = None for tag in tags[1:]: - if tag.group(1) == '/': + if tag.group(1) == "/": tag_nesting -= 1 else: tag_nesting += 1 @@ -140,18 +139,18 @@ class PrettyOutputBase(object, metaclass=abc.ABCMeta): # Use the method on the top of the stack for text prior to the open # tag. - before = text[:open_tag.start()] + before = text[: open_tag.start()] if before: self._stack[-1](before, lock=_null_lock, stream=stream) # Use the specified color for the tag itself. color = open_tag.group(1) - within = text[open_tag.end():close_tag.start()] + within = text[open_tag.end() : close_tag.start()] if within: colors[color](within, lock=_null_lock, stream=stream) # Use the method on the top of the stack for text after the close tag. - after = text[close_tag.end():] + after = text[close_tag.end() :] if after: self._stack[-1](after, lock=_null_lock, stream=stream) @@ -168,16 +167,17 @@ class PrettyOutputBase(object, metaclass=abc.ABCMeta): for line in lines: # This is just being cute for the sake of cuteness, but why # not? - line = line.replace('DExTer', '<r>D<y>E<g>x<b>T</></>e</>r</>') + line = line.replace("DExTer", "<r>D<y>E<g>x<b>T</></>e</>r</>") # Apply the appropriate color method if the expression matches # any of # the patterns we have set up. - for fn, regexs in ((self.red, self.auto_reds), - (self.yellow, self.auto_yellows), - (self.green, - self.auto_greens), (self.blue, - self.auto_blues)): + for fn, regexs in ( + (self.red, self.auto_reds), + (self.yellow, self.auto_yellows), + (self.green, self.auto_greens), + (self.blue, self.auto_blues), + ): if any(re.search(regex, line) for regex in regexs): fn(line, stream=stream, lock=_null_lock) break @@ -203,190 +203,197 @@ class PrettyOutputBase(object, metaclass=abc.ABCMeta): pass def yellow(self, *args, **kwargs): - return self._call_color_impl(self.yellow, self.yellow_impl, *args, - **kwargs) + return self._call_color_impl(self.yellow, self.yellow_impl, *args, **kwargs) @abc.abstractmethod def green_impl(self, text, stream=None, **kwargs): pass def green(self, *args, **kwargs): - return self._call_color_impl(self.green, self.green_impl, *args, - **kwargs) + return self._call_color_impl(self.green, self.green_impl, *args, **kwargs) @abc.abstractmethod def blue_impl(self, text, stream=None, **kwargs): pass def blue(self, *args, **kwargs): - return self._call_color_impl(self.blue, self.blue_impl, *args, - **kwargs) + return self._call_color_impl(self.blue, self.blue_impl, *args, **kwargs) @abc.abstractmethod def default_impl(self, text, stream=None, **kwargs): pass def default(self, *args, **kwargs): - return self._call_color_impl(self.default, self.default_impl, *args, - **kwargs) + return self._call_color_impl(self.default, self.default_impl, *args, **kwargs) def colortest(self): from itertools import combinations, permutations - fns = ((self.red, 'rrr'), (self.yellow, 'yyy'), (self.green, 'ggg'), - (self.blue, 'bbb'), (self.default, 'ddd')) + fns = ( + (self.red, "rrr"), + (self.yellow, "yyy"), + (self.green, "ggg"), + (self.blue, "bbb"), + (self.default, "ddd"), + ) for l in range(1, len(fns) + 1): for comb in combinations(fns, l): for perm in permutations(comb): for stream in (None, self.__class__.stderr): - perm[0][0]('stdout ' - if stream is None else 'stderr ', stream) + perm[0][0]("stdout " if stream is None else "stderr ", stream) for fn, string in perm: fn(string, stream) - self.default('\n', stream) + self.default("\n", stream) tests = [ - (self.auto, 'default1<r>red2</>default3'), - (self.red, 'red1<r>red2</>red3'), - (self.blue, 'blue1<r>red2</>blue3'), - (self.red, 'red1<y>yellow2</>red3'), - (self.auto, 'default1<y>yellow2<r>red3</></>'), - (self.auto, 'default1<g>green2<r>red3</></>'), - (self.auto, 'default1<g>green2<r>red3</>green4</>default5'), - (self.auto, 'default1<g>green2</>default3<g>green4</>default5'), - (self.auto, '<r>red1<g>green2</>red3<g>green4</>red5</>'), - (self.auto, '<r>red1<y><g>green2</>yellow3</>green4</>default5'), - (self.auto, '<r><y><g><b><d>default1</></><r></></></>red2</>'), - (self.auto, '<r>red1</>default2<r>red3</><g>green4</>default5'), - (self.blue, '<r>red1</>blue2<r><r>red3</><g><g>green</></></>'), - (self.blue, '<r>r<r>r<y>y<r><r><r><r>r</></></></></></></>b'), + (self.auto, "default1<r>red2</>default3"), + (self.red, "red1<r>red2</>red3"), + (self.blue, "blue1<r>red2</>blue3"), + (self.red, "red1<y>yellow2</>red3"), + (self.auto, "default1<y>yellow2<r>red3</></>"), + (self.auto, "default1<g>green2<r>red3</></>"), + (self.auto, "default1<g>green2<r>red3</>green4</>default5"), + (self.auto, "default1<g>green2</>default3<g>green4</>default5"), + (self.auto, "<r>red1<g>green2</>red3<g>green4</>red5</>"), + (self.auto, "<r>red1<y><g>green2</>yellow3</>green4</>default5"), + (self.auto, "<r><y><g><b><d>default1</></><r></></></>red2</>"), + (self.auto, "<r>red1</>default2<r>red3</><g>green4</>default5"), + (self.blue, "<r>red1</>blue2<r><r>red3</><g><g>green</></></>"), + (self.blue, "<r>r<r>r<y>y<r><r><r><r>r</></></></></></></>b"), ] for fn, text in tests: for stream in (None, self.__class__.stderr): - stream_name = 'stdout' if stream is None else 'stderr' - fn('{} {}\n'.format(stream_name, text), stream) + stream_name = "stdout" if stream is None else "stderr" + fn("{} {}\n".format(stream_name, text), stream) class TestPrettyOutput(unittest.TestCase): class MockPrettyOutput(PrettyOutputBase): def red_impl(self, text, stream=None, **kwargs): - self._write('[R]{}[/R]'.format(text), stream) + self._write("[R]{}[/R]".format(text), stream) def yellow_impl(self, text, stream=None, **kwargs): - self._write('[Y]{}[/Y]'.format(text), stream) + self._write("[Y]{}[/Y]".format(text), stream) def green_impl(self, text, stream=None, **kwargs): - self._write('[G]{}[/G]'.format(text), stream) + self._write("[G]{}[/G]".format(text), stream) def blue_impl(self, text, stream=None, **kwargs): - self._write('[B]{}[/B]'.format(text), stream) + self._write("[B]{}[/B]".format(text), stream) def default_impl(self, text, stream=None, **kwargs): - self._write('[D]{}[/D]'.format(text), stream) + self._write("[D]{}[/D]".format(text), stream) def test_red(self): with TestPrettyOutput.MockPrettyOutput() as o: stream = Stream(StringIO()) - o.red('hello', stream) - self.assertEqual(stream.py.getvalue(), '[R]hello[/R]') + o.red("hello", stream) + self.assertEqual(stream.py.getvalue(), "[R]hello[/R]") def test_yellow(self): with TestPrettyOutput.MockPrettyOutput() as o: stream = Stream(StringIO()) - o.yellow('hello', stream) - self.assertEqual(stream.py.getvalue(), '[Y]hello[/Y]') + o.yellow("hello", stream) + self.assertEqual(stream.py.getvalue(), "[Y]hello[/Y]") def test_green(self): with TestPrettyOutput.MockPrettyOutput() as o: stream = Stream(StringIO()) - o.green('hello', stream) - self.assertEqual(stream.py.getvalue(), '[G]hello[/G]') + o.green("hello", stream) + self.assertEqual(stream.py.getvalue(), "[G]hello[/G]") def test_blue(self): with TestPrettyOutput.MockPrettyOutput() as o: stream = Stream(StringIO()) - o.blue('hello', stream) - self.assertEqual(stream.py.getvalue(), '[B]hello[/B]') + o.blue("hello", stream) + self.assertEqual(stream.py.getvalue(), "[B]hello[/B]") def test_default(self): with TestPrettyOutput.MockPrettyOutput() as o: stream = Stream(StringIO()) - o.default('hello', stream) - self.assertEqual(stream.py.getvalue(), '[D]hello[/D]') + o.default("hello", stream) + self.assertEqual(stream.py.getvalue(), "[D]hello[/D]") def test_auto(self): with TestPrettyOutput.MockPrettyOutput() as o: stream = Stream(StringIO()) - o.auto_reds.append('foo') - o.auto('bar\n', stream) - o.auto('foo\n', stream) - o.auto('baz\n', stream) - self.assertEqual(stream.py.getvalue(), - '[D]bar\n[/D][R]foo\n[/R][D]baz\n[/D]') + o.auto_reds.append("foo") + o.auto("bar\n", stream) + o.auto("foo\n", stream) + o.auto("baz\n", stream) + self.assertEqual( + stream.py.getvalue(), "[D]bar\n[/D][R]foo\n[/R][D]baz\n[/D]" + ) stream = Stream(StringIO()) - o.auto('bar\nfoo\nbaz\n', stream) - self.assertEqual(stream.py.getvalue(), - '[D]bar\n[/D][R]foo\n[/R][D]baz\n[/D]') + o.auto("bar\nfoo\nbaz\n", stream) + self.assertEqual( + stream.py.getvalue(), "[D]bar\n[/D][R]foo\n[/R][D]baz\n[/D]" + ) stream = Stream(StringIO()) - o.auto('barfoobaz\nbardoobaz\n', stream) - self.assertEqual(stream.py.getvalue(), - '[R]barfoobaz\n[/R][D]bardoobaz\n[/D]') + o.auto("barfoobaz\nbardoobaz\n", stream) + self.assertEqual( + stream.py.getvalue(), "[R]barfoobaz\n[/R][D]bardoobaz\n[/D]" + ) - o.auto_greens.append('doo') + o.auto_greens.append("doo") stream = Stream(StringIO()) - o.auto('barfoobaz\nbardoobaz\n', stream) - self.assertEqual(stream.py.getvalue(), - '[R]barfoobaz\n[/R][G]bardoobaz\n[/G]') + o.auto("barfoobaz\nbardoobaz\n", stream) + self.assertEqual( + stream.py.getvalue(), "[R]barfoobaz\n[/R][G]bardoobaz\n[/G]" + ) def test_PreserveAutoColors(self): with TestPrettyOutput.MockPrettyOutput() as o: - o.auto_reds.append('foo') + o.auto_reds.append("foo") with PreserveAutoColors(o): - o.auto_greens.append('bar') + o.auto_greens.append("bar") stream = Stream(StringIO()) - o.auto('foo\nbar\nbaz\n', stream) - self.assertEqual(stream.py.getvalue(), - '[R]foo\n[/R][G]bar\n[/G][D]baz\n[/D]') + o.auto("foo\nbar\nbaz\n", stream) + self.assertEqual( + stream.py.getvalue(), "[R]foo\n[/R][G]bar\n[/G][D]baz\n[/D]" + ) stream = Stream(StringIO()) - o.auto('foo\nbar\nbaz\n', stream) - self.assertEqual(stream.py.getvalue(), - '[R]foo\n[/R][D]bar\n[/D][D]baz\n[/D]') + o.auto("foo\nbar\nbaz\n", stream) + self.assertEqual( + stream.py.getvalue(), "[R]foo\n[/R][D]bar\n[/D][D]baz\n[/D]" + ) stream = Stream(StringIO()) - o.yellow('<a>foo</>bar<a>baz</>', stream) + o.yellow("<a>foo</>bar<a>baz</>", stream) self.assertEqual( stream.py.getvalue(), - '[Y][Y][/Y][R]foo[/R][Y][Y]bar[/Y][D]baz[/D][Y][/Y][/Y][/Y]') + "[Y][Y][/Y][R]foo[/R][Y][Y]bar[/Y][D]baz[/D][Y][/Y][/Y][/Y]", + ) def test_tags(self): with TestPrettyOutput.MockPrettyOutput() as o: stream = Stream(StringIO()) - o.auto('<r>hi</>', stream) - self.assertEqual(stream.py.getvalue(), - '[D][D][/D][R]hi[/R][D][/D][/D]') + o.auto("<r>hi</>", stream) + self.assertEqual(stream.py.getvalue(), "[D][D][/D][R]hi[/R][D][/D][/D]") stream = Stream(StringIO()) - o.auto('<r><y>a</>b</>c', stream) + o.auto("<r><y>a</>b</>c", stream) self.assertEqual( stream.py.getvalue(), - '[D][D][/D][R][R][/R][Y]a[/Y][R]b[/R][/R][D]c[/D][/D]') + "[D][D][/D][R][R][/R][Y]a[/Y][R]b[/R][/R][D]c[/D][/D]", + ) - with self.assertRaisesRegex(Error, 'tag mismatch'): - o.auto('<r>hi', stream) + with self.assertRaisesRegex(Error, "tag mismatch"): + o.auto("<r>hi", stream) - with self.assertRaisesRegex(Error, 'tag mismatch'): - o.auto('hi</>', stream) + with self.assertRaisesRegex(Error, "tag mismatch"): + o.auto("hi</>", stream) - with self.assertRaisesRegex(Error, 'tag mismatch'): - o.auto('<r><y>hi</>', stream) + with self.assertRaisesRegex(Error, "tag mismatch"): + o.auto("<r><y>hi</>", stream) - with self.assertRaisesRegex(Error, 'tag mismatch'): - o.auto('<r><y>hi</><r></>', stream) + with self.assertRaisesRegex(Error, "tag mismatch"): + o.auto("<r><y>hi</><r></>", stream) - with self.assertRaisesRegex(Error, 'tag mismatch'): - o.auto('</>hi<r>', stream) + with self.assertRaisesRegex(Error, "tag mismatch"): + o.auto("</>hi<r>", stream) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/ReturnCode.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/ReturnCode.py index 487d225..a325791 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/ReturnCode.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/ReturnCode.py @@ -9,12 +9,12 @@ from enum import Enum class ReturnCode(Enum): - """Used to indicate whole program success status.""" + """Used to indicate whole program success status.""" - OK = 0 - _ERROR = 1 # Unhandled exceptions result in exit(1) by default. - # Usage of _ERROR is discouraged: - # If the program cannot run, raise an exception. - # If the program runs successfully but the result is - # "failure" based on the inputs, return FAIL - FAIL = 2 + OK = 0 + _ERROR = 1 # Unhandled exceptions result in exit(1) by default. + # Usage of _ERROR is discouraged: + # If the program cannot run, raise an exception. + # If the program runs successfully but the result is + # "failure" based on the inputs, return FAIL + FAIL = 2 diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/RootDirectory.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/RootDirectory.py index 57f204c..c41797c 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/RootDirectory.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/RootDirectory.py @@ -10,6 +10,6 @@ import os def get_root_directory(): - root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) - assert os.path.basename(root) == 'dex', root + root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + assert os.path.basename(root) == "dex", root return root diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Timeout.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Timeout.py index d59d7d7..c356206 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Timeout.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Timeout.py @@ -11,8 +11,8 @@ means a timeout will never be triggered, i.e. timed_out() will always return Fal import time -class Timeout(object): +class Timeout(object): def __init__(self, duration: float): self.start = self.now self.duration = duration diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Timer.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Timer.py index 63726f1..6548086 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Timer.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Timer.py @@ -13,7 +13,7 @@ import time def _indent(indent): - return '| ' * indent + return "| " * indent class Timer(object): @@ -28,17 +28,18 @@ class Timer(object): def __enter__(self): Timer.indent += 1 if Timer.display and self.name: - indent = _indent(Timer.indent - 1) + ' _' - Timer.fn('{}\n'.format(_indent(Timer.indent - 1))) - Timer.fn('{} start {}\n'.format(indent, self.name)) + indent = _indent(Timer.indent - 1) + " _" + Timer.fn("{}\n".format(_indent(Timer.indent - 1))) + Timer.fn("{} start {}\n".format(indent, self.name)) return self def __exit__(self, *args): if Timer.display and self.name: - indent = _indent(Timer.indent - 1) + '|_' - Timer.fn('{} {} time taken: {:0.1f}s\n'.format( - indent, self.name, self.elapsed)) - Timer.fn('{}\n'.format(_indent(Timer.indent - 1))) + indent = _indent(Timer.indent - 1) + "|_" + Timer.fn( + "{} {} time taken: {:0.1f}s\n".format(indent, self.name, self.elapsed) + ) + Timer.fn("{}\n".format(_indent(Timer.indent - 1))) Timer.indent -= 1 @property diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/UnitTests.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/UnitTests.py index cfddce5..0c49991 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/UnitTests.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/UnitTests.py @@ -24,14 +24,14 @@ class DexTestLoader(unittest.TestLoader): """ d = os.path.basename(os.path.dirname(full_path)) if is_native_windows(): - if d == 'posix': + if d == "posix": return False - if d == 'windows': + if d == "windows": return has_pywin32() else: - if d == 'windows': + if d == "windows": return False - elif d == 'dbgeng': + elif d == "dbgeng": return False return fnmatch(path, pattern) @@ -39,20 +39,18 @@ class DexTestLoader(unittest.TestLoader): def unit_tests_ok(context): unittest.TestCase.maxDiff = None # remove size limit from diff output. - with Timer('unit tests'): - suite = DexTestLoader().discover( - context.root_directory, pattern='*.py') + with Timer("unit tests"): + suite = DexTestLoader().discover(context.root_directory, pattern="*.py") stream = StringIO() result = unittest.TextTestRunner(verbosity=2, stream=stream).run(suite) ok = result.wasSuccessful() - if not ok or context.options.unittest == 'show-all': + if not ok or context.options.unittest == "show-all": with PreserveAutoColors(context.o): - context.o.auto_reds.extend( - [r'FAIL(ED|\:)', r'\.\.\.\s(FAIL|ERROR)$']) - context.o.auto_greens.extend([r'^OK$', r'\.\.\.\sok$']) - context.o.auto_blues.extend([r'^Ran \d+ test']) - context.o.default('\n') + context.o.auto_reds.extend([r"FAIL(ED|\:)", r"\.\.\.\s(FAIL|ERROR)$"]) + context.o.auto_greens.extend([r"^OK$", r"\.\.\.\sok$"]) + context.o.auto_blues.extend([r"^Ran \d+ test"]) + context.o.default("\n") for line in stream.getvalue().splitlines(True): context.o.auto(line, stream=PrettyOutput.stderr) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Version.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Version.py index 1a257fa..505aeba 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Version.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Version.py @@ -16,17 +16,26 @@ from dex import __version__ def _git_version(): dir_ = os.path.dirname(__file__) try: - branch = (check_output( - ['git', 'rev-parse', '--abbrev-ref', 'HEAD'], - stderr=STDOUT, - cwd=dir_).rstrip().decode('utf-8')) - hash_ = check_output( - ['git', 'rev-parse', 'HEAD'], stderr=STDOUT, - cwd=dir_).rstrip().decode('utf-8') - repo = check_output( - ['git', 'remote', 'get-url', 'origin'], stderr=STDOUT, - cwd=dir_).rstrip().decode('utf-8') - return '[{} {}] ({})'.format(branch, hash_, repo) + branch = ( + check_output( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], stderr=STDOUT, cwd=dir_ + ) + .rstrip() + .decode("utf-8") + ) + hash_ = ( + check_output(["git", "rev-parse", "HEAD"], stderr=STDOUT, cwd=dir_) + .rstrip() + .decode("utf-8") + ) + repo = ( + check_output( + ["git", "remote", "get-url", "origin"], stderr=STDOUT, cwd=dir_ + ) + .rstrip() + .decode("utf-8") + ) + return "[{} {}] ({})".format(branch, hash_, repo) except (OSError, CalledProcessError): pass return None @@ -34,7 +43,6 @@ def _git_version(): def version(name): lines = [] - lines.append(' '.join( - [s for s in [name, __version__, _git_version()] if s])) - lines.append(' using Python {}'.format(sys.version)) - return '\n'.join(lines) + lines.append(" ".join([s for s in [name, __version__, _git_version()] if s])) + lines.append(" using Python {}".format(sys.version)) + return "\n".join(lines) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/WorkingDirectory.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/WorkingDirectory.py index 06d776f..28bcff7 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/WorkingDirectory.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/WorkingDirectory.py @@ -13,12 +13,13 @@ import time from dex.utils.Exceptions import Error + class WorkingDirectory(object): def __init__(self, context, *args, **kwargs): self.context = context self.orig_cwd = os.getcwd() - dir_ = kwargs.get('dir', None) + dir_ = kwargs.get("dir", None) if dir_ and not os.path.isdir(dir_): os.makedirs(dir_, exist_ok=True) self.path = tempfile.mkdtemp(*args, **kwargs) @@ -30,8 +31,7 @@ class WorkingDirectory(object): def __exit__(self, *args): os.chdir(self.orig_cwd) if self.context.options.save_temps: - self.context.o.blue('"{}" left in place [--save-temps]\n'.format( - self.path)) + self.context.o.blue('"{}" left in place [--save-temps]\n'.format(self.path)) return for _ in range(100): @@ -41,5 +41,7 @@ class WorkingDirectory(object): except OSError: time.sleep(0.1) - self.context.logger.warning(f'"{self.path}" left in place (couldn\'t delete)', enable_prefix=True) + self.context.logger.warning( + f'"{self.path}" left in place (couldn\'t delete)', enable_prefix=True + ) return diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/posix/PrettyOutput.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/posix/PrettyOutput.py index 82cfed5..1597e1a 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/posix/PrettyOutput.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/posix/PrettyOutput.py @@ -15,7 +15,7 @@ class PrettyOutput(PrettyOutputBase): stream = self._set_valid_stream(stream) with lock: if stream.color_enabled: - text = '\033[{}m{}\033[0m'.format(color, text) + text = "\033[{}m{}\033[0m".format(color, text) self._write(text, stream) def red_impl(self, text, stream=None, **kwargs): diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/windows/PrettyOutput.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/windows/PrettyOutput.py index 657406a..bb82e24 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/windows/PrettyOutput.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/windows/PrettyOutput.py @@ -16,11 +16,13 @@ from ..PrettyOutputBase import PrettyOutputBase, Stream, _lock, _null_lock class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure): # pylint: disable=protected-access - _fields_ = [('dwSize', ctypes.wintypes._COORD), ('dwCursorPosition', - ctypes.wintypes._COORD), - ('wAttributes', - ctypes.c_ushort), ('srWindow', ctypes.wintypes._SMALL_RECT), - ('dwMaximumWindowSize', ctypes.wintypes._COORD)] + _fields_ = [ + ("dwSize", ctypes.wintypes._COORD), + ("dwCursorPosition", ctypes.wintypes._COORD), + ("wAttributes", ctypes.c_ushort), + ("srWindow", ctypes.wintypes._SMALL_RECT), + ("dwMaximumWindowSize", ctypes.wintypes._COORD), + ] # pylint: enable=protected-access @@ -33,8 +35,7 @@ class PrettyOutput(PrettyOutputBase): info = _CONSOLE_SCREEN_BUFFER_INFO() for s in (PrettyOutput.stdout, PrettyOutput.stderr): - ctypes.windll.kernel32.GetConsoleScreenBufferInfo( - s.os, ctypes.byref(info)) + ctypes.windll.kernel32.GetConsoleScreenBufferInfo(s.os, ctypes.byref(info)) s.orig_color = info.wAttributes return self @@ -52,15 +53,15 @@ class PrettyOutput(PrettyOutputBase): self.flush(stream) if stream.orig_color: ctypes.windll.kernel32.SetConsoleTextAttribute( - stream.os, stream.orig_color) + stream.os, stream.orig_color + ) def _color(self, text, color, stream, lock=_lock): stream = self._set_valid_stream(stream) with lock: try: if stream.color_enabled: - ctypes.windll.kernel32.SetConsoleTextAttribute( - stream.os, color) + ctypes.windll.kernel32.SetConsoleTextAttribute(stream.os, color) self._write(text, stream) finally: if stream.color_enabled: diff --git a/cross-project-tests/debuginfo-tests/dexter/dexter.py b/cross-project-tests/debuginfo-tests/dexter/dexter.py index 49ba85d..8473cff 100755 --- a/cross-project-tests/debuginfo-tests/dexter/dexter.py +++ b/cross-project-tests/debuginfo-tests/dexter/dexter.py @@ -16,6 +16,6 @@ if sys.version_info < (3, 6, 0): from dex.tools import main -if __name__ == '__main__': +if __name__ == "__main__": return_code = main() sys.exit(return_code.value) diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/dex_and_source/lit.local.cfg.py b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/dex_and_source/lit.local.cfg.py index 159c376..70e844d 100644 --- a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/dex_and_source/lit.local.cfg.py +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/dex_and_source/lit.local.cfg.py @@ -1 +1 @@ -config.suffixes = ['.cpp'] +config.suffixes = [".cpp"] diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/precompiled_binary/lit.local.cfg.py b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/precompiled_binary/lit.local.cfg.py index e65498f..3dfcaa7 100644 --- a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/precompiled_binary/lit.local.cfg.py +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/precompiled_binary/lit.local.cfg.py @@ -1 +1 @@ -config.suffixes = ['.dex'] +config.suffixes = [".dex"] diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/precompiled_binary_different_dir/lit.local.cfg.py b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/precompiled_binary_different_dir/lit.local.cfg.py index e65498f..3dfcaa7 100644 --- a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/precompiled_binary_different_dir/lit.local.cfg.py +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/precompiled_binary_different_dir/lit.local.cfg.py @@ -1 +1 @@ -config.suffixes = ['.dex'] +config.suffixes = [".dex"] diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/windows_noncanonical_path/lit.local.cfg.py b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/windows_noncanonical_path/lit.local.cfg.py index e65498f..3dfcaa7 100644 --- a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/windows_noncanonical_path/lit.local.cfg.py +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/dex_declare_file/windows_noncanonical_path/lit.local.cfg.py @@ -1 +1 @@ -config.suffixes = ['.dex'] +config.suffixes = [".dex"] diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/lit.local.cfg b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/lit.local.cfg index 4f3bbe2..b6cc2f7 100644 --- a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/lit.local.cfg +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/limit_steps/lit.local.cfg @@ -1,4 +1,3 @@ # The dbgeng driver doesn't support DexLimitSteps yet. if config.is_msvc: config.unsupported = True - diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/lit.local.cfg b/cross-project-tests/debuginfo-tests/dexter/feature_tests/lit.local.cfg index 6eb9f0a..16b9690 100644 --- a/cross-project-tests/debuginfo-tests/dexter/feature_tests/lit.local.cfg +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/lit.local.cfg @@ -1,5 +1,5 @@ -if 'dexter' not in config.available_features: +if "dexter" not in config.available_features: config.unsupported = True config.name = "DExTer feature tests" -config.suffixes = ['.cpp', '.c', '.test'] +config.suffixes = [".cpp", ".c", ".test"] diff --git a/cross-project-tests/debuginfo-tests/lit.local.cfg b/cross-project-tests/debuginfo-tests/lit.local.cfg index 62f90a1..530f4c01 100644 --- a/cross-project-tests/debuginfo-tests/lit.local.cfg +++ b/cross-project-tests/debuginfo-tests/lit.local.cfg @@ -1,2 +1,2 @@ -if 'clang' not in config.available_features: +if "clang" not in config.available_features: config.unsupported = True diff --git a/cross-project-tests/debuginfo-tests/llgdb-tests/lit.local.cfg b/cross-project-tests/debuginfo-tests/llgdb-tests/lit.local.cfg index 725aa59..2b79104 100644 --- a/cross-project-tests/debuginfo-tests/llgdb-tests/lit.local.cfg +++ b/cross-project-tests/debuginfo-tests/llgdb-tests/lit.local.cfg @@ -1,5 +1,3 @@ # debuginfo-tests are not expected to pass in a cross-compilation setup. -if 'native' not in config.available_features or config.is_msvc: +if "native" not in config.available_features or config.is_msvc: config.unsupported = True - - diff --git a/cross-project-tests/debuginfo-tests/llgdb-tests/llgdb.py b/cross-project-tests/debuginfo-tests/llgdb-tests/llgdb.py index a97a039..6795d3b 100755 --- a/cross-project-tests/debuginfo-tests/llgdb-tests/llgdb.py +++ b/cross-project-tests/debuginfo-tests/llgdb-tests/llgdb.py @@ -6,14 +6,17 @@ commands to run the tests in the debuginfo-tests repository with lldb. # ---------------------------------------------------------------------- # Auto-detect lldb python module. -import subprocess, platform, os, sys +import subprocess, platform, os, sys + try: # Just try for LLDB in case PYTHONPATH is already correctly setup. import lldb except ImportError: # Ask the command line driver for the path to the lldb module. Copy over # the environment so that SDKROOT is propagated to xcrun. - command = ['xcrun', 'lldb', '-P'] if platform.system() == 'Darwin' else ['lldb', '-P'] + command = ( + ["xcrun", "lldb", "-P"] if platform.system() == "Darwin" else ["lldb", "-P"] + ) # Extend the PYTHONPATH if the path exists and isn't already there. lldb_python_path = subprocess.check_output(command).decode("utf-8").strip() if os.path.exists(lldb_python_path) and not sys.path.__contains__(lldb_python_path): @@ -21,21 +24,27 @@ except ImportError: # Try importing LLDB again. try: import lldb - print('imported lldb from: "%s"'%lldb_python_path) + + print('imported lldb from: "%s"' % lldb_python_path) except ImportError: - print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly") + print( + "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" + ) sys.exit(1) # ---------------------------------------------------------------------- # Command line option handling. import argparse + parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument('--quiet', '-q', action="store_true", help='ignored') -parser.add_argument('-batch', action="store_true", - help='exit after processing comand line') -parser.add_argument('-n', action="store_true", help='ignore .lldb file') -parser.add_argument('-x', dest='script', type=argparse.FileType('r'), - help='execute commands from file') +parser.add_argument("--quiet", "-q", action="store_true", help="ignored") +parser.add_argument( + "-batch", action="store_true", help="exit after processing comand line" +) +parser.add_argument("-n", action="store_true", help="ignore .lldb file") +parser.add_argument( + "-x", dest="script", type=argparse.FileType("r"), help="execute commands from file" +) parser.add_argument("target", help="the program to debug") args = parser.parse_args() @@ -46,19 +55,23 @@ debugger.SkipLLDBInitFiles(args.n) # Make sure to clean up the debugger on exit. import atexit + + def on_exit(): debugger.Terminate() + + atexit.register(on_exit) # Don't return from lldb function calls until the process stops. debugger.SetAsync(False) # Create a target from a file and arch. -arch = os.popen("file "+args.target).read().split()[-1] +arch = os.popen("file " + args.target).read().split()[-1] target = debugger.CreateTargetWithFileAndArch(args.target, arch) if not target: - print("Could not create target %s"% args.target) + print("Could not create target %s" % args.target) sys.exit(1) if not args.script: @@ -66,16 +79,17 @@ if not args.script: sys.exit(1) import re + for command in args.script: # Strip newline and whitespaces and split into words. cmd = command[:-1].strip().split() if not cmd: continue - print('> %s'% command[:-1]) + print("> %s" % command[:-1]) try: - if re.match('^r|(run)$', cmd[0]): + if re.match("^r|(run)$", cmd[0]): error = lldb.SBError() launchinfo = lldb.SBLaunchInfo([]) launchinfo.SetWorkingDirectory(os.getcwd()) @@ -84,25 +98,27 @@ for command in args.script: if not process or error.fail: state = process.GetState() print("State = %d" % state) - print(""" + print( + """ ERROR: Could not launch process. NOTE: There are several reasons why this may happen: * Root needs to run "DevToolsSecurity --enable". * Older versions of lldb cannot launch more than one process simultaneously. -""") +""" + ) sys.exit(1) - elif re.match('^b|(break)$', cmd[0]) and len(cmd) == 2: - if re.match('[0-9]+', cmd[1]): + elif re.match("^b|(break)$", cmd[0]) and len(cmd) == 2: + if re.match("[0-9]+", cmd[1]): # b line - mainfile = target.FindFunctions('main')[0].compile_unit.file + mainfile = target.FindFunctions("main")[0].compile_unit.file print(target.BreakpointCreateByLocation(mainfile, int(cmd[1]))) else: # b file:line - file, line = cmd[1].split(':') + file, line = cmd[1].split(":") print(target.BreakpointCreateByLocation(file, int(line))) - elif re.match('^ptype$', cmd[0]) and len(cmd) == 2: + elif re.match("^ptype$", cmd[0]) and len(cmd) == 2: # GDB's ptype has multiple incarnations depending on its # argument (global variable, function, type). The definition # here is for looking up the signature of a function and only @@ -113,36 +129,35 @@ NOTE: There are several reasons why this may happen: continue print(target.FindFirstType(cmd[1])) - elif re.match('^po$', cmd[0]) and len(cmd) > 1: + elif re.match("^po$", cmd[0]) and len(cmd) > 1: try: opts = lldb.SBExpressionOptions() opts.SetFetchDynamicValue(True) opts.SetCoerceResultToId(True) - print(target.EvaluateExpression(' '.join(cmd[1:]), opts)) + print(target.EvaluateExpression(" ".join(cmd[1:]), opts)) except: # FIXME: This is a fallback path for the lab.llvm.org # buildbot running OS X 10.7; it should be removed. thread = process.GetThreadAtIndex(0) frame = thread.GetFrameAtIndex(0) - print(frame.EvaluateExpression(' '.join(cmd[1:]))) + print(frame.EvaluateExpression(" ".join(cmd[1:]))) - elif re.match('^p|(print)$', cmd[0]) and len(cmd) > 1: + elif re.match("^p|(print)$", cmd[0]) and len(cmd) > 1: thread = process.GetThreadAtIndex(0) frame = thread.GetFrameAtIndex(0) - print(frame.EvaluateExpression(' '.join(cmd[1:]))) + print(frame.EvaluateExpression(" ".join(cmd[1:]))) - elif re.match('^n|(next)$', cmd[0]): + elif re.match("^n|(next)$", cmd[0]): thread = process.GetThreadAtIndex(0) thread.StepOver() - elif re.match('^q|(quit)$', cmd[0]): + elif re.match("^q|(quit)$", cmd[0]): sys.exit(0) else: - print(debugger.HandleCommand(' '.join(cmd))) + print(debugger.HandleCommand(" ".join(cmd))) except SystemExit: raise except: - print('Could not handle the command "%s"' % ' '.join(cmd)) - + print('Could not handle the command "%s"' % " ".join(cmd)) diff --git a/cross-project-tests/debuginfo-tests/llvm-prettyprinters/gdb/lit.local.cfg b/cross-project-tests/debuginfo-tests/llvm-prettyprinters/gdb/lit.local.cfg index a4200fb..222448f 100644 --- a/cross-project-tests/debuginfo-tests/llvm-prettyprinters/gdb/lit.local.cfg +++ b/cross-project-tests/debuginfo-tests/llvm-prettyprinters/gdb/lit.local.cfg @@ -1,13 +1,11 @@ import lit.util # debuginfo-tests are not expected to pass in a cross-compilation setup. -if 'native' not in config.available_features or lit.util.which('gdb') is None: +if "native" not in config.available_features or lit.util.which("gdb") is None: config.unsupported = True if config.mlir_src_root: - config.substitutions.append(("%mlir_src_root", config.mlir_src_root)) - config.available_features.add('mlir') - -config.suffixes = ['.gdb'] - + config.substitutions.append(("%mlir_src_root", config.mlir_src_root)) + config.available_features.add("mlir") +config.suffixes = [".gdb"] diff --git a/cross-project-tests/intrinsic-header-tests/lit.local.cfg b/cross-project-tests/intrinsic-header-tests/lit.local.cfg index 62f90a1..530f4c01 100644 --- a/cross-project-tests/intrinsic-header-tests/lit.local.cfg +++ b/cross-project-tests/intrinsic-header-tests/lit.local.cfg @@ -1,2 +1,2 @@ -if 'clang' not in config.available_features: +if "clang" not in config.available_features: config.unsupported = True diff --git a/cross-project-tests/lit.cfg.py b/cross-project-tests/lit.cfg.py index 4bdd2d0..350dd45 100644 --- a/cross-project-tests/lit.cfg.py +++ b/cross-project-tests/lit.cfg.py @@ -16,18 +16,18 @@ from lit.llvm.subst import ToolSubst # Configuration file for the 'lit' test runner. # name: The name of this test suite. -config.name = 'cross-project-tests' +config.name = "cross-project-tests" # testFormat: The test format to use to interpret tests. config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell) # suffixes: A list of file extensions to treat as test files. -config.suffixes = ['.c', '.cl', '.cpp', '.m'] +config.suffixes = [".c", ".cl", ".cpp", ".m"] # excludes: A list of directories to exclude from the testsuite. The 'Inputs' # subdirectories contain auxiliary inputs for various tests in their parent # directories. -config.excludes = ['Inputs'] +config.excludes = ["Inputs"] # test_source_root: The root path where tests are located. config.test_source_root = config.cross_project_tests_src_root @@ -38,154 +38,191 @@ config.test_exec_root = config.cross_project_tests_obj_root llvm_config.use_default_substitutions() tools = [ - ToolSubst('%test_debuginfo', command=os.path.join( - config.cross_project_tests_src_root, 'debuginfo-tests', - 'llgdb-tests', 'test_debuginfo.pl')), + ToolSubst( + "%test_debuginfo", + command=os.path.join( + config.cross_project_tests_src_root, + "debuginfo-tests", + "llgdb-tests", + "test_debuginfo.pl", + ), + ), ToolSubst("%llvm_src_root", config.llvm_src_root), ToolSubst("%llvm_tools_dir", config.llvm_tools_dir), ] + def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if attr_value == None: - lit_config.fatal( - "No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) - return attr_value + attr_value = getattr(config, attr_name, None) + if attr_value == None: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name + ) + return attr_value + # If this is an MSVC environment, the tests at the root of the tree are # unsupported. The local win_cdb test suite, however, is supported. is_msvc = get_required_attr(config, "is_msvc") if is_msvc: - config.available_features.add('msvc') + config.available_features.add("msvc") # FIXME: We should add some llvm lit utility code to find the Windows SDK # and set up the environment appopriately. - win_sdk = 'C:/Program Files (x86)/Windows Kits/10/' - arch = 'x64' - llvm_config.with_system_environment(['LIB', 'LIBPATH', 'INCLUDE']) + win_sdk = "C:/Program Files (x86)/Windows Kits/10/" + arch = "x64" + llvm_config.with_system_environment(["LIB", "LIBPATH", "INCLUDE"]) # Clear _NT_SYMBOL_PATH to prevent cdb from attempting to load symbols from # the network. - llvm_config.with_environment('_NT_SYMBOL_PATH', '') - tools.append(ToolSubst('%cdb', '"%s"' % os.path.join(win_sdk, 'Debuggers', - arch, 'cdb.exe'))) + llvm_config.with_environment("_NT_SYMBOL_PATH", "") + tools.append( + ToolSubst("%cdb", '"%s"' % os.path.join(win_sdk, "Debuggers", arch, "cdb.exe")) + ) # clang_src_dir and lld_src_dir are not used by these tests, but are required by # use_clang() and use_lld() respectively, so set them to "", if needed. -if not hasattr(config, 'clang_src_dir'): +if not hasattr(config, "clang_src_dir"): config.clang_src_dir = "" -llvm_config.use_clang(required=('clang' in config.llvm_enabled_projects)) +llvm_config.use_clang(required=("clang" in config.llvm_enabled_projects)) -if not hasattr(config, 'lld_src_dir'): +if not hasattr(config, "lld_src_dir"): config.lld_src_dir = "" -llvm_config.use_lld(required=('lld' in config.llvm_enabled_projects)) +llvm_config.use_lld(required=("lld" in config.llvm_enabled_projects)) -if 'compiler-rt' in config.llvm_enabled_projects: - config.available_features.add('compiler-rt') +if "compiler-rt" in config.llvm_enabled_projects: + config.available_features.add("compiler-rt") # Check which debuggers are available: -lldb_path = llvm_config.use_llvm_tool('lldb', search_env='LLDB') +lldb_path = llvm_config.use_llvm_tool("lldb", search_env="LLDB") if lldb_path is not None: - config.available_features.add('lldb') + config.available_features.add("lldb") + def configure_dexter_substitutions(): - """Configure substitutions for host platform and return list of dependencies - """ - # Produce dexter path, lldb path, and combine into the %dexter substitution - # for running a test. - dexter_path = os.path.join(config.cross_project_tests_src_root, - 'debuginfo-tests', 'dexter', 'dexter.py') - dexter_test_cmd = '"{}" "{}" test'.format(sys.executable, dexter_path) - if lldb_path is not None: - dexter_test_cmd += ' --lldb-executable "{}"'.format(lldb_path) - tools.append(ToolSubst('%dexter', dexter_test_cmd)) - - # For testing other bits of dexter that aren't under the "test" subcommand, - # have a %dexter_base substitution. - dexter_base_cmd = '"{}" "{}"'.format(sys.executable, dexter_path) - tools.append(ToolSubst('%dexter_base', dexter_base_cmd)) - - # Set up commands for DexTer regression tests. - # Builder, debugger, optimisation level and several other flags differ - # depending on whether we're running a unix like or windows os. - if platform.system() == 'Windows': - # The Windows builder script uses lld. - dependencies = ['clang', 'lld-link'] - dexter_regression_test_builder = 'clang-cl_vs2015' - dexter_regression_test_debugger = 'dbgeng' - dexter_regression_test_cflags = '/Zi /Od' - dexter_regression_test_ldflags = '/Zi' - else: - # Use lldb as the debugger on non-Windows platforms. - dependencies = ['clang', 'lldb'] - dexter_regression_test_builder = 'clang' - dexter_regression_test_debugger = 'lldb' - dexter_regression_test_cflags = '-O0 -glldb' - dexter_regression_test_ldflags = '' - - tools.append(ToolSubst('%dexter_regression_test_builder', dexter_regression_test_builder)) - tools.append(ToolSubst('%dexter_regression_test_debugger', dexter_regression_test_debugger)) - tools.append(ToolSubst('%dexter_regression_test_cflags', dexter_regression_test_cflags)) - tools.append(ToolSubst('%dexter_regression_test_ldflags', dexter_regression_test_cflags)) - - # Typical command would take the form: - # ./path_to_py/python.exe ./path_to_dex/dexter.py test --fail-lt 1.0 -w --builder clang --debugger lldb --cflags '-O0 -g' - # Exclude build flags for %dexter_regression_base. - dexter_regression_test_base = ' '.join( - # "python", "dexter.py", test, fail_mode, builder, debugger, cflags, ldflags - ['"{}"'.format(sys.executable), - '"{}"'.format(dexter_path), - 'test', - '--fail-lt 1.0 -w', - '--debugger', dexter_regression_test_debugger]) - tools.append(ToolSubst('%dexter_regression_base', dexter_regression_test_base)) - - # Include build flags for %dexter_regression_test. - dexter_regression_test_build = ' '.join([ - dexter_regression_test_base, - '--builder', dexter_regression_test_builder, - '--cflags "', dexter_regression_test_cflags + '"', - '--ldflags "', dexter_regression_test_ldflags + '"']) - tools.append(ToolSubst('%dexter_regression_test', dexter_regression_test_build)) - return dependencies + """Configure substitutions for host platform and return list of dependencies""" + # Produce dexter path, lldb path, and combine into the %dexter substitution + # for running a test. + dexter_path = os.path.join( + config.cross_project_tests_src_root, "debuginfo-tests", "dexter", "dexter.py" + ) + dexter_test_cmd = '"{}" "{}" test'.format(sys.executable, dexter_path) + if lldb_path is not None: + dexter_test_cmd += ' --lldb-executable "{}"'.format(lldb_path) + tools.append(ToolSubst("%dexter", dexter_test_cmd)) + + # For testing other bits of dexter that aren't under the "test" subcommand, + # have a %dexter_base substitution. + dexter_base_cmd = '"{}" "{}"'.format(sys.executable, dexter_path) + tools.append(ToolSubst("%dexter_base", dexter_base_cmd)) + + # Set up commands for DexTer regression tests. + # Builder, debugger, optimisation level and several other flags differ + # depending on whether we're running a unix like or windows os. + if platform.system() == "Windows": + # The Windows builder script uses lld. + dependencies = ["clang", "lld-link"] + dexter_regression_test_builder = "clang-cl_vs2015" + dexter_regression_test_debugger = "dbgeng" + dexter_regression_test_cflags = "/Zi /Od" + dexter_regression_test_ldflags = "/Zi" + else: + # Use lldb as the debugger on non-Windows platforms. + dependencies = ["clang", "lldb"] + dexter_regression_test_builder = "clang" + dexter_regression_test_debugger = "lldb" + dexter_regression_test_cflags = "-O0 -glldb" + dexter_regression_test_ldflags = "" + + tools.append( + ToolSubst("%dexter_regression_test_builder", dexter_regression_test_builder) + ) + tools.append( + ToolSubst("%dexter_regression_test_debugger", dexter_regression_test_debugger) + ) + tools.append( + ToolSubst("%dexter_regression_test_cflags", dexter_regression_test_cflags) + ) + tools.append( + ToolSubst("%dexter_regression_test_ldflags", dexter_regression_test_cflags) + ) + + # Typical command would take the form: + # ./path_to_py/python.exe ./path_to_dex/dexter.py test --fail-lt 1.0 -w --builder clang --debugger lldb --cflags '-O0 -g' + # Exclude build flags for %dexter_regression_base. + dexter_regression_test_base = " ".join( + # "python", "dexter.py", test, fail_mode, builder, debugger, cflags, ldflags + [ + '"{}"'.format(sys.executable), + '"{}"'.format(dexter_path), + "test", + "--fail-lt 1.0 -w", + "--debugger", + dexter_regression_test_debugger, + ] + ) + tools.append(ToolSubst("%dexter_regression_base", dexter_regression_test_base)) + + # Include build flags for %dexter_regression_test. + dexter_regression_test_build = " ".join( + [ + dexter_regression_test_base, + "--builder", + dexter_regression_test_builder, + '--cflags "', + dexter_regression_test_cflags + '"', + '--ldflags "', + dexter_regression_test_ldflags + '"', + ] + ) + tools.append(ToolSubst("%dexter_regression_test", dexter_regression_test_build)) + return dependencies + def add_host_triple(clang): - return '{} --target={}'.format(clang, config.host_triple) + return "{} --target={}".format(clang, config.host_triple) + # The set of arches we can build. targets = set(config.targets_to_build) # Add aliases to the target set. -if 'AArch64' in targets: - targets.add('arm64') -if 'ARM' in config.targets_to_build: - targets.add('thumbv7') +if "AArch64" in targets: + targets.add("arm64") +if "ARM" in config.targets_to_build: + targets.add("thumbv7") + def can_target_host(): - # Check if the targets set contains anything that looks like our host arch. - # The arch name in the triple and targets set may be spelled differently - # (e.g. x86 vs X86). - return any(config.host_triple.lower().startswith(x.lower()) - for x in targets) + # Check if the targets set contains anything that looks like our host arch. + # The arch name in the triple and targets set may be spelled differently + # (e.g. x86 vs X86). + return any(config.host_triple.lower().startswith(x.lower()) for x in targets) + # Dexter tests run on the host machine. If the host arch is supported add # 'dexter' as an available feature and force the dexter tests to use the host # triple. if can_target_host(): - if config.host_triple != config.target_triple: - print('Forcing dexter tests to use host triple {}.'.format(config.host_triple)) - dependencies = configure_dexter_substitutions() - if all(d in config.available_features for d in dependencies): - config.available_features.add('dexter') - llvm_config.with_environment('PATHTOCLANG', - add_host_triple(llvm_config.config.clang)) - llvm_config.with_environment('PATHTOCLANGPP', - add_host_triple(llvm_config.use_llvm_tool('clang++'))) - llvm_config.with_environment('PATHTOCLANGCL', - add_host_triple(llvm_config.use_llvm_tool('clang-cl'))) + if config.host_triple != config.target_triple: + print("Forcing dexter tests to use host triple {}.".format(config.host_triple)) + dependencies = configure_dexter_substitutions() + if all(d in config.available_features for d in dependencies): + config.available_features.add("dexter") + llvm_config.with_environment( + "PATHTOCLANG", add_host_triple(llvm_config.config.clang) + ) + llvm_config.with_environment( + "PATHTOCLANGPP", add_host_triple(llvm_config.use_llvm_tool("clang++")) + ) + llvm_config.with_environment( + "PATHTOCLANGCL", add_host_triple(llvm_config.use_llvm_tool("clang-cl")) + ) else: - print('Host triple {} not supported. Skipping dexter tests in the ' - 'debuginfo-tests project.'.format(config.host_triple)) + print( + "Host triple {} not supported. Skipping dexter tests in the " + "debuginfo-tests project.".format(config.host_triple) + ) tool_dirs = [config.llvm_tools_dir] @@ -193,47 +230,54 @@ llvm_config.add_tool_substitutions(tools, tool_dirs) lit.util.usePlatformSdkOnDarwin(config, lit_config) -if platform.system() == 'Darwin': - xcode_lldb_vers = subprocess.check_output(['xcrun', 'lldb', '--version']).decode("utf-8") - match = re.search('lldb-(\d+)', xcode_lldb_vers) +if platform.system() == "Darwin": + xcode_lldb_vers = subprocess.check_output(["xcrun", "lldb", "--version"]).decode( + "utf-8" + ) + match = re.search("lldb-(\d+)", xcode_lldb_vers) if match: apple_lldb_vers = int(match.group(1)) if apple_lldb_vers < 1000: - config.available_features.add('apple-lldb-pre-1000') + config.available_features.add("apple-lldb-pre-1000") + def get_gdb_version_string(): - """Return gdb's version string, or None if gdb cannot be found or the - --version output is formatted unexpectedly. - """ - # See if we can get a gdb version, e.g. - # $ gdb --version - # GNU gdb (GDB) 10.2 - # ...More stuff... - try: - gdb_vers_lines = subprocess.check_output(['gdb', '--version']).decode().splitlines() - except: - return None # We coudln't find gdb or something went wrong running it. - if len(gdb_vers_lines) < 1: - print("Unkown GDB version format (too few lines)", file=sys.stderr) - return None - match = re.search('GNU gdb \(.*?\) ((\d|\.)+)', gdb_vers_lines[0].strip()) - if match is None: - print(f"Unkown GDB version format: {gdb_vers_lines[0]}", file=sys.stderr) - return None - return match.group(1) + """Return gdb's version string, or None if gdb cannot be found or the + --version output is formatted unexpectedly. + """ + # See if we can get a gdb version, e.g. + # $ gdb --version + # GNU gdb (GDB) 10.2 + # ...More stuff... + try: + gdb_vers_lines = ( + subprocess.check_output(["gdb", "--version"]).decode().splitlines() + ) + except: + return None # We coudln't find gdb or something went wrong running it. + if len(gdb_vers_lines) < 1: + print("Unkown GDB version format (too few lines)", file=sys.stderr) + return None + match = re.search("GNU gdb \(.*?\) ((\d|\.)+)", gdb_vers_lines[0].strip()) + if match is None: + print(f"Unkown GDB version format: {gdb_vers_lines[0]}", file=sys.stderr) + return None + return match.group(1) + def get_clang_default_dwarf_version_string(triple): - """Return the default dwarf version string for clang on this (host) platform - or None if we can't work it out. - """ - # Get the flags passed by the driver and look for -dwarf-version. - cmd = f'{llvm_config.use_llvm_tool("clang")} -g -xc -c - -v -### --target={triple}' - stderr = subprocess.run(cmd.split(), stderr=subprocess.PIPE).stderr.decode() - match = re.search('-dwarf-version=(\d+)', stderr) - if match is None: - print("Cannot determine default dwarf version", file=sys.stderr) - return None - return match.group(1) + """Return the default dwarf version string for clang on this (host) platform + or None if we can't work it out. + """ + # Get the flags passed by the driver and look for -dwarf-version. + cmd = f'{llvm_config.use_llvm_tool("clang")} -g -xc -c - -v -### --target={triple}' + stderr = subprocess.run(cmd.split(), stderr=subprocess.PIPE).stderr.decode() + match = re.search("-dwarf-version=(\d+)", stderr) + if match is None: + print("Cannot determine default dwarf version", file=sys.stderr) + return None + return match.group(1) + # Some cross-project-tests use gdb, but not all versions of gdb are compatible # with clang's dwarf. Add feature `gdb-clang-incompatibility` to signal that @@ -242,17 +286,18 @@ def get_clang_default_dwarf_version_string(triple): dwarf_version_string = get_clang_default_dwarf_version_string(config.host_triple) gdb_version_string = get_gdb_version_string() if dwarf_version_string and gdb_version_string: - if int(dwarf_version_string) >= 5: - if LooseVersion(gdb_version_string) < LooseVersion('10.1'): - # Example for llgdb-tests, which use lldb on darwin but gdb elsewhere: - # XFAIL: !system-darwin && gdb-clang-incompatibility - config.available_features.add('gdb-clang-incompatibility') - print("XFAIL some tests: use gdb version >= 10.1 to restore test coverage", file=sys.stderr) - -llvm_config.feature_config( - [('--build-mode', {'Debug|RelWithDebInfo': 'debug-info'})] -) + if int(dwarf_version_string) >= 5: + if LooseVersion(gdb_version_string) < LooseVersion("10.1"): + # Example for llgdb-tests, which use lldb on darwin but gdb elsewhere: + # XFAIL: !system-darwin && gdb-clang-incompatibility + config.available_features.add("gdb-clang-incompatibility") + print( + "XFAIL some tests: use gdb version >= 10.1 to restore test coverage", + file=sys.stderr, + ) + +llvm_config.feature_config([("--build-mode", {"Debug|RelWithDebInfo": "debug-info"})]) # Allow 'REQUIRES: XXX-registered-target' in tests. for arch in config.targets_to_build: - config.available_features.add(arch.lower() + '-registered-target') + config.available_features.add(arch.lower() + "-registered-target") |