Skip to content

Commit e80abfd

Browse files
committed
[lldb] Implement CLI support for reverse-continue
This introduces the options "-F/--forward" and "-R/--reverse" to `process continue`. These only work if you're running with a gdbserver backend that supports reverse execution, such as rr. For testing we rely on the fake reverse- execution functionality in `lldbreverse.py`.
1 parent 58551fa commit e80abfd

File tree

7 files changed

+154
-3
lines changed

7 files changed

+154
-3
lines changed

lldb/source/Commands/CommandObjectProcess.cpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,13 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
468468
case'b':
469469
m_run_to_bkpt_args.AppendArgument(option_arg);
470470
m_any_bkpts_specified = true;
471-
break;
471+
break;
472+
case'F':
473+
m_base_direction = lldb::RunDirection::eRunForward;
474+
break;
475+
case'R':
476+
m_base_direction = lldb::RunDirection::eRunReverse;
477+
break;
472478
default:
473479
llvm_unreachable("Unimplemented option");
474480
}
@@ -479,6 +485,7 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
479485
m_ignore = 0;
480486
m_run_to_bkpt_args.Clear();
481487
m_any_bkpts_specified = false;
488+
m_base_direction = std::nullopt;
482489
}
483490

484491
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -488,6 +495,7 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
488495
uint32_t m_ignore = 0;
489496
Args m_run_to_bkpt_args;
490497
bool m_any_bkpts_specified = false;
498+
std::optional<lldb::RunDirection> m_base_direction;
491499
};
492500

493501
voidDoExecute(Args &command, CommandReturnObject &result) override {
@@ -654,6 +662,9 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
654662
}
655663
}
656664

665+
if (m_options.m_base_direction.has_value())
666+
process->SetBaseDirection(*m_options.m_base_direction);
667+
657668
constuint32_t iohandler_id = process->GetIOHandlerID();
658669

659670
StreamString stream;

lldb/source/Commands/Options.td

+6-2
Original file line numberDiff line numberDiff line change
@@ -737,13 +737,17 @@ let Command = "process attach" in {
737737
}
738738

739739
let Command = "process continue" in {
740-
def process_continue_ignore_count : Option<"ignore-count", "i">, Group<1>,
740+
def process_continue_ignore_count : Option<"ignore-count", "i">, Groups<[1,2]>,
741741
Arg<"UnsignedInteger">, Desc<"Ignore <N> crossings of the breakpoint (if it"
742742
" exists) for the currently selected thread.">;
743-
def process_continue_run_to_bkpt : Option<"continue-to-bkpt", "b">, Group<2>,
743+
def process_continue_run_to_bkpt : Option<"continue-to-bkpt", "b">, Groups<[3,4]>,
744744
Arg<"BreakpointIDRange">, Desc<"Specify a breakpoint to continue to, temporarily "
745745
"ignoring other breakpoints. Can be specified more than once. "
746746
"The continue action will be done synchronously if this option is specified.">;
747+
def thread_continue_forward : Option<"forward", "F">, Groups<[1,3]>,
748+
Desc<"Set the direction to forward before continuing.">;
749+
def thread_continue_reverse : Option<"reverse", "R">, Groups<[2,4]>,
750+
Desc<"Set the direction to reverse before continuing.">;
747751
}
748752

749753
let Command = "process detach" in {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""
2+
Test the "process continue --reverse" and "--forward" options.
3+
"""
4+
5+
6+
importlldb
7+
fromlldbsuite.test.lldbtestimport*
8+
fromlldbsuite.test.decoratorsimport*
9+
fromlldbsuite.test.gdbclientutilsimport*
10+
fromlldbsuite.test.lldbreverseimportReverseTestBase
11+
fromlldbsuite.testimportlldbutil
12+
13+
14+
classTestReverseContinue(ReverseTestBase):
15+
@skipIfRemote
16+
deftest_reverse_continue(self):
17+
target, _, _=self.setup_recording()
18+
19+
# Set breakpoint and reverse-continue
20+
trigger_bkpt=target.BreakpointCreateByName("trigger_breakpoint", None)
21+
self.assertTrue(trigger_bkpt.GetNumLocations() >0)
22+
self.expect(
23+
"process continue --reverse",
24+
substrs=["stop reason = breakpoint {0}.1".format(trigger_bkpt.GetID())],
25+
)
26+
# `process continue` should preserve current base direction.
27+
self.expect(
28+
"process continue",
29+
STOPPED_DUE_TO_HISTORY_BOUNDARY,
30+
substrs=["stopped", "stop reason = history boundary"],
31+
)
32+
self.expect(
33+
"process continue --forward",
34+
substrs=["stop reason = breakpoint {0}.1".format(trigger_bkpt.GetID())],
35+
)
36+
37+
defsetup_recording(self):
38+
"""
39+
Record execution of code between "start_recording" and "stop_recording" breakpoints.
40+
41+
Returns with the target stopped at "stop_recording", with recording disabled,
42+
ready to reverse-execute.
43+
"""
44+
self.build()
45+
target=self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
46+
process=self.connect(target)
47+
48+
# Record execution from the start of the function "start_recording"
49+
# to the start of the function "stop_recording". We want to keep the
50+
# interval that we record as small as possible to minimize the run-time
51+
# of our single-stepping recorder.
52+
start_recording_bkpt=target.BreakpointCreateByName("start_recording", None)
53+
self.assertTrue(start_recording_bkpt.GetNumLocations() >0)
54+
initial_threads=lldbutil.continue_to_breakpoint(process, start_recording_bkpt)
55+
self.assertEqual(len(initial_threads), 1)
56+
target.BreakpointDelete(start_recording_bkpt.GetID())
57+
self.start_recording()
58+
stop_recording_bkpt=target.BreakpointCreateByName("stop_recording", None)
59+
self.assertTrue(stop_recording_bkpt.GetNumLocations() >0)
60+
lldbutil.continue_to_breakpoint(process, stop_recording_bkpt)
61+
target.BreakpointDelete(stop_recording_bkpt.GetID())
62+
self.stop_recording()
63+
64+
self.dbg.SetAsync(False)
65+
66+
returntarget, process, initial_threads
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
Test the "process continue --reverse" and "--forward" options
3+
when reverse-continue is not supported.
4+
"""
5+
6+
7+
importlldb
8+
fromlldbsuite.test.lldbtestimport*
9+
fromlldbsuite.test.decoratorsimport*
10+
fromlldbsuite.testimportlldbutil
11+
12+
13+
classTestReverseContinueNotSupported(TestBase):
14+
deftest_reverse_continue_not_supported(self):
15+
target=self.connect()
16+
17+
# Set breakpoint and reverse-continue
18+
trigger_bkpt=target.BreakpointCreateByName("trigger_breakpoint", None)
19+
self.assertTrue(trigger_bkpt, VALID_BREAKPOINT)
20+
# `process continue --forward` should work.
21+
self.expect(
22+
"process continue --forward",
23+
substrs=["stop reason = breakpoint {0}.1".format(trigger_bkpt.GetID())],
24+
)
25+
self.expect(
26+
"process continue --reverse",
27+
error=True,
28+
substrs=["target does not support reverse-continue"],
29+
)
30+
31+
deftest_reverse_continue_forward_and_reverse(self):
32+
self.connect()
33+
34+
self.expect(
35+
"process continue --forward --reverse",
36+
error=True,
37+
substrs=["invalid combination of options for the given command"],
38+
)
39+
40+
defconnect(self):
41+
self.build()
42+
exe=self.getBuildArtifact("a.out")
43+
target=self.dbg.CreateTarget(exe)
44+
self.assertTrue(target, VALID_TARGET)
45+
46+
main_bkpt=target.BreakpointCreateByName("main", None)
47+
self.assertTrue(main_bkpt, VALID_BREAKPOINT)
48+
49+
process=target.LaunchSimple(None, None, self.get_process_working_directory())
50+
self.assertTrue(process, PROCESS_IS_VALID)
51+
returntarget
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
staticvoidstart_recording() {}
2+
3+
staticvoidtrigger_breakpoint() {}
4+
5+
staticvoidstop_recording() {}
6+
7+
intmain() {
8+
start_recording();
9+
trigger_breakpoint();
10+
stop_recording();
11+
return0;
12+
}

llvm/docs/ReleaseNotes.md

+4
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ Changes to LLDB
223223
* The `min-gdbserver-port` and `max-gdbserver-port` options have been removed
224224
from `lldb-server`'s platform mode. Since the changes to `lldb-server`'s port
225225
handling in LLDB 20, these options have had no effect.
226+
* LLDB now supports `process continue --reverse` when used with debug servers
227+
supporting reverse execution, such as [rr](https://rr-project.org).
228+
When using reverse execution, `process continue --forward` returns to the
229+
forward execution.
226230

227231
### Changes to lldb-dap
228232

0 commit comments

Comments
 (0)
close