diff --git a/explain/explain.py b/explain/explain.py index fa84fb9..88ebbfa 100644 --- a/explain/explain.py +++ b/explain/explain.py @@ -453,6 +453,7 @@ def _call_handler(_): @report @source_context @collect_output + @revert_time_on_failure @chain_of_thought def tool_ugo_sender(self) -> None: """ @@ -489,6 +490,7 @@ def tool_reverse_next(self) -> None: @report @source_context @collect_output + @revert_time_on_failure @chain_of_thought def tool_reverse_finish(self, target_fn: str) -> None: """ @@ -499,29 +501,24 @@ def tool_reverse_finish(self, target_fn: str) -> None: which will improve performance. On success it will pop at least one stack frame, even in recursive calls. On failure it will - return to the originally-selected stack frame. + return to the original point in time. Params: target_fn: the function you want to reverse-finish back to. This must be present in the current backtrace or the command will fail. """ - orig_frame = gdbutils.selected_frame() - try: - frame = orig_frame.older() - while frame and frame.name() != target_fn: - frame = frame.older() + frame = gdbutils.selected_frame().older() + while frame and frame.name() != target_fn: + frame = frame.older() - if not frame: - raise Exception("No such frame in current backtrace.") + if not frame: + raise Exception("No such frame in current backtrace.") - # Finish out into the specified frame. - frame.newer().select() - self.udb.execution.reverse_finish(cmd="reverse-finish") + # Finish out into the specified frame. + frame.newer().select() + self.udb.execution.reverse_finish(cmd="reverse-finish") - assert gdbutils.selected_frame().name() == target_fn - except: - orig_frame.select() - raise + assert gdbutils.selected_frame().name() == target_fn def _reverse_into_target_function(self, target_fn: str) -> str: """ @@ -595,20 +592,19 @@ def _reverse_into_target_function(self, target_fn: str) -> str: assert gdb.selected_frame().name() == target_fn func = gdb.selected_frame().function() - if func and func.type.target() != gdb.TYPE_CODE_VOID: + if func and func.type.target().code != gdb.TYPE_CODE_VOID: # Step further back to ensure we're at the return statement. with gdbutils.temporary_parameter("listsize", 1): while "return" not in gdbutils.execute_to_string("list"): + if target_start_bp.hit_count > 1: + # We've gone back to the start of the function without finding + # a return statement. This can happen with single-line functions. + break self.udb.execution.reverse_next(cmd="reverse-next") # Check we're still in the function we intended. assert gdb.selected_frame().name() == target_fn - # And that we've not gone back further than planned. - assert ( - target_start_bp.hit_count == 1 - ), "Unexpectedly reached the start of the target function." - if LOG_LEVEL == "DEBUG": print(f"_reverse_into_target_function internal messages:\n{collector.output}")