180814287SRaphael Isemann //===-- GDBRemoteClientBase.cpp -------------------------------------------===//
28c1b6bd7SPavel Labath //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68c1b6bd7SPavel Labath //
78c1b6bd7SPavel Labath //===----------------------------------------------------------------------===//
88c1b6bd7SPavel Labath
98c1b6bd7SPavel Labath #include "GDBRemoteClientBase.h"
108c1b6bd7SPavel Labath
118c1b6bd7SPavel Labath #include "llvm/ADT/StringExtras.h"
128c1b6bd7SPavel Labath
138c1b6bd7SPavel Labath #include "lldb/Target/UnixSignals.h"
148c1b6bd7SPavel Labath #include "lldb/Utility/LLDBAssert.h"
158c1b6bd7SPavel Labath
168c1b6bd7SPavel Labath #include "ProcessGDBRemoteLog.h"
178c1b6bd7SPavel Labath
188c1b6bd7SPavel Labath using namespace lldb;
198c1b6bd7SPavel Labath using namespace lldb_private;
208c1b6bd7SPavel Labath using namespace lldb_private::process_gdb_remote;
211eff73c3SPavel Labath using namespace std::chrono;
228c1b6bd7SPavel Labath
23379f24ffSJim Ingham // When we've sent a continue packet and are waiting for the target to stop,
24379f24ffSJim Ingham // we wake up the wait with this interval to make sure the stub hasn't gone
25379f24ffSJim Ingham // away while we were waiting.
26379f24ffSJim Ingham static const seconds kWakeupInterval(5);
278c1b6bd7SPavel Labath
288c1b6bd7SPavel Labath /////////////////////////
298c1b6bd7SPavel Labath // GDBRemoteClientBase //
308c1b6bd7SPavel Labath /////////////////////////
318c1b6bd7SPavel Labath
328c1b6bd7SPavel Labath GDBRemoteClientBase::ContinueDelegate::~ContinueDelegate() = default;
338c1b6bd7SPavel Labath
GDBRemoteClientBase(const char * comm_name,const char * listener_name)34b9c1b51eSKate Stone GDBRemoteClientBase::GDBRemoteClientBase(const char *comm_name,
35b9c1b51eSKate Stone const char *listener_name)
36b9c1b51eSKate Stone : GDBRemoteCommunication(comm_name, listener_name), m_async_count(0),
37b9c1b51eSKate Stone m_is_running(false), m_should_stop(false) {}
388c1b6bd7SPavel Labath
SendContinuePacketAndWaitForResponse(ContinueDelegate & delegate,const UnixSignals & signals,llvm::StringRef payload,std::chrono::seconds interrupt_timeout,StringExtractorGDBRemote & response)39b9c1b51eSKate Stone StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse(
40b9c1b51eSKate Stone ContinueDelegate &delegate, const UnixSignals &signals,
41379f24ffSJim Ingham llvm::StringRef payload, std::chrono::seconds interrupt_timeout,
42379f24ffSJim Ingham StringExtractorGDBRemote &response) {
43b1127753SPavel Labath Log *log = GetLog(GDBRLog::Process);
448c1b6bd7SPavel Labath response.Clear();
458c1b6bd7SPavel Labath
468c1b6bd7SPavel Labath {
478c1b6bd7SPavel Labath std::lock_guard<std::mutex> lock(m_mutex);
48adcd0268SBenjamin Kramer m_continue_packet = std::string(payload);
498c1b6bd7SPavel Labath m_should_stop = false;
508c1b6bd7SPavel Labath }
518c1b6bd7SPavel Labath ContinueLock cont_lock(*this);
528c1b6bd7SPavel Labath if (!cont_lock)
538c1b6bd7SPavel Labath return eStateInvalid;
548c1b6bd7SPavel Labath OnRunPacketSent(true);
55379f24ffSJim Ingham // The main ReadPacket loop wakes up at computed_timeout intervals, just to
56379f24ffSJim Ingham // check that the connection hasn't dropped. When we wake up we also check
57379f24ffSJim Ingham // whether there is an interrupt request that has reached its endpoint.
58379f24ffSJim Ingham // If we want a shorter interrupt timeout that kWakeupInterval, we need to
59379f24ffSJim Ingham // choose the shorter interval for the wake up as well.
60379f24ffSJim Ingham std::chrono::seconds computed_timeout = std::min(interrupt_timeout,
61379f24ffSJim Ingham kWakeupInterval);
62b9c1b51eSKate Stone for (;;) {
63379f24ffSJim Ingham PacketResult read_result = ReadPacket(response, computed_timeout, false);
64379f24ffSJim Ingham // Reset the computed_timeout to the default value in case we are going
65379f24ffSJim Ingham // round again.
66379f24ffSJim Ingham computed_timeout = std::min(interrupt_timeout, kWakeupInterval);
67b9c1b51eSKate Stone switch (read_result) {
68b9c1b51eSKate Stone case PacketResult::ErrorReplyTimeout: {
698c1b6bd7SPavel Labath std::lock_guard<std::mutex> lock(m_mutex);
70379f24ffSJim Ingham if (m_async_count == 0) {
718c1b6bd7SPavel Labath continue;
72379f24ffSJim Ingham }
73379f24ffSJim Ingham auto cur_time = steady_clock::now();
74379f24ffSJim Ingham if (cur_time >= m_interrupt_endpoint)
758c1b6bd7SPavel Labath return eStateInvalid;
76379f24ffSJim Ingham else {
77379f24ffSJim Ingham // We woke up and found an interrupt is in flight, but we haven't
78379f24ffSJim Ingham // exceeded the interrupt wait time. So reset the wait time to the
79379f24ffSJim Ingham // time left till the interrupt timeout. But don't wait longer
80379f24ffSJim Ingham // than our wakeup timeout.
81379f24ffSJim Ingham auto new_wait = m_interrupt_endpoint - cur_time;
82379f24ffSJim Ingham computed_timeout = std::min(kWakeupInterval,
83379f24ffSJim Ingham std::chrono::duration_cast<std::chrono::seconds>(new_wait));
84379f24ffSJim Ingham continue;
85379f24ffSJim Ingham }
86a01e024aSAdrian Prantl break;
878c1b6bd7SPavel Labath }
888c1b6bd7SPavel Labath case PacketResult::Success:
898c1b6bd7SPavel Labath break;
908c1b6bd7SPavel Labath default:
9163e5fb76SJonas Devlieghere LLDB_LOGF(log, "GDBRemoteClientBase::%s () ReadPacket(...) => false",
92b9c1b51eSKate Stone __FUNCTION__);
938c1b6bd7SPavel Labath return eStateInvalid;
948c1b6bd7SPavel Labath }
958c1b6bd7SPavel Labath if (response.Empty())
968c1b6bd7SPavel Labath return eStateInvalid;
978c1b6bd7SPavel Labath
988c1b6bd7SPavel Labath const char stop_type = response.GetChar();
9963e5fb76SJonas Devlieghere LLDB_LOGF(log, "GDBRemoteClientBase::%s () got packet: %s", __FUNCTION__,
100d35b42f2SJonas Devlieghere response.GetStringRef().data());
1018c1b6bd7SPavel Labath
102b9c1b51eSKate Stone switch (stop_type) {
1038c1b6bd7SPavel Labath case 'W':
1048c1b6bd7SPavel Labath case 'X':
1058c1b6bd7SPavel Labath return eStateExited;
1068c1b6bd7SPavel Labath case 'E':
1078c1b6bd7SPavel Labath // ERROR
1088c1b6bd7SPavel Labath return eStateInvalid;
1098c1b6bd7SPavel Labath default:
11063e5fb76SJonas Devlieghere LLDB_LOGF(log, "GDBRemoteClientBase::%s () unrecognized async packet",
111b9c1b51eSKate Stone __FUNCTION__);
1128c1b6bd7SPavel Labath return eStateInvalid;
113b9c1b51eSKate Stone case 'O': {
1148c1b6bd7SPavel Labath std::string inferior_stdout;
1158c1b6bd7SPavel Labath response.GetHexByteString(inferior_stdout);
1168c1b6bd7SPavel Labath delegate.HandleAsyncStdout(inferior_stdout);
1178c1b6bd7SPavel Labath break;
1188c1b6bd7SPavel Labath }
1198c1b6bd7SPavel Labath case 'A':
120b9c1b51eSKate Stone delegate.HandleAsyncMisc(
121b9c1b51eSKate Stone llvm::StringRef(response.GetStringRef()).substr(1));
1228c1b6bd7SPavel Labath break;
12375930019STodd Fiala case 'J':
124fcdb1af6STodd Fiala delegate.HandleAsyncStructuredDataPacket(response.GetStringRef());
12575930019STodd Fiala break;
1268c1b6bd7SPavel Labath case 'T':
1278c1b6bd7SPavel Labath case 'S':
1288c1b6bd7SPavel Labath // Do this with the continue lock held.
1298c1b6bd7SPavel Labath const bool should_stop = ShouldStop(signals, response);
1308c1b6bd7SPavel Labath response.SetFilePos(0);
1318c1b6bd7SPavel Labath
13205097246SAdrian Prantl // The packet we should resume with. In the future we should check our
13305097246SAdrian Prantl // thread list and "do the right thing" for new threads that show up
13405097246SAdrian Prantl // while we stop and run async packets. Setting the packet to 'c' to
13505097246SAdrian Prantl // continue all threads is the right thing to do 99.99% of the time
13605097246SAdrian Prantl // because if a thread was single stepping, and we sent an interrupt, we
13705097246SAdrian Prantl // will notice above that we didn't stop due to an interrupt but stopped
13805097246SAdrian Prantl // due to stepping and we would _not_ continue. This packet may get
13905097246SAdrian Prantl // modified by the async actions (e.g. to send a signal).
1408c1b6bd7SPavel Labath m_continue_packet = 'c';
1418c1b6bd7SPavel Labath cont_lock.unlock();
1428c1b6bd7SPavel Labath
1438c1b6bd7SPavel Labath delegate.HandleStopReply();
1448c1b6bd7SPavel Labath if (should_stop)
1458c1b6bd7SPavel Labath return eStateStopped;
1468c1b6bd7SPavel Labath
147b9c1b51eSKate Stone switch (cont_lock.lock()) {
1488c1b6bd7SPavel Labath case ContinueLock::LockResult::Success:
1498c1b6bd7SPavel Labath break;
1508c1b6bd7SPavel Labath case ContinueLock::LockResult::Failed:
1518c1b6bd7SPavel Labath return eStateInvalid;
1528c1b6bd7SPavel Labath case ContinueLock::LockResult::Cancelled:
1538c1b6bd7SPavel Labath return eStateStopped;
1548c1b6bd7SPavel Labath }
1558c1b6bd7SPavel Labath OnRunPacketSent(false);
1568c1b6bd7SPavel Labath break;
1578c1b6bd7SPavel Labath }
1588c1b6bd7SPavel Labath }
1598c1b6bd7SPavel Labath }
1608c1b6bd7SPavel Labath
SendAsyncSignal(int signo,std::chrono::seconds interrupt_timeout)161379f24ffSJim Ingham bool GDBRemoteClientBase::SendAsyncSignal(
162379f24ffSJim Ingham int signo, std::chrono::seconds interrupt_timeout) {
163379f24ffSJim Ingham Lock lock(*this, interrupt_timeout);
1648c1b6bd7SPavel Labath if (!lock || !lock.DidInterrupt())
1658c1b6bd7SPavel Labath return false;
1668c1b6bd7SPavel Labath
1678c1b6bd7SPavel Labath m_continue_packet = 'C';
1688c1b6bd7SPavel Labath m_continue_packet += llvm::hexdigit((signo / 16) % 16);
1698c1b6bd7SPavel Labath m_continue_packet += llvm::hexdigit(signo % 16);
1708c1b6bd7SPavel Labath return true;
1718c1b6bd7SPavel Labath }
1728c1b6bd7SPavel Labath
Interrupt(std::chrono::seconds interrupt_timeout)173379f24ffSJim Ingham bool GDBRemoteClientBase::Interrupt(std::chrono::seconds interrupt_timeout) {
174379f24ffSJim Ingham Lock lock(*this, interrupt_timeout);
1758c1b6bd7SPavel Labath if (!lock.DidInterrupt())
1768c1b6bd7SPavel Labath return false;
1778c1b6bd7SPavel Labath m_should_stop = true;
1788c1b6bd7SPavel Labath return true;
1798c1b6bd7SPavel Labath }
180379f24ffSJim Ingham
1818c1b6bd7SPavel Labath GDBRemoteCommunication::PacketResult
SendPacketAndWaitForResponse(llvm::StringRef payload,StringExtractorGDBRemote & response,std::chrono::seconds interrupt_timeout)182b9c1b51eSKate Stone GDBRemoteClientBase::SendPacketAndWaitForResponse(
183b9c1b51eSKate Stone llvm::StringRef payload, StringExtractorGDBRemote &response,
184379f24ffSJim Ingham std::chrono::seconds interrupt_timeout) {
185379f24ffSJim Ingham Lock lock(*this, interrupt_timeout);
186b9c1b51eSKate Stone if (!lock) {
187b1127753SPavel Labath if (Log *log = GetLog(GDBRLog::Process))
18863e5fb76SJonas Devlieghere LLDB_LOGF(log,
18963e5fb76SJonas Devlieghere "GDBRemoteClientBase::%s failed to get mutex, not sending "
190379f24ffSJim Ingham "packet '%.*s'",
191379f24ffSJim Ingham __FUNCTION__, int(payload.size()), payload.data());
1928c1b6bd7SPavel Labath return PacketResult::ErrorSendFailed;
1938c1b6bd7SPavel Labath }
1948c1b6bd7SPavel Labath
1955c95ee4dSPavel Labath return SendPacketAndWaitForResponseNoLock(payload, response);
1968c1b6bd7SPavel Labath }
1978c1b6bd7SPavel Labath
1988c1b6bd7SPavel Labath GDBRemoteCommunication::PacketResult
SendPacketAndReceiveResponseWithOutputSupport(llvm::StringRef payload,StringExtractorGDBRemote & response,std::chrono::seconds interrupt_timeout,llvm::function_ref<void (llvm::StringRef)> output_callback)1997da84753SPavel Labath GDBRemoteClientBase::SendPacketAndReceiveResponseWithOutputSupport(
2007da84753SPavel Labath llvm::StringRef payload, StringExtractorGDBRemote &response,
201379f24ffSJim Ingham std::chrono::seconds interrupt_timeout,
2027da84753SPavel Labath llvm::function_ref<void(llvm::StringRef)> output_callback) {
203379f24ffSJim Ingham Lock lock(*this, interrupt_timeout);
2047da84753SPavel Labath if (!lock) {
205b1127753SPavel Labath if (Log *log = GetLog(GDBRLog::Process))
20663e5fb76SJonas Devlieghere LLDB_LOGF(log,
20763e5fb76SJonas Devlieghere "GDBRemoteClientBase::%s failed to get mutex, not sending "
208379f24ffSJim Ingham "packet '%.*s'",
209379f24ffSJim Ingham __FUNCTION__, int(payload.size()), payload.data());
2107da84753SPavel Labath return PacketResult::ErrorSendFailed;
2117da84753SPavel Labath }
2127da84753SPavel Labath
2137da84753SPavel Labath PacketResult packet_result = SendPacketNoLock(payload);
2147da84753SPavel Labath if (packet_result != PacketResult::Success)
2157da84753SPavel Labath return packet_result;
2167da84753SPavel Labath
2177da84753SPavel Labath return ReadPacketWithOutputSupport(response, GetPacketTimeout(), true,
2187da84753SPavel Labath output_callback);
2197da84753SPavel Labath }
2207da84753SPavel Labath
2217da84753SPavel Labath GDBRemoteCommunication::PacketResult
SendPacketAndWaitForResponseNoLock(llvm::StringRef payload,StringExtractorGDBRemote & response)222b9c1b51eSKate Stone GDBRemoteClientBase::SendPacketAndWaitForResponseNoLock(
223b9c1b51eSKate Stone llvm::StringRef payload, StringExtractorGDBRemote &response) {
22426709df8SZachary Turner PacketResult packet_result = SendPacketNoLock(payload);
2258c1b6bd7SPavel Labath if (packet_result != PacketResult::Success)
2268c1b6bd7SPavel Labath return packet_result;
2278c1b6bd7SPavel Labath
2288c1b6bd7SPavel Labath const size_t max_response_retries = 3;
229b9c1b51eSKate Stone for (size_t i = 0; i < max_response_retries; ++i) {
2301eff73c3SPavel Labath packet_result = ReadPacket(response, GetPacketTimeout(), true);
2318c1b6bd7SPavel Labath // Make sure we received a response
2328c1b6bd7SPavel Labath if (packet_result != PacketResult::Success)
2338c1b6bd7SPavel Labath return packet_result;
2348c1b6bd7SPavel Labath // Make sure our response is valid for the payload that was sent
2358c1b6bd7SPavel Labath if (response.ValidateResponse())
2368c1b6bd7SPavel Labath return packet_result;
2378c1b6bd7SPavel Labath // Response says it wasn't valid
238b1127753SPavel Labath Log *log = GetLog(GDBRLog::Packets);
23963e5fb76SJonas Devlieghere LLDB_LOGF(
24063e5fb76SJonas Devlieghere log,
241b9c1b51eSKate Stone "error: packet with payload \"%.*s\" got invalid response \"%s\": %s",
242d35b42f2SJonas Devlieghere int(payload.size()), payload.data(), response.GetStringRef().data(),
243b9c1b51eSKate Stone (i == (max_response_retries - 1))
244b9c1b51eSKate Stone ? "using invalid response and giving up"
2458c1b6bd7SPavel Labath : "ignoring response and waiting for another");
2468c1b6bd7SPavel Labath }
2478c1b6bd7SPavel Labath return packet_result;
2488c1b6bd7SPavel Labath }
2498c1b6bd7SPavel Labath
ShouldStop(const UnixSignals & signals,StringExtractorGDBRemote & response)250b9c1b51eSKate Stone bool GDBRemoteClientBase::ShouldStop(const UnixSignals &signals,
251b9c1b51eSKate Stone StringExtractorGDBRemote &response) {
2528c1b6bd7SPavel Labath std::lock_guard<std::mutex> lock(m_mutex);
2538c1b6bd7SPavel Labath
2548c1b6bd7SPavel Labath if (m_async_count == 0)
2558c1b6bd7SPavel Labath return true; // We were not interrupted. The process stopped on its own.
2568c1b6bd7SPavel Labath
25705097246SAdrian Prantl // Older debugserver stubs (before April 2016) can return two stop-reply
25805097246SAdrian Prantl // packets in response to a ^C packet. Additionally, all debugservers still
25905097246SAdrian Prantl // return two stop replies if the inferior stops due to some other reason
26005097246SAdrian Prantl // before the remote stub manages to interrupt it. We need to wait for this
26105097246SAdrian Prantl // additional packet to make sure the packet sequence does not get skewed.
2628c1b6bd7SPavel Labath StringExtractorGDBRemote extra_stop_reply_packet;
2631eff73c3SPavel Labath ReadPacket(extra_stop_reply_packet, milliseconds(100), false);
2648c1b6bd7SPavel Labath
26505097246SAdrian Prantl // Interrupting is typically done using SIGSTOP or SIGINT, so if the process
26605097246SAdrian Prantl // stops with some other signal, we definitely want to stop.
2678c1b6bd7SPavel Labath const uint8_t signo = response.GetHexU8(UINT8_MAX);
268b9c1b51eSKate Stone if (signo != signals.GetSignalNumberFromName("SIGSTOP") &&
269b9c1b51eSKate Stone signo != signals.GetSignalNumberFromName("SIGINT"))
2708c1b6bd7SPavel Labath return true;
2718c1b6bd7SPavel Labath
272b9c1b51eSKate Stone // We probably only stopped to perform some async processing, so continue
273b9c1b51eSKate Stone // after that is done.
274b9c1b51eSKate Stone // TODO: This is not 100% correct, as the process may have been stopped with
2751eff73c3SPavel Labath // SIGINT or SIGSTOP that was not caused by us (e.g. raise(SIGINT)). This will
2761eff73c3SPavel Labath // normally cause a stop, but if it's done concurrently with a async
2771eff73c3SPavel Labath // interrupt, that stop will get eaten (llvm.org/pr20231).
2788c1b6bd7SPavel Labath return false;
2798c1b6bd7SPavel Labath }
2808c1b6bd7SPavel Labath
OnRunPacketSent(bool first)281b9c1b51eSKate Stone void GDBRemoteClientBase::OnRunPacketSent(bool first) {
2828c1b6bd7SPavel Labath if (first)
283248a1305SKonrad Kleine BroadcastEvent(eBroadcastBitRunPacketSent, nullptr);
2848c1b6bd7SPavel Labath }
2858c1b6bd7SPavel Labath
2868c1b6bd7SPavel Labath ///////////////////////////////////////
2878c1b6bd7SPavel Labath // GDBRemoteClientBase::ContinueLock //
2888c1b6bd7SPavel Labath ///////////////////////////////////////
2898c1b6bd7SPavel Labath
ContinueLock(GDBRemoteClientBase & comm)290b9c1b51eSKate Stone GDBRemoteClientBase::ContinueLock::ContinueLock(GDBRemoteClientBase &comm)
291b9c1b51eSKate Stone : m_comm(comm), m_acquired(false) {
2928c1b6bd7SPavel Labath lock();
2938c1b6bd7SPavel Labath }
2948c1b6bd7SPavel Labath
~ContinueLock()295b9c1b51eSKate Stone GDBRemoteClientBase::ContinueLock::~ContinueLock() {
2968c1b6bd7SPavel Labath if (m_acquired)
2978c1b6bd7SPavel Labath unlock();
2988c1b6bd7SPavel Labath }
2998c1b6bd7SPavel Labath
unlock()300b9c1b51eSKate Stone void GDBRemoteClientBase::ContinueLock::unlock() {
3018c1b6bd7SPavel Labath lldbassert(m_acquired);
3028c1b6bd7SPavel Labath {
3038c1b6bd7SPavel Labath std::unique_lock<std::mutex> lock(m_comm.m_mutex);
3048c1b6bd7SPavel Labath m_comm.m_is_running = false;
3058c1b6bd7SPavel Labath }
3068c1b6bd7SPavel Labath m_comm.m_cv.notify_all();
3078c1b6bd7SPavel Labath m_acquired = false;
3088c1b6bd7SPavel Labath }
3098c1b6bd7SPavel Labath
3108c1b6bd7SPavel Labath GDBRemoteClientBase::ContinueLock::LockResult
lock()311b9c1b51eSKate Stone GDBRemoteClientBase::ContinueLock::lock() {
312b1127753SPavel Labath Log *log = GetLog(GDBRLog::Process);
31363e5fb76SJonas Devlieghere LLDB_LOGF(log, "GDBRemoteClientBase::ContinueLock::%s() resuming with %s",
314b9c1b51eSKate Stone __FUNCTION__, m_comm.m_continue_packet.c_str());
3158c1b6bd7SPavel Labath
3168c1b6bd7SPavel Labath lldbassert(!m_acquired);
3178c1b6bd7SPavel Labath std::unique_lock<std::mutex> lock(m_comm.m_mutex);
3188c1b6bd7SPavel Labath m_comm.m_cv.wait(lock, [this] { return m_comm.m_async_count == 0; });
319b9c1b51eSKate Stone if (m_comm.m_should_stop) {
3208c1b6bd7SPavel Labath m_comm.m_should_stop = false;
32163e5fb76SJonas Devlieghere LLDB_LOGF(log, "GDBRemoteClientBase::ContinueLock::%s() cancelled",
322b9c1b51eSKate Stone __FUNCTION__);
3238c1b6bd7SPavel Labath return LockResult::Cancelled;
3248c1b6bd7SPavel Labath }
325b9c1b51eSKate Stone if (m_comm.SendPacketNoLock(m_comm.m_continue_packet) !=
326b9c1b51eSKate Stone PacketResult::Success)
3278c1b6bd7SPavel Labath return LockResult::Failed;
3288c1b6bd7SPavel Labath
3298c1b6bd7SPavel Labath lldbassert(!m_comm.m_is_running);
3308c1b6bd7SPavel Labath m_comm.m_is_running = true;
3318c1b6bd7SPavel Labath m_acquired = true;
3328c1b6bd7SPavel Labath return LockResult::Success;
3338c1b6bd7SPavel Labath }
3348c1b6bd7SPavel Labath
3358c1b6bd7SPavel Labath ///////////////////////////////
3368c1b6bd7SPavel Labath // GDBRemoteClientBase::Lock //
3378c1b6bd7SPavel Labath ///////////////////////////////
3388c1b6bd7SPavel Labath
Lock(GDBRemoteClientBase & comm,std::chrono::seconds interrupt_timeout)339379f24ffSJim Ingham GDBRemoteClientBase::Lock::Lock(GDBRemoteClientBase &comm,
340379f24ffSJim Ingham std::chrono::seconds interrupt_timeout)
341b9c1b51eSKate Stone : m_async_lock(comm.m_async_mutex, std::defer_lock), m_comm(comm),
342379f24ffSJim Ingham m_interrupt_timeout(interrupt_timeout), m_acquired(false),
343379f24ffSJim Ingham m_did_interrupt(false) {
344379f24ffSJim Ingham SyncWithContinueThread();
3458c1b6bd7SPavel Labath if (m_acquired)
3468c1b6bd7SPavel Labath m_async_lock.lock();
3478c1b6bd7SPavel Labath }
3488c1b6bd7SPavel Labath
SyncWithContinueThread()349379f24ffSJim Ingham void GDBRemoteClientBase::Lock::SyncWithContinueThread() {
350*63865e1fSJim Ingham Log *log = GetLog(GDBRLog::Process|GDBRLog::Packets);
3518c1b6bd7SPavel Labath std::unique_lock<std::mutex> lock(m_comm.m_mutex);
352379f24ffSJim Ingham if (m_comm.m_is_running && m_interrupt_timeout == std::chrono::seconds(0))
353b9c1b51eSKate Stone return; // We were asked to avoid interrupting the sender. Lock is not
354b9c1b51eSKate Stone // acquired.
3558c1b6bd7SPavel Labath
3568c1b6bd7SPavel Labath ++m_comm.m_async_count;
357b9c1b51eSKate Stone if (m_comm.m_is_running) {
358b9c1b51eSKate Stone if (m_comm.m_async_count == 1) {
359b9c1b51eSKate Stone // The sender has sent the continue packet and we are the first async
360b9c1b51eSKate Stone // packet. Let's interrupt it.
3618c1b6bd7SPavel Labath const char ctrl_c = '\x03';
3628c1b6bd7SPavel Labath ConnectionStatus status = eConnectionStatusSuccess;
363248a1305SKonrad Kleine size_t bytes_written = m_comm.Write(&ctrl_c, 1, status, nullptr);
364b9c1b51eSKate Stone if (bytes_written == 0) {
3658c1b6bd7SPavel Labath --m_comm.m_async_count;
36663e5fb76SJonas Devlieghere LLDB_LOGF(log, "GDBRemoteClientBase::Lock::Lock failed to send "
367b9c1b51eSKate Stone "interrupt packet");
3688c1b6bd7SPavel Labath return;
3698c1b6bd7SPavel Labath }
370379f24ffSJim Ingham m_comm.m_interrupt_endpoint = steady_clock::now() + m_interrupt_timeout;
3718c1b6bd7SPavel Labath if (log)
3728c1b6bd7SPavel Labath log->PutCString("GDBRemoteClientBase::Lock::Lock sent packet: \\x03");
3738c1b6bd7SPavel Labath }
374a6682a41SJonas Devlieghere m_comm.m_cv.wait(lock, [this] { return !m_comm.m_is_running; });
3758c1b6bd7SPavel Labath m_did_interrupt = true;
3768c1b6bd7SPavel Labath }
3778c1b6bd7SPavel Labath m_acquired = true;
3788c1b6bd7SPavel Labath }
3798c1b6bd7SPavel Labath
~Lock()380b9c1b51eSKate Stone GDBRemoteClientBase::Lock::~Lock() {
3818c1b6bd7SPavel Labath if (!m_acquired)
3828c1b6bd7SPavel Labath return;
3838c1b6bd7SPavel Labath {
3848c1b6bd7SPavel Labath std::unique_lock<std::mutex> lock(m_comm.m_mutex);
3858c1b6bd7SPavel Labath --m_comm.m_async_count;
3868c1b6bd7SPavel Labath }
3878c1b6bd7SPavel Labath m_comm.m_cv.notify_one();
3888c1b6bd7SPavel Labath }
389