Skip to content

Commit 0caba6c

Browse files
Reland "[lldb] Implement basic support for reverse-continue" (#123906)" (#123945)
This reverts commit 22561cf and fixes b7b9ccf (#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 (using https://lldb.llvm.org/resources/lldbgdbremote.html#qmemoryregioninfo-addr) and constrain the read to within that region. Since we know the stack is all readable and writeable. I have also added skipIfRemote to the tests, since getting them working in that context is too complex to be worth it. Memory write failures now display the range they tried to write, and register write errors will show the name of the register where possible. The patch also includes a workaround for a an issue where the test code could mistake an `x` response that happens to begin with an `O` for an output packet (stdout). This workaround will not be necessary one we start using the [new implementation](https://discourse.llvm.org/t/rfc-fixing-incompatibilties-of-the-x-packet-w-r-t-gdb/84288) of the `x` packet. --------- Co-authored-by: Pavel Labath <pavel@labath.sk>
1 parent fe1f6b4 commit 0caba6c

40 files changed

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