diff --git a/cross-project-tests/debuginfo-tests/dexter/Commands.md b/cross-project-tests/debuginfo-tests/dexter/Commands.md index a98261a500094..afec3e6bbd002 100644 --- a/cross-project-tests/debuginfo-tests/dexter/Commands.md +++ b/cross-project-tests/debuginfo-tests/dexter/Commands.md @@ -13,7 +13,8 @@ * [DexDeclareFile](Commands.md#DexDeclareFile) * [DexFinishTest](Commands.md#DexFinishTest) * [DexCommandLine](Commands.md#DexCommandLine) - +* [DexStepFunction](Commands.md#DexStepFunction) +* [DexContinue](Commands.md#DexContinue) --- ## DexExpectProgramState DexExpectProgramState(state [,**times]) @@ -377,3 +378,44 @@ line this command is found on. ### Heuristic [Deprecated] + + +--- +## DexStepFunction + DexStepFunction(function_name[, **hit_count=0]) + + Arg list: + function_name (str): function to step through. + hit_count (int): If provided, limit the number of times the command + triggers. + +### Description +NOTE: Only supported for DAP-based debuggers. + +This command controls stepping behaviour: Tell dexter to set a function +breakpoint and step through the function after hitting it. Composes well with +itself (you can may a callstack with multiple targets to step through) and +`DexContinue`. + +--- +## DexContinue + DexContinue(*[expr, *values], **from_line[, **to_line, **hit_count]) + + Arg list: + function_name (str): function to step through. + hit_count (int): If provided, limit the number of times the command + triggers. + +### Description +NOTE: Only supported for DAP-based debuggers. + +This command controls stepping behaviour: Tell dexter to set a breakpoint on +`from_line`. When it is hit and optionally '(expr) == (values[n])' is true, +optionally set a breakpoint on `to_line`. Then 'continue' (tell the debugger to +run freely until a breakpoint is hit). Composed with `DexStepFunction` this +lets you avoid stepping over certain regions (many loop iterations, for +example). Continue-ing off the end of a `DexStepFunction` is well defined; +stepping will resume in `DexStepFunction` targets deeper in the callstack. + +FIXME: hit_count should probably be inverted, like `DexLimitSteps`, to trigger +AFTER that many hits? diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexContinue.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexContinue.py new file mode 100644 index 0000000000000..113f34bedd131 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexContinue.py @@ -0,0 +1,55 @@ +# DExTer : Debugging Experience Tester +# ~~~~~~ ~ ~~ ~ ~~ +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +"""A Command that tells dexter to set a breakpoint which, after hitting, +signals that the debugger should 'continue' until another is hit. Continuing +out of a function being stepped through with DexStepFunction is well defined: +stepping will resume in other functions tracked further down the stacktrace. + +NOTE: Only supported for DAP-based debuggers. +""" + +from dex.command.CommandBase import CommandBase + + +class DexContinue(CommandBase): + def __init__(self, *args, **kwargs): + # DexContinue(*[expr, *values], **from_line[, **to_line, **hit_count]) + + # Optional positional args: expr, values. + if len(args) == 0: + self.expression = None + self.values = [] + elif len(args) == 1: + raise TypeError("expected 0 or at least 2 positional arguments") + else: + self.expression = args[0] + self.values = [str(arg) for arg in args[1:]] + + # Required keyword arg: from_line. + try: + self.from_line = kwargs.pop("from_line") + except: + raise TypeError("Missing from_line argument") + + # Optional conditional args: to_line, hit_count. + self.to_line = kwargs.pop("to_line", None) + self.hit_count = kwargs.pop("hit_count", None) + + if kwargs: + raise TypeError("unexpected named args: {}".format(", ".join(kwargs))) + super(DexContinue, self).__init__() + + def eval(self): + raise NotImplementedError("DexContinue commands cannot be evaled.") + + @staticmethod + def get_name(): + return __class__.__name__ + + @staticmethod + def get_subcommands() -> dict: + return None diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexStepFunction.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexStepFunction.py new file mode 100644 index 0000000000000..4b80de87a1bb2 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexStepFunction.py @@ -0,0 +1,38 @@ +# DExTer : Debugging Experience Tester +# ~~~~~~ ~ ~~ ~ ~~ +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +"""A Command that tells dexter to set a function breakpoint and step through +the function after hitting it. + +NOTE: Only supported for DAP-based debuggers. +""" + +from dex.command.CommandBase import CommandBase + + +class DexStepFunction(CommandBase): + def __init__(self, *args, **kwargs): + if len(args) < 1: + raise TypeError("expected 1 positional argument") + self.function = str(args[0]) + self.hit_count = kwargs.pop("hit_count", None) + if kwargs: + raise TypeError(f"unexpected named args: {', '.join(kwargs)}") + super(DexStepFunction, self).__init__() + + def eval(self): + raise NotImplementedError("DexStepFunction commands cannot be evaled.") + + def get_function(self): + return self.function + + @staticmethod + def get_name(): + return __class__.__name__ + + @staticmethod + def get_subcommands() -> dict: + return None