Skip to content

Commit 87af424

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 06cb7b1 commit 87af424

File tree

7 files changed

+152
-3
lines changed

7 files changed

+152
-3
lines changed

lldb/source/Commands/CommandObjectProcess.cpp

Lines changed: 12 additions & 1 deletion
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
void DoExecute(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
const uint32_t iohandler_id = process->GetIOHandlerID();
658669

659670
StreamString stream;

lldb/source/Commands/Options.td

Lines changed: 6 additions & 2 deletions
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 {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 66 additions & 0 deletions
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+
import lldb
7+
from lldbsuite.test.lldbtest import *
8+
from lldbsuite.test.decorators import *
9+
from lldbsuite.test.gdbclientutils import *
10+
from lldbsuite.test.lldbreverse import ReverseTestBase
11+
from lldbsuite.test import lldbutil
12+
13+
14+
class TestReverseContinue(ReverseTestBase):
15+
@skipIfRemote
16+
def test_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+
def setup_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+
return target, process, initial_threads
Lines changed: 51 additions & 0 deletions
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+
import lldb
8+
from lldbsuite.test.lldbtest import *
9+
from lldbsuite.test.decorators import *
10+
from lldbsuite.test import lldbutil
11+
12+
13+
class TestReverseContinueNotSupported(TestBase):
14+
def test_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+
def test_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+
def connect(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+
return target
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
static void start_recording() {}
2+
3+
static void trigger_breakpoint() {}
4+
5+
static void stop_recording() {}
6+
7+
int main() {
8+
start_recording();
9+
trigger_breakpoint();
10+
stop_recording();
11+
return 0;
12+
}

llvm/docs/ReleaseNotes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ Changes to LLDB
220220
information about the current state of the debugger at the bottom of the
221221
terminal. This is on by default and can be configured using the
222222
`show-statusline` and `statusline-format` settings.
223+
* LLDB now supports `process continue --reverse` when used with backends
224+
supporting reverse execution, such as [rr](https://rr-project.org).
223225

224226
### Changes to lldb-dap
225227

0 commit comments

Comments
 (0)