Skip to content

Commit 1b23719

Browse files
authored
Reapply "[lldb] Implement basic support for reverse-continue (#125242)" (again) (#128156)
This reverts commit 87b7f63, reapplying 7e66cf7 with a small (and probably temporary) change to generate more debug info to help with diagnosing buildbot issues.
1 parent 83356f3 commit 1b23719

40 files changed

+1406
-47
lines changed

lldb/include/lldb/API/SBProcess.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ class LLDB_API SBProcess {
159159
lldb::SBError Destroy();
160160

161161
lldb::SBError Continue();
162+
lldb::SBError ContinueInDirection(lldb::RunDirection direction);
162163

163164
lldb::SBError Stop();
164165

lldb/include/lldb/Target/Process.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,13 @@ class Process : public std::enable_shared_from_this<Process>,
10891089
/// Returns an error object.
10901090
virtual Status WillResume() { return Status(); }
10911091

1092+
/// Reports whether this process supports reverse execution.
1093+
///
1094+
/// \return
1095+
/// Returns true if the process supports reverse execution (at least
1096+
/// under some circumstances).
1097+
virtual bool SupportsReverseDirection() { return false; }
1098+
10921099
/// Resumes all of a process's threads as configured using the Thread run
10931100
/// control functions.
10941101
///
@@ -1104,9 +1111,13 @@ class Process : public std::enable_shared_from_this<Process>,
11041111
/// \see Thread:Resume()
11051112
/// \see Thread:Step()
11061113
/// \see Thread:Suspend()
1107-
virtual Status DoResume() {
1114+
virtual Status DoResume(lldb::RunDirection direction) {
1115+
if (direction == lldb::RunDirection::eRunForward)
1116+
return Status::FromErrorStringWithFormatv(
1117+
"error: {0} does not support resuming processes", GetPluginName());
11081118
return Status::FromErrorStringWithFormatv(
1109-
"error: {0} does not support resuming processes", GetPluginName());
1119+
"error: {0} does not support reverse execution of processes",
1120+
GetPluginName());
11101121
}
11111122

11121123
/// Called after resuming a process.
@@ -2677,6 +2688,18 @@ void PruneThreadPlans();
26772688
const AddressRange &range, size_t alignment,
26782689
Status &error);
26792690

2691+
/// Get the base run direction for the process.
2692+
/// The base direction is the direction the process will execute in
2693+
/// (forward or backward) if no thread plan overrides the direction.
2694+
lldb::RunDirection GetBaseDirection() const { return m_base_direction; }
2695+
/// Set the base run direction for the process.
2696+
/// As a side-effect, if this changes the base direction, then we
2697+
/// discard all non-base thread plans to ensure that when execution resumes
2698+
/// we definitely execute in the requested direction.
2699+
/// FIXME: this is overkill. In some situations ensuring the latter
2700+
/// would not require discarding all non-base thread plans.
2701+
void SetBaseDirection(lldb::RunDirection direction);
2702+
26802703
protected:
26812704
friend class Trace;
26822705

@@ -3076,6 +3099,7 @@ void PruneThreadPlans();
30763099
ThreadList
30773100
m_extended_thread_list; ///< Constituent for extended threads that may be
30783101
/// generated, cleared on natural stops
3102+
lldb::RunDirection m_base_direction; ///< ThreadPlanBase run direction
30793103
uint32_t m_extended_thread_stop_id; ///< The natural stop id when
30803104
///extended_thread_list was last updated
30813105
QueueList

lldb/include/lldb/Target/StopInfo.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ namespace lldb_private {
2020
class StopInfo : public std::enable_shared_from_this<StopInfo> {
2121
friend class Process::ProcessEventData;
2222
friend class ThreadPlanBase;
23+
friend class ThreadPlanReverseContinue;
2324

2425
public:
2526
// Constructors and Destructors
@@ -154,6 +155,12 @@ class StopInfo : public std::enable_shared_from_this<StopInfo> {
154155
static lldb::StopInfoSP
155156
CreateStopReasonProcessorTrace(Thread &thread, const char *description);
156157

158+
// This creates a StopInfo indicating that execution stopped because
159+
// it was replaying some recorded execution history, and execution reached
160+
// the end of that recorded history.
161+
static lldb::StopInfoSP
162+
CreateStopReasonHistoryBoundary(Thread &thread, const char *description);
163+
157164
static lldb::StopInfoSP CreateStopReasonFork(Thread &thread,
158165
lldb::pid_t child_pid,
159166
lldb::tid_t child_tid);

lldb/include/lldb/Target/Thread.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,13 @@ class Thread : public std::enable_shared_from_this<Thread>,
201201
/// The User resume state for this thread.
202202
lldb::StateType GetResumeState() const { return m_resume_state; }
203203

204-
/// This function is called on all the threads before "ShouldResume" and
205-
/// "WillResume" in case a thread needs to change its state before the
206-
/// ThreadList polls all the threads to figure out which ones actually will
207-
/// get to run and how.
204+
// This function is called to determine whether the thread needs to
205+
// step over a breakpoint and if so, push a step-over-breakpoint thread
206+
// plan.
208207
///
209208
/// \return
210209
/// True if we pushed a ThreadPlanStepOverBreakpoint
211-
bool SetupForResume();
210+
bool SetupToStepOverBreakpointIfNeeded(lldb::RunDirection direction);
212211

213212
// Do not override this function, it is for thread plan logic only
214213
bool ShouldResume(lldb::StateType resume_state);

lldb/include/lldb/Target/ThreadList.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,18 @@ class ThreadList : public ThreadCollection {
113113
/// If a thread can "resume" without having to resume the target, it
114114
/// will return false for WillResume, and then the process will not be
115115
/// restarted.
116+
/// Sets *direction to the run direction of the thread(s) that will
117+
/// be resumed. If threads that we want to run disagree about the
118+
/// direction, we execute forwards and pop any of the thread plans
119+
/// that requested reverse execution.
116120
///
117121
/// \return
118122
/// \b true instructs the process to resume normally,
119123
/// \b false means start & stopped events will be generated, but
120124
/// the process will not actually run. The thread must then return
121125
/// the correct StopInfo when asked.
122126
///
123-
bool WillResume();
127+
bool WillResume(lldb::RunDirection &direction);
124128

125129
void DidResume();
126130

lldb/include/lldb/Target/ThreadPlan.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,15 @@ namespace lldb_private {
283283
// report_run_vote argument to the constructor works like report_stop_vote, and
284284
// is a way for a plan to instruct a sub-plan on how to respond to
285285
// ShouldReportStop.
286+
//
287+
// Reverse execution:
288+
//
289+
// Every thread plan has an associated RunDirection (forward or backward).
290+
// For ThreadPlanBase, this direction is the Process's base direction.
291+
// Whenever we resume the target, we need to ensure that the topmost thread
292+
// plans for each runnable thread all agree on their direction. This is
293+
// ensured in ThreadList::WillResume(), which chooses a direction and then
294+
// discards thread plans incompatible with that direction.
286295

287296
class ThreadPlan : public std::enable_shared_from_this<ThreadPlan>,
288297
public UserID {
@@ -497,6 +506,10 @@ class ThreadPlan : public std::enable_shared_from_this<ThreadPlan>,
497506

498507
virtual lldb::StateType GetPlanRunState() = 0;
499508

509+
virtual lldb::RunDirection GetDirection() const {
510+
return lldb::RunDirection::eRunForward;
511+
}
512+
500513
protected:
501514
// Constructors and Destructors
502515
ThreadPlan(ThreadPlanKind kind, const char *name, Thread &thread,

lldb/include/lldb/Target/ThreadPlanBase.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class ThreadPlanBase : public ThreadPlan {
3838

3939
bool IsBasePlan() override { return true; }
4040

41+
lldb::RunDirection GetDirection() const override;
42+
4143
protected:
4244
bool DoWillResume(lldb::StateType resume_state, bool current_plan) override;
4345
bool DoPlanExplainsStop(Event *event_ptr) override;

lldb/include/lldb/lldb-enumerations.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ FLAGS_ENUM(LaunchFlags){
135135
/// Thread Run Modes.
136136
enum RunMode { eOnlyThisThread, eAllThreads, eOnlyDuringStepping };
137137

138+
/// Execution directions
139+
enum RunDirection { eRunForward, eRunReverse };
140+
138141
/// Byte ordering definitions.
139142
enum ByteOrder {
140143
eByteOrderInvalid = 0,
@@ -254,6 +257,9 @@ enum StopReason {
254257
eStopReasonVFork,
255258
eStopReasonVForkDone,
256259
eStopReasonInterrupt, ///< Thread requested interrupt
260+
// Indicates that execution stopped because the debugger backend relies
261+
// on recorded data and we reached the end of that data.
262+
eStopReasonHistoryBoundary,
257263
};
258264

259265
/// Command Return Status Types.

lldb/packages/Python/lldbsuite/test/gdbclientutils.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,9 @@ def start(self):
516516
self._thread.start()
517517

518518
def stop(self):
519-
self._thread.join()
520-
self._thread = None
519+
if self._thread is not None:
520+
self._thread.join()
521+
self._thread = None
521522

522523
def get_connect_address(self):
523524
return self._socket.get_connect_address()
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import logging
2+
import os
3+
import os.path
4+
import random
5+
6+
import lldb
7+
from lldbsuite.test.lldbtest import *
8+
from lldbsuite.test.gdbclientutils import *
9+
import lldbgdbserverutils
10+
from lldbsuite.support import seven
11+
12+
13+
class GDBProxyTestBase(TestBase):
14+
"""
15+
Base class for gdbserver proxy tests.
16+
17+
This class will setup and start a mock GDB server for the test to use.
18+
It pases through requests to a regular lldb-server/debugserver and
19+
forwards replies back to the LLDB under test.
20+
"""
21+
22+
"""The gdbserver that we implement."""
23+
server = None
24+
"""The inner lldb-server/debugserver process that we proxy requests into."""
25+
monitor_server = None
26+
monitor_sock = None
27+
28+
server_socket_class = TCPServerSocket
29+
30+
DEFAULT_TIMEOUT = 20 * (10 if ("ASAN_OPTIONS" in os.environ) else 1)
31+
32+
_verbose_log_handler = None
33+
_log_formatter = logging.Formatter(fmt="%(asctime)-15s %(levelname)-8s %(message)s")
34+
35+
def setUpBaseLogging(self):
36+
self.logger = logging.getLogger(__name__)
37+
38+
self.logger.propagate = False
39+
self.logger.setLevel(logging.DEBUG)
40+
41+
# log all warnings to stderr
42+
self._stderr_log_handler = logging.StreamHandler()
43+
self._stderr_log_handler.setLevel(
44+
logging.DEBUG if self.TraceOn() else logging.WARNING
45+
)
46+
self._stderr_log_handler.setFormatter(self._log_formatter)
47+
self.logger.addHandler(self._stderr_log_handler)
48+
49+
def setUp(self):
50+
TestBase.setUp(self)
51+
52+
self.setUpBaseLogging()
53+
54+
if self.isVerboseLoggingRequested():
55+
# If requested, full logs go to a log file
56+
log_file_name = self.getLogBasenameForCurrentTest() + "-proxy.log"
57+
self._verbose_log_handler = logging.FileHandler(log_file_name)
58+
self._verbose_log_handler.setFormatter(self._log_formatter)
59+
self._verbose_log_handler.setLevel(logging.DEBUG)
60+
self.logger.addHandler(self._verbose_log_handler)
61+
62+
if lldbplatformutil.getPlatform() == "macosx":
63+
self.debug_monitor_exe = lldbgdbserverutils.get_debugserver_exe()
64+
self.debug_monitor_extra_args = []
65+
else:
66+
self.debug_monitor_exe = lldbgdbserverutils.get_lldb_server_exe()
67+
self.debug_monitor_extra_args = ["gdbserver"]
68+
self.assertIsNotNone(self.debug_monitor_exe)
69+
70+
self.server = MockGDBServer(self.server_socket_class())
71+
self.server.responder = self
72+
73+
def tearDown(self):
74+
# TestBase.tearDown will kill the process, but we need to kill it early
75+
# so its client connection closes and we can stop the server before
76+
# finally calling the base tearDown.
77+
if self.process() is not None:
78+
self.process().Kill()
79+
self.server.stop()
80+
81+
self.logger.removeHandler(self._verbose_log_handler)
82+
self._verbose_log_handler = None
83+
self.logger.removeHandler(self._stderr_log_handler)
84+
self._stderr_log_handler = None
85+
86+
TestBase.tearDown(self)
87+
88+
def isVerboseLoggingRequested(self):
89+
# We will report our detailed logs if the user requested that the "gdb-remote" channel is
90+
# logged.
91+
return any(("gdb-remote" in channel) for channel in lldbtest_config.channels)
92+
93+
def connect(self, target):
94+
"""
95+
Create a process by connecting to the mock GDB server.
96+
"""
97+
self.prep_debug_monitor_and_inferior()
98+
self.server.start()
99+
100+
listener = self.dbg.GetListener()
101+
error = lldb.SBError()
102+
process = target.ConnectRemote(
103+
listener, self.server.get_connect_url(), "gdb-remote", error
104+
)
105+
self.assertTrue(error.Success(), error.description)
106+
self.assertTrue(process, PROCESS_IS_VALID)
107+
return process
108+
109+
def prep_debug_monitor_and_inferior(self):
110+
inferior_exe_path = self.getBuildArtifact("a.out")
111+
self.connect_to_debug_monitor([inferior_exe_path])
112+
self.assertIsNotNone(self.monitor_server)
113+
self.initial_handshake()
114+
115+
def initial_handshake(self):
116+
self.monitor_server.send_packet(seven.bitcast_to_bytes("+"))
117+
reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())
118+
self.assertEqual(reply, "+")
119+
self.monitor_server.send_packet(seven.bitcast_to_bytes("QStartNoAckMode"))
120+
reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())
121+
self.assertEqual(reply, "+")
122+
reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())
123+
self.assertEqual(reply, "OK")
124+
self.monitor_server.set_validate_checksums(False)
125+
self.monitor_server.send_packet(seven.bitcast_to_bytes("+"))
126+
reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())
127+
self.assertEqual(reply, "+")
128+
129+
def get_debug_monitor_command_line_args(self, connect_address, launch_args):
130+
return (
131+
self.debug_monitor_extra_args
132+
+ ["--reverse-connect", connect_address]
133+
+ launch_args
134+
)
135+
136+
def launch_debug_monitor(self, launch_args):
137+
family, type, proto, _, addr = socket.getaddrinfo(
138+
"localhost", 0, proto=socket.IPPROTO_TCP
139+
)[0]
140+
sock = socket.socket(family, type, proto)
141+
sock.settimeout(self.DEFAULT_TIMEOUT)
142+
sock.bind(addr)
143+
sock.listen(1)
144+
addr = sock.getsockname()
145+
connect_address = "[{}]:{}".format(*addr)
146+
147+
commandline_args = self.get_debug_monitor_command_line_args(
148+
connect_address, launch_args
149+
)
150+
151+
# Start the server.
152+
self.logger.info(f"Spawning monitor {commandline_args}")
153+
monitor_process = self.spawnSubprocess(
154+
self.debug_monitor_exe, commandline_args, install_remote=False
155+
)
156+
self.assertIsNotNone(monitor_process)
157+
158+
self.monitor_sock = sock.accept()[0]
159+
self.monitor_sock.settimeout(self.DEFAULT_TIMEOUT)
160+
return monitor_process
161+
162+
def connect_to_debug_monitor(self, launch_args):
163+
monitor_process = self.launch_debug_monitor(launch_args)
164+
# Turn off checksum validation because debugserver does not produce
165+
# correct checksums.
166+
self.monitor_server = lldbgdbserverutils.Server(
167+
self.monitor_sock, monitor_process
168+
)
169+
170+
def respond(self, packet):
171+
"""Subclasses can override this to change how packets are handled."""
172+
return self.pass_through(packet)
173+
174+
def pass_through(self, packet):
175+
self.logger.info(f"Sending packet {packet}")
176+
self.monitor_server.send_packet(seven.bitcast_to_bytes(packet))
177+
reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())
178+
self.logger.info(f"Received reply {reply}")
179+
return reply

0 commit comments

Comments
 (0)