https://github.com/OCHyams updated https://github.com/llvm/llvm-project/pull/152721
>From 959301a6ac75360afec7b7f64b3b0830e9769a4f Mon Sep 17 00:00:00 2001 From: Orlando Cazalet-Hyams <orlando.hy...@sony.com> Date: Thu, 7 Aug 2025 16:53:56 +0100 Subject: [PATCH 1/4] [Dexter] Implement DexStepFunction and DexContinue Adding together in a single commit as their implementations are linked. Only supported for DAP debuggers. These two commands make it a bit easier to drive dexter: DexStepFunction tells dexter to step-next though a function and DexContinue tells dexter to continue (run free) from one breakpoint to another within the current DexStepFunction function. When the DexStepFunction function breakpoint is triggered, dexter sets an instruction breakpoint at the return-address. This is so that stepping can resume in any other DexStepFunctions deeps in the call stack. --- .../dexter/dex/command/ParseCommand.py | 4 + .../ConditionalController.py | 241 +++++++++++++++--- .../dexter/dex/tools/test/Tool.py | 3 +- .../commands/control/dex-continue.cpp | 64 +++++ .../commands/control/dex_step_function.cpp | 39 +++ 5 files changed, 313 insertions(+), 38 deletions(-) create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex-continue.cpp create mode 100644 cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp 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 4496fdf3cb0e8..1a30e0e8f3753 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py @@ -35,6 +35,8 @@ from dex.command.commands.DexFinishTest import DexFinishTest from dex.command.commands.DexUnreachable import DexUnreachable from dex.command.commands.DexWatch import DexWatch +from dex.command.commands.DexStepFunction import DexStepFunction +from dex.command.commands.DexContinue import DexContinue from dex.utils import Timer from dex.utils.Exceptions import CommandParseError, DebuggerException @@ -59,6 +61,8 @@ def _get_valid_commands(): DexFinishTest.get_name(): DexFinishTest, DexUnreachable.get_name(): DexUnreachable, DexWatch.get_name(): DexWatch, + DexStepFunction.get_name(): DexStepFunction, + DexContinue.get_name(): DexContinue, } 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 c53f1419ee138..87e86ae2d9395 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 @@ -22,7 +22,7 @@ from dex.debugger.DebuggerBase import DebuggerBase from dex.utils.Exceptions import DebuggerException from dex.utils.Timeout import Timeout - +from dex.dextIR import LocIR class BreakpointRange: """A range of breakpoints and a set of conditions. @@ -51,6 +51,9 @@ def __init__( values: list, hit_count: int, finish_on_remove: bool, + is_continue: bool = False, + function: str | None = None, + addr: str | None = None, ): self.expression = expression self.path = path @@ -60,6 +63,72 @@ def __init__( self.max_hit_count = hit_count self.current_hit_count = 0 self.finish_on_remove = finish_on_remove + self.is_continue = is_continue + self.function = function + self.addr = addr + + def limit_steps( + expression: str, + path: str, + range_from: int, + range_to: int, + values: list, + hit_count: int, + ): + return BreakpointRange( + expression, + path, + range_from, + range_to, + values, + hit_count, + False, + ) + + def finish_test( + expression: str, path: str, on_line: int, values: list, hit_count: int + ): + return BreakpointRange( + expression, + path, + on_line, + on_line, + values, + hit_count, + True, + ) + + def continue_from_to( + expression: str, + path: str, + from_line: int, + to_line: int, + values: list, + hit_count: int, + ): + return BreakpointRange( + expression, + path, + from_line, + to_line, + values, + hit_count, + finish_on_remove=False, + is_continue=True, + ) + + def step_function(function: str, path: str, hit_count: int): + return BreakpointRange( + None, + path, + None, + None, + None, + hit_count, + finish_on_remove=False, + is_continue=False, + function=function, + ) def has_conditions(self): return self.expression is not None @@ -96,36 +165,40 @@ def __init__(self, context, step_collection): def _build_bp_ranges(self): commands = self.step_collection.commands self._bp_ranges = [] - try: - 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, - ) - self._bp_ranges.append(bpr) - except KeyError: + + cond_controller_cmds = ["DexLimitSteps", "DexStepFunction", "DexContinue"] + if not any(c in commands for c in cond_controller_cmds): raise DebuggerException( - "Missing DexLimitSteps commands, cannot conditionally step." + f"No conditional commands {cond_controller_cmds}, cannot conditionally step." ) + + if "DexLimitSteps" in commands: + for c in commands["DexLimitSteps"]: + bpr = BreakpointRange.limit_steps( + c.expression, + c.path, + c.from_line, + c.to_line, + c.values, + c.hit_count, + ) + self._bp_ranges.append(bpr) 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, + for c in commands["DexFinishTest"]: + bpr = BreakpointRange.finish_test( + c.expression, c.path, c.on_line, c.values, c.hit_count + 1 ) self._bp_ranges.append(bpr) + if "DexContinue" in commands: + for c in commands["DexContinue"]: + bpr = BreakpointRange.continue_from_to( + c.expression, c.path, c.from_line, c.to_line, c.values, c.hit_count + ) + self._bp_ranges.append(bpr) + if "DexStepFunction" in commands: + for c in commands["DexStepFunction"]: + bpr = BreakpointRange.step_function(c.expression, c.path, c.hit_count) + self._bp_ranges.append(bpr) def _set_leading_bps(self): # Set a leading breakpoint for each BreakpointRange, building a @@ -138,6 +211,12 @@ def _set_leading_bps(self): bpr.path, bpr.range_from, cond_expr ) self._leading_bp_handles[id] = bpr + elif bpr.function is not None: + id = self.debugger.add_function_breakpoint(bpr.function) + self.context.logger.warning( + f"Set leading breakpoint {id} at {bpr.function}" + ) + self._leading_bp_handles[id] = bpr else: # Add an unconditional breakpoint. id = self.debugger.add_breakpoint(bpr.path, bpr.range_from) @@ -163,6 +242,9 @@ def _run_debugger_custom(self, cmdline): timed_out = False total_timeout = Timeout(self.context.options.timeout_total) + step_function_backtraces: list[list[str]] = [] + self.instr_bp_ids = set() + while not self.debugger.is_finished: breakpoint_timeout = Timeout(self.context.options.timeout_breakpoint) while self.debugger.is_running and not timed_out: @@ -185,21 +267,26 @@ def _run_debugger_custom(self, cmdline): break step_info = self.debugger.get_step_info(self._watches, self._step_index) + backtrace = None if step_info.current_frame: self._step_index += 1 - update_step_watches( - step_info, self._watches, self.step_collection.commands - ) - self.step_collection.new_step(self.context, step_info) + backtrace = list([f.function for f in step_info.frames]) + log_step = False + debugger_continue = False bp_to_delete = [] for bp_id in self.debugger.get_triggered_breakpoint_ids(): try: # See if this is one of our leading breakpoints. bpr = self._leading_bp_handles[bp_id] + log_step = True except KeyError: # This is a trailing bp. Mark it for removal. bp_to_delete.append(bp_id) + if bp_id in self.instr_bp_ids: + self.instr_bp_ids.remove(bp_id) + else: + log_step = True continue bpr.add_hit() @@ -208,17 +295,97 @@ def _run_debugger_custom(self, cmdline): exit_desired = True bp_to_delete.append(bp_id) del self._leading_bp_handles[bp_id] - # Add a range of trailing breakpoints covering the lines - # requested in the DexLimitSteps command. Ignore first line as - # that's covered by the leading bp we just hit and include the - # final line. - for line in range(bpr.range_from + 1, bpr.range_to + 1): - self.debugger.add_breakpoint(bpr.path, line) + + if bpr.function is not None: + if step_info.frames: + # Add this backtrace to the stack. While the current + # backtrace matches the top of the stack we'll step, + # and while there's a backtrace in the stack that + # is a subset of the current backtrack we'll step-out. + if ( + len(step_function_backtraces) == 0 + or backtrace != step_function_backtraces[-1] + ): + step_function_backtraces.append(backtrace) + + # Add an address breakpoint so we don't fall out + # the end of nested DexStepFunctions with a DexContinue. + addr = self.debugger.get_pc(frame_idx=1) + instr_id = self.debugger.add_instruction_breakpoint(addr) + # Note the breakpoint so we don't log the source location + # it in the trace later. + self.instr_bp_ids.add(instr_id) + + elif bpr.is_continue: + debugger_continue = True + self.debugger.add_breakpoint(bpr.path, bpr.range_to) + + else: + # Add a range of trailing breakpoints covering the lines + # requested in the DexLimitSteps command. Ignore first line as + # that's covered by the leading bp we just hit and include the + # final line. + for line in range(bpr.range_from + 1, bpr.range_to + 1): + id = self.debugger.add_breakpoint(bpr.path, line) + self.context.logger.warning( + f"Set trailing breakpoint {id} at {line}" + ) # Remove any trailing or expired leading breakpoints we just hit. self.debugger.delete_breakpoints(bp_to_delete) + debugger_next = False + debugger_out = False + if ( + not debugger_continue + and step_info.current_frame + and step_info.frames + ): + while len(step_function_backtraces) > 0: + match_subtrace = False # Backtrace contains a target trace. + match_trace = False # Backtrace matches top of target stack. + if len(backtrace) >= len(step_function_backtraces[-1]): + match_subtrace = True + match_trace = len(backtrace) == len( + step_function_backtraces[-1] + ) + for i, f in enumerate(reversed(step_function_backtraces[-1])): + if backtrace[-1 - i] != f: + match_subtrace = False + match_trace = False + break + + if match_trace: + # We want to step through this function; do so and + # log the steps in the step trace. + debugger_next = True + log_step = True + break + elif match_subtrace: + # There's a function we care about buried in the + # current backtrace. Step-out until we get to it. + debugger_out = True + break + else: + # Drop backtraces that are not match_subtraces of the current + # backtrace; the functions we wanted to step through + # there are no longer reachable. + step_function_backtraces.pop() + + + if log_step and step_info.current_frame: + # Record the step. + update_step_watches( + step_info, self._watches, self.step_collection.commands + ) + self.step_collection.new_step(self.context, step_info) + if exit_desired: break - self.debugger.go() + elif debugger_next: + self.debugger.step_next() + elif debugger_out: + self.debugger.step_out() + else: + self.debugger.go() time.sleep(self._pause_between_steps) 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 c366062cec7a9..693c05b97af7c 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 @@ -121,7 +121,8 @@ def _init_debugger_controller(self): self.context.options.source_files.extend(list(new_source_files)) - if "DexLimitSteps" in step_collection.commands: + cond_controller_cmds = ["DexLimitSteps", "DexStepFunction", "DexContinue"] + if any(c in step_collection.commands for c in cond_controller_cmds): debugger_controller = ConditionalController(self.context, step_collection) else: debugger_controller = DefaultController(self.context, step_collection) diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex-continue.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex-continue.cpp new file mode 100644 index 0000000000000..bb51b85cbe992 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex-continue.cpp @@ -0,0 +1,64 @@ +// Purpose: +// Test \DexStepFunction usage with \DexContinue. Continuing out of `c` +// should result in stepping resuming in `a` (check there's no issue when +// `b` is inlined). Then continuing out of `a` should run on to `f` where +// stepping resumes again. Stepping out of `f` into `main`, run free +// again until the program exits. +// +// This command is only implemented for debuggers with DAP support. +// UNSUPPORTED: system-windows +// +// RUN: %dexter_regression_test_cxx_build %s -o %t +// RUN: %dexter_regression_test_run -v --binary %t -- %s 2>&1 | FileCheck %s + +int g = 0; +int c(int) { + ++g; + ++g; + ++g; + ++g; + ++g; + return 0; +} + +__attribute__((always_inline)) +int b(int) { + ++g; + return c(g); +} + +int a(int) { + ++g; + b(g); + ++g; + return g; +} + +void f() { + ++g; +} + +int main() { + int x = a(g); + f(); + return x; +} + +// DexStepFunction('c') +// DexContinue(from_line=17, to_line=19) +// DexContinue(from_line=20) +// DexStepFunction('a') +// DexContinue(from_line=33) +// DexStepFunction('f') + +// CHECK: ## BEGIN ## +// CHECK-NEXT: . [0, "a(int)", "{{.*}}dex-continue.cpp", 31, 3, "StopReason.BREAKPOINT", "StepKind.FUNC", []] +// CHECK-NEXT: . [1, "a(int)", "{{.*}}dex-continue.cpp", 32, 5, "StopReason.STEP", "StepKind.VERTICAL_FORWARD", []] +// CHECK-NEXT: . . . [2, "c(int)", "{{.*}}dex-continue.cpp", 16, 3, "StopReason.BREAKPOINT", "StepKind.FUNC", []] +// CHECK-NEXT: . . . [3, "c(int)", "{{.*}}dex-continue.cpp", 17, 3, "StopReason.BREAKPOINT", "StepKind.VERTICAL_FORWARD", []] +// CHECK-NEXT: . . . [4, "c(int)", "{{.*}}dex-continue.cpp", 19, 3, "StopReason.BREAKPOINT", "StepKind.VERTICAL_FORWARD", []] +// CHECK-NEXT: . . . [5, "c(int)", "{{.*}}dex-continue.cpp", 20, 3, "StopReason.BREAKPOINT", "StepKind.VERTICAL_FORWARD", []] +// CHECK-NEXT: . [6, "a(int)", "{{.*}}dex-continue.cpp", 33, 3, "StopReason.BREAKPOINT", "StepKind.VERTICAL_FORWARD", []] +// CHECK-NEXT: . [8, "f()", "{{.*}}dex-continue.cpp", 38, 3, "StopReason.BREAKPOINT", "StepKind.VERTICAL_FORWARD", []] +// CHECK-NEXT: . [9, "f()", "{{.*}}dex-continue.cpp", 39, 1, "StopReason.STEP", "StepKind.VERTICAL_FORWARD", []] +// CHECK-NEXT: ## END (9 steps) ## diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp new file mode 100644 index 0000000000000..e7e666d0e0dc4 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/control/dex_step_function.cpp @@ -0,0 +1,39 @@ +// Purpose: +// \DexStepFunction smoke test. Only steps in a and c should be logged. +// +// This command is only implemented for debuggers with DAP support. +// UNSUPPORTED: system-windows +// +// RUN: %dexter_regression_test_cxx_build %s -o %t +// RUN: %dexter_regression_test_run -v --binary %t -- %s 2>&1 | FileCheck %s + +int g = 0; +int c(int) { + ++g; + return 0; +} + +int b(int) { + ++g; + return c(g); +} + +int a(int) { + ++g; + return b(g); +} + +int main() { + return a(g); +} + +// DexStepFunction('a') +// DexStepFunction('c') + +// CHECK: ## BEGIN ## +// CHECK-NEXT:. [0, "a(int)", "{{.*}}dex_step_function.cpp", 22, 3, "StopReason.BREAKPOINT", "StepKind.FUNC", []] +// CHECK-NEXT:. [1, "a(int)", "{{.*}}dex_step_function.cpp", 23, 12, "StopReason.STEP", "StepKind.VERTICAL_FORWARD", []] +// CHECK-NEXT:. . . [2, "c(int)", "{{.*}}dex_step_function.cpp", 12, 3, "StopReason.BREAKPOINT", "StepKind.FUNC", []] +// CHECK-NEXT:. . . [3, "c(int)", "{{.*}}dex_step_function.cpp", 13, 3, "StopReason.STEP", "StepKind.VERTICAL_FORWARD", []] +// CHECK-NEXT:. [6, "a(int)", "{{.*}}dex_step_function.cpp", 23, 3, "StopReason.STEP", "StepKind.HORIZONTAL_BACKWARD", []] +// CHECK-NEXT: ## END (5 steps) ## >From 3d15954e55d5c559c6a4fb1e8e33d41dffbeaa40 Mon Sep 17 00:00:00 2001 From: Orlando Cazalet-Hyams <orlando.hy...@sony.com> Date: Tue, 12 Aug 2025 11:51:40 +0100 Subject: [PATCH 2/4] use get_function method rather than accessing a (renamed) field --- .../dex/debugger/DebuggerControllers/ConditionalController.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 87e86ae2d9395..5df6585ddc749 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 @@ -197,7 +197,7 @@ def _build_bp_ranges(self): self._bp_ranges.append(bpr) if "DexStepFunction" in commands: for c in commands["DexStepFunction"]: - bpr = BreakpointRange.step_function(c.expression, c.path, c.hit_count) + bpr = BreakpointRange.step_function(c.get_function(), c.path, c.hit_count) self._bp_ranges.append(bpr) def _set_leading_bps(self): >From 0605b1e550c37cf017d9b1caafb67ac95044013f Mon Sep 17 00:00:00 2001 From: Orlando Cazalet-Hyams <orlando.hy...@sony.com> Date: Tue, 12 Aug 2025 12:01:39 +0100 Subject: [PATCH 3/4] fmt --- .../DebuggerControllers/ConditionalController.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) 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 5df6585ddc749..bdc3cc3ce9f21 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 @@ -197,7 +197,9 @@ def _build_bp_ranges(self): self._bp_ranges.append(bpr) if "DexStepFunction" in commands: for c in commands["DexStepFunction"]: - bpr = BreakpointRange.step_function(c.get_function(), c.path, c.hit_count) + bpr = BreakpointRange.step_function( + c.get_function(), c.path, c.hit_count + ) self._bp_ranges.append(bpr) def _set_leading_bps(self): @@ -336,14 +338,10 @@ def _run_debugger_custom(self, cmdline): debugger_next = False debugger_out = False - if ( - not debugger_continue - and step_info.current_frame - and step_info.frames - ): + if not debugger_continue and step_info.current_frame and step_info.frames: while len(step_function_backtraces) > 0: - match_subtrace = False # Backtrace contains a target trace. - match_trace = False # Backtrace matches top of target stack. + match_subtrace = False # Backtrace contains a target trace. + match_trace = False # Backtrace matches top of target stack. if len(backtrace) >= len(step_function_backtraces[-1]): match_subtrace = True match_trace = len(backtrace) == len( @@ -372,7 +370,6 @@ def _run_debugger_custom(self, cmdline): # there are no longer reachable. step_function_backtraces.pop() - if log_step and step_info.current_frame: # Record the step. update_step_watches( >From 40e394e35bc057eb0044983ef2716a9afb2a97a3 Mon Sep 17 00:00:00 2001 From: Orlando Cazalet-Hyams <orlando.hy...@sony.com> Date: Wed, 27 Aug 2025 15:16:00 +0100 Subject: [PATCH 4/4] address feedback --- .../ConditionalController.py | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) 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 bdc3cc3ce9f21..c32781597841e 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 @@ -215,9 +215,6 @@ def _set_leading_bps(self): self._leading_bp_handles[id] = bpr elif bpr.function is not None: id = self.debugger.add_function_breakpoint(bpr.function) - self.context.logger.warning( - f"Set leading breakpoint {id} at {bpr.function}" - ) self._leading_bp_handles[id] = bpr else: # Add an unconditional breakpoint. @@ -272,23 +269,23 @@ def _run_debugger_custom(self, cmdline): backtrace = None if step_info.current_frame: self._step_index += 1 - backtrace = list([f.function for f in step_info.frames]) + backtrace = [f.function for f in step_info.frames] - log_step = False + record_step = False debugger_continue = False bp_to_delete = [] for bp_id in self.debugger.get_triggered_breakpoint_ids(): try: # See if this is one of our leading breakpoints. bpr = self._leading_bp_handles[bp_id] - log_step = True + record_step = True except KeyError: # This is a trailing bp. Mark it for removal. bp_to_delete.append(bp_id) if bp_id in self.instr_bp_ids: self.instr_bp_ids.remove(bp_id) else: - log_step = True + record_step = True continue bpr.add_hit() @@ -303,7 +300,7 @@ def _run_debugger_custom(self, cmdline): # Add this backtrace to the stack. While the current # backtrace matches the top of the stack we'll step, # and while there's a backtrace in the stack that - # is a subset of the current backtrack we'll step-out. + # is a subset of the current backtrace we'll step-out. if ( len(step_function_backtraces) == 0 or backtrace != step_function_backtraces[-1] @@ -320,7 +317,8 @@ def _run_debugger_custom(self, cmdline): elif bpr.is_continue: debugger_continue = True - self.debugger.add_breakpoint(bpr.path, bpr.range_to) + if bpr.range_to != None: + self.debugger.add_breakpoint(bpr.path, bpr.range_to) else: # Add a range of trailing breakpoints covering the lines @@ -342,22 +340,25 @@ def _run_debugger_custom(self, cmdline): while len(step_function_backtraces) > 0: match_subtrace = False # Backtrace contains a target trace. match_trace = False # Backtrace matches top of target stack. - if len(backtrace) >= len(step_function_backtraces[-1]): - match_subtrace = True - match_trace = len(backtrace) == len( - step_function_backtraces[-1] + + # The top of the step_function_backtraces stack contains a + # backtrace that we want to step through. Check if the + # current backtrace ("backtrace") either matches that trace + # or otherwise contains it. + target_backtrace = step_function_backtraces[-1] + if len(backtrace) >= len(target_backtrace): + match_trace = len(backtrace) == len(target_backtrace) + # Check if backtrace contains target_backtrace, matching + # from the end (bottom of call stack) backwards. + match_subtrace = ( + backtrace[-len(target_backtrace) :] == target_backtrace ) - for i, f in enumerate(reversed(step_function_backtraces[-1])): - if backtrace[-1 - i] != f: - match_subtrace = False - match_trace = False - break if match_trace: # We want to step through this function; do so and # log the steps in the step trace. debugger_next = True - log_step = True + record_step = True break elif match_subtrace: # There's a function we care about buried in the @@ -370,7 +371,7 @@ def _run_debugger_custom(self, cmdline): # there are no longer reachable. step_function_backtraces.pop() - if log_step and step_info.current_frame: + if record_step and step_info.current_frame: # Record the step. update_step_watches( step_info, self._watches, self.step_collection.commands _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits