Skip to content

Commit 2397180

Browse files
authored
[lldb] Implement CLI support for reverse-continue (llvm#132783)
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 4f36ada commit 2397180

File tree

7 files changed

+154
-3
lines changed

7 files changed

+154
-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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,10 @@ Changes to LLDB
235235
* The `min-gdbserver-port` and `max-gdbserver-port` options have been removed
236236
from `lldb-server`'s platform mode. Since the changes to `lldb-server`'s port
237237
handling in LLDB 20, these options have had no effect.
238+
* LLDB now supports `process continue --reverse` when used with debug servers
239+
supporting reverse execution, such as [rr](https://rr-project.org).
240+
When using reverse execution, `process continue --forward` returns to the
241+
forward execution.
238242

239243
### Changes to lldb-dap
240244

0 commit comments

Comments
 (0)