Skip to content

Commit 9558b60

Browse files
committed
Add an "interrupt timeout" to Process, and pipe that through the
ProcessGDBRemote plugin layers. Also fix a bug where if we tried to interrupt, but the ReadPacket wakeup timer woke us up just after the timeout, we would break out the switch, but then since we immediately check if the response is empty & fail if it is, we could end up actually only giving a small interval to the interrupt. Differential Revision: https://reviews.llvm.org/D102085
1 parent 384dd9d commit 9558b60

File tree

14 files changed

+327
-256
lines changed

14 files changed

+327
-256
lines changed

lldb/include/lldb/Target/Process.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class ProcessProperties : public Properties {
9494
bool GetWarningsUnsupportedLanguage() const;
9595
bool GetStopOnExec() const;
9696
std::chrono::seconds GetUtilityExpressionTimeout() const;
97+
std::chrono::seconds GetInterruptTimeout() const;
9798
bool GetOSPluginReportsAllThreads() const;
9899
void SetOSPluginReportsAllThreads(bool does_report);
99100
bool GetSteppingRunsAllThreads() const;

lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -740,8 +740,8 @@ const UnixSignalsSP &PlatformRemoteGDBServer::GetRemoteUnixSignals() {
740740
m_remote_signals_sp = UnixSignals::Create(GetRemoteSystemArchitecture());
741741

742742
StringExtractorGDBRemote response;
743-
auto result = m_gdb_client.SendPacketAndWaitForResponse("jSignalsInfo",
744-
response, false);
743+
auto result =
744+
m_gdb_client.SendPacketAndWaitForResponse("jSignalsInfo", response);
745745

746746
if (result != decltype(result)::Success ||
747747
response.GetResponseType() != response.eResponse)

lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ using namespace lldb_private;
2020
using namespace lldb_private::process_gdb_remote;
2121
using namespace std::chrono;
2222

23-
static const seconds kInterruptTimeout(5);
23+
// When we've sent a continue packet and are waiting for the target to stop,
24+
// we wake up the wait with this interval to make sure the stub hasn't gone
25+
// away while we were waiting.
26+
static const seconds kWakeupInterval(5);
2427

2528
/////////////////////////
2629
// GDBRemoteClientBase //
@@ -35,7 +38,8 @@ GDBRemoteClientBase::GDBRemoteClientBase(const char *comm_name,
3538

3639
StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse(
3740
ContinueDelegate &delegate, const UnixSignals &signals,
38-
llvm::StringRef payload, StringExtractorGDBRemote &response) {
41+
llvm::StringRef payload, std::chrono::seconds interrupt_timeout,
42+
StringExtractorGDBRemote &response) {
3943
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
4044
response.Clear();
4145

@@ -48,16 +52,34 @@ StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse(
4852
if (!cont_lock)
4953
return eStateInvalid;
5054
OnRunPacketSent(true);
51-
55+
// The main ReadPacket loop wakes up at computed_timeout intervals, just to
56+
// check that the connection hasn't dropped. When we wake up we also check
57+
// whether there is an interrupt request that has reached its endpoint.
58+
// If we want a shorter interrupt timeout that kWakeupInterval, we need to
59+
// choose the shorter interval for the wake up as well.
60+
std::chrono::seconds computed_timeout = std::min(interrupt_timeout,
61+
kWakeupInterval);
5262
for (;;) {
53-
PacketResult read_result = ReadPacket(response, kInterruptTimeout, false);
63+
PacketResult read_result = ReadPacket(response, computed_timeout, false);
5464
switch (read_result) {
5565
case PacketResult::ErrorReplyTimeout: {
5666
std::lock_guard<std::mutex> lock(m_mutex);
57-
if (m_async_count == 0)
67+
if (m_async_count == 0) {
5868
continue;
59-
if (steady_clock::now() >= m_interrupt_time + kInterruptTimeout)
69+
}
70+
auto cur_time = steady_clock::now();
71+
if (cur_time >= m_interrupt_endpoint)
6072
return eStateInvalid;
73+
else {
74+
// We woke up and found an interrupt is in flight, but we haven't
75+
// exceeded the interrupt wait time. So reset the wait time to the
76+
// time left till the interrupt timeout. But don't wait longer
77+
// than our wakeup timeout.
78+
auto new_wait = m_interrupt_endpoint - cur_time;
79+
computed_timeout = std::min(kWakeupInterval,
80+
std::chrono::duration_cast<std::chrono::seconds>(new_wait));
81+
continue;
82+
}
6183
break;
6284
}
6385
case PacketResult::Success:
@@ -133,8 +155,9 @@ StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse(
133155
}
134156
}
135157

136-
bool GDBRemoteClientBase::SendAsyncSignal(int signo) {
137-
Lock lock(*this, true);
158+
bool GDBRemoteClientBase::SendAsyncSignal(
159+
int signo, std::chrono::seconds interrupt_timeout) {
160+
Lock lock(*this, interrupt_timeout);
138161
if (!lock || !lock.DidInterrupt())
139162
return false;
140163

@@ -144,25 +167,26 @@ bool GDBRemoteClientBase::SendAsyncSignal(int signo) {
144167
return true;
145168
}
146169

147-
bool GDBRemoteClientBase::Interrupt() {
148-
Lock lock(*this, true);
170+
bool GDBRemoteClientBase::Interrupt(std::chrono::seconds interrupt_timeout) {
171+
Lock lock(*this, interrupt_timeout);
149172
if (!lock.DidInterrupt())
150173
return false;
151174
m_should_stop = true;
152175
return true;
153176
}
177+
154178
GDBRemoteCommunication::PacketResult
155179
GDBRemoteClientBase::SendPacketAndWaitForResponse(
156180
llvm::StringRef payload, StringExtractorGDBRemote &response,
157-
bool send_async) {
158-
Lock lock(*this, send_async);
181+
std::chrono::seconds interrupt_timeout) {
182+
Lock lock(*this, interrupt_timeout);
159183
if (!lock) {
160184
if (Log *log =
161185
ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS))
162186
LLDB_LOGF(log,
163187
"GDBRemoteClientBase::%s failed to get mutex, not sending "
164-
"packet '%.*s' (send_async=%d)",
165-
__FUNCTION__, int(payload.size()), payload.data(), send_async);
188+
"packet '%.*s'",
189+
__FUNCTION__, int(payload.size()), payload.data());
166190
return PacketResult::ErrorSendFailed;
167191
}
168192

@@ -172,16 +196,16 @@ GDBRemoteClientBase::SendPacketAndWaitForResponse(
172196
GDBRemoteCommunication::PacketResult
173197
GDBRemoteClientBase::SendPacketAndReceiveResponseWithOutputSupport(
174198
llvm::StringRef payload, StringExtractorGDBRemote &response,
175-
bool send_async,
199+
std::chrono::seconds interrupt_timeout,
176200
llvm::function_ref<void(llvm::StringRef)> output_callback) {
177-
Lock lock(*this, send_async);
201+
Lock lock(*this, interrupt_timeout);
178202
if (!lock) {
179203
if (Log *log =
180204
ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS))
181205
LLDB_LOGF(log,
182206
"GDBRemoteClientBase::%s failed to get mutex, not sending "
183-
"packet '%.*s' (send_async=%d)",
184-
__FUNCTION__, int(payload.size()), payload.data(), send_async);
207+
"packet '%.*s'",
208+
__FUNCTION__, int(payload.size()), payload.data());
185209
return PacketResult::ErrorSendFailed;
186210
}
187211

@@ -222,13 +246,14 @@ GDBRemoteClientBase::SendPacketAndWaitForResponseNoLock(
222246
return packet_result;
223247
}
224248

225-
bool GDBRemoteClientBase::SendvContPacket(llvm::StringRef payload,
226-
StringExtractorGDBRemote &response) {
249+
bool GDBRemoteClientBase::SendvContPacket(
250+
llvm::StringRef payload, std::chrono::seconds interrupt_timeout,
251+
StringExtractorGDBRemote &response) {
227252
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
228253
LLDB_LOGF(log, "GDBRemoteCommunicationClient::%s ()", __FUNCTION__);
229254

230255
// we want to lock down packet sending while we continue
231-
Lock lock(*this, true);
256+
Lock lock(*this, interrupt_timeout);
232257

233258
LLDB_LOGF(log,
234259
"GDBRemoteCommunicationClient::%s () sending vCont packet: %.*s",
@@ -336,18 +361,20 @@ GDBRemoteClientBase::ContinueLock::lock() {
336361
// GDBRemoteClientBase::Lock //
337362
///////////////////////////////
338363

339-
GDBRemoteClientBase::Lock::Lock(GDBRemoteClientBase &comm, bool interrupt)
364+
GDBRemoteClientBase::Lock::Lock(GDBRemoteClientBase &comm,
365+
std::chrono::seconds interrupt_timeout)
340366
: m_async_lock(comm.m_async_mutex, std::defer_lock), m_comm(comm),
341-
m_acquired(false), m_did_interrupt(false) {
342-
SyncWithContinueThread(interrupt);
367+
m_interrupt_timeout(interrupt_timeout), m_acquired(false),
368+
m_did_interrupt(false) {
369+
SyncWithContinueThread();
343370
if (m_acquired)
344371
m_async_lock.lock();
345372
}
346373

347-
void GDBRemoteClientBase::Lock::SyncWithContinueThread(bool interrupt) {
374+
void GDBRemoteClientBase::Lock::SyncWithContinueThread() {
348375
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
349376
std::unique_lock<std::mutex> lock(m_comm.m_mutex);
350-
if (m_comm.m_is_running && !interrupt)
377+
if (m_comm.m_is_running && m_interrupt_timeout == std::chrono::seconds(0))
351378
return; // We were asked to avoid interrupting the sender. Lock is not
352379
// acquired.
353380

@@ -365,9 +392,9 @@ void GDBRemoteClientBase::Lock::SyncWithContinueThread(bool interrupt) {
365392
"interrupt packet");
366393
return;
367394
}
395+
m_comm.m_interrupt_endpoint = steady_clock::now() + m_interrupt_timeout;
368396
if (log)
369397
log->PutCString("GDBRemoteClientBase::Lock::Lock sent packet: \\x03");
370-
m_comm.m_interrupt_time = steady_clock::now();
371398
}
372399
m_comm.m_cv.wait(lock, [this] { return !m_comm.m_is_running; });
373400
m_did_interrupt = true;

lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,46 @@ class GDBRemoteClientBase : public GDBRemoteCommunication {
3333

3434
GDBRemoteClientBase(const char *comm_name, const char *listener_name);
3535

36-
bool SendAsyncSignal(int signo);
36+
bool SendAsyncSignal(int signo, std::chrono::seconds interrupt_timeout);
3737

38-
bool Interrupt();
38+
bool Interrupt(std::chrono::seconds interrupt_timeout);
3939

4040
lldb::StateType SendContinuePacketAndWaitForResponse(
4141
ContinueDelegate &delegate, const UnixSignals &signals,
42-
llvm::StringRef payload, StringExtractorGDBRemote &response);
43-
44-
PacketResult SendPacketAndWaitForResponse(llvm::StringRef payload,
45-
StringExtractorGDBRemote &response,
46-
bool send_async);
42+
llvm::StringRef payload, std::chrono::seconds interrupt_timeout,
43+
StringExtractorGDBRemote &response);
44+
45+
// If interrupt_timeout == 0 seconds, don't interrupt the target.
46+
// Only send the packet if the target is stopped.
47+
// If you want to use this mode, use the fact that the timeout is defaulted
48+
// so it's clear from the call-site that you are using no-interrupt.
49+
// If it is non-zero, interrupt the target if it is running, and
50+
// send the packet.
51+
// It the target doesn't respond within the given timeout, it returns
52+
// ErrorReplyTimeout.
53+
PacketResult SendPacketAndWaitForResponse(
54+
llvm::StringRef payload, StringExtractorGDBRemote &response,
55+
std::chrono::seconds interrupt_timeout = std::chrono::seconds(0));
4756

4857
PacketResult SendPacketAndReceiveResponseWithOutputSupport(
4958
llvm::StringRef payload, StringExtractorGDBRemote &response,
50-
bool send_async,
59+
std::chrono::seconds interrupt_timeout,
5160
llvm::function_ref<void(llvm::StringRef)> output_callback);
5261

5362
bool SendvContPacket(llvm::StringRef payload,
63+
std::chrono::seconds interrupt_timeout,
5464
StringExtractorGDBRemote &response);
5565

5666
class Lock {
5767
public:
58-
Lock(GDBRemoteClientBase &comm, bool interrupt);
68+
// If interrupt_timeout == 0 seconds, only take the lock if the target is
69+
// not running. If using this option, use the fact that the
70+
// interrupt_timeout is defaulted so it will be obvious at the call site
71+
// that you are choosing this mode. If it is non-zero, interrupt the target
72+
// if it is running, waiting for the given timeout for the interrupt to
73+
// succeed.
74+
Lock(GDBRemoteClientBase &comm,
75+
std::chrono::seconds interrupt_timeout = std::chrono::seconds(0));
5976
~Lock();
6077

6178
explicit operator bool() { return m_acquired; }
@@ -67,10 +84,11 @@ class GDBRemoteClientBase : public GDBRemoteCommunication {
6784
private:
6885
std::unique_lock<std::recursive_mutex> m_async_lock;
6986
GDBRemoteClientBase &m_comm;
87+
std::chrono::seconds m_interrupt_timeout;
7088
bool m_acquired;
7189
bool m_did_interrupt;
7290

73-
void SyncWithContinueThread(bool interrupt);
91+
void SyncWithContinueThread();
7492
};
7593

7694
protected:
@@ -109,7 +127,7 @@ class GDBRemoteClientBase : public GDBRemoteCommunication {
109127

110128
/// When was the interrupt packet sent. Used to make sure we time out if the
111129
/// stub does not respond to interrupt requests.
112-
std::chrono::time_point<std::chrono::steady_clock> m_interrupt_time;
130+
std::chrono::time_point<std::chrono::steady_clock> m_interrupt_endpoint;
113131

114132
/// Number of threads interested in sending.
115133
uint32_t m_async_count;

0 commit comments

Comments
 (0)