Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
39642 views
//===-- GDBRemoteClientBase.cpp -------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "GDBRemoteClientBase.h"910#include "llvm/ADT/StringExtras.h"1112#include "lldb/Target/UnixSignals.h"13#include "lldb/Utility/LLDBAssert.h"1415#include "ProcessGDBRemoteLog.h"1617using namespace lldb;18using namespace lldb_private;19using namespace lldb_private::process_gdb_remote;20using namespace std::chrono;2122// When we've sent a continue packet and are waiting for the target to stop,23// we wake up the wait with this interval to make sure the stub hasn't gone24// away while we were waiting.25static const seconds kWakeupInterval(5);2627/////////////////////////28// GDBRemoteClientBase //29/////////////////////////3031GDBRemoteClientBase::ContinueDelegate::~ContinueDelegate() = default;3233GDBRemoteClientBase::GDBRemoteClientBase(const char *comm_name)34: GDBRemoteCommunication(), Broadcaster(nullptr, comm_name),35m_async_count(0), m_is_running(false), m_should_stop(false) {}3637StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse(38ContinueDelegate &delegate, const UnixSignals &signals,39llvm::StringRef payload, std::chrono::seconds interrupt_timeout,40StringExtractorGDBRemote &response) {41Log *log = GetLog(GDBRLog::Process);42response.Clear();4344{45std::lock_guard<std::mutex> lock(m_mutex);46m_continue_packet = std::string(payload);47m_should_stop = false;48}49ContinueLock cont_lock(*this);50if (!cont_lock)51return eStateInvalid;52OnRunPacketSent(true);53// The main ReadPacket loop wakes up at computed_timeout intervals, just to54// check that the connection hasn't dropped. When we wake up we also check55// whether there is an interrupt request that has reached its endpoint.56// If we want a shorter interrupt timeout that kWakeupInterval, we need to57// choose the shorter interval for the wake up as well.58std::chrono::seconds computed_timeout = std::min(interrupt_timeout,59kWakeupInterval);60for (;;) {61PacketResult read_result = ReadPacket(response, computed_timeout, false);62// Reset the computed_timeout to the default value in case we are going63// round again.64computed_timeout = std::min(interrupt_timeout, kWakeupInterval);65switch (read_result) {66case PacketResult::ErrorReplyTimeout: {67std::lock_guard<std::mutex> lock(m_mutex);68if (m_async_count == 0) {69continue;70}71auto cur_time = steady_clock::now();72if (cur_time >= m_interrupt_endpoint)73return eStateInvalid;74else {75// We woke up and found an interrupt is in flight, but we haven't76// exceeded the interrupt wait time. So reset the wait time to the77// time left till the interrupt timeout. But don't wait longer78// than our wakeup timeout.79auto new_wait = m_interrupt_endpoint - cur_time;80computed_timeout = std::min(kWakeupInterval,81std::chrono::duration_cast<std::chrono::seconds>(new_wait));82continue;83}84break;85}86case PacketResult::Success:87break;88default:89LLDB_LOGF(log, "GDBRemoteClientBase::%s () ReadPacket(...) => false",90__FUNCTION__);91return eStateInvalid;92}93if (response.Empty())94return eStateInvalid;9596const char stop_type = response.GetChar();97LLDB_LOGF(log, "GDBRemoteClientBase::%s () got packet: %s", __FUNCTION__,98response.GetStringRef().data());99100switch (stop_type) {101case 'W':102case 'X':103return eStateExited;104case 'E':105// ERROR106return eStateInvalid;107default:108LLDB_LOGF(log, "GDBRemoteClientBase::%s () unrecognized async packet",109__FUNCTION__);110return eStateInvalid;111case 'O': {112std::string inferior_stdout;113response.GetHexByteString(inferior_stdout);114delegate.HandleAsyncStdout(inferior_stdout);115break;116}117case 'A':118delegate.HandleAsyncMisc(119llvm::StringRef(response.GetStringRef()).substr(1));120break;121case 'J':122delegate.HandleAsyncStructuredDataPacket(response.GetStringRef());123break;124case 'T':125case 'S':126// Do this with the continue lock held.127const bool should_stop = ShouldStop(signals, response);128response.SetFilePos(0);129130// The packet we should resume with. In the future we should check our131// thread list and "do the right thing" for new threads that show up132// while we stop and run async packets. Setting the packet to 'c' to133// continue all threads is the right thing to do 99.99% of the time134// because if a thread was single stepping, and we sent an interrupt, we135// will notice above that we didn't stop due to an interrupt but stopped136// due to stepping and we would _not_ continue. This packet may get137// modified by the async actions (e.g. to send a signal).138m_continue_packet = 'c';139cont_lock.unlock();140141delegate.HandleStopReply();142if (should_stop)143return eStateStopped;144145switch (cont_lock.lock()) {146case ContinueLock::LockResult::Success:147break;148case ContinueLock::LockResult::Failed:149return eStateInvalid;150case ContinueLock::LockResult::Cancelled:151return eStateStopped;152}153OnRunPacketSent(false);154break;155}156}157}158159bool GDBRemoteClientBase::SendAsyncSignal(160int signo, std::chrono::seconds interrupt_timeout) {161Lock lock(*this, interrupt_timeout);162if (!lock || !lock.DidInterrupt())163return false;164165m_continue_packet = 'C';166m_continue_packet += llvm::hexdigit((signo / 16) % 16);167m_continue_packet += llvm::hexdigit(signo % 16);168return true;169}170171bool GDBRemoteClientBase::Interrupt(std::chrono::seconds interrupt_timeout) {172Lock lock(*this, interrupt_timeout);173if (!lock.DidInterrupt())174return false;175m_should_stop = true;176return true;177}178179GDBRemoteCommunication::PacketResult180GDBRemoteClientBase::SendPacketAndWaitForResponse(181llvm::StringRef payload, StringExtractorGDBRemote &response,182std::chrono::seconds interrupt_timeout) {183Lock lock(*this, interrupt_timeout);184if (!lock) {185if (Log *log = GetLog(GDBRLog::Process))186LLDB_LOGF(log,187"GDBRemoteClientBase::%s failed to get mutex, not sending "188"packet '%.*s'",189__FUNCTION__, int(payload.size()), payload.data());190return PacketResult::ErrorSendFailed;191}192193return SendPacketAndWaitForResponseNoLock(payload, response);194}195196GDBRemoteCommunication::PacketResult197GDBRemoteClientBase::ReadPacketWithOutputSupport(198StringExtractorGDBRemote &response, Timeout<std::micro> timeout,199bool sync_on_timeout,200llvm::function_ref<void(llvm::StringRef)> output_callback) {201auto result = ReadPacket(response, timeout, sync_on_timeout);202while (result == PacketResult::Success && response.IsNormalResponse() &&203response.PeekChar() == 'O') {204response.GetChar();205std::string output;206if (response.GetHexByteString(output))207output_callback(output);208result = ReadPacket(response, timeout, sync_on_timeout);209}210return result;211}212213GDBRemoteCommunication::PacketResult214GDBRemoteClientBase::SendPacketAndReceiveResponseWithOutputSupport(215llvm::StringRef payload, StringExtractorGDBRemote &response,216std::chrono::seconds interrupt_timeout,217llvm::function_ref<void(llvm::StringRef)> output_callback) {218Lock lock(*this, interrupt_timeout);219if (!lock) {220if (Log *log = GetLog(GDBRLog::Process))221LLDB_LOGF(log,222"GDBRemoteClientBase::%s failed to get mutex, not sending "223"packet '%.*s'",224__FUNCTION__, int(payload.size()), payload.data());225return PacketResult::ErrorSendFailed;226}227228PacketResult packet_result = SendPacketNoLock(payload);229if (packet_result != PacketResult::Success)230return packet_result;231232return ReadPacketWithOutputSupport(response, GetPacketTimeout(), true,233output_callback);234}235236GDBRemoteCommunication::PacketResult237GDBRemoteClientBase::SendPacketAndWaitForResponseNoLock(238llvm::StringRef payload, StringExtractorGDBRemote &response) {239PacketResult packet_result = SendPacketNoLock(payload);240if (packet_result != PacketResult::Success)241return packet_result;242243const size_t max_response_retries = 3;244for (size_t i = 0; i < max_response_retries; ++i) {245packet_result = ReadPacket(response, GetPacketTimeout(), true);246// Make sure we received a response247if (packet_result != PacketResult::Success)248return packet_result;249// Make sure our response is valid for the payload that was sent250if (response.ValidateResponse())251return packet_result;252// Response says it wasn't valid253Log *log = GetLog(GDBRLog::Packets);254LLDB_LOGF(255log,256"error: packet with payload \"%.*s\" got invalid response \"%s\": %s",257int(payload.size()), payload.data(), response.GetStringRef().data(),258(i == (max_response_retries - 1))259? "using invalid response and giving up"260: "ignoring response and waiting for another");261}262return packet_result;263}264265bool GDBRemoteClientBase::ShouldStop(const UnixSignals &signals,266StringExtractorGDBRemote &response) {267std::lock_guard<std::mutex> lock(m_mutex);268269if (m_async_count == 0)270return true; // We were not interrupted. The process stopped on its own.271272// Older debugserver stubs (before April 2016) can return two stop-reply273// packets in response to a ^C packet. Additionally, all debugservers still274// return two stop replies if the inferior stops due to some other reason275// before the remote stub manages to interrupt it. We need to wait for this276// additional packet to make sure the packet sequence does not get skewed.277StringExtractorGDBRemote extra_stop_reply_packet;278ReadPacket(extra_stop_reply_packet, milliseconds(100), false);279280// Interrupting is typically done using SIGSTOP or SIGINT, so if the process281// stops with some other signal, we definitely want to stop.282const uint8_t signo = response.GetHexU8(UINT8_MAX);283if (signo != signals.GetSignalNumberFromName("SIGSTOP") &&284signo != signals.GetSignalNumberFromName("SIGINT"))285return true;286287// We probably only stopped to perform some async processing, so continue288// after that is done.289// TODO: This is not 100% correct, as the process may have been stopped with290// SIGINT or SIGSTOP that was not caused by us (e.g. raise(SIGINT)). This will291// normally cause a stop, but if it's done concurrently with a async292// interrupt, that stop will get eaten (llvm.org/pr20231).293return false;294}295296void GDBRemoteClientBase::OnRunPacketSent(bool first) {297if (first)298BroadcastEvent(eBroadcastBitRunPacketSent, nullptr);299}300301///////////////////////////////////////302// GDBRemoteClientBase::ContinueLock //303///////////////////////////////////////304305GDBRemoteClientBase::ContinueLock::ContinueLock(GDBRemoteClientBase &comm)306: m_comm(comm), m_acquired(false) {307lock();308}309310GDBRemoteClientBase::ContinueLock::~ContinueLock() {311if (m_acquired)312unlock();313}314315void GDBRemoteClientBase::ContinueLock::unlock() {316lldbassert(m_acquired);317{318std::unique_lock<std::mutex> lock(m_comm.m_mutex);319m_comm.m_is_running = false;320}321m_comm.m_cv.notify_all();322m_acquired = false;323}324325GDBRemoteClientBase::ContinueLock::LockResult326GDBRemoteClientBase::ContinueLock::lock() {327Log *log = GetLog(GDBRLog::Process);328LLDB_LOGF(log, "GDBRemoteClientBase::ContinueLock::%s() resuming with %s",329__FUNCTION__, m_comm.m_continue_packet.c_str());330331lldbassert(!m_acquired);332std::unique_lock<std::mutex> lock(m_comm.m_mutex);333m_comm.m_cv.wait(lock, [this] { return m_comm.m_async_count == 0; });334if (m_comm.m_should_stop) {335m_comm.m_should_stop = false;336LLDB_LOGF(log, "GDBRemoteClientBase::ContinueLock::%s() cancelled",337__FUNCTION__);338return LockResult::Cancelled;339}340if (m_comm.SendPacketNoLock(m_comm.m_continue_packet) !=341PacketResult::Success)342return LockResult::Failed;343344lldbassert(!m_comm.m_is_running);345m_comm.m_is_running = true;346m_acquired = true;347return LockResult::Success;348}349350///////////////////////////////351// GDBRemoteClientBase::Lock //352///////////////////////////////353354GDBRemoteClientBase::Lock::Lock(GDBRemoteClientBase &comm,355std::chrono::seconds interrupt_timeout)356: m_async_lock(comm.m_async_mutex, std::defer_lock), m_comm(comm),357m_interrupt_timeout(interrupt_timeout), m_acquired(false),358m_did_interrupt(false) {359SyncWithContinueThread();360if (m_acquired)361m_async_lock.lock();362}363364void GDBRemoteClientBase::Lock::SyncWithContinueThread() {365Log *log = GetLog(GDBRLog::Process|GDBRLog::Packets);366std::unique_lock<std::mutex> lock(m_comm.m_mutex);367if (m_comm.m_is_running && m_interrupt_timeout == std::chrono::seconds(0))368return; // We were asked to avoid interrupting the sender. Lock is not369// acquired.370371++m_comm.m_async_count;372if (m_comm.m_is_running) {373if (m_comm.m_async_count == 1) {374// The sender has sent the continue packet and we are the first async375// packet. Let's interrupt it.376const char ctrl_c = '\x03';377ConnectionStatus status = eConnectionStatusSuccess;378size_t bytes_written = m_comm.Write(&ctrl_c, 1, status, nullptr);379if (bytes_written == 0) {380--m_comm.m_async_count;381LLDB_LOGF(log, "GDBRemoteClientBase::Lock::Lock failed to send "382"interrupt packet");383return;384}385m_comm.m_interrupt_endpoint = steady_clock::now() + m_interrupt_timeout;386if (log)387log->PutCString("GDBRemoteClientBase::Lock::Lock sent packet: \\x03");388}389m_comm.m_cv.wait(lock, [this] { return !m_comm.m_is_running; });390m_did_interrupt = true;391}392m_acquired = true;393}394395GDBRemoteClientBase::Lock::~Lock() {396if (!m_acquired)397return;398{399std::unique_lock<std::mutex> lock(m_comm.m_mutex);400--m_comm.m_async_count;401}402m_comm.m_cv.notify_one();403}404405406