#include "GDBRemoteClientBase.h"
#include "llvm/ADT/StringExtras.h"
#include "lldb/Target/UnixSignals.h"
#include "lldb/Utility/LLDBAssert.h"
#include "ProcessGDBRemoteLog.h"
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::process_gdb_remote;
using namespace std::chrono;
static const seconds kWakeupInterval(5);
GDBRemoteClientBase::ContinueDelegate::~ContinueDelegate() = default;
GDBRemoteClientBase::GDBRemoteClientBase(const char *comm_name)
: GDBRemoteCommunication(), Broadcaster(nullptr, comm_name),
m_async_count(0), m_is_running(false), m_should_stop(false) {}
StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse(
ContinueDelegate &delegate, const UnixSignals &signals,
llvm::StringRef payload, std::chrono::seconds interrupt_timeout,
StringExtractorGDBRemote &response) {
Log *log = GetLog(GDBRLog::Process);
response.Clear();
{
std::lock_guard<std::mutex> lock(m_mutex);
m_continue_packet = std::string(payload);
m_should_stop = false;
}
ContinueLock cont_lock(*this);
if (!cont_lock)
return eStateInvalid;
OnRunPacketSent(true);
std::chrono::seconds computed_timeout = std::min(interrupt_timeout,
kWakeupInterval);
for (;;) {
PacketResult read_result = ReadPacket(response, computed_timeout, false);
computed_timeout = std::min(interrupt_timeout, kWakeupInterval);
switch (read_result) {
case PacketResult::ErrorReplyTimeout: {
std::lock_guard<std::mutex> lock(m_mutex);
if (m_async_count == 0) {
continue;
}
auto cur_time = steady_clock::now();
if (cur_time >= m_interrupt_endpoint)
return eStateInvalid;
else {
auto new_wait = m_interrupt_endpoint - cur_time;
computed_timeout = std::min(kWakeupInterval,
std::chrono::duration_cast<std::chrono::seconds>(new_wait));
continue;
}
break;
}
case PacketResult::Success:
break;
default:
LLDB_LOGF(log, "GDBRemoteClientBase::%s () ReadPacket(...) => false",
__FUNCTION__);
return eStateInvalid;
}
if (response.Empty())
return eStateInvalid;
const char stop_type = response.GetChar();
LLDB_LOGF(log, "GDBRemoteClientBase::%s () got packet: %s", __FUNCTION__,
response.GetStringRef().data());
switch (stop_type) {
case 'W':
case 'X':
return eStateExited;
case 'E':
return eStateInvalid;
default:
LLDB_LOGF(log, "GDBRemoteClientBase::%s () unrecognized async packet",
__FUNCTION__);
return eStateInvalid;
case 'O': {
std::string inferior_stdout;
response.GetHexByteString(inferior_stdout);
delegate.HandleAsyncStdout(inferior_stdout);
break;
}
case 'A':
delegate.HandleAsyncMisc(
llvm::StringRef(response.GetStringRef()).substr(1));
break;
case 'J':
delegate.HandleAsyncStructuredDataPacket(response.GetStringRef());
break;
case 'T':
case 'S':
const bool should_stop = ShouldStop(signals, response);
response.SetFilePos(0);
m_continue_packet = 'c';
cont_lock.unlock();
delegate.HandleStopReply();
if (should_stop)
return eStateStopped;
switch (cont_lock.lock()) {
case ContinueLock::LockResult::Success:
break;
case ContinueLock::LockResult::Failed:
return eStateInvalid;
case ContinueLock::LockResult::Cancelled:
return eStateStopped;
}
OnRunPacketSent(false);
break;
}
}
}
bool GDBRemoteClientBase::SendAsyncSignal(
int signo, std::chrono::seconds interrupt_timeout) {
Lock lock(*this, interrupt_timeout);
if (!lock || !lock.DidInterrupt())
return false;
m_continue_packet = 'C';
m_continue_packet += llvm::hexdigit((signo / 16) % 16);
m_continue_packet += llvm::hexdigit(signo % 16);
return true;
}
bool GDBRemoteClientBase::Interrupt(std::chrono::seconds interrupt_timeout) {
Lock lock(*this, interrupt_timeout);
if (!lock.DidInterrupt())
return false;
m_should_stop = true;
return true;
}
GDBRemoteCommunication::PacketResult
GDBRemoteClientBase::SendPacketAndWaitForResponse(
llvm::StringRef payload, StringExtractorGDBRemote &response,
std::chrono::seconds interrupt_timeout) {
Lock lock(*this, interrupt_timeout);
if (!lock) {
if (Log *log = GetLog(GDBRLog::Process))
LLDB_LOGF(log,
"GDBRemoteClientBase::%s failed to get mutex, not sending "
"packet '%.*s'",
__FUNCTION__, int(payload.size()), payload.data());
return PacketResult::ErrorSendFailed;
}
return SendPacketAndWaitForResponseNoLock(payload, response);
}
GDBRemoteCommunication::PacketResult
GDBRemoteClientBase::ReadPacketWithOutputSupport(
StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
bool sync_on_timeout,
llvm::function_ref<void(llvm::StringRef)> output_callback) {
auto result = ReadPacket(response, timeout, sync_on_timeout);
while (result == PacketResult::Success && response.IsNormalResponse() &&
response.PeekChar() == 'O') {
response.GetChar();
std::string output;
if (response.GetHexByteString(output))
output_callback(output);
result = ReadPacket(response, timeout, sync_on_timeout);
}
return result;
}
GDBRemoteCommunication::PacketResult
GDBRemoteClientBase::SendPacketAndReceiveResponseWithOutputSupport(
llvm::StringRef payload, StringExtractorGDBRemote &response,
std::chrono::seconds interrupt_timeout,
llvm::function_ref<void(llvm::StringRef)> output_callback) {
Lock lock(*this, interrupt_timeout);
if (!lock) {
if (Log *log = GetLog(GDBRLog::Process))
LLDB_LOGF(log,
"GDBRemoteClientBase::%s failed to get mutex, not sending "
"packet '%.*s'",
__FUNCTION__, int(payload.size()), payload.data());
return PacketResult::ErrorSendFailed;
}
PacketResult packet_result = SendPacketNoLock(payload);
if (packet_result != PacketResult::Success)
return packet_result;
return ReadPacketWithOutputSupport(response, GetPacketTimeout(), true,
output_callback);
}
GDBRemoteCommunication::PacketResult
GDBRemoteClientBase::SendPacketAndWaitForResponseNoLock(
llvm::StringRef payload, StringExtractorGDBRemote &response) {
PacketResult packet_result = SendPacketNoLock(payload);
if (packet_result != PacketResult::Success)
return packet_result;
const size_t max_response_retries = 3;
for (size_t i = 0; i < max_response_retries; ++i) {
packet_result = ReadPacket(response, GetPacketTimeout(), true);
if (packet_result != PacketResult::Success)
return packet_result;
if (response.ValidateResponse())
return packet_result;
Log *log = GetLog(GDBRLog::Packets);
LLDB_LOGF(
log,
"error: packet with payload \"%.*s\" got invalid response \"%s\": %s",
int(payload.size()), payload.data(), response.GetStringRef().data(),
(i == (max_response_retries - 1))
? "using invalid response and giving up"
: "ignoring response and waiting for another");
}
return packet_result;
}
bool GDBRemoteClientBase::ShouldStop(const UnixSignals &signals,
StringExtractorGDBRemote &response) {
std::lock_guard<std::mutex> lock(m_mutex);
if (m_async_count == 0)
return true;
StringExtractorGDBRemote extra_stop_reply_packet;
ReadPacket(extra_stop_reply_packet, milliseconds(100), false);
const uint8_t signo = response.GetHexU8(UINT8_MAX);
if (signo != signals.GetSignalNumberFromName("SIGSTOP") &&
signo != signals.GetSignalNumberFromName("SIGINT"))
return true;
return false;
}
void GDBRemoteClientBase::OnRunPacketSent(bool first) {
if (first)
BroadcastEvent(eBroadcastBitRunPacketSent, nullptr);
}
GDBRemoteClientBase::ContinueLock::ContinueLock(GDBRemoteClientBase &comm)
: m_comm(comm), m_acquired(false) {
lock();
}
GDBRemoteClientBase::ContinueLock::~ContinueLock() {
if (m_acquired)
unlock();
}
void GDBRemoteClientBase::ContinueLock::unlock() {
lldbassert(m_acquired);
{
std::unique_lock<std::mutex> lock(m_comm.m_mutex);
m_comm.m_is_running = false;
}
m_comm.m_cv.notify_all();
m_acquired = false;
}
GDBRemoteClientBase::ContinueLock::LockResult
GDBRemoteClientBase::ContinueLock::lock() {
Log *log = GetLog(GDBRLog::Process);
LLDB_LOGF(log, "GDBRemoteClientBase::ContinueLock::%s() resuming with %s",
__FUNCTION__, m_comm.m_continue_packet.c_str());
lldbassert(!m_acquired);
std::unique_lock<std::mutex> lock(m_comm.m_mutex);
m_comm.m_cv.wait(lock, [this] { return m_comm.m_async_count == 0; });
if (m_comm.m_should_stop) {
m_comm.m_should_stop = false;
LLDB_LOGF(log, "GDBRemoteClientBase::ContinueLock::%s() cancelled",
__FUNCTION__);
return LockResult::Cancelled;
}
if (m_comm.SendPacketNoLock(m_comm.m_continue_packet) !=
PacketResult::Success)
return LockResult::Failed;
lldbassert(!m_comm.m_is_running);
m_comm.m_is_running = true;
m_acquired = true;
return LockResult::Success;
}
GDBRemoteClientBase::Lock::Lock(GDBRemoteClientBase &comm,
std::chrono::seconds interrupt_timeout)
: m_async_lock(comm.m_async_mutex, std::defer_lock), m_comm(comm),
m_interrupt_timeout(interrupt_timeout), m_acquired(false),
m_did_interrupt(false) {
SyncWithContinueThread();
if (m_acquired)
m_async_lock.lock();
}
void GDBRemoteClientBase::Lock::SyncWithContinueThread() {
Log *log = GetLog(GDBRLog::Process|GDBRLog::Packets);
std::unique_lock<std::mutex> lock(m_comm.m_mutex);
if (m_comm.m_is_running && m_interrupt_timeout == std::chrono::seconds(0))
return;
++m_comm.m_async_count;
if (m_comm.m_is_running) {
if (m_comm.m_async_count == 1) {
const char ctrl_c = '\x03';
ConnectionStatus status = eConnectionStatusSuccess;
size_t bytes_written = m_comm.Write(&ctrl_c, 1, status, nullptr);
if (bytes_written == 0) {
--m_comm.m_async_count;
LLDB_LOGF(log, "GDBRemoteClientBase::Lock::Lock failed to send "
"interrupt packet");
return;
}
m_comm.m_interrupt_endpoint = steady_clock::now() + m_interrupt_timeout;
if (log)
log->PutCString("GDBRemoteClientBase::Lock::Lock sent packet: \\x03");
}
m_comm.m_cv.wait(lock, [this] { return !m_comm.m_is_running; });
m_did_interrupt = true;
}
m_acquired = true;
}
GDBRemoteClientBase::Lock::~Lock() {
if (!m_acquired)
return;
{
std::unique_lock<std::mutex> lock(m_comm.m_mutex);
--m_comm.m_async_count;
}
m_comm.m_cv.notify_one();
}