forked from OSchip/llvm-project
Revert "Rewrite gdb-remote's SendContinuePacketAndWaitForResponse"
This reverts commit r277139, because: - broken unittest on windows (likely typo on my part) - seems to break TestCallThatRestart (needs investigation) llvm-svn: 277154
This commit is contained in:
parent
0702c4e86e
commit
4cb699260c
|
@ -3,7 +3,6 @@ if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
|||
endif()
|
||||
|
||||
add_lldb_library(lldbPluginProcessGDBRemote
|
||||
GDBRemoteClientBase.cpp
|
||||
GDBRemoteCommunication.cpp
|
||||
GDBRemoteCommunicationClient.cpp
|
||||
GDBRemoteCommunicationServer.cpp
|
||||
|
|
|
@ -1,378 +0,0 @@
|
|||
//===-- GDBRemoteClientBase.cpp ---------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#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;
|
||||
|
||||
static const std::chrono::seconds kInterruptTimeout(5);
|
||||
|
||||
/////////////////////////
|
||||
// GDBRemoteClientBase //
|
||||
/////////////////////////
|
||||
|
||||
GDBRemoteClientBase::ContinueDelegate::~ContinueDelegate() = default;
|
||||
|
||||
GDBRemoteClientBase::GDBRemoteClientBase(const char *comm_name, const char *listener_name)
|
||||
: GDBRemoteCommunication(comm_name, listener_name), m_async_count(0), m_is_running(false), m_should_stop(false)
|
||||
{
|
||||
}
|
||||
|
||||
StateType
|
||||
GDBRemoteClientBase::SendContinuePacketAndWaitForResponse(ContinueDelegate &delegate, const UnixSignals &signals,
|
||||
llvm::StringRef payload, StringExtractorGDBRemote &response)
|
||||
{
|
||||
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
|
||||
response.Clear();
|
||||
|
||||
m_continue_packet = payload;
|
||||
ContinueLock cont_lock(*this);
|
||||
if (!cont_lock)
|
||||
return eStateInvalid;
|
||||
OnRunPacketSent(true);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
PacketResult read_result = ReadPacket(
|
||||
response, std::chrono::duration_cast<std::chrono::microseconds>(kInterruptTimeout).count(), false);
|
||||
switch (read_result)
|
||||
{
|
||||
case PacketResult::ErrorReplyTimeout:
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (m_async_count == 0)
|
||||
continue;
|
||||
if (std::chrono::steady_clock::now() >= m_interrupt_time + kInterruptTimeout)
|
||||
return eStateInvalid;
|
||||
}
|
||||
case PacketResult::Success:
|
||||
break;
|
||||
default:
|
||||
if (log)
|
||||
log->Printf("GDBRemoteClientBase::%s () ReadPacket(...) => false", __FUNCTION__);
|
||||
return eStateInvalid;
|
||||
}
|
||||
if (response.Empty())
|
||||
return eStateInvalid;
|
||||
|
||||
const char stop_type = response.GetChar();
|
||||
if (log)
|
||||
log->Printf("GDBRemoteClientBase::%s () got packet: %s", __FUNCTION__, response.GetStringRef().c_str());
|
||||
|
||||
switch (stop_type)
|
||||
{
|
||||
case 'W':
|
||||
case 'X':
|
||||
return eStateExited;
|
||||
case 'E':
|
||||
// ERROR
|
||||
return eStateInvalid;
|
||||
default:
|
||||
if (log)
|
||||
log->Printf("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 'T':
|
||||
case 'S':
|
||||
// Do this with the continue lock held.
|
||||
const bool should_stop = ShouldStop(signals, response);
|
||||
response.SetFilePos(0);
|
||||
|
||||
// The packet we should resume with. In the future
|
||||
// we should check our thread list and "do the right thing"
|
||||
// for new threads that show up while we stop and run async
|
||||
// packets. Setting the packet to 'c' to continue all threads
|
||||
// is the right thing to do 99.99% of the time because if a
|
||||
// thread was single stepping, and we sent an interrupt, we
|
||||
// will notice above that we didn't stop due to an interrupt
|
||||
// but stopped due to stepping and we would _not_ continue.
|
||||
// This packet may get modified by the async actions (e.g. to send a signal).
|
||||
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)
|
||||
{
|
||||
Lock lock(*this, true);
|
||||
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()
|
||||
{
|
||||
Lock lock(*this, true);
|
||||
if (!lock.DidInterrupt())
|
||||
return false;
|
||||
m_should_stop = true;
|
||||
return true;
|
||||
}
|
||||
GDBRemoteCommunication::PacketResult
|
||||
GDBRemoteClientBase::SendPacketAndWaitForResponse(llvm::StringRef payload, StringExtractorGDBRemote &response,
|
||||
bool send_async)
|
||||
{
|
||||
Lock lock(*this, send_async);
|
||||
if (!lock)
|
||||
{
|
||||
if (Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS))
|
||||
log->Printf("GDBRemoteClientBase::%s failed to get mutex, not sending packet '%.*s' (send_async=%d)",
|
||||
__FUNCTION__, int(payload.size()), payload.data(), send_async);
|
||||
return PacketResult::ErrorSendFailed;
|
||||
}
|
||||
|
||||
return SendPacketAndWaitForResponseNoLock(payload, response);
|
||||
}
|
||||
|
||||
GDBRemoteCommunication::PacketResult
|
||||
GDBRemoteClientBase::SendPacketAndWaitForResponseNoLock(llvm::StringRef payload, StringExtractorGDBRemote &response)
|
||||
{
|
||||
PacketResult packet_result = SendPacketNoLock(payload.data(), payload.size());
|
||||
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, GetPacketTimeoutInMicroSeconds(), true);
|
||||
// Make sure we received a response
|
||||
if (packet_result != PacketResult::Success)
|
||||
return packet_result;
|
||||
// Make sure our response is valid for the payload that was sent
|
||||
if (response.ValidateResponse())
|
||||
return packet_result;
|
||||
// Response says it wasn't valid
|
||||
Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS);
|
||||
if (log)
|
||||
log->Printf("error: packet with payload \"%.*s\" got invalid response \"%s\": %s", int(payload.size()),
|
||||
payload.data(), response.GetStringRef().c_str(),
|
||||
(i == (max_response_retries - 1)) ? "using invalid response and giving up"
|
||||
: "ignoring response and waiting for another");
|
||||
}
|
||||
return packet_result;
|
||||
}
|
||||
|
||||
bool
|
||||
GDBRemoteClientBase::SendvContPacket(llvm::StringRef payload, StringExtractorGDBRemote &response)
|
||||
{
|
||||
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
|
||||
if (log)
|
||||
log->Printf("GDBRemoteCommunicationClient::%s ()", __FUNCTION__);
|
||||
|
||||
// we want to lock down packet sending while we continue
|
||||
Lock lock(*this, true);
|
||||
|
||||
if (log)
|
||||
log->Printf("GDBRemoteCommunicationClient::%s () sending vCont packet: %.*s", __FUNCTION__, int(payload.size()),
|
||||
payload.data());
|
||||
|
||||
if (SendPacketNoLock(payload.data(), payload.size()) != PacketResult::Success)
|
||||
return false;
|
||||
|
||||
OnRunPacketSent(true);
|
||||
|
||||
// wait for the response to the vCont
|
||||
if (ReadPacket(response, UINT32_MAX, false) == PacketResult::Success)
|
||||
{
|
||||
if (response.IsOKResponse())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool
|
||||
GDBRemoteClientBase::ShouldStop(const UnixSignals &signals, StringExtractorGDBRemote &response)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
if (m_async_count == 0)
|
||||
return true; // We were not interrupted. The process stopped on its own.
|
||||
|
||||
// Older debugserver stubs (before April 2016) can return two
|
||||
// stop-reply packets in response to a ^C packet.
|
||||
// Additionally, all debugservers still return two stop replies if
|
||||
// the inferior stops due to some other reason before the remote
|
||||
// stub manages to interrupt it. We need to wait for this
|
||||
// additional packet to make sure the packet sequence does not get
|
||||
// skewed.
|
||||
StringExtractorGDBRemote extra_stop_reply_packet;
|
||||
uint32_t timeout_usec = 100000; // 100ms
|
||||
ReadPacket(extra_stop_reply_packet, timeout_usec, false);
|
||||
|
||||
// Interrupting is typically done using SIGSTOP or SIGINT, so if
|
||||
// the process stops with some other signal, we definitely want to
|
||||
// stop.
|
||||
const uint8_t signo = response.GetHexU8(UINT8_MAX);
|
||||
if (signo != signals.GetSignalNumberFromName("SIGSTOP") && signo != signals.GetSignalNumberFromName("SIGINT"))
|
||||
return true;
|
||||
|
||||
// We probably only stopped to perform some async processing, so continue after that is done.
|
||||
// TODO: This is not 100% correct, as the process may have been stopped with SIGINT or SIGSTOP
|
||||
// that was not caused by us (e.g. raise(SIGINT)). This will normally cause a stop, but if it's
|
||||
// done concurrently with a async interrupt, that stop will get eaten (llvm.org/pr20231).
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
GDBRemoteClientBase::OnRunPacketSent(bool first)
|
||||
{
|
||||
if (first)
|
||||
BroadcastEvent(eBroadcastBitRunPacketSent, NULL);
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
// GDBRemoteClientBase::ContinueLock //
|
||||
///////////////////////////////////////
|
||||
|
||||
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 = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS);
|
||||
if (log)
|
||||
log->Printf("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;
|
||||
return LockResult::Cancelled;
|
||||
}
|
||||
if (m_comm.SendPacketNoLock(m_comm.m_continue_packet.data(), m_comm.m_continue_packet.size()) !=
|
||||
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 //
|
||||
///////////////////////////////
|
||||
|
||||
GDBRemoteClientBase::Lock::Lock(GDBRemoteClientBase &comm, bool interrupt)
|
||||
: m_async_lock(comm.m_async_mutex, std::defer_lock), m_comm(comm), m_acquired(false), m_did_interrupt(false)
|
||||
{
|
||||
SyncWithContinueThread(interrupt);
|
||||
if (m_acquired)
|
||||
m_async_lock.lock();
|
||||
}
|
||||
|
||||
void
|
||||
GDBRemoteClientBase::Lock::SyncWithContinueThread(bool interrupt)
|
||||
{
|
||||
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
|
||||
std::unique_lock<std::mutex> lock(m_comm.m_mutex);
|
||||
if (m_comm.m_is_running && !interrupt)
|
||||
return; // We were asked to avoid interrupting the sender. Lock is not acquired.
|
||||
|
||||
++m_comm.m_async_count;
|
||||
if (m_comm.m_is_running)
|
||||
{
|
||||
if (m_comm.m_async_count == 1)
|
||||
{
|
||||
// The sender has sent the continue packet and we are the first async packet. Let's interrupt it.
|
||||
const char ctrl_c = '\x03';
|
||||
ConnectionStatus status = eConnectionStatusSuccess;
|
||||
size_t bytes_written = m_comm.Write(&ctrl_c, 1, status, NULL);
|
||||
if (bytes_written == 0)
|
||||
{
|
||||
--m_comm.m_async_count;
|
||||
if (log)
|
||||
log->Printf("GDBRemoteClientBase::Lock::Lock failed to send interrupt packet");
|
||||
return;
|
||||
}
|
||||
if (log)
|
||||
log->PutCString("GDBRemoteClientBase::Lock::Lock sent packet: \\x03");
|
||||
m_comm.m_interrupt_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
m_comm.m_cv.wait(lock, [this] { return m_comm.m_is_running == false; });
|
||||
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();
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
//===-- GDBRemoteClientBase.h -----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef liblldb_GDBRemoteClientBase_h_
|
||||
#define liblldb_GDBRemoteClientBase_h_
|
||||
|
||||
#include "GDBRemoteCommunication.h"
|
||||
|
||||
#include <condition_variable>
|
||||
|
||||
namespace lldb_private
|
||||
{
|
||||
namespace process_gdb_remote
|
||||
{
|
||||
|
||||
class GDBRemoteClientBase : public GDBRemoteCommunication
|
||||
{
|
||||
public:
|
||||
struct ContinueDelegate
|
||||
{
|
||||
virtual ~ContinueDelegate();
|
||||
virtual void
|
||||
HandleAsyncStdout(llvm::StringRef out) = 0;
|
||||
virtual void
|
||||
HandleAsyncMisc(llvm::StringRef data) = 0;
|
||||
virtual void
|
||||
HandleStopReply() = 0;
|
||||
};
|
||||
|
||||
GDBRemoteClientBase(const char *comm_name, const char *listener_name);
|
||||
|
||||
bool
|
||||
SendAsyncSignal(int signo);
|
||||
|
||||
bool
|
||||
Interrupt();
|
||||
|
||||
lldb::StateType
|
||||
SendContinuePacketAndWaitForResponse(ContinueDelegate &delegate, const UnixSignals &signals,
|
||||
llvm::StringRef payload, StringExtractorGDBRemote &response);
|
||||
|
||||
PacketResult
|
||||
SendPacketAndWaitForResponse(const char *payload, size_t len, StringExtractorGDBRemote &response, bool send_async)
|
||||
{
|
||||
return SendPacketAndWaitForResponse(llvm::StringRef(payload, len), response, send_async);
|
||||
}
|
||||
|
||||
PacketResult
|
||||
SendPacketAndWaitForResponse(llvm::StringRef payload, StringExtractorGDBRemote &response, bool send_async);
|
||||
|
||||
bool
|
||||
SendvContPacket(llvm::StringRef payload, StringExtractorGDBRemote &response);
|
||||
|
||||
class Lock
|
||||
{
|
||||
public:
|
||||
Lock(GDBRemoteClientBase &comm, bool interrupt);
|
||||
~Lock();
|
||||
|
||||
explicit operator bool() { return m_acquired; }
|
||||
|
||||
// Whether we had to interrupt the continue thread to acquire the connection.
|
||||
bool
|
||||
DidInterrupt() const
|
||||
{
|
||||
return m_did_interrupt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_lock<std::recursive_mutex> m_async_lock;
|
||||
GDBRemoteClientBase &m_comm;
|
||||
bool m_acquired;
|
||||
bool m_did_interrupt;
|
||||
|
||||
void
|
||||
SyncWithContinueThread(bool interrupt);
|
||||
};
|
||||
|
||||
protected:
|
||||
PacketResult
|
||||
SendPacketAndWaitForResponseNoLock(llvm::StringRef payload, StringExtractorGDBRemote &response);
|
||||
|
||||
virtual void
|
||||
OnRunPacketSent(bool first);
|
||||
|
||||
private:
|
||||
// Variables handling synchronization between the Continue thread and any other threads
|
||||
// wishing to send packets over the connection. Either the continue thread has control over
|
||||
// the connection (m_is_running == true) or the connection is free for an arbitrary number of
|
||||
// other senders to take which indicate their interest by incrementing m_async_count.
|
||||
// Semantics of individual states:
|
||||
// - m_continue_packet == false, m_async_count == 0: connection is free
|
||||
// - m_continue_packet == true, m_async_count == 0: only continue thread is present
|
||||
// - m_continue_packet == true, m_async_count > 0: continue thread has control, async threads
|
||||
// should interrupt it and wait for it to set m_continue_packet to false
|
||||
// - m_continue_packet == false, m_async_count > 0: async threads have control, continue
|
||||
// thread needs to wait for them to finish (m_async_count goes down to 0).
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
// Packet with which to resume after an async interrupt. Can be changed by an async thread
|
||||
// e.g. to inject a signal.
|
||||
std::string m_continue_packet;
|
||||
// When was the interrupt packet sent. Used to make sure we time out if the stub does not
|
||||
// respond to interrupt requests.
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_interrupt_time;
|
||||
uint32_t m_async_count;
|
||||
bool m_is_running;
|
||||
bool m_should_stop; // Whether we should resume after a stop.
|
||||
// end of continue thread synchronization block
|
||||
|
||||
// This handles the synchronization between individual async threads. For now they just use a
|
||||
// simple mutex.
|
||||
std::recursive_mutex m_async_mutex;
|
||||
|
||||
bool
|
||||
ShouldStop(const UnixSignals &signals, StringExtractorGDBRemote &response);
|
||||
|
||||
class ContinueLock
|
||||
{
|
||||
public:
|
||||
enum class LockResult
|
||||
{
|
||||
Success,
|
||||
Cancelled,
|
||||
Failed
|
||||
};
|
||||
|
||||
explicit ContinueLock(GDBRemoteClientBase &comm);
|
||||
~ContinueLock();
|
||||
explicit operator bool() { return m_acquired; }
|
||||
|
||||
LockResult
|
||||
lock();
|
||||
|
||||
void
|
||||
unlock();
|
||||
|
||||
private:
|
||||
GDBRemoteClientBase &m_comm;
|
||||
bool m_acquired;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace process_gdb_remote
|
||||
} // namespace lldb_private
|
||||
|
||||
#endif // liblldb_GDBRemoteCommunicationClient_h_
|
|
@ -161,6 +161,9 @@ GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name, const char
|
|||
#endif
|
||||
m_echo_number(0),
|
||||
m_supports_qEcho(eLazyBoolCalculate),
|
||||
m_sequence_mutex(),
|
||||
m_public_is_running(false),
|
||||
m_private_is_running(false),
|
||||
m_history(512),
|
||||
m_send_acks(true),
|
||||
m_compression_type(CompressionType::None),
|
||||
|
@ -222,6 +225,13 @@ GDBRemoteCommunication::SendNack ()
|
|||
return bytes_written;
|
||||
}
|
||||
|
||||
GDBRemoteCommunication::PacketResult
|
||||
GDBRemoteCommunication::SendPacket (const char *payload, size_t payload_length)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_sequence_mutex);
|
||||
return SendPacketNoLock (payload, payload_length);
|
||||
}
|
||||
|
||||
GDBRemoteCommunication::PacketResult
|
||||
GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_length)
|
||||
{
|
||||
|
@ -311,6 +321,22 @@ GDBRemoteCommunication::GetAck ()
|
|||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
GDBRemoteCommunication::GetSequenceMutex(std::unique_lock<std::recursive_mutex> &lock, const char *failure_message)
|
||||
{
|
||||
if (IsRunning())
|
||||
return (lock = std::unique_lock<std::recursive_mutex>(m_sequence_mutex, std::try_to_lock)).owns_lock();
|
||||
|
||||
lock = std::unique_lock<std::recursive_mutex>(m_sequence_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GDBRemoteCommunication::WaitForNotRunningPrivate(const std::chrono::microseconds &timeout)
|
||||
{
|
||||
return m_private_is_running.WaitForValueEqualTo(false, timeout, NULL);
|
||||
}
|
||||
|
||||
GDBRemoteCommunication::PacketResult
|
||||
GDBRemoteCommunication::ReadPacket (StringExtractorGDBRemote &response, uint32_t timeout_usec, bool sync_on_timeout)
|
||||
{
|
||||
|
|
|
@ -114,11 +114,20 @@ public:
|
|||
CalculcateChecksum (const char *payload,
|
||||
size_t payload_length);
|
||||
|
||||
bool
|
||||
GetSequenceMutex(std::unique_lock<std::recursive_mutex> &lock, const char *failure_message = nullptr);
|
||||
|
||||
PacketType
|
||||
CheckForPacket (const uint8_t *src,
|
||||
size_t src_len,
|
||||
StringExtractorGDBRemote &packet);
|
||||
|
||||
bool
|
||||
IsRunning() const
|
||||
{
|
||||
return m_public_is_running.GetValue();
|
||||
}
|
||||
|
||||
bool
|
||||
GetSendAcks ()
|
||||
{
|
||||
|
@ -276,6 +285,13 @@ protected:
|
|||
uint32_t m_packet_timeout;
|
||||
uint32_t m_echo_number;
|
||||
LazyBool m_supports_qEcho;
|
||||
#ifdef ENABLE_MUTEX_ERROR_CHECKING
|
||||
#error TrackingMutex is no longer supported
|
||||
#else
|
||||
std::recursive_mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time
|
||||
#endif
|
||||
Predicate<bool> m_public_is_running;
|
||||
Predicate<bool> m_private_is_running;
|
||||
History m_history;
|
||||
bool m_send_acks;
|
||||
bool m_is_platform; // Set to true if this class represents a platform,
|
||||
|
@ -284,6 +300,10 @@ protected:
|
|||
|
||||
CompressionType m_compression_type;
|
||||
|
||||
PacketResult
|
||||
SendPacket (const char *payload,
|
||||
size_t payload_length);
|
||||
|
||||
PacketResult
|
||||
SendPacketNoLock (const char *payload,
|
||||
size_t payload_length);
|
||||
|
@ -300,6 +320,9 @@ protected:
|
|||
uint32_t timeout_usec,
|
||||
bool sync_on_timeout);
|
||||
|
||||
bool
|
||||
WaitForNotRunningPrivate(const std::chrono::microseconds &timeout);
|
||||
|
||||
bool
|
||||
CompressionIsEnabled ()
|
||||
{
|
||||
|
|
|
@ -19,18 +19,24 @@
|
|||
#include <numeric>
|
||||
|
||||
// Other libraries and framework includes
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "lldb/Interpreter/Args.h"
|
||||
#include "lldb/Core/Log.h"
|
||||
#include "lldb/Core/ModuleSpec.h"
|
||||
#include "lldb/Core/State.h"
|
||||
#include "lldb/Core/StreamGDBRemote.h"
|
||||
#include "lldb/Core/StreamString.h"
|
||||
#include "lldb/Host/ConnectionFileDescriptor.h"
|
||||
#include "lldb/Host/Endian.h"
|
||||
#include "lldb/Host/Host.h"
|
||||
#include "lldb/Host/HostInfo.h"
|
||||
#include "lldb/Host/StringConvert.h"
|
||||
#include "lldb/Host/TimeValue.h"
|
||||
#include "lldb/Interpreter/Args.h"
|
||||
#include "lldb/Symbol/Symbol.h"
|
||||
#include "lldb/Target/MemoryRegionInfo.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
#include "lldb/Target/MemoryRegionInfo.h"
|
||||
#include "lldb/Target/UnixSignals.h"
|
||||
|
||||
// Project includes
|
||||
#include "Utility/StringExtractorGDBRemote.h"
|
||||
|
@ -50,7 +56,7 @@ using namespace lldb_private::process_gdb_remote;
|
|||
// GDBRemoteCommunicationClient constructor
|
||||
//----------------------------------------------------------------------
|
||||
GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
|
||||
: GDBRemoteClientBase("gdb-remote.client", "gdb-remote.client.rx_packet"),
|
||||
: GDBRemoteCommunication("gdb-remote.client", "gdb-remote.client.rx_packet"),
|
||||
m_supports_not_sending_acks(eLazyBoolCalculate),
|
||||
m_supports_thread_suffix(eLazyBoolCalculate),
|
||||
m_supports_threads_in_stop_reply(eLazyBoolCalculate),
|
||||
|
@ -82,7 +88,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
|
|||
m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate),
|
||||
m_supports_jThreadExtendedInfo(eLazyBoolCalculate),
|
||||
m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate),
|
||||
m_supports_jGetSharedCacheInfo(eLazyBoolCalculate),
|
||||
m_supports_jGetSharedCacheInfo (eLazyBoolCalculate),
|
||||
m_supports_qProcessInfoPID(true),
|
||||
m_supports_qfProcessInfo(true),
|
||||
m_supports_qUserName(true),
|
||||
|
@ -103,6 +109,14 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient()
|
|||
m_curr_tid(LLDB_INVALID_THREAD_ID),
|
||||
m_curr_tid_run(LLDB_INVALID_THREAD_ID),
|
||||
m_num_supported_hardware_watchpoints(0),
|
||||
m_async_mutex(),
|
||||
m_async_packet_predicate(false),
|
||||
m_async_packet(),
|
||||
m_async_result(PacketResult::Success),
|
||||
m_async_response(),
|
||||
m_async_signal(-1),
|
||||
m_interrupt_sent(false),
|
||||
m_thread_id_to_used_usec_map(),
|
||||
m_host_arch(),
|
||||
m_process_arch(),
|
||||
m_os_version_major(UINT32_MAX),
|
||||
|
@ -706,8 +720,10 @@ GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses
|
|||
std::string &response_string
|
||||
)
|
||||
{
|
||||
Lock lock(*this, false);
|
||||
if (!lock)
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
if (!GetSequenceMutex(
|
||||
lock,
|
||||
"ProcessGDBRemote::SendPacketsAndConcatenateResponses() failed due to not getting the sequence mutex"))
|
||||
{
|
||||
Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS));
|
||||
if (log)
|
||||
|
@ -729,7 +745,9 @@ GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses
|
|||
// Construct payload
|
||||
char sizeDescriptor[128];
|
||||
snprintf(sizeDescriptor, sizeof(sizeDescriptor), "%x,%x", offset, response_size);
|
||||
PacketResult result = SendPacketAndWaitForResponseNoLock(payload_prefix_str + sizeDescriptor, this_response);
|
||||
PacketResult result = SendPacketAndWaitForResponse((payload_prefix_str + sizeDescriptor).c_str(),
|
||||
this_response,
|
||||
/*send_async=*/false);
|
||||
if (result != PacketResult::Success)
|
||||
return result;
|
||||
|
||||
|
@ -749,6 +767,722 @@ GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses
|
|||
}
|
||||
}
|
||||
|
||||
GDBRemoteCommunicationClient::PacketResult
|
||||
GDBRemoteCommunicationClient::SendPacketAndWaitForResponse
|
||||
(
|
||||
const char *payload,
|
||||
StringExtractorGDBRemote &response,
|
||||
bool send_async
|
||||
)
|
||||
{
|
||||
return SendPacketAndWaitForResponse (payload,
|
||||
::strlen (payload),
|
||||
response,
|
||||
send_async);
|
||||
}
|
||||
|
||||
GDBRemoteCommunicationClient::PacketResult
|
||||
GDBRemoteCommunicationClient::SendPacketAndWaitForResponseNoLock (const char *payload,
|
||||
size_t payload_length,
|
||||
StringExtractorGDBRemote &response)
|
||||
{
|
||||
PacketResult packet_result = SendPacketNoLock(payload, payload_length);
|
||||
if (packet_result == PacketResult::Success)
|
||||
{
|
||||
const size_t max_response_retries = 3;
|
||||
for (size_t i=0; i<max_response_retries; ++i)
|
||||
{
|
||||
packet_result = ReadPacket(response, GetPacketTimeoutInMicroSeconds (), true);
|
||||
// Make sure we received a response
|
||||
if (packet_result != PacketResult::Success)
|
||||
return packet_result;
|
||||
// Make sure our response is valid for the payload that was sent
|
||||
if (response.ValidateResponse())
|
||||
return packet_result;
|
||||
// Response says it wasn't valid
|
||||
Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS);
|
||||
if (log)
|
||||
log->Printf("error: packet with payload \"%*s\" got invalid response \"%s\": %s",
|
||||
(int)payload_length,
|
||||
payload,
|
||||
response.GetStringRef().c_str(),
|
||||
(i == (max_response_retries - 1)) ? "using invalid response and giving up" : "ignoring response and waiting for another");
|
||||
}
|
||||
}
|
||||
return packet_result;
|
||||
}
|
||||
|
||||
GDBRemoteCommunicationClient::PacketResult
|
||||
GDBRemoteCommunicationClient::SendPacketAndWaitForResponse
|
||||
(
|
||||
const char *payload,
|
||||
size_t payload_length,
|
||||
StringExtractorGDBRemote &response,
|
||||
bool send_async
|
||||
)
|
||||
{
|
||||
PacketResult packet_result = PacketResult::ErrorSendFailed;
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
|
||||
|
||||
// In order to stop async notifications from being processed in the middle of the
|
||||
// send/receive sequence Hijack the broadcast. Then rebroadcast any events when we are done.
|
||||
static ListenerSP hijack_listener_sp(Listener::MakeListener("lldb.NotifyHijacker"));
|
||||
HijackBroadcaster(hijack_listener_sp, eBroadcastBitGdbReadThreadGotNotify);
|
||||
|
||||
if (GetSequenceMutex(lock))
|
||||
{
|
||||
packet_result = SendPacketAndWaitForResponseNoLock (payload, payload_length, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (send_async)
|
||||
{
|
||||
if (IsRunning())
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_async_mutex);
|
||||
m_async_packet.assign(payload, payload_length);
|
||||
m_async_response.CopyResponseValidator(response);
|
||||
m_async_packet_predicate.SetValue (true, eBroadcastNever);
|
||||
|
||||
if (log)
|
||||
log->Printf ("async: async packet = %s", m_async_packet.c_str());
|
||||
|
||||
bool timed_out = false;
|
||||
if (SendInterrupt(lock, 2, timed_out))
|
||||
{
|
||||
if (m_interrupt_sent)
|
||||
{
|
||||
m_interrupt_sent = false;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> until;
|
||||
until = std::chrono::system_clock::now() + std::chrono::seconds(m_packet_timeout);
|
||||
|
||||
if (log)
|
||||
log->Printf ("async: sent interrupt");
|
||||
|
||||
if (m_async_packet_predicate.WaitForValueEqualTo(
|
||||
false, std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
until - std::chrono::system_clock::now()),
|
||||
&timed_out))
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("async: got response");
|
||||
|
||||
// Swap the response buffer to avoid malloc and string copy
|
||||
response.GetStringRef().swap (m_async_response.GetStringRef());
|
||||
packet_result = m_async_result;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("async: timed out waiting for response");
|
||||
}
|
||||
|
||||
// Make sure we wait until the continue packet has been sent again...
|
||||
if (m_private_is_running.WaitForValueEqualTo(
|
||||
true, std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
until - std::chrono::system_clock::now()),
|
||||
&timed_out))
|
||||
{
|
||||
if (log)
|
||||
{
|
||||
if (timed_out)
|
||||
log->Printf ("async: timed out waiting for process to resume, but process was resumed");
|
||||
else
|
||||
log->Printf ("async: async packet sent");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("async: timed out waiting for process to resume");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We had a racy condition where we went to send the interrupt
|
||||
// yet we were able to get the lock, so the process must have
|
||||
// just stopped?
|
||||
if (log)
|
||||
log->Printf ("async: got lock without sending interrupt");
|
||||
// Send the packet normally since we got the lock
|
||||
packet_result = SendPacketAndWaitForResponseNoLock (payload, payload_length, response);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("async: failed to interrupt");
|
||||
}
|
||||
|
||||
m_async_response.SetResponseValidator(nullptr, nullptr);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("async: not running, async is ignored");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log)
|
||||
log->Printf("error: failed to get packet sequence mutex, not sending packet '%*s'", (int) payload_length, payload);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove our Hijacking listener from the broadcast.
|
||||
RestoreBroadcaster();
|
||||
|
||||
// If a notification event occurred, rebroadcast since it can now be processed safely.
|
||||
EventSP event_sp;
|
||||
if (hijack_listener_sp->GetNextEvent(event_sp))
|
||||
BroadcastEvent(event_sp);
|
||||
|
||||
return packet_result;
|
||||
}
|
||||
|
||||
static const char *end_delimiter = "--end--;";
|
||||
static const int end_delimiter_len = 8;
|
||||
|
||||
std::string
|
||||
GDBRemoteCommunicationClient::HarmonizeThreadIdsForProfileData
|
||||
( ProcessGDBRemote *process,
|
||||
StringExtractorGDBRemote& profileDataExtractor
|
||||
)
|
||||
{
|
||||
std::map<uint64_t, uint32_t> new_thread_id_to_used_usec_map;
|
||||
std::stringstream final_output;
|
||||
std::string name, value;
|
||||
|
||||
// Going to assuming thread_used_usec comes first, else bail out.
|
||||
while (profileDataExtractor.GetNameColonValue(name, value))
|
||||
{
|
||||
if (name.compare("thread_used_id") == 0)
|
||||
{
|
||||
StringExtractor threadIDHexExtractor(value.c_str());
|
||||
uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0);
|
||||
|
||||
bool has_used_usec = false;
|
||||
uint32_t curr_used_usec = 0;
|
||||
std::string usec_name, usec_value;
|
||||
uint32_t input_file_pos = profileDataExtractor.GetFilePos();
|
||||
if (profileDataExtractor.GetNameColonValue(usec_name, usec_value))
|
||||
{
|
||||
if (usec_name.compare("thread_used_usec") == 0)
|
||||
{
|
||||
has_used_usec = true;
|
||||
curr_used_usec = strtoull(usec_value.c_str(), NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We didn't find what we want, it is probably
|
||||
// an older version. Bail out.
|
||||
profileDataExtractor.SetFilePos(input_file_pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_used_usec)
|
||||
{
|
||||
uint32_t prev_used_usec = 0;
|
||||
std::map<uint64_t, uint32_t>::iterator iterator = m_thread_id_to_used_usec_map.find(thread_id);
|
||||
if (iterator != m_thread_id_to_used_usec_map.end())
|
||||
{
|
||||
prev_used_usec = m_thread_id_to_used_usec_map[thread_id];
|
||||
}
|
||||
|
||||
uint32_t real_used_usec = curr_used_usec - prev_used_usec;
|
||||
// A good first time record is one that runs for at least 0.25 sec
|
||||
bool good_first_time = (prev_used_usec == 0) && (real_used_usec > 250000);
|
||||
bool good_subsequent_time = (prev_used_usec > 0) &&
|
||||
((real_used_usec > 0) || (process->HasAssignedIndexIDToThread(thread_id)));
|
||||
|
||||
if (good_first_time || good_subsequent_time)
|
||||
{
|
||||
// We try to avoid doing too many index id reservation,
|
||||
// resulting in fast increase of index ids.
|
||||
|
||||
final_output << name << ":";
|
||||
int32_t index_id = process->AssignIndexIDToThread(thread_id);
|
||||
final_output << index_id << ";";
|
||||
|
||||
final_output << usec_name << ":" << usec_value << ";";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip past 'thread_used_name'.
|
||||
std::string local_name, local_value;
|
||||
profileDataExtractor.GetNameColonValue(local_name, local_value);
|
||||
}
|
||||
|
||||
// Store current time as previous time so that they can be compared later.
|
||||
new_thread_id_to_used_usec_map[thread_id] = curr_used_usec;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bail out and use old string.
|
||||
final_output << name << ":" << value << ";";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
final_output << name << ":" << value << ";";
|
||||
}
|
||||
}
|
||||
final_output << end_delimiter;
|
||||
m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map;
|
||||
|
||||
return final_output.str();
|
||||
}
|
||||
|
||||
bool
|
||||
GDBRemoteCommunicationClient::SendvContPacket
|
||||
(
|
||||
ProcessGDBRemote *process,
|
||||
const char *payload,
|
||||
size_t packet_length,
|
||||
StringExtractorGDBRemote &response
|
||||
)
|
||||
{
|
||||
|
||||
m_curr_tid = LLDB_INVALID_THREAD_ID;
|
||||
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
|
||||
if (log)
|
||||
log->Printf("GDBRemoteCommunicationClient::%s ()", __FUNCTION__);
|
||||
|
||||
// we want to lock down packet sending while we continue
|
||||
std::lock_guard<std::recursive_mutex> guard(m_sequence_mutex);
|
||||
|
||||
// here we broadcast this before we even send the packet!!
|
||||
// this signals doContinue() to exit
|
||||
BroadcastEvent(eBroadcastBitRunPacketSent, NULL);
|
||||
|
||||
// set the public state to running
|
||||
m_public_is_running.SetValue(true, eBroadcastNever);
|
||||
|
||||
// Set the starting continue packet into "continue_packet". This packet
|
||||
// may change if we are interrupted and we continue after an async packet...
|
||||
std::string continue_packet(payload, packet_length);
|
||||
|
||||
if (log)
|
||||
log->Printf("GDBRemoteCommunicationClient::%s () sending vCont packet: %s", __FUNCTION__, continue_packet.c_str());
|
||||
|
||||
if (SendPacketNoLock(continue_packet.c_str(), continue_packet.size()) != PacketResult::Success)
|
||||
return false;
|
||||
|
||||
// set the private state to running and broadcast this
|
||||
m_private_is_running.SetValue(true, eBroadcastAlways);
|
||||
|
||||
if (log)
|
||||
log->Printf("GDBRemoteCommunicationClient::%s () ReadPacket(%s)", __FUNCTION__, continue_packet.c_str());
|
||||
|
||||
// wait for the response to the vCont
|
||||
if (ReadPacket(response, UINT32_MAX, false) == PacketResult::Success)
|
||||
{
|
||||
if (response.IsOKResponse())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
StateType
|
||||
GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse
|
||||
(
|
||||
ProcessGDBRemote *process,
|
||||
const char *payload,
|
||||
size_t packet_length,
|
||||
StringExtractorGDBRemote &response
|
||||
)
|
||||
{
|
||||
m_curr_tid = LLDB_INVALID_THREAD_ID;
|
||||
Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS));
|
||||
if (log)
|
||||
log->Printf ("GDBRemoteCommunicationClient::%s ()", __FUNCTION__);
|
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(m_sequence_mutex);
|
||||
StateType state = eStateRunning;
|
||||
|
||||
m_public_is_running.SetValue (true, eBroadcastNever);
|
||||
// Set the starting continue packet into "continue_packet". This packet
|
||||
// may change if we are interrupted and we continue after an async packet...
|
||||
std::string continue_packet(payload, packet_length);
|
||||
|
||||
const auto sigstop_signo = process->GetUnixSignals()->GetSignalNumberFromName("SIGSTOP");
|
||||
const auto sigint_signo = process->GetUnixSignals()->GetSignalNumberFromName("SIGINT");
|
||||
|
||||
bool got_async_packet = false;
|
||||
bool broadcast_sent = false;
|
||||
|
||||
while (state == eStateRunning)
|
||||
{
|
||||
if (!got_async_packet)
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("GDBRemoteCommunicationClient::%s () sending continue packet: %s", __FUNCTION__, continue_packet.c_str());
|
||||
if (SendPacketNoLock(continue_packet.c_str(), continue_packet.size()) != PacketResult::Success)
|
||||
state = eStateInvalid;
|
||||
else
|
||||
m_interrupt_sent = false;
|
||||
|
||||
if (! broadcast_sent)
|
||||
{
|
||||
BroadcastEvent(eBroadcastBitRunPacketSent, NULL);
|
||||
broadcast_sent = true;
|
||||
}
|
||||
|
||||
m_private_is_running.SetValue (true, eBroadcastAlways);
|
||||
}
|
||||
|
||||
got_async_packet = false;
|
||||
|
||||
if (log)
|
||||
log->Printf ("GDBRemoteCommunicationClient::%s () ReadPacket(%s)", __FUNCTION__, continue_packet.c_str());
|
||||
|
||||
if (ReadPacket(response, UINT32_MAX, false) == PacketResult::Success)
|
||||
{
|
||||
if (response.Empty())
|
||||
state = eStateInvalid;
|
||||
else
|
||||
{
|
||||
const char stop_type = response.GetChar();
|
||||
if (log)
|
||||
log->Printf ("GDBRemoteCommunicationClient::%s () got packet: %s", __FUNCTION__, response.GetStringRef().c_str());
|
||||
switch (stop_type)
|
||||
{
|
||||
case 'T':
|
||||
case 'S':
|
||||
{
|
||||
if (process->GetStopID() == 0)
|
||||
{
|
||||
if (process->GetID() == LLDB_INVALID_PROCESS_ID)
|
||||
{
|
||||
lldb::pid_t pid = GetCurrentProcessID ();
|
||||
if (pid != LLDB_INVALID_PROCESS_ID)
|
||||
process->SetID (pid);
|
||||
}
|
||||
process->BuildDynamicRegisterInfo (true);
|
||||
}
|
||||
|
||||
// Privately notify any internal threads that we have stopped
|
||||
// in case we wanted to interrupt our process, yet we might
|
||||
// send a packet and continue without returning control to the
|
||||
// user.
|
||||
m_private_is_running.SetValue (false, eBroadcastAlways);
|
||||
|
||||
const uint8_t signo = response.GetHexU8 (UINT8_MAX);
|
||||
|
||||
bool continue_after_async = m_async_signal != -1 || m_async_packet_predicate.GetValue();
|
||||
if (continue_after_async || m_interrupt_sent)
|
||||
{
|
||||
// We sent an interrupt packet to stop the inferior process
|
||||
// for an async signal or to send an async packet while running
|
||||
// but we might have been single stepping and received the
|
||||
// stop packet for the step instead of for the interrupt packet.
|
||||
// Typically when an interrupt is sent a SIGINT or SIGSTOP
|
||||
// is used, so if we get anything else, we need to try and
|
||||
// get another stop reply packet that may have been sent
|
||||
// due to sending the interrupt when the target is stopped
|
||||
// which will just re-send a copy of the last stop reply
|
||||
// packet. If we don't do this, then the reply for our
|
||||
// async packet will be the repeat stop reply packet and cause
|
||||
// a lot of trouble for us! We also have some debugserver
|
||||
// binaries that would send two stop replies anytime the process
|
||||
// was interrupted, so we need to also check for an extra
|
||||
// stop reply packet if we interrupted the process
|
||||
const bool received_nonstop_signal = signo != sigint_signo && signo != sigstop_signo;
|
||||
if (m_interrupt_sent || received_nonstop_signal)
|
||||
{
|
||||
if (received_nonstop_signal)
|
||||
continue_after_async = false;
|
||||
|
||||
// Try for a very brief time (0.1s) to get another stop reply
|
||||
// packet to make sure it doesn't get in the way
|
||||
StringExtractorGDBRemote extra_stop_reply_packet;
|
||||
uint32_t timeout_usec = 100000;
|
||||
if (ReadPacket (extra_stop_reply_packet, timeout_usec, false) == PacketResult::Success)
|
||||
{
|
||||
switch (extra_stop_reply_packet.GetChar())
|
||||
{
|
||||
case 'T':
|
||||
case 'S':
|
||||
// We did get an extra stop reply, which means
|
||||
// our interrupt didn't stop the target so we
|
||||
// shouldn't continue after the async signal
|
||||
// or packet is sent...
|
||||
continue_after_async = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_async_signal != -1)
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("async: send signo = %s", Host::GetSignalAsCString (m_async_signal));
|
||||
|
||||
// Save off the async signal we are supposed to send
|
||||
const int async_signal = m_async_signal;
|
||||
// Clear the async signal member so we don't end up
|
||||
// sending the signal multiple times...
|
||||
m_async_signal = -1;
|
||||
// Check which signal we stopped with
|
||||
if (signo == async_signal)
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("async: stopped with signal %s, we are done running", Host::GetSignalAsCString (signo));
|
||||
|
||||
// We already stopped with a signal that we wanted
|
||||
// to stop with, so we are done
|
||||
}
|
||||
else
|
||||
{
|
||||
// We stopped with a different signal that the one
|
||||
// we wanted to stop with, so now we must resume
|
||||
// with the signal we want
|
||||
char signal_packet[32];
|
||||
int signal_packet_len = 0;
|
||||
signal_packet_len = ::snprintf (signal_packet,
|
||||
sizeof (signal_packet),
|
||||
"C%2.2x",
|
||||
async_signal);
|
||||
|
||||
if (log)
|
||||
log->Printf ("async: stopped with signal %s, resume with %s",
|
||||
Host::GetSignalAsCString (signo),
|
||||
Host::GetSignalAsCString (async_signal));
|
||||
|
||||
// Set the continue packet to resume even if the
|
||||
// interrupt didn't cause our stop (ignore continue_after_async)
|
||||
continue_packet.assign(signal_packet, signal_packet_len);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (m_async_packet_predicate.GetValue())
|
||||
{
|
||||
Log * packet_log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
|
||||
|
||||
// We are supposed to send an asynchronous packet while
|
||||
// we are running.
|
||||
m_async_response.Clear();
|
||||
if (m_async_packet.empty())
|
||||
{
|
||||
m_async_result = PacketResult::ErrorSendFailed;
|
||||
if (packet_log)
|
||||
packet_log->Printf ("async: error: empty async packet");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (packet_log)
|
||||
packet_log->Printf ("async: sending packet");
|
||||
|
||||
m_async_result = SendPacketAndWaitForResponse (&m_async_packet[0],
|
||||
m_async_packet.size(),
|
||||
m_async_response,
|
||||
false);
|
||||
}
|
||||
// Let the other thread that was trying to send the async
|
||||
// packet know that the packet has been sent and response is
|
||||
// ready...
|
||||
m_async_packet_predicate.SetValue(false, eBroadcastAlways);
|
||||
|
||||
if (packet_log)
|
||||
packet_log->Printf ("async: sent packet, continue_after_async = %i", continue_after_async);
|
||||
|
||||
// Set the continue packet to resume if our interrupt
|
||||
// for the async packet did cause the stop
|
||||
if (continue_after_async)
|
||||
{
|
||||
// Reverting this for now as it is causing deadlocks
|
||||
// in programs (<rdar://problem/11529853>). In the future
|
||||
// we should check our thread list and "do the right thing"
|
||||
// for new threads that show up while we stop and run async
|
||||
// packets. Setting the packet to 'c' to continue all threads
|
||||
// is the right thing to do 99.99% of the time because if a
|
||||
// thread was single stepping, and we sent an interrupt, we
|
||||
// will notice above that we didn't stop due to an interrupt
|
||||
// but stopped due to stepping and we would _not_ continue.
|
||||
continue_packet.assign (1, 'c');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Stop with signal and thread info
|
||||
state = eStateStopped;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'W':
|
||||
case 'X':
|
||||
// process exited
|
||||
state = eStateExited;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
// STDOUT
|
||||
{
|
||||
got_async_packet = true;
|
||||
std::string inferior_stdout;
|
||||
inferior_stdout.reserve(response.GetBytesLeft () / 2);
|
||||
|
||||
uint8_t ch;
|
||||
while (response.GetHexU8Ex(ch))
|
||||
{
|
||||
if (ch != 0)
|
||||
inferior_stdout.append(1, (char)ch);
|
||||
}
|
||||
process->AppendSTDOUT (inferior_stdout.c_str(), inferior_stdout.size());
|
||||
}
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
// Async miscellaneous reply. Right now, only profile data is coming through this channel.
|
||||
{
|
||||
got_async_packet = true;
|
||||
std::string input = response.GetStringRef().substr(1); // '1' to move beyond 'A'
|
||||
if (m_partial_profile_data.length() > 0)
|
||||
{
|
||||
m_partial_profile_data.append(input);
|
||||
input = m_partial_profile_data;
|
||||
m_partial_profile_data.clear();
|
||||
}
|
||||
|
||||
size_t found, pos = 0, len = input.length();
|
||||
while ((found = input.find(end_delimiter, pos)) != std::string::npos)
|
||||
{
|
||||
StringExtractorGDBRemote profileDataExtractor(input.substr(pos, found).c_str());
|
||||
std::string profile_data = HarmonizeThreadIdsForProfileData(process, profileDataExtractor);
|
||||
process->BroadcastAsyncProfileData (profile_data);
|
||||
|
||||
pos = found + end_delimiter_len;
|
||||
}
|
||||
|
||||
if (pos < len)
|
||||
{
|
||||
// Last incomplete chunk.
|
||||
m_partial_profile_data = input.substr(pos);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
// ERROR
|
||||
state = eStateInvalid;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (log)
|
||||
log->Printf ("GDBRemoteCommunicationClient::%s () unrecognized async packet", __FUNCTION__);
|
||||
state = eStateInvalid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("GDBRemoteCommunicationClient::%s () ReadPacket(...) => false", __FUNCTION__);
|
||||
state = eStateInvalid;
|
||||
}
|
||||
}
|
||||
if (log)
|
||||
log->Printf ("GDBRemoteCommunicationClient::%s () => %s", __FUNCTION__, StateAsCString(state));
|
||||
response.SetFilePos(0);
|
||||
m_private_is_running.SetValue (false, eBroadcastAlways);
|
||||
m_public_is_running.SetValue (false, eBroadcastAlways);
|
||||
return state;
|
||||
}
|
||||
|
||||
bool
|
||||
GDBRemoteCommunicationClient::SendAsyncSignal (int signo)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_async_mutex);
|
||||
m_async_signal = signo;
|
||||
bool timed_out = false;
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
if (SendInterrupt(lock, 1, timed_out))
|
||||
return true;
|
||||
m_async_signal = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// This function takes a mutex locker as a parameter in case the GetSequenceMutex
|
||||
// actually succeeds. If it doesn't succeed in acquiring the sequence mutex
|
||||
// (the expected result), then it will send the halt packet. If it does succeed
|
||||
// then the caller that requested the interrupt will want to keep the sequence
|
||||
// locked down so that no one else can send packets while the caller has control.
|
||||
// This function usually gets called when we are running and need to stop the
|
||||
// target. It can also be used when we are running and we need to do something
|
||||
// else (like read/write memory), so we need to interrupt the running process
|
||||
// (gdb remote protocol requires this), and do what we need to do, then resume.
|
||||
|
||||
bool
|
||||
GDBRemoteCommunicationClient::SendInterrupt(std::unique_lock<std::recursive_mutex> &lock,
|
||||
uint32_t seconds_to_wait_for_stop, bool &timed_out)
|
||||
{
|
||||
timed_out = false;
|
||||
Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS));
|
||||
|
||||
if (IsRunning())
|
||||
{
|
||||
// Only send an interrupt if our debugserver is running...
|
||||
if (GetSequenceMutex(lock))
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("SendInterrupt () - got sequence mutex without having to interrupt");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Someone has the mutex locked waiting for a response or for the
|
||||
// inferior to stop, so send the interrupt on the down low...
|
||||
char ctrl_c = '\x03';
|
||||
ConnectionStatus status = eConnectionStatusSuccess;
|
||||
size_t bytes_written = Write (&ctrl_c, 1, status, NULL);
|
||||
if (log)
|
||||
log->PutCString("send packet: \\x03");
|
||||
if (bytes_written > 0)
|
||||
{
|
||||
m_interrupt_sent = true;
|
||||
if (seconds_to_wait_for_stop)
|
||||
{
|
||||
if (m_private_is_running.WaitForValueEqualTo(false, std::chrono::seconds(seconds_to_wait_for_stop),
|
||||
&timed_out))
|
||||
{
|
||||
if (log)
|
||||
log->PutCString ("SendInterrupt () - sent interrupt, private state stopped");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("SendInterrupt () - sent interrupt, timed out wating for async thread resume");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("SendInterrupt () - sent interrupt, not waiting for stop...");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("SendInterrupt () - failed to write interrupt");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (log)
|
||||
log->Printf ("SendInterrupt () - not running");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
lldb::pid_t
|
||||
GDBRemoteCommunicationClient::GetCurrentProcessID (bool allow_lazy)
|
||||
{
|
||||
|
@ -2917,18 +3651,18 @@ size_t
|
|||
GDBRemoteCommunicationClient::GetCurrentThreadIDs (std::vector<lldb::tid_t> &thread_ids,
|
||||
bool &sequence_mutex_unavailable)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
thread_ids.clear();
|
||||
|
||||
Lock lock(*this, false);
|
||||
if (lock)
|
||||
if (GetSequenceMutex(lock, "ProcessGDBRemote::UpdateThreadList() failed due to not getting the sequence mutex"))
|
||||
{
|
||||
sequence_mutex_unavailable = false;
|
||||
StringExtractorGDBRemote response;
|
||||
|
||||
PacketResult packet_result;
|
||||
for (packet_result = SendPacketAndWaitForResponseNoLock("qfThreadInfo", response);
|
||||
for (packet_result = SendPacketAndWaitForResponseNoLock ("qfThreadInfo", strlen("qfThreadInfo"), response);
|
||||
packet_result == PacketResult::Success && response.IsNormalResponse();
|
||||
packet_result = SendPacketAndWaitForResponseNoLock("qsThreadInfo", response))
|
||||
packet_result = SendPacketAndWaitForResponseNoLock ("qsThreadInfo", strlen("qsThreadInfo"), response))
|
||||
{
|
||||
char ch = response.GetChar();
|
||||
if (ch == 'l')
|
||||
|
@ -2976,8 +3710,7 @@ GDBRemoteCommunicationClient::GetCurrentThreadIDs (std::vector<lldb::tid_t> &thr
|
|||
lldb::addr_t
|
||||
GDBRemoteCommunicationClient::GetShlibInfoAddr()
|
||||
{
|
||||
Lock lock(*this, false);
|
||||
if (lock)
|
||||
if (!IsRunning())
|
||||
{
|
||||
StringExtractorGDBRemote response;
|
||||
if (SendPacketAndWaitForResponse("qShlibInfoAddr", ::strlen ("qShlibInfoAddr"), response, false) == PacketResult::Success)
|
||||
|
@ -2986,11 +3719,6 @@ GDBRemoteCommunicationClient::GetShlibInfoAddr()
|
|||
return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
|
||||
}
|
||||
}
|
||||
else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS))
|
||||
{
|
||||
log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex for qShlibInfoAddr packet.",
|
||||
__FUNCTION__);
|
||||
}
|
||||
return LLDB_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
|
@ -3473,8 +4201,8 @@ GDBRemoteCommunicationClient::AvoidGPackets (ProcessGDBRemote *process)
|
|||
bool
|
||||
GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid, uint32_t reg, StringExtractorGDBRemote &response)
|
||||
{
|
||||
Lock lock(*this, false);
|
||||
if (lock)
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
if (GetSequenceMutex(lock, "Didn't get sequence mutex for p packet."))
|
||||
{
|
||||
const bool thread_suffix_supported = GetThreadSuffixSupported();
|
||||
|
||||
|
@ -3490,10 +4218,6 @@ GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid, uint32_t reg, String
|
|||
return SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success;
|
||||
}
|
||||
}
|
||||
else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS))
|
||||
{
|
||||
log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex for p packet.", __FUNCTION__);
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
@ -3502,8 +4226,8 @@ GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid, uint32_t reg, String
|
|||
bool
|
||||
GDBRemoteCommunicationClient::ReadAllRegisters (lldb::tid_t tid, StringExtractorGDBRemote &response)
|
||||
{
|
||||
Lock lock(*this, false);
|
||||
if (lock)
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
if (GetSequenceMutex(lock, "Didn't get sequence mutex for g packet."))
|
||||
{
|
||||
const bool thread_suffix_supported = GetThreadSuffixSupported();
|
||||
|
||||
|
@ -3520,10 +4244,6 @@ GDBRemoteCommunicationClient::ReadAllRegisters (lldb::tid_t tid, StringExtractor
|
|||
return SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success;
|
||||
}
|
||||
}
|
||||
else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS))
|
||||
{
|
||||
log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex for g packet.", __FUNCTION__);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool
|
||||
|
@ -3534,8 +4254,8 @@ GDBRemoteCommunicationClient::SaveRegisterState (lldb::tid_t tid, uint32_t &save
|
|||
return false;
|
||||
|
||||
m_supports_QSaveRegisterState = eLazyBoolYes;
|
||||
Lock lock(*this, false);
|
||||
if (lock)
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
if (GetSequenceMutex(lock, "Didn't get sequence mutex for QSaveRegisterState."))
|
||||
{
|
||||
const bool thread_suffix_supported = GetThreadSuffixSupported();
|
||||
if (thread_suffix_supported || SetCurrentThread(tid))
|
||||
|
@ -3565,11 +4285,6 @@ GDBRemoteCommunicationClient::SaveRegisterState (lldb::tid_t tid, uint32_t &save
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS))
|
||||
{
|
||||
log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex for QSaveRegisterState packet.",
|
||||
__FUNCTION__);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -3582,8 +4297,8 @@ GDBRemoteCommunicationClient::RestoreRegisterState (lldb::tid_t tid, uint32_t sa
|
|||
if (m_supports_QSaveRegisterState == eLazyBoolNo)
|
||||
return false;
|
||||
|
||||
Lock lock(*this, false);
|
||||
if (lock)
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
if (GetSequenceMutex(lock, "Didn't get sequence mutex for QRestoreRegisterState."))
|
||||
{
|
||||
const bool thread_suffix_supported = GetThreadSuffixSupported();
|
||||
if (thread_suffix_supported || SetCurrentThread(tid))
|
||||
|
@ -3611,11 +4326,6 @@ GDBRemoteCommunicationClient::RestoreRegisterState (lldb::tid_t tid, uint32_t sa
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS))
|
||||
{
|
||||
log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex for QRestoreRegisterState packet.",
|
||||
__FUNCTION__);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -3818,13 +4528,15 @@ GDBRemoteCommunicationClient::ServeSymbolLookups(lldb_private::Process *process)
|
|||
|
||||
if (m_supports_qSymbol && m_qSymbol_requests_done == false)
|
||||
{
|
||||
Lock lock(*this, false);
|
||||
if (lock)
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
if (GetSequenceMutex(
|
||||
lock,
|
||||
"GDBRemoteCommunicationClient::ServeSymbolLookups() failed due to not getting the sequence mutex"))
|
||||
{
|
||||
StreamString packet;
|
||||
packet.PutCString ("qSymbol::");
|
||||
StringExtractorGDBRemote response;
|
||||
while (SendPacketAndWaitForResponseNoLock(packet.GetString(), response) == PacketResult::Success)
|
||||
while (SendPacketAndWaitForResponseNoLock(packet.GetData(), packet.GetSize(), response) == PacketResult::Success)
|
||||
{
|
||||
if (response.IsOKResponse())
|
||||
{
|
||||
|
@ -3936,18 +4648,6 @@ GDBRemoteCommunicationClient::ServeSymbolLookups(lldb_private::Process *process)
|
|||
return;
|
||||
|
||||
}
|
||||
else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS))
|
||||
{
|
||||
log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex.", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GDBRemoteCommunicationClient::OnRunPacketSent(bool first)
|
||||
{
|
||||
GDBRemoteClientBase::OnRunPacketSent(first);
|
||||
if (first)
|
||||
BroadcastEvent(eBroadcastBitRunPacketSent, NULL);
|
||||
m_curr_tid = LLDB_INVALID_THREAD_ID;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
// C Includes
|
||||
// C++ Includes
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
@ -24,12 +23,12 @@
|
|||
#include "lldb/Core/StructuredData.h"
|
||||
#include "lldb/Target/Process.h"
|
||||
|
||||
#include "GDBRemoteClientBase.h"
|
||||
#include "GDBRemoteCommunication.h"
|
||||
|
||||
namespace lldb_private {
|
||||
namespace process_gdb_remote {
|
||||
|
||||
class GDBRemoteCommunicationClient : public GDBRemoteClientBase
|
||||
class GDBRemoteCommunicationClient : public GDBRemoteCommunication
|
||||
{
|
||||
public:
|
||||
GDBRemoteCommunicationClient();
|
||||
|
@ -43,6 +42,17 @@ public:
|
|||
bool
|
||||
HandshakeWithServer (Error *error_ptr);
|
||||
|
||||
PacketResult
|
||||
SendPacketAndWaitForResponse (const char *send_payload,
|
||||
StringExtractorGDBRemote &response,
|
||||
bool send_async);
|
||||
|
||||
PacketResult
|
||||
SendPacketAndWaitForResponse (const char *send_payload,
|
||||
size_t send_length,
|
||||
StringExtractorGDBRemote &response,
|
||||
bool send_async);
|
||||
|
||||
// For packets which specify a range of output to be returned,
|
||||
// return all of the output via a series of request packets of the form
|
||||
// <prefix>0,<size>
|
||||
|
@ -64,6 +74,18 @@ public:
|
|||
SendPacketsAndConcatenateResponses (const char *send_payload_prefix,
|
||||
std::string &response_string);
|
||||
|
||||
lldb::StateType
|
||||
SendContinuePacketAndWaitForResponse (ProcessGDBRemote *process,
|
||||
const char *packet_payload,
|
||||
size_t packet_length,
|
||||
StringExtractorGDBRemote &response);
|
||||
|
||||
bool
|
||||
SendvContPacket (ProcessGDBRemote *process,
|
||||
const char *payload,
|
||||
size_t packet_length,
|
||||
StringExtractorGDBRemote &response);
|
||||
|
||||
bool
|
||||
GetThreadSuffixSupported () override;
|
||||
|
||||
|
@ -79,6 +101,12 @@ public:
|
|||
void
|
||||
GetListThreadsInStopReplySupported ();
|
||||
|
||||
bool
|
||||
SendAsyncSignal (int signo);
|
||||
|
||||
bool
|
||||
SendInterrupt(std::unique_lock<std::recursive_mutex> &lock, uint32_t seconds_to_wait_for_stop, bool &timed_out);
|
||||
|
||||
lldb::pid_t
|
||||
GetCurrentProcessID (bool allow_lazy = true);
|
||||
|
||||
|
@ -433,6 +461,12 @@ public:
|
|||
GetCurrentThreadIDs (std::vector<lldb::tid_t> &thread_ids,
|
||||
bool &sequence_mutex_unavailable);
|
||||
|
||||
bool
|
||||
GetInterruptWasSent () const
|
||||
{
|
||||
return m_interrupt_sent;
|
||||
}
|
||||
|
||||
lldb::user_id_t
|
||||
OpenFile (const FileSpec& file_spec, uint32_t flags, mode_t mode, Error &error);
|
||||
|
||||
|
@ -486,6 +520,9 @@ public:
|
|||
bool
|
||||
CalculateMD5 (const FileSpec& file_spec, uint64_t &high, uint64_t &low);
|
||||
|
||||
std::string
|
||||
HarmonizeThreadIdsForProfileData (ProcessGDBRemote *process,
|
||||
StringExtractorGDBRemote &inputStringExtractor);
|
||||
|
||||
bool
|
||||
ReadRegister(lldb::tid_t tid,
|
||||
|
@ -595,6 +632,18 @@ protected:
|
|||
|
||||
uint32_t m_num_supported_hardware_watchpoints;
|
||||
|
||||
// If we need to send a packet while the target is running, the m_async_XXX
|
||||
// member variables take care of making this happen.
|
||||
std::recursive_mutex m_async_mutex;
|
||||
Predicate<bool> m_async_packet_predicate;
|
||||
std::string m_async_packet;
|
||||
PacketResult m_async_result;
|
||||
StringExtractorGDBRemote m_async_response;
|
||||
int m_async_signal; // We were asked to deliver a signal to the inferior process.
|
||||
bool m_interrupt_sent;
|
||||
std::string m_partial_profile_data;
|
||||
std::map<uint64_t, uint32_t> m_thread_id_to_used_usec_map;
|
||||
|
||||
ArchSpec m_host_arch;
|
||||
ArchSpec m_process_arch;
|
||||
uint32_t m_os_version_major;
|
||||
|
@ -608,6 +657,11 @@ protected:
|
|||
uint32_t m_default_packet_timeout;
|
||||
uint64_t m_max_packet_size; // as returned by qSupported
|
||||
|
||||
PacketResult
|
||||
SendPacketAndWaitForResponseNoLock (const char *payload,
|
||||
size_t payload_length,
|
||||
StringExtractorGDBRemote &response);
|
||||
|
||||
bool
|
||||
GetCurrentProcessInfo (bool allow_lazy_pid = true);
|
||||
|
||||
|
@ -623,9 +677,6 @@ protected:
|
|||
DecodeProcessInfoResponse (StringExtractorGDBRemote &response,
|
||||
ProcessInstanceInfo &process_info);
|
||||
|
||||
void
|
||||
OnRunPacketSent(bool first) override;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunicationClient);
|
||||
};
|
||||
|
|
|
@ -372,6 +372,12 @@ GDBRemoteRegisterContext::WriteRegisterBytes (const RegisterInfo *reg_info, Data
|
|||
return false;
|
||||
|
||||
GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *)process)->GetGDBRemote());
|
||||
// FIXME: This check isn't right because IsRunning checks the Public state, but this
|
||||
// is work you need to do - for instance in ShouldStop & friends - before the public
|
||||
// state has been changed.
|
||||
// if (gdb_comm.IsRunning())
|
||||
// return false;
|
||||
|
||||
|
||||
#if defined (LLDB_CONFIGURATION_DEBUG)
|
||||
assert (m_reg_data.GetByteSize() >= reg_info->byte_offset + reg_info->byte_size);
|
||||
|
@ -395,8 +401,8 @@ GDBRemoteRegisterContext::WriteRegisterBytes (const RegisterInfo *reg_info, Data
|
|||
reg_info->byte_size, // dst length
|
||||
m_reg_data.GetByteOrder())) // dst byte order
|
||||
{
|
||||
GDBRemoteClientBase::Lock lock(gdb_comm, false);
|
||||
if (lock)
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
if (gdb_comm.GetSequenceMutex(lock, "Didn't get sequence mutex for write register."))
|
||||
{
|
||||
const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported();
|
||||
ProcessSP process_sp (m_thread.GetProcess());
|
||||
|
@ -564,8 +570,8 @@ GDBRemoteRegisterContext::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
|
|||
|
||||
const bool use_g_packet = gdb_comm.AvoidGPackets ((ProcessGDBRemote *)process) == false;
|
||||
|
||||
GDBRemoteClientBase::Lock lock(gdb_comm, false);
|
||||
if (lock)
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
if (gdb_comm.GetSequenceMutex(lock, "Didn't get sequence mutex for read all registers."))
|
||||
{
|
||||
SyncThreadState(process);
|
||||
|
||||
|
@ -673,8 +679,8 @@ GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data
|
|||
const bool use_g_packet = gdb_comm.AvoidGPackets ((ProcessGDBRemote *)process) == false;
|
||||
|
||||
StringExtractorGDBRemote response;
|
||||
GDBRemoteClientBase::Lock lock(gdb_comm, false);
|
||||
if (lock)
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
if (gdb_comm.GetSequenceMutex(lock, "Didn't get sequence mutex for write all registers."))
|
||||
{
|
||||
const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported();
|
||||
ProcessSP process_sp (m_thread.GetProcess());
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
|
||||
#include "lldb/Breakpoint/Watchpoint.h"
|
||||
#include "lldb/Interpreter/Args.h"
|
||||
|
@ -2714,6 +2713,9 @@ ProcessGDBRemote::DoHalt (bool &caused_stop)
|
|||
{
|
||||
Error error;
|
||||
|
||||
bool timed_out = false;
|
||||
std::unique_lock<std::recursive_mutex> lock;
|
||||
|
||||
if (m_public_state.GetValue() == eStateAttaching)
|
||||
{
|
||||
// We are being asked to halt during an attach. We need to just close
|
||||
|
@ -2721,7 +2723,17 @@ ProcessGDBRemote::DoHalt (bool &caused_stop)
|
|||
m_gdb_comm.Disconnect();
|
||||
}
|
||||
else
|
||||
caused_stop = m_gdb_comm.Interrupt();
|
||||
{
|
||||
if (!m_gdb_comm.SendInterrupt(lock, 2, timed_out))
|
||||
{
|
||||
if (timed_out)
|
||||
error.SetErrorString("timed out sending interrupt packet");
|
||||
else
|
||||
error.SetErrorString("unknown error sending interrupt packet");
|
||||
}
|
||||
|
||||
caused_stop = m_gdb_comm.GetInterruptWasSent ();
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -3874,8 +3886,7 @@ ProcessGDBRemote::AsyncThread (void *arg)
|
|||
if (process->GetTarget().GetNonStopModeEnabled())
|
||||
{
|
||||
// send the vCont packet
|
||||
if (!process->GetGDBRemote().SendvContPacket(
|
||||
llvm::StringRef(continue_cstr, continue_cstr_len), response))
|
||||
if (!process->GetGDBRemote().SendvContPacket(process, continue_cstr, continue_cstr_len, response))
|
||||
{
|
||||
// Something went wrong
|
||||
done = true;
|
||||
|
@ -3885,9 +3896,7 @@ ProcessGDBRemote::AsyncThread (void *arg)
|
|||
// If in All-Stop-Mode
|
||||
else
|
||||
{
|
||||
StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse(
|
||||
*process, *process->GetUnixSignals(),
|
||||
llvm::StringRef(continue_cstr, continue_cstr_len), response);
|
||||
StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response);
|
||||
|
||||
// We need to immediately clear the thread ID list so we are sure to get a valid list of threads.
|
||||
// The thread ID list might be contained within the "response", or the stop reply packet that
|
||||
|
@ -5029,144 +5038,6 @@ ProcessGDBRemote::ModulesDidLoad (ModuleList &module_list)
|
|||
m_gdb_comm.ServeSymbolLookups(this);
|
||||
}
|
||||
|
||||
void
|
||||
ProcessGDBRemote::HandleAsyncStdout(llvm::StringRef out)
|
||||
{
|
||||
AppendSTDOUT(out.data(), out.size());
|
||||
}
|
||||
|
||||
static const char *end_delimiter = "--end--;";
|
||||
static const int end_delimiter_len = 8;
|
||||
|
||||
void
|
||||
ProcessGDBRemote::HandleAsyncMisc(llvm::StringRef data)
|
||||
{
|
||||
std::string input = data.str(); // '1' to move beyond 'A'
|
||||
if (m_partial_profile_data.length() > 0)
|
||||
{
|
||||
m_partial_profile_data.append(input);
|
||||
input = m_partial_profile_data;
|
||||
m_partial_profile_data.clear();
|
||||
}
|
||||
|
||||
size_t found, pos = 0, len = input.length();
|
||||
while ((found = input.find(end_delimiter, pos)) != std::string::npos)
|
||||
{
|
||||
StringExtractorGDBRemote profileDataExtractor(input.substr(pos, found).c_str());
|
||||
std::string profile_data = HarmonizeThreadIdsForProfileData(profileDataExtractor);
|
||||
BroadcastAsyncProfileData(profile_data);
|
||||
|
||||
pos = found + end_delimiter_len;
|
||||
}
|
||||
|
||||
if (pos < len)
|
||||
{
|
||||
// Last incomplete chunk.
|
||||
m_partial_profile_data = input.substr(pos);
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
ProcessGDBRemote::HarmonizeThreadIdsForProfileData(StringExtractorGDBRemote &profileDataExtractor)
|
||||
{
|
||||
std::map<uint64_t, uint32_t> new_thread_id_to_used_usec_map;
|
||||
std::stringstream final_output;
|
||||
std::string name, value;
|
||||
|
||||
// Going to assuming thread_used_usec comes first, else bail out.
|
||||
while (profileDataExtractor.GetNameColonValue(name, value))
|
||||
{
|
||||
if (name.compare("thread_used_id") == 0)
|
||||
{
|
||||
StringExtractor threadIDHexExtractor(value.c_str());
|
||||
uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0);
|
||||
|
||||
bool has_used_usec = false;
|
||||
uint32_t curr_used_usec = 0;
|
||||
std::string usec_name, usec_value;
|
||||
uint32_t input_file_pos = profileDataExtractor.GetFilePos();
|
||||
if (profileDataExtractor.GetNameColonValue(usec_name, usec_value))
|
||||
{
|
||||
if (usec_name.compare("thread_used_usec") == 0)
|
||||
{
|
||||
has_used_usec = true;
|
||||
curr_used_usec = strtoull(usec_value.c_str(), NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We didn't find what we want, it is probably
|
||||
// an older version. Bail out.
|
||||
profileDataExtractor.SetFilePos(input_file_pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_used_usec)
|
||||
{
|
||||
uint32_t prev_used_usec = 0;
|
||||
std::map<uint64_t, uint32_t>::iterator iterator = m_thread_id_to_used_usec_map.find(thread_id);
|
||||
if (iterator != m_thread_id_to_used_usec_map.end())
|
||||
{
|
||||
prev_used_usec = m_thread_id_to_used_usec_map[thread_id];
|
||||
}
|
||||
|
||||
uint32_t real_used_usec = curr_used_usec - prev_used_usec;
|
||||
// A good first time record is one that runs for at least 0.25 sec
|
||||
bool good_first_time = (prev_used_usec == 0) && (real_used_usec > 250000);
|
||||
bool good_subsequent_time =
|
||||
(prev_used_usec > 0) && ((real_used_usec > 0) || (HasAssignedIndexIDToThread(thread_id)));
|
||||
|
||||
if (good_first_time || good_subsequent_time)
|
||||
{
|
||||
// We try to avoid doing too many index id reservation,
|
||||
// resulting in fast increase of index ids.
|
||||
|
||||
final_output << name << ":";
|
||||
int32_t index_id = AssignIndexIDToThread(thread_id);
|
||||
final_output << index_id << ";";
|
||||
|
||||
final_output << usec_name << ":" << usec_value << ";";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip past 'thread_used_name'.
|
||||
std::string local_name, local_value;
|
||||
profileDataExtractor.GetNameColonValue(local_name, local_value);
|
||||
}
|
||||
|
||||
// Store current time as previous time so that they can be compared later.
|
||||
new_thread_id_to_used_usec_map[thread_id] = curr_used_usec;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bail out and use old string.
|
||||
final_output << name << ":" << value << ";";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
final_output << name << ":" << value << ";";
|
||||
}
|
||||
}
|
||||
final_output << end_delimiter;
|
||||
m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map;
|
||||
|
||||
return final_output.str();
|
||||
}
|
||||
|
||||
void
|
||||
ProcessGDBRemote::HandleStopReply()
|
||||
{
|
||||
if (GetStopID() != 0)
|
||||
return;
|
||||
|
||||
if (GetID() == LLDB_INVALID_PROCESS_ID)
|
||||
{
|
||||
lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID();
|
||||
if (pid != LLDB_INVALID_PROCESS_ID)
|
||||
SetID(pid);
|
||||
}
|
||||
BuildDynamicRegisterInfo(true);
|
||||
}
|
||||
|
||||
class CommandObjectProcessGDBRemoteSpeedTest: public CommandObjectParsed
|
||||
{
|
||||
|
@ -5374,7 +5245,7 @@ public:
|
|||
|
||||
if (strstr(packet_cstr, "qGetProfileData") != NULL)
|
||||
{
|
||||
response_str = process->HarmonizeThreadIdsForProfileData(response);
|
||||
response_str = process->GetGDBRemote().HarmonizeThreadIdsForProfileData(process, response);
|
||||
}
|
||||
|
||||
if (response_str.empty())
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace process_gdb_remote {
|
|||
|
||||
class ThreadGDBRemote;
|
||||
|
||||
class ProcessGDBRemote : public Process, private GDBRemoteClientBase::ContinueDelegate
|
||||
class ProcessGDBRemote : public Process
|
||||
{
|
||||
public:
|
||||
ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp);
|
||||
|
@ -273,9 +273,6 @@ public:
|
|||
StructuredData::ObjectSP
|
||||
GetSharedCacheInfo () override;
|
||||
|
||||
std::string
|
||||
HarmonizeThreadIdsForProfileData(StringExtractorGDBRemote &inputStringExtractor);
|
||||
|
||||
protected:
|
||||
friend class ThreadGDBRemote;
|
||||
friend class GDBRemoteCommunicationClient;
|
||||
|
@ -488,25 +485,12 @@ private:
|
|||
//------------------------------------------------------------------
|
||||
// For ProcessGDBRemote only
|
||||
//------------------------------------------------------------------
|
||||
std::string m_partial_profile_data;
|
||||
std::map<uint64_t, uint32_t> m_thread_id_to_used_usec_map;
|
||||
|
||||
static bool
|
||||
NewThreadNotifyBreakpointHit (void *baton,
|
||||
StoppointCallbackContext *context,
|
||||
lldb::user_id_t break_id,
|
||||
lldb::user_id_t break_loc_id);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// ContinueDelegate interface
|
||||
//------------------------------------------------------------------
|
||||
void
|
||||
HandleAsyncStdout(llvm::StringRef out) override;
|
||||
void
|
||||
HandleAsyncMisc(llvm::StringRef data) override;
|
||||
void
|
||||
HandleStopReply() override;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN (ProcessGDBRemote);
|
||||
};
|
||||
|
||||
|
|
|
@ -433,7 +433,6 @@ size_t
|
|||
StringExtractor::GetHexByteString (std::string &str)
|
||||
{
|
||||
str.clear();
|
||||
str.reserve(GetBytesLeft() / 2);
|
||||
char ch;
|
||||
while ((ch = GetHexU8()) != '\0')
|
||||
str.append(1, ch);
|
||||
|
|
|
@ -43,7 +43,6 @@ add_subdirectory(Editline)
|
|||
add_subdirectory(Expression)
|
||||
add_subdirectory(Host)
|
||||
add_subdirectory(Interpreter)
|
||||
add_subdirectory(Process)
|
||||
add_subdirectory(ScriptInterpreter)
|
||||
add_subdirectory(Symbol)
|
||||
add_subdirectory(SymbolFile)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
add_subdirectory(gdb-remote)
|
|
@ -1,3 +0,0 @@
|
|||
add_lldb_unittest(ProcessGdbRemoteTests
|
||||
GDBRemoteClientBaseTest.cpp
|
||||
)
|
|
@ -1,396 +0,0 @@
|
|||
//===-- GDBRemoteClientBaseTest.cpp -----------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0)
|
||||
// Workaround for MSVC standard library bug, which fails to include <thread> when
|
||||
// exceptions are disabled.
|
||||
#include <eh.h>
|
||||
#endif
|
||||
#include <future>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Plugins/Process/Utility/LinuxSignals.h"
|
||||
#include "Plugins/Process/gdb-remote/GDBRemoteClientBase.h"
|
||||
#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h"
|
||||
|
||||
#include "lldb/Host/common/TCPSocket.h"
|
||||
#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
|
||||
using namespace lldb_private::process_gdb_remote;
|
||||
using namespace lldb_private;
|
||||
using namespace lldb;
|
||||
typedef GDBRemoteCommunication::PacketResult PacketResult;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct MockDelegate : public GDBRemoteClientBase::ContinueDelegate
|
||||
{
|
||||
std::string output;
|
||||
std::string misc_data;
|
||||
unsigned stop_reply_called = 0;
|
||||
|
||||
void
|
||||
HandleAsyncStdout(llvm::StringRef out)
|
||||
{
|
||||
output += out;
|
||||
}
|
||||
void
|
||||
HandleAsyncMisc(llvm::StringRef data)
|
||||
{
|
||||
misc_data += data;
|
||||
}
|
||||
void
|
||||
HandleStopReply()
|
||||
{
|
||||
++stop_reply_called;
|
||||
}
|
||||
};
|
||||
|
||||
struct MockServer : public GDBRemoteCommunicationServer
|
||||
{
|
||||
MockServer() : GDBRemoteCommunicationServer("mock-server", "mock-server.listener") { m_send_acks = false; }
|
||||
|
||||
bool
|
||||
GetThreadSuffixSupported() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PacketResult
|
||||
SendPacket(llvm::StringRef payload)
|
||||
{
|
||||
return GDBRemoteCommunicationServer::SendPacketNoLock(payload.data(), payload.size());
|
||||
}
|
||||
|
||||
PacketResult
|
||||
GetPacket(StringExtractorGDBRemote &response)
|
||||
{
|
||||
const unsigned timeout_usec = 1000000; // 1s
|
||||
const bool sync_on_timeout = false;
|
||||
return WaitForPacketWithTimeoutMicroSecondsNoLock(response, timeout_usec, sync_on_timeout);
|
||||
}
|
||||
};
|
||||
|
||||
struct TestClient : public GDBRemoteClientBase
|
||||
{
|
||||
TestClient() : GDBRemoteClientBase("test.client", "test.client.listener") { m_send_acks = false; }
|
||||
|
||||
bool
|
||||
GetThreadSuffixSupported() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct ContinueFixture
|
||||
{
|
||||
MockDelegate delegate;
|
||||
TestClient client;
|
||||
MockServer server;
|
||||
ListenerSP listener_sp;
|
||||
|
||||
ContinueFixture();
|
||||
|
||||
StateType
|
||||
SendCPacket(StringExtractorGDBRemote &response)
|
||||
{
|
||||
return client.SendContinuePacketAndWaitForResponse(delegate, LinuxSignals(), "c", response);
|
||||
}
|
||||
|
||||
void
|
||||
WaitForRunEvent()
|
||||
{
|
||||
EventSP event_sp;
|
||||
listener_sp->WaitForEventForBroadcasterWithType(std::chrono::microseconds(0), &client,
|
||||
TestClient::eBroadcastBitRunPacketSent, event_sp);
|
||||
}
|
||||
};
|
||||
|
||||
ContinueFixture::ContinueFixture() : listener_sp(Listener::MakeListener("listener"))
|
||||
{
|
||||
bool child_processes_inherit = false;
|
||||
Error error;
|
||||
TCPSocket listen_socket(child_processes_inherit, error);
|
||||
EXPECT_FALSE(error.Fail());
|
||||
error = listen_socket.Listen("127.0.0.1:0", 5);
|
||||
EXPECT_FALSE(error.Fail());
|
||||
|
||||
Socket *accept_socket;
|
||||
std::future<Error> accept_error = std::async(std::launch::async, [&] {
|
||||
return listen_socket.Accept("127.0.0.1:0", child_processes_inherit, accept_socket);
|
||||
});
|
||||
|
||||
char connect_remote_address[64];
|
||||
snprintf(connect_remote_address, sizeof(connect_remote_address), "connect://localhost:%u",
|
||||
listen_socket.GetLocalPortNumber());
|
||||
|
||||
std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor());
|
||||
EXPECT_EQ(conn_ap->Connect(connect_remote_address, nullptr), lldb::eConnectionStatusSuccess);
|
||||
|
||||
client.SetConnection(conn_ap.release());
|
||||
EXPECT_TRUE(accept_error.get().Success());
|
||||
server.SetConnection(new ConnectionFileDescriptor(accept_socket));
|
||||
|
||||
listener_sp->StartListeningForEvents(&client, TestClient::eBroadcastBitRunPacketSent);
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
class GDBRemoteClientBaseTest : public testing::Test
|
||||
{
|
||||
public:
|
||||
static void
|
||||
SetUpTestCase()
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
WSADATA data;
|
||||
::WSAStartup(MAKEWORD(2, 2), &data);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
TearDownTestCase()
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
::WSACleanup();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
TEST(GDBRemoteClientBaseTest, SendContinueAndWait)
|
||||
{
|
||||
StringExtractorGDBRemote response;
|
||||
ContinueFixture fix;
|
||||
if (HasFailure())
|
||||
return;
|
||||
|
||||
// Continue. The inferior will stop with a signal.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01"));
|
||||
ASSERT_EQ(eStateStopped, fix.SendCPacket(response));
|
||||
ASSERT_EQ("T01", response.GetStringRef());
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("c", response.GetStringRef());
|
||||
|
||||
// Continue. The inferior will exit.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("W01"));
|
||||
ASSERT_EQ(eStateExited, fix.SendCPacket(response));
|
||||
ASSERT_EQ("W01", response.GetStringRef());
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("c", response.GetStringRef());
|
||||
|
||||
// Continue. The inferior will get killed.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("X01"));
|
||||
ASSERT_EQ(eStateExited, fix.SendCPacket(response));
|
||||
ASSERT_EQ("X01", response.GetStringRef());
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("c", response.GetStringRef());
|
||||
}
|
||||
|
||||
TEST(GDBRemoteClientBaseTest, SendContinueAndAsyncSignal)
|
||||
{
|
||||
StringExtractorGDBRemote continue_response, response;
|
||||
ContinueFixture fix;
|
||||
if (HasFailure())
|
||||
return;
|
||||
|
||||
// SendAsyncSignal should do nothing when we are not running.
|
||||
ASSERT_FALSE(fix.client.SendAsyncSignal(0x47));
|
||||
|
||||
// Continue. After the run packet is sent, send an async signal.
|
||||
std::future<StateType> continue_state =
|
||||
std::async(std::launch::async, [&] { return fix.SendCPacket(continue_response); });
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("c", response.GetStringRef());
|
||||
fix.WaitForRunEvent();
|
||||
|
||||
std::future<bool> async_result = std::async(std::launch::async, [&] { return fix.client.SendAsyncSignal(0x47); });
|
||||
|
||||
// First we'll get interrupted.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("\x03", response.GetStringRef());
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13"));
|
||||
|
||||
// Then we get the signal packet.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("C47", response.GetStringRef());
|
||||
ASSERT_TRUE(async_result.get());
|
||||
|
||||
// And we report back a signal stop.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T47"));
|
||||
ASSERT_EQ(eStateStopped, continue_state.get());
|
||||
ASSERT_EQ("T47", continue_response.GetStringRef());
|
||||
}
|
||||
|
||||
TEST(GDBRemoteClientBaseTest, SendContinueAndAsyncPacket)
|
||||
{
|
||||
StringExtractorGDBRemote continue_response, async_response, response;
|
||||
const bool send_async = true;
|
||||
ContinueFixture fix;
|
||||
if (HasFailure())
|
||||
return;
|
||||
|
||||
// Continue. After the run packet is sent, send an async packet.
|
||||
std::future<StateType> continue_state =
|
||||
std::async(std::launch::async, [&] { return fix.SendCPacket(continue_response); });
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("c", response.GetStringRef());
|
||||
fix.WaitForRunEvent();
|
||||
|
||||
// Sending without async enabled should fail.
|
||||
ASSERT_EQ(PacketResult::ErrorSendFailed, fix.client.SendPacketAndWaitForResponse("qTest1", response, !send_async));
|
||||
|
||||
std::future<PacketResult> async_result = std::async(std::launch::async, [&] {
|
||||
return fix.client.SendPacketAndWaitForResponse("qTest2", async_response, send_async);
|
||||
});
|
||||
|
||||
// First we'll get interrupted.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("\x03", response.GetStringRef());
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13"));
|
||||
|
||||
// Then we get the async packet.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("qTest2", response.GetStringRef());
|
||||
|
||||
// Send the response and receive it.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("QTest2"));
|
||||
ASSERT_EQ(PacketResult::Success, async_result.get());
|
||||
ASSERT_EQ("QTest2", async_response.GetStringRef());
|
||||
|
||||
// And we get resumed again.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("c", response.GetStringRef());
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01"));
|
||||
ASSERT_EQ(eStateStopped, continue_state.get());
|
||||
ASSERT_EQ("T01", continue_response.GetStringRef());
|
||||
}
|
||||
|
||||
TEST(GDBRemoteClientBaseTest, SendContinueAndInterrupt)
|
||||
{
|
||||
StringExtractorGDBRemote continue_response, response;
|
||||
ContinueFixture fix;
|
||||
if (HasFailure())
|
||||
return;
|
||||
|
||||
// Interrupt should do nothing when we're not running.
|
||||
ASSERT_FALSE(fix.client.Interrupt());
|
||||
|
||||
// Continue. After the run packet is sent, send an interrupt.
|
||||
std::future<StateType> continue_state =
|
||||
std::async(std::launch::async, [&] { return fix.SendCPacket(continue_response); });
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("c", response.GetStringRef());
|
||||
fix.WaitForRunEvent();
|
||||
|
||||
std::future<bool> async_result = std::async(std::launch::async, [&] { return fix.client.Interrupt(); });
|
||||
|
||||
// We get interrupted.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("\x03", response.GetStringRef());
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13"));
|
||||
|
||||
// And that's it.
|
||||
ASSERT_EQ(eStateStopped, continue_state.get());
|
||||
ASSERT_EQ("T13", continue_response.GetStringRef());
|
||||
ASSERT_TRUE(async_result.get());
|
||||
}
|
||||
|
||||
TEST(GDBRemoteClientBaseTest, SendContinueAndInterrupt2PacketBug)
|
||||
{
|
||||
StringExtractorGDBRemote continue_response, async_response, response;
|
||||
const bool send_async = true;
|
||||
ContinueFixture fix;
|
||||
if (HasFailure())
|
||||
return;
|
||||
|
||||
// Interrupt should do nothing when we're not running.
|
||||
ASSERT_FALSE(fix.client.Interrupt());
|
||||
|
||||
// Continue. After the run packet is sent, send an async signal.
|
||||
std::future<StateType> continue_state =
|
||||
std::async(std::launch::async, [&] { return fix.SendCPacket(continue_response); });
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("c", response.GetStringRef());
|
||||
fix.WaitForRunEvent();
|
||||
|
||||
std::future<bool> interrupt_result = std::async(std::launch::async, [&] { return fix.client.Interrupt(); });
|
||||
|
||||
// We get interrupted. We'll send two packets to simulate a buggy stub.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("\x03", response.GetStringRef());
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13"));
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13"));
|
||||
|
||||
// We should stop.
|
||||
ASSERT_EQ(eStateStopped, continue_state.get());
|
||||
ASSERT_EQ("T13", continue_response.GetStringRef());
|
||||
ASSERT_TRUE(interrupt_result.get());
|
||||
|
||||
// Packet stream should remain synchronized.
|
||||
std::future<PacketResult> send_result = std::async(std::launch::async, [&] {
|
||||
return fix.client.SendPacketAndWaitForResponse("qTest", async_response, !send_async);
|
||||
});
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("qTest", response.GetStringRef());
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("QTest"));
|
||||
ASSERT_EQ(PacketResult::Success, send_result.get());
|
||||
ASSERT_EQ("QTest", async_response.GetStringRef());
|
||||
}
|
||||
|
||||
TEST(GDBRemoteClientBaseTest, SendContinueDelegateInterface)
|
||||
{
|
||||
StringExtractorGDBRemote response;
|
||||
ContinueFixture fix;
|
||||
if (HasFailure())
|
||||
return;
|
||||
|
||||
// Continue. We'll have the server send a bunch of async packets before it stops.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("O4142"));
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("Apro"));
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("O4344"));
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("Afile"));
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01"));
|
||||
ASSERT_EQ(eStateStopped, fix.SendCPacket(response));
|
||||
ASSERT_EQ("T01", response.GetStringRef());
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("c", response.GetStringRef());
|
||||
|
||||
EXPECT_EQ("ABCD", fix.delegate.output);
|
||||
EXPECT_EQ("profile", fix.delegate.misc_data);
|
||||
EXPECT_EQ(1u, fix.delegate.stop_reply_called);
|
||||
}
|
||||
|
||||
TEST(GDBRemoteClientBaseTest, InterruptNoResponse)
|
||||
{
|
||||
StringExtractorGDBRemote continue_response, response;
|
||||
ContinueFixture fix;
|
||||
if (HasFailure())
|
||||
return;
|
||||
|
||||
// Continue. After the run packet is sent, send an interrupt.
|
||||
std::future<StateType> continue_state =
|
||||
std::async(std::launch::async, [&] { return fix.SendCPacket(continue_response); });
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("c", response.GetStringRef());
|
||||
fix.WaitForRunEvent();
|
||||
|
||||
std::future<bool> async_result = std::async(std::launch::async, [&] { return fix.client.Interrupt(); });
|
||||
|
||||
// We get interrupted, but we don't send a stop packet.
|
||||
ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response));
|
||||
ASSERT_EQ("\x03", response.GetStringRef());
|
||||
|
||||
// The functions should still terminate (after a timeout).
|
||||
ASSERT_TRUE(async_result.get());
|
||||
ASSERT_EQ(eStateInvalid, continue_state.get());
|
||||
}
|
Loading…
Reference in New Issue