Skip to content

Commit ecb1b90

Browse files
committed
Reland "[lldb] Implement basic support for reverse-continue" (llvm#123906)"
This reverts commit 22561cf and fixes b7b9ccf (llvm#112079). The problem is that x86_64 and Arm 32-bit have memory regions above the stack that are readable but not writeable. First Arm: ``` (lldb) memory region --all <...> [0x00000000fffcf000-0x00000000ffff0000) rw- [stack] [0x00000000ffff0000-0x00000000ffff1000) r-x [vectors] [0x00000000ffff1000-0xffffffffffffffff) --- ``` Then x86_64: ``` $ cat /proc/self/maps <...> 7ffdcd148000-7ffdcd16a000 rw-p 00000000 00:00 0 [stack] 7ffdcd193000-7ffdcd196000 r--p 00000000 00:00 0 [vvar] 7ffdcd196000-7ffdcd197000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall] ``` Compare this to AArch64 where the test did pass: ``` $ cat /proc/self/maps <...> ffffb87dc000-ffffb87dd000 r--p 00000000 00:00 0 [vvar] ffffb87dd000-ffffb87de000 r-xp 00000000 00:00 0 [vdso] ffffb87de000-ffffb87e0000 r--p 0002a000 00:3c 76927217 /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 ffffb87e0000-ffffb87e2000 rw-p 0002c000 00:3c 76927217 /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 fffff4216000-fffff4237000 rw-p 00000000 00:00 0 [stack] ``` To solve this, look up the memory region of the stack pointer and constrain the read to within that region. Since we know the stack is all readable and writeable.
1 parent c745ece commit ecb1b90

40 files changed

+1369
-47
lines changed

lldb/include/lldb/API/SBProcess.h

+1
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

+26-2
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.
@@ -2676,6 +2687,18 @@ void PruneThreadPlans();
26762687
const AddressRange &range, size_t alignment,
26772688
Status &error);
26782689

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

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

lldb/include/lldb/Target/StopInfo.h

+7
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

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

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

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

lldb/include/lldb/Target/ThreadList.h

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

127131
void DidResume();
128132

lldb/include/lldb/Target/ThreadPlan.h

+13
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

+2
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

+6
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

+3-2
Original file line numberDiff line numberDiff line change
@@ -510,8 +510,9 @@ def start(self):
510510
self._thread.start()
511511

512512
def stop(self):
513-
self._thread.join()
514-
self._thread = None
513+
if self._thread is not None:
514+
self._thread.join()
515+
self._thread = None
515516

516517
def get_connect_address(self):
517518
return self._socket.get_connect_address()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
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+
handler = logging.StreamHandler()
43+
handler.setLevel(logging.WARNING)
44+
handler.setFormatter(self._log_formatter)
45+
self.logger.addHandler(handler)
46+
47+
def setUp(self):
48+
TestBase.setUp(self)
49+
50+
self.setUpBaseLogging()
51+
52+
if self.isVerboseLoggingRequested():
53+
# If requested, full logs go to a log file
54+
log_file_name = self.getLogBasenameForCurrentTest() + "-proxy.log"
55+
self._verbose_log_handler = logging.FileHandler(log_file_name)
56+
self._verbose_log_handler.setFormatter(self._log_formatter)
57+
self._verbose_log_handler.setLevel(logging.DEBUG)
58+
self.logger.addHandler(self._verbose_log_handler)
59+
60+
if lldbplatformutil.getPlatform() == "macosx":
61+
self.debug_monitor_exe = lldbgdbserverutils.get_debugserver_exe()
62+
self.debug_monitor_extra_args = []
63+
else:
64+
self.debug_monitor_exe = lldbgdbserverutils.get_lldb_server_exe()
65+
self.debug_monitor_extra_args = ["gdbserver"]
66+
self.assertIsNotNone(self.debug_monitor_exe)
67+
68+
self.server = MockGDBServer(self.server_socket_class())
69+
self.server.responder = self
70+
71+
def tearDown(self):
72+
# TestBase.tearDown will kill the process, but we need to kill it early
73+
# so its client connection closes and we can stop the server before
74+
# finally calling the base tearDown.
75+
if self.process() is not None:
76+
self.process().Kill()
77+
self.server.stop()
78+
79+
self.logger.removeHandler(self._verbose_log_handler)
80+
self._verbose_log_handler = None
81+
82+
TestBase.tearDown(self)
83+
84+
def isVerboseLoggingRequested(self):
85+
# We will report our detailed logs if the user requested that the "gdb-remote" channel is
86+
# logged.
87+
return any(("gdb-remote" in channel) for channel in lldbtest_config.channels)
88+
89+
def connect(self, target):
90+
"""
91+
Create a process by connecting to the mock GDB server.
92+
"""
93+
self.prep_debug_monitor_and_inferior()
94+
self.server.start()
95+
96+
listener = self.dbg.GetListener()
97+
error = lldb.SBError()
98+
process = target.ConnectRemote(
99+
listener, self.server.get_connect_url(), "gdb-remote", error
100+
)
101+
self.assertTrue(error.Success(), error.description)
102+
self.assertTrue(process, PROCESS_IS_VALID)
103+
return process
104+
105+
def prep_debug_monitor_and_inferior(self):
106+
inferior_exe_path = self.getBuildArtifact("a.out")
107+
self.connect_to_debug_monitor([inferior_exe_path])
108+
self.assertIsNotNone(self.monitor_server)
109+
self.initial_handshake()
110+
111+
def initial_handshake(self):
112+
self.monitor_server.send_packet(seven.bitcast_to_bytes("+"))
113+
reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())
114+
self.assertEqual(reply, "+")
115+
self.monitor_server.send_packet(seven.bitcast_to_bytes("QStartNoAckMode"))
116+
reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())
117+
self.assertEqual(reply, "+")
118+
reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())
119+
self.assertEqual(reply, "OK")
120+
self.monitor_server.set_validate_checksums(False)
121+
self.monitor_server.send_packet(seven.bitcast_to_bytes("+"))
122+
reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())
123+
self.assertEqual(reply, "+")
124+
125+
def get_debug_monitor_command_line_args(self, connect_address, launch_args):
126+
return (
127+
self.debug_monitor_extra_args
128+
+ ["--reverse-connect", connect_address]
129+
+ launch_args
130+
)
131+
132+
def launch_debug_monitor(self, launch_args):
133+
family, type, proto, _, addr = socket.getaddrinfo(
134+
"localhost", 0, proto=socket.IPPROTO_TCP
135+
)[0]
136+
sock = socket.socket(family, type, proto)
137+
sock.settimeout(self.DEFAULT_TIMEOUT)
138+
sock.bind(addr)
139+
sock.listen(1)
140+
addr = sock.getsockname()
141+
connect_address = "[{}]:{}".format(*addr)
142+
143+
commandline_args = self.get_debug_monitor_command_line_args(
144+
connect_address, launch_args
145+
)
146+
147+
# Start the server.
148+
self.logger.info(f"Spawning monitor {commandline_args}")
149+
monitor_process = self.spawnSubprocess(
150+
self.debug_monitor_exe, commandline_args, install_remote=False
151+
)
152+
self.assertIsNotNone(monitor_process)
153+
154+
self.monitor_sock = sock.accept()[0]
155+
self.monitor_sock.settimeout(self.DEFAULT_TIMEOUT)
156+
return monitor_process
157+
158+
def connect_to_debug_monitor(self, launch_args):
159+
monitor_process = self.launch_debug_monitor(launch_args)
160+
# Turn off checksum validation because debugserver does not produce
161+
# correct checksums.
162+
self.monitor_server = lldbgdbserverutils.Server(
163+
self.monitor_sock, monitor_process
164+
)
165+
166+
def respond(self, packet):
167+
"""Subclasses can override this to change how packets are handled."""
168+
return self.pass_through(packet)
169+
170+
def pass_through(self, packet):
171+
self.logger.info(f"Sending packet {packet}")
172+
self.monitor_server.send_packet(seven.bitcast_to_bytes(packet))
173+
reply = seven.bitcast_to_string(self.monitor_server.get_normal_packet())
174+
self.logger.info(f"Received reply {reply}")
175+
return reply

0 commit comments

Comments
 (0)