Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 17 additions & 21 deletions explain/explain.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"""
Expand Down Expand Up @@ -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:
"""
Expand All @@ -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:
"""
Expand Down Expand Up @@ -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}")

Expand Down