diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2019-12-20 19:53:05 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2019-12-20 19:53:05 +0000 | 
| commit | 0b57cec536236d46e3dba9bd041533462f33dbb7 (patch) | |
| tree | 56229dbdbbf76d18580f72f789003db17246c8d9 /contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote | |
| parent | 718ef55ec7785aae63f98f8ca05dc07ed399c16d (diff) | |
Notes
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote')
26 files changed, 20898 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp new file mode 100644 index 000000000000..fe7ef6b3acea --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp @@ -0,0 +1,394 @@ +//===-- GDBRemoteClientBase.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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 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(); + +  { +    std::lock_guard<std::mutex> lock(m_mutex); +    m_continue_packet = payload; +    m_should_stop = false; +  } +  ContinueLock cont_lock(*this); +  if (!cont_lock) +    return eStateInvalid; +  OnRunPacketSent(true); + +  for (;;) { +    PacketResult read_result = ReadPacket(response, kInterruptTimeout, false); +    switch (read_result) { +    case PacketResult::ErrorReplyTimeout: { +      std::lock_guard<std::mutex> lock(m_mutex); +      if (m_async_count == 0) +        continue; +      if (steady_clock::now() >= m_interrupt_time + kInterruptTimeout) +        return eStateInvalid; +      break; +    } +    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 'J': +      delegate.HandleAsyncStructuredDataPacket(response.GetStringRef()); +      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::SendPacketAndReceiveResponseWithOutputSupport( +    llvm::StringRef payload, StringExtractorGDBRemote &response, +    bool send_async, +    llvm::function_ref<void(llvm::StringRef)> output_callback) { +  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; +  } + +  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); +    // 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) != PacketResult::Success) +    return false; + +  OnRunPacketSent(true); + +  // wait for the response to the vCont +  if (ReadPacket(response, llvm::None, 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; +  ReadPacket(extra_stop_reply_packet, milliseconds(100), 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, nullptr); +} + +/////////////////////////////////////// +// 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; +    if (log) +      log->Printf("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 // +/////////////////////////////// + +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, nullptr); +      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 = steady_clock::now(); +    } +    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(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h new file mode 100644 index 000000000000..54f69e8caac6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h @@ -0,0 +1,148 @@ +//===-- GDBRemoteClientBase.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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; + +    // ========================================================================= +    /// Process asynchronously-received structured data. +    /// +    /// \param[in] data +    ///   The complete data packet, expected to start with JSON-async. +    // ========================================================================= +    virtual void HandleAsyncStructuredDataPacket(llvm::StringRef data) = 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(llvm::StringRef payload, +                                            StringExtractorGDBRemote &response, +                                            bool send_async); + +  PacketResult SendPacketAndReceiveResponseWithOutputSupport( +      llvm::StringRef payload, StringExtractorGDBRemote &response, +      bool send_async, +      llvm::function_ref<void(llvm::StringRef)> output_callback); + +  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_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp new file mode 100644 index 000000000000..11052eff948f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -0,0 +1,1398 @@ +//===-- GDBRemoteCommunication.cpp ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteCommunication.h" + +#include <future> +#include <limits.h> +#include <string.h> +#include <sys/stat.h> + +#include "lldb/Core/StreamFile.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/Pipe.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Host/Socket.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" +#include "lldb/Target/Platform.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/StreamString.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ScopedPrinter.h" + +#include "ProcessGDBRemoteLog.h" + +#if defined(__APPLE__) +#define DEBUGSERVER_BASENAME "debugserver" +#else +#define DEBUGSERVER_BASENAME "lldb-server" +#endif + +#if defined(HAVE_LIBCOMPRESSION) +#include <compression.h> +#endif + +#if defined(HAVE_LIBZ) +#include <zlib.h> +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +// GDBRemoteCommunication constructor +GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name, +                                               const char *listener_name) +    : Communication(comm_name), +#ifdef LLDB_CONFIGURATION_DEBUG +      m_packet_timeout(1000), +#else +      m_packet_timeout(1), +#endif +      m_echo_number(0), m_supports_qEcho(eLazyBoolCalculate), m_history(512), +      m_send_acks(true), m_compression_type(CompressionType::None), +      m_listen_url() { +} + +// Destructor +GDBRemoteCommunication::~GDBRemoteCommunication() { +  if (IsConnected()) { +    Disconnect(); +  } + +#if defined(HAVE_LIBCOMPRESSION) +  if (m_decompression_scratch) +    free (m_decompression_scratch); +#endif + +  // Stop the communications read thread which is used to parse all incoming +  // packets.  This function will block until the read thread returns. +  if (m_read_thread_enabled) +    StopReadThread(); +} + +char GDBRemoteCommunication::CalculcateChecksum(llvm::StringRef payload) { +  int checksum = 0; + +  for (char c : payload) +    checksum += c; + +  return checksum & 255; +} + +size_t GDBRemoteCommunication::SendAck() { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS)); +  ConnectionStatus status = eConnectionStatusSuccess; +  char ch = '+'; +  const size_t bytes_written = Write(&ch, 1, status, nullptr); +  if (log) +    log->Printf("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch); +  m_history.AddPacket(ch, GDBRemoteCommunicationHistory::ePacketTypeSend, +                      bytes_written); +  return bytes_written; +} + +size_t GDBRemoteCommunication::SendNack() { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS)); +  ConnectionStatus status = eConnectionStatusSuccess; +  char ch = '-'; +  const size_t bytes_written = Write(&ch, 1, status, nullptr); +  if (log) +    log->Printf("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch); +  m_history.AddPacket(ch, GDBRemoteCommunicationHistory::ePacketTypeSend, +                      bytes_written); +  return bytes_written; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::SendPacketNoLock(llvm::StringRef payload) { +  StreamString packet(0, 4, eByteOrderBig); +  packet.PutChar('$'); +  packet.Write(payload.data(), payload.size()); +  packet.PutChar('#'); +  packet.PutHex8(CalculcateChecksum(payload)); +  std::string packet_str = packet.GetString(); + +  return SendRawPacketNoLock(packet_str); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::SendRawPacketNoLock(llvm::StringRef packet, +                                            bool skip_ack) { +  if (IsConnected()) { +    Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS)); +    ConnectionStatus status = eConnectionStatusSuccess; +    const char *packet_data = packet.data(); +    const size_t packet_length = packet.size(); +    size_t bytes_written = Write(packet_data, packet_length, status, nullptr); +    if (log) { +      size_t binary_start_offset = 0; +      if (strncmp(packet_data, "$vFile:pwrite:", strlen("$vFile:pwrite:")) == +          0) { +        const char *first_comma = strchr(packet_data, ','); +        if (first_comma) { +          const char *second_comma = strchr(first_comma + 1, ','); +          if (second_comma) +            binary_start_offset = second_comma - packet_data + 1; +        } +      } + +      // If logging was just enabled and we have history, then dump out what we +      // have to the log so we get the historical context. The Dump() call that +      // logs all of the packet will set a boolean so that we don't dump this +      // more than once +      if (!m_history.DidDumpToLog()) +        m_history.Dump(log); + +      if (binary_start_offset) { +        StreamString strm; +        // Print non binary data header +        strm.Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written, +                    (int)binary_start_offset, packet_data); +        const uint8_t *p; +        // Print binary data exactly as sent +        for (p = (const uint8_t *)packet_data + binary_start_offset; *p != '#'; +             ++p) +          strm.Printf("\\x%2.2x", *p); +        // Print the checksum +        strm.Printf("%*s", (int)3, p); +        log->PutString(strm.GetString()); +      } else +        log->Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written, +                    (int)packet_length, packet_data); +    } + +    m_history.AddPacket(packet.str(), packet_length, +                        GDBRemoteCommunicationHistory::ePacketTypeSend, +                        bytes_written); + +    if (bytes_written == packet_length) { +      if (!skip_ack && GetSendAcks()) +        return GetAck(); +      else +        return PacketResult::Success; +    } else { +      if (log) +        log->Printf("error: failed to send packet: %.*s", (int)packet_length, +                    packet_data); +    } +  } +  return PacketResult::ErrorSendFailed; +} + +GDBRemoteCommunication::PacketResult GDBRemoteCommunication::GetAck() { +  StringExtractorGDBRemote packet; +  PacketResult result = ReadPacket(packet, GetPacketTimeout(), false); +  if (result == PacketResult::Success) { +    if (packet.GetResponseType() == +        StringExtractorGDBRemote::ResponseType::eAck) +      return PacketResult::Success; +    else +      return PacketResult::ErrorSendAck; +  } +  return result; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::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 +GDBRemoteCommunication::ReadPacket(StringExtractorGDBRemote &response, +                                   Timeout<std::micro> timeout, +                                   bool sync_on_timeout) { +  if (m_read_thread_enabled) +    return PopPacketFromQueue(response, timeout); +  else +    return WaitForPacketNoLock(response, timeout, sync_on_timeout); +} + +// This function is called when a packet is requested. +// A whole packet is popped from the packet queue and returned to the caller. +// Packets are placed into this queue from the communication read thread. See +// GDBRemoteCommunication::AppendBytesToCache. +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::PopPacketFromQueue(StringExtractorGDBRemote &response, +                                           Timeout<std::micro> timeout) { +  auto pred = [&] { return !m_packet_queue.empty() && IsConnected(); }; +  // lock down the packet queue +  std::unique_lock<std::mutex> lock(m_packet_queue_mutex); + +  if (!timeout) +    m_condition_queue_not_empty.wait(lock, pred); +  else { +    if (!m_condition_queue_not_empty.wait_for(lock, *timeout, pred)) +      return PacketResult::ErrorReplyTimeout; +    if (!IsConnected()) +      return PacketResult::ErrorDisconnected; +  } + +  // get the front element of the queue +  response = m_packet_queue.front(); + +  // remove the front element +  m_packet_queue.pop(); + +  // we got a packet +  return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::WaitForPacketNoLock(StringExtractorGDBRemote &packet, +                                            Timeout<std::micro> timeout, +                                            bool sync_on_timeout) { +  uint8_t buffer[8192]; +  Status error; + +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS)); + +  // Check for a packet from our cache first without trying any reading... +  if (CheckForPacket(nullptr, 0, packet) != PacketType::Invalid) +    return PacketResult::Success; + +  bool timed_out = false; +  bool disconnected = false; +  while (IsConnected() && !timed_out) { +    lldb::ConnectionStatus status = eConnectionStatusNoConnection; +    size_t bytes_read = Read(buffer, sizeof(buffer), timeout, status, &error); + +    LLDB_LOGV(log, +              "Read(buffer, sizeof(buffer), timeout = {0}, " +              "status = {1}, error = {2}) => bytes_read = {3}", +              timeout, Communication::ConnectionStatusAsCString(status), error, +              bytes_read); + +    if (bytes_read > 0) { +      if (CheckForPacket(buffer, bytes_read, packet) != PacketType::Invalid) +        return PacketResult::Success; +    } else { +      switch (status) { +      case eConnectionStatusTimedOut: +      case eConnectionStatusInterrupted: +        if (sync_on_timeout) { +          /// Sync the remote GDB server and make sure we get a response that +          /// corresponds to what we send. +          /// +          /// Sends a "qEcho" packet and makes sure it gets the exact packet +          /// echoed back. If the qEcho packet isn't supported, we send a qC +          /// packet and make sure we get a valid thread ID back. We use the +          /// "qC" packet since its response if very unique: is responds with +          /// "QC%x" where %x is the thread ID of the current thread. This +          /// makes the response unique enough from other packet responses to +          /// ensure we are back on track. +          /// +          /// This packet is needed after we time out sending a packet so we +          /// can ensure that we are getting the response for the packet we +          /// are sending. There are no sequence IDs in the GDB remote +          /// protocol (there used to be, but they are not supported anymore) +          /// so if you timeout sending packet "abc", you might then send +          /// packet "cde" and get the response for the previous "abc" packet. +          /// Many responses are "OK" or "" (unsupported) or "EXX" (error) so +          /// many responses for packets can look like responses for other +          /// packets. So if we timeout, we need to ensure that we can get +          /// back on track. If we can't get back on track, we must +          /// disconnect. +          bool sync_success = false; +          bool got_actual_response = false; +          // We timed out, we need to sync back up with the +          char echo_packet[32]; +          int echo_packet_len = 0; +          RegularExpression response_regex; + +          if (m_supports_qEcho == eLazyBoolYes) { +            echo_packet_len = ::snprintf(echo_packet, sizeof(echo_packet), +                                         "qEcho:%u", ++m_echo_number); +            std::string regex_str = "^"; +            regex_str += echo_packet; +            regex_str += "$"; +            response_regex.Compile(regex_str); +          } else { +            echo_packet_len = +                ::snprintf(echo_packet, sizeof(echo_packet), "qC"); +            response_regex.Compile(llvm::StringRef("^QC[0-9A-Fa-f]+$")); +          } + +          PacketResult echo_packet_result = +              SendPacketNoLock(llvm::StringRef(echo_packet, echo_packet_len)); +          if (echo_packet_result == PacketResult::Success) { +            const uint32_t max_retries = 3; +            uint32_t successful_responses = 0; +            for (uint32_t i = 0; i < max_retries; ++i) { +              StringExtractorGDBRemote echo_response; +              echo_packet_result = +                  WaitForPacketNoLock(echo_response, timeout, false); +              if (echo_packet_result == PacketResult::Success) { +                ++successful_responses; +                if (response_regex.Execute(echo_response.GetStringRef())) { +                  sync_success = true; +                  break; +                } else if (successful_responses == 1) { +                  // We got something else back as the first successful +                  // response, it probably is the  response to the packet we +                  // actually wanted, so copy it over if this is the first +                  // success and continue to try to get the qEcho response +                  packet = echo_response; +                  got_actual_response = true; +                } +              } else if (echo_packet_result == PacketResult::ErrorReplyTimeout) +                continue; // Packet timed out, continue waiting for a response +              else +                break; // Something else went wrong getting the packet back, we +                       // failed and are done trying +            } +          } + +          // We weren't able to sync back up with the server, we must abort +          // otherwise all responses might not be from the right packets... +          if (sync_success) { +            // We timed out, but were able to recover +            if (got_actual_response) { +              // We initially timed out, but we did get a response that came in +              // before the successful reply to our qEcho packet, so lets say +              // everything is fine... +              return PacketResult::Success; +            } +          } else { +            disconnected = true; +            Disconnect(); +          } +        } +        timed_out = true; +        break; +      case eConnectionStatusSuccess: +        // printf ("status = success but error = %s\n", +        // error.AsCString("<invalid>")); +        break; + +      case eConnectionStatusEndOfFile: +      case eConnectionStatusNoConnection: +      case eConnectionStatusLostConnection: +      case eConnectionStatusError: +        disconnected = true; +        Disconnect(); +        break; +      } +    } +  } +  packet.Clear(); +  if (disconnected) +    return PacketResult::ErrorDisconnected; +  if (timed_out) +    return PacketResult::ErrorReplyTimeout; +  else +    return PacketResult::ErrorReplyFailed; +} + +bool GDBRemoteCommunication::DecompressPacket() { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS)); + +  if (!CompressionIsEnabled()) +    return true; + +  size_t pkt_size = m_bytes.size(); + +  // Smallest possible compressed packet is $N#00 - an uncompressed empty +  // reply, most commonly indicating an unsupported packet.  Anything less than +  // 5 characters, it's definitely not a compressed packet. +  if (pkt_size < 5) +    return true; + +  if (m_bytes[0] != '$' && m_bytes[0] != '%') +    return true; +  if (m_bytes[1] != 'C' && m_bytes[1] != 'N') +    return true; + +  size_t hash_mark_idx = m_bytes.find('#'); +  if (hash_mark_idx == std::string::npos) +    return true; +  if (hash_mark_idx + 2 >= m_bytes.size()) +    return true; + +  if (!::isxdigit(m_bytes[hash_mark_idx + 1]) || +      !::isxdigit(m_bytes[hash_mark_idx + 2])) +    return true; + +  size_t content_length = +      pkt_size - +      5; // not counting '$', 'C' | 'N', '#', & the two hex checksum chars +  size_t content_start = 2; // The first character of the +                            // compressed/not-compressed text of the packet +  size_t checksum_idx = +      hash_mark_idx + +      1; // The first character of the two hex checksum characters + +  // Normally size_of_first_packet == m_bytes.size() but m_bytes may contain +  // multiple packets. size_of_first_packet is the size of the initial packet +  // which we'll replace with the decompressed version of, leaving the rest of +  // m_bytes unmodified. +  size_t size_of_first_packet = hash_mark_idx + 3; + +  // Compressed packets ("$C") start with a base10 number which is the size of +  // the uncompressed payload, then a : and then the compressed data.  e.g. +  // $C1024:<binary>#00 Update content_start and content_length to only include +  // the <binary> part of the packet. + +  uint64_t decompressed_bufsize = ULONG_MAX; +  if (m_bytes[1] == 'C') { +    size_t i = content_start; +    while (i < hash_mark_idx && isdigit(m_bytes[i])) +      i++; +    if (i < hash_mark_idx && m_bytes[i] == ':') { +      i++; +      content_start = i; +      content_length = hash_mark_idx - content_start; +      std::string bufsize_str(m_bytes.data() + 2, i - 2 - 1); +      errno = 0; +      decompressed_bufsize = ::strtoul(bufsize_str.c_str(), nullptr, 10); +      if (errno != 0 || decompressed_bufsize == ULONG_MAX) { +        m_bytes.erase(0, size_of_first_packet); +        return false; +      } +    } +  } + +  if (GetSendAcks()) { +    char packet_checksum_cstr[3]; +    packet_checksum_cstr[0] = m_bytes[checksum_idx]; +    packet_checksum_cstr[1] = m_bytes[checksum_idx + 1]; +    packet_checksum_cstr[2] = '\0'; +    long packet_checksum = strtol(packet_checksum_cstr, nullptr, 16); + +    long actual_checksum = CalculcateChecksum( +        llvm::StringRef(m_bytes).substr(1, hash_mark_idx - 1)); +    bool success = packet_checksum == actual_checksum; +    if (!success) { +      if (log) +        log->Printf( +            "error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x", +            (int)(pkt_size), m_bytes.c_str(), (uint8_t)packet_checksum, +            (uint8_t)actual_checksum); +    } +    // Send the ack or nack if needed +    if (!success) { +      SendNack(); +      m_bytes.erase(0, size_of_first_packet); +      return false; +    } else { +      SendAck(); +    } +  } + +  if (m_bytes[1] == 'N') { +    // This packet was not compressed -- delete the 'N' character at the start +    // and the packet may be processed as-is. +    m_bytes.erase(1, 1); +    return true; +  } + +  // Reverse the gdb-remote binary escaping that was done to the compressed +  // text to guard characters like '$', '#', '}', etc. +  std::vector<uint8_t> unescaped_content; +  unescaped_content.reserve(content_length); +  size_t i = content_start; +  while (i < hash_mark_idx) { +    if (m_bytes[i] == '}') { +      i++; +      unescaped_content.push_back(m_bytes[i] ^ 0x20); +    } else { +      unescaped_content.push_back(m_bytes[i]); +    } +    i++; +  } + +  uint8_t *decompressed_buffer = nullptr; +  size_t decompressed_bytes = 0; + +  if (decompressed_bufsize != ULONG_MAX) { +    decompressed_buffer = (uint8_t *)malloc(decompressed_bufsize); +    if (decompressed_buffer == nullptr) { +      m_bytes.erase(0, size_of_first_packet); +      return false; +    } +  } + +#if defined(HAVE_LIBCOMPRESSION) +  if (m_compression_type == CompressionType::ZlibDeflate || +      m_compression_type == CompressionType::LZFSE || +      m_compression_type == CompressionType::LZ4 || +      m_compression_type == CompressionType::LZMA) { +    compression_algorithm compression_type; +    if (m_compression_type == CompressionType::LZFSE) +      compression_type = COMPRESSION_LZFSE; +    else if (m_compression_type == CompressionType::ZlibDeflate) +      compression_type = COMPRESSION_ZLIB; +    else if (m_compression_type == CompressionType::LZ4) +      compression_type = COMPRESSION_LZ4_RAW; +    else if (m_compression_type == CompressionType::LZMA) +      compression_type = COMPRESSION_LZMA; + +    if (m_decompression_scratch_type != m_compression_type) { +      if (m_decompression_scratch) { +        free (m_decompression_scratch); +        m_decompression_scratch = nullptr; +      } +      size_t scratchbuf_size = 0; +      if (m_compression_type == CompressionType::LZFSE) +        scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZFSE); +      else if (m_compression_type == CompressionType::LZ4) +        scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZ4_RAW); +      else if (m_compression_type == CompressionType::ZlibDeflate) +        scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_ZLIB); +      else if (m_compression_type == CompressionType::LZMA) +        scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZMA); +      else if (m_compression_type == CompressionType::LZFSE) +        scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZFSE); +      if (scratchbuf_size > 0) { +        m_decompression_scratch = (void*) malloc (scratchbuf_size); +        m_decompression_scratch_type = m_compression_type; +      } +    } + +    if (decompressed_bufsize != ULONG_MAX && decompressed_buffer != nullptr) { +      decompressed_bytes = compression_decode_buffer( +          decompressed_buffer, decompressed_bufsize, +          (uint8_t *)unescaped_content.data(), unescaped_content.size(), +          m_decompression_scratch, compression_type); +    } +  } +#endif + +#if defined(HAVE_LIBZ) +  if (decompressed_bytes == 0 && decompressed_bufsize != ULONG_MAX && +      decompressed_buffer != nullptr && +      m_compression_type == CompressionType::ZlibDeflate) { +    z_stream stream; +    memset(&stream, 0, sizeof(z_stream)); +    stream.next_in = (Bytef *)unescaped_content.data(); +    stream.avail_in = (uInt)unescaped_content.size(); +    stream.total_in = 0; +    stream.next_out = (Bytef *)decompressed_buffer; +    stream.avail_out = decompressed_bufsize; +    stream.total_out = 0; +    stream.zalloc = Z_NULL; +    stream.zfree = Z_NULL; +    stream.opaque = Z_NULL; + +    if (inflateInit2(&stream, -15) == Z_OK) { +      int status = inflate(&stream, Z_NO_FLUSH); +      inflateEnd(&stream); +      if (status == Z_STREAM_END) { +        decompressed_bytes = stream.total_out; +      } +    } +  } +#endif + +  if (decompressed_bytes == 0 || decompressed_buffer == nullptr) { +    if (decompressed_buffer) +      free(decompressed_buffer); +    m_bytes.erase(0, size_of_first_packet); +    return false; +  } + +  std::string new_packet; +  new_packet.reserve(decompressed_bytes + 6); +  new_packet.push_back(m_bytes[0]); +  new_packet.append((const char *)decompressed_buffer, decompressed_bytes); +  new_packet.push_back('#'); +  if (GetSendAcks()) { +    uint8_t decompressed_checksum = CalculcateChecksum( +        llvm::StringRef((const char *)decompressed_buffer, decompressed_bytes)); +    char decompressed_checksum_str[3]; +    snprintf(decompressed_checksum_str, 3, "%02x", decompressed_checksum); +    new_packet.append(decompressed_checksum_str); +  } else { +    new_packet.push_back('0'); +    new_packet.push_back('0'); +  } + +  m_bytes.replace(0, size_of_first_packet, new_packet.data(), +                  new_packet.size()); + +  free(decompressed_buffer); +  return true; +} + +GDBRemoteCommunication::PacketType +GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len, +                                       StringExtractorGDBRemote &packet) { +  // Put the packet data into the buffer in a thread safe fashion +  std::lock_guard<std::recursive_mutex> guard(m_bytes_mutex); + +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS)); + +  if (src && src_len > 0) { +    if (log && log->GetVerbose()) { +      StreamString s; +      log->Printf("GDBRemoteCommunication::%s adding %u bytes: %.*s", +                  __FUNCTION__, (uint32_t)src_len, (uint32_t)src_len, src); +    } +    m_bytes.append((const char *)src, src_len); +  } + +  bool isNotifyPacket = false; + +  // Parse up the packets into gdb remote packets +  if (!m_bytes.empty()) { +    // end_idx must be one past the last valid packet byte. Start it off with +    // an invalid value that is the same as the current index. +    size_t content_start = 0; +    size_t content_length = 0; +    size_t total_length = 0; +    size_t checksum_idx = std::string::npos; + +    // Size of packet before it is decompressed, for logging purposes +    size_t original_packet_size = m_bytes.size(); +    if (CompressionIsEnabled()) { +      if (!DecompressPacket()) { +        packet.Clear(); +        return GDBRemoteCommunication::PacketType::Standard; +      } +    } + +    switch (m_bytes[0]) { +    case '+':                            // Look for ack +    case '-':                            // Look for cancel +    case '\x03':                         // ^C to halt target +      content_length = total_length = 1; // The command is one byte long... +      break; + +    case '%': // Async notify packet +      isNotifyPacket = true; +      LLVM_FALLTHROUGH; + +    case '$': +      // Look for a standard gdb packet? +      { +        size_t hash_pos = m_bytes.find('#'); +        if (hash_pos != std::string::npos) { +          if (hash_pos + 2 < m_bytes.size()) { +            checksum_idx = hash_pos + 1; +            // Skip the dollar sign +            content_start = 1; +            // Don't include the # in the content or the $ in the content +            // length +            content_length = hash_pos - 1; + +            total_length = +                hash_pos + 3; // Skip the # and the two hex checksum bytes +          } else { +            // Checksum bytes aren't all here yet +            content_length = std::string::npos; +          } +        } +      } +      break; + +    default: { +      // We have an unexpected byte and we need to flush all bad data that is +      // in m_bytes, so we need to find the first byte that is a '+' (ACK), '-' +      // (NACK), \x03 (CTRL+C interrupt), or '$' character (start of packet +      // header) or of course, the end of the data in m_bytes... +      const size_t bytes_len = m_bytes.size(); +      bool done = false; +      uint32_t idx; +      for (idx = 1; !done && idx < bytes_len; ++idx) { +        switch (m_bytes[idx]) { +        case '+': +        case '-': +        case '\x03': +        case '%': +        case '$': +          done = true; +          break; + +        default: +          break; +        } +      } +      if (log) +        log->Printf("GDBRemoteCommunication::%s tossing %u junk bytes: '%.*s'", +                    __FUNCTION__, idx - 1, idx - 1, m_bytes.c_str()); +      m_bytes.erase(0, idx - 1); +    } break; +    } + +    if (content_length == std::string::npos) { +      packet.Clear(); +      return GDBRemoteCommunication::PacketType::Invalid; +    } else if (total_length > 0) { + +      // We have a valid packet... +      assert(content_length <= m_bytes.size()); +      assert(total_length <= m_bytes.size()); +      assert(content_length <= total_length); +      size_t content_end = content_start + content_length; + +      bool success = true; +      std::string &packet_str = packet.GetStringRef(); +      if (log) { +        // If logging was just enabled and we have history, then dump out what +        // we have to the log so we get the historical context. The Dump() call +        // that logs all of the packet will set a boolean so that we don't dump +        // this more than once +        if (!m_history.DidDumpToLog()) +          m_history.Dump(log); + +        bool binary = false; +        // Only detect binary for packets that start with a '$' and have a +        // '#CC' checksum +        if (m_bytes[0] == '$' && total_length > 4) { +          for (size_t i = 0; !binary && i < total_length; ++i) { +            unsigned char c = m_bytes[i]; +            if (isprint(c) == 0 && isspace(c) == 0) { +              binary = true; +            } +          } +        } +        if (binary) { +          StreamString strm; +          // Packet header... +          if (CompressionIsEnabled()) +            strm.Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %c", +                        (uint64_t)original_packet_size, (uint64_t)total_length, +                        m_bytes[0]); +          else +            strm.Printf("<%4" PRIu64 "> read packet: %c", +                        (uint64_t)total_length, m_bytes[0]); +          for (size_t i = content_start; i < content_end; ++i) { +            // Remove binary escaped bytes when displaying the packet... +            const char ch = m_bytes[i]; +            if (ch == 0x7d) { +              // 0x7d is the escape character.  The next character is to be +              // XOR'd with 0x20. +              const char escapee = m_bytes[++i] ^ 0x20; +              strm.Printf("%2.2x", escapee); +            } else { +              strm.Printf("%2.2x", (uint8_t)ch); +            } +          } +          // Packet footer... +          strm.Printf("%c%c%c", m_bytes[total_length - 3], +                      m_bytes[total_length - 2], m_bytes[total_length - 1]); +          log->PutString(strm.GetString()); +        } else { +          if (CompressionIsEnabled()) +            log->Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %.*s", +                        (uint64_t)original_packet_size, (uint64_t)total_length, +                        (int)(total_length), m_bytes.c_str()); +          else +            log->Printf("<%4" PRIu64 "> read packet: %.*s", +                        (uint64_t)total_length, (int)(total_length), +                        m_bytes.c_str()); +        } +      } + +      m_history.AddPacket(m_bytes, total_length, +                          GDBRemoteCommunicationHistory::ePacketTypeRecv, +                          total_length); + +      // Clear packet_str in case there is some existing data in it. +      packet_str.clear(); +      // Copy the packet from m_bytes to packet_str expanding the run-length +      // encoding in the process. Reserve enough byte for the most common case +      // (no RLE used) +      packet_str.reserve(m_bytes.length()); +      for (std::string::const_iterator c = m_bytes.begin() + content_start; +           c != m_bytes.begin() + content_end; ++c) { +        if (*c == '*') { +          // '*' indicates RLE. Next character will give us the repeat count +          // and previous character is what is to be repeated. +          char char_to_repeat = packet_str.back(); +          // Number of time the previous character is repeated +          int repeat_count = *++c + 3 - ' '; +          // We have the char_to_repeat and repeat_count. Now push it in the +          // packet. +          for (int i = 0; i < repeat_count; ++i) +            packet_str.push_back(char_to_repeat); +        } else if (*c == 0x7d) { +          // 0x7d is the escape character.  The next character is to be XOR'd +          // with 0x20. +          char escapee = *++c ^ 0x20; +          packet_str.push_back(escapee); +        } else { +          packet_str.push_back(*c); +        } +      } + +      if (m_bytes[0] == '$' || m_bytes[0] == '%') { +        assert(checksum_idx < m_bytes.size()); +        if (::isxdigit(m_bytes[checksum_idx + 0]) || +            ::isxdigit(m_bytes[checksum_idx + 1])) { +          if (GetSendAcks()) { +            const char *packet_checksum_cstr = &m_bytes[checksum_idx]; +            char packet_checksum = strtol(packet_checksum_cstr, nullptr, 16); +            char actual_checksum = CalculcateChecksum( +                llvm::StringRef(m_bytes).slice(content_start, content_end)); +            success = packet_checksum == actual_checksum; +            if (!success) { +              if (log) +                log->Printf("error: checksum mismatch: %.*s expected 0x%2.2x, " +                            "got 0x%2.2x", +                            (int)(total_length), m_bytes.c_str(), +                            (uint8_t)packet_checksum, (uint8_t)actual_checksum); +            } +            // Send the ack or nack if needed +            if (!success) +              SendNack(); +            else +              SendAck(); +          } +        } else { +          success = false; +          if (log) +            log->Printf("error: invalid checksum in packet: '%s'\n", +                        m_bytes.c_str()); +        } +      } + +      m_bytes.erase(0, total_length); +      packet.SetFilePos(0); + +      if (isNotifyPacket) +        return GDBRemoteCommunication::PacketType::Notify; +      else +        return GDBRemoteCommunication::PacketType::Standard; +    } +  } +  packet.Clear(); +  return GDBRemoteCommunication::PacketType::Invalid; +} + +Status GDBRemoteCommunication::StartListenThread(const char *hostname, +                                                 uint16_t port) { +  if (m_listen_thread.IsJoinable()) +    return Status("listen thread already running"); + +  char listen_url[512]; +  if (hostname && hostname[0]) +    snprintf(listen_url, sizeof(listen_url), "listen://%s:%i", hostname, port); +  else +    snprintf(listen_url, sizeof(listen_url), "listen://%i", port); +  m_listen_url = listen_url; +  SetConnection(new ConnectionFileDescriptor()); +  llvm::Expected<HostThread> listen_thread = ThreadLauncher::LaunchThread( +      listen_url, GDBRemoteCommunication::ListenThread, this); +  if (!listen_thread) +    return Status(listen_thread.takeError()); +  m_listen_thread = *listen_thread; + +  return Status(); +} + +bool GDBRemoteCommunication::JoinListenThread() { +  if (m_listen_thread.IsJoinable()) +    m_listen_thread.Join(nullptr); +  return true; +} + +lldb::thread_result_t +GDBRemoteCommunication::ListenThread(lldb::thread_arg_t arg) { +  GDBRemoteCommunication *comm = (GDBRemoteCommunication *)arg; +  Status error; +  ConnectionFileDescriptor *connection = +      (ConnectionFileDescriptor *)comm->GetConnection(); + +  if (connection) { +    // Do the listen on another thread so we can continue on... +    if (connection->Connect(comm->m_listen_url.c_str(), &error) != +        eConnectionStatusSuccess) +      comm->SetConnection(nullptr); +  } +  return {}; +} + +Status GDBRemoteCommunication::StartDebugserverProcess( +    const char *url, Platform *platform, ProcessLaunchInfo &launch_info, +    uint16_t *port, const Args *inferior_args, int pass_comm_fd) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  if (log) +    log->Printf("GDBRemoteCommunication::%s(url=%s, port=%" PRIu16 ")", +                __FUNCTION__, url ? url : "<empty>", +                port ? *port : uint16_t(0)); + +  Status error; +  // If we locate debugserver, keep that located version around +  static FileSpec g_debugserver_file_spec; + +  char debugserver_path[PATH_MAX]; +  FileSpec &debugserver_file_spec = launch_info.GetExecutableFile(); + +  Environment host_env = Host::GetEnvironment(); + +  // Always check to see if we have an environment override for the path to the +  // debugserver to use and use it if we do. +  std::string env_debugserver_path = host_env.lookup("LLDB_DEBUGSERVER_PATH"); +  if (!env_debugserver_path.empty()) { +    debugserver_file_spec.SetFile(env_debugserver_path, +                                  FileSpec::Style::native); +    if (log) +      log->Printf("GDBRemoteCommunication::%s() gdb-remote stub exe path set " +                  "from environment variable: %s", +                  __FUNCTION__, env_debugserver_path.c_str()); +  } else +    debugserver_file_spec = g_debugserver_file_spec; +  bool debugserver_exists = +      FileSystem::Instance().Exists(debugserver_file_spec); +  if (!debugserver_exists) { +    // The debugserver binary is in the LLDB.framework/Resources directory. +    debugserver_file_spec = HostInfo::GetSupportExeDir(); +    if (debugserver_file_spec) { +      debugserver_file_spec.AppendPathComponent(DEBUGSERVER_BASENAME); +      debugserver_exists = FileSystem::Instance().Exists(debugserver_file_spec); +      if (debugserver_exists) { +        if (log) +          log->Printf( +              "GDBRemoteCommunication::%s() found gdb-remote stub exe '%s'", +              __FUNCTION__, debugserver_file_spec.GetPath().c_str()); + +        g_debugserver_file_spec = debugserver_file_spec; +      } else { +        if (platform) +          debugserver_file_spec = +              platform->LocateExecutable(DEBUGSERVER_BASENAME); +        else +          debugserver_file_spec.Clear(); +        if (debugserver_file_spec) { +          // Platform::LocateExecutable() wouldn't return a path if it doesn't +          // exist +          debugserver_exists = true; +        } else { +          if (log) +            log->Printf("GDBRemoteCommunication::%s() could not find " +                        "gdb-remote stub exe '%s'", +                        __FUNCTION__, debugserver_file_spec.GetPath().c_str()); +        } +        // Don't cache the platform specific GDB server binary as it could +        // change from platform to platform +        g_debugserver_file_spec.Clear(); +      } +    } +  } + +  if (debugserver_exists) { +    debugserver_file_spec.GetPath(debugserver_path, sizeof(debugserver_path)); + +    Args &debugserver_args = launch_info.GetArguments(); +    debugserver_args.Clear(); + +    // Start args with "debugserver /file/path -r --" +    debugserver_args.AppendArgument(llvm::StringRef(debugserver_path)); + +#if !defined(__APPLE__) +    // First argument to lldb-server must be mode in which to run. +    debugserver_args.AppendArgument(llvm::StringRef("gdbserver")); +#endif + +    // If a url is supplied then use it +    if (url) +      debugserver_args.AppendArgument(llvm::StringRef(url)); + +    if (pass_comm_fd >= 0) { +      StreamString fd_arg; +      fd_arg.Printf("--fd=%i", pass_comm_fd); +      debugserver_args.AppendArgument(fd_arg.GetString()); +      // Send "pass_comm_fd" down to the inferior so it can use it to +      // communicate back with this process +      launch_info.AppendDuplicateFileAction(pass_comm_fd, pass_comm_fd); +    } + +    // use native registers, not the GDB registers +    debugserver_args.AppendArgument(llvm::StringRef("--native-regs")); + +    if (launch_info.GetLaunchInSeparateProcessGroup()) { +      debugserver_args.AppendArgument(llvm::StringRef("--setsid")); +    } + +    llvm::SmallString<128> named_pipe_path; +    // socket_pipe is used by debug server to communicate back either +    // TCP port or domain socket name which it listens on. +    // The second purpose of the pipe to serve as a synchronization point - +    // once data is written to the pipe, debug server is up and running. +    Pipe socket_pipe; + +    // port is null when debug server should listen on domain socket - we're +    // not interested in port value but rather waiting for debug server to +    // become available. +    if (pass_comm_fd == -1) { +      if (url) { +// Create a temporary file to get the stdout/stderr and redirect the output of +// the command into this file. We will later read this file if all goes well +// and fill the data into "command_output_ptr" +#if defined(__APPLE__) +        // Binding to port zero, we need to figure out what port it ends up +        // using using a named pipe... +        error = socket_pipe.CreateWithUniqueName("debugserver-named-pipe", +                                                 false, named_pipe_path); +        if (error.Fail()) { +          if (log) +            log->Printf("GDBRemoteCommunication::%s() " +                        "named pipe creation failed: %s", +                        __FUNCTION__, error.AsCString()); +          return error; +        } +        debugserver_args.AppendArgument(llvm::StringRef("--named-pipe")); +        debugserver_args.AppendArgument(named_pipe_path); +#else +        // Binding to port zero, we need to figure out what port it ends up +        // using using an unnamed pipe... +        error = socket_pipe.CreateNew(true); +        if (error.Fail()) { +          if (log) +            log->Printf("GDBRemoteCommunication::%s() " +                        "unnamed pipe creation failed: %s", +                        __FUNCTION__, error.AsCString()); +          return error; +        } +        pipe_t write = socket_pipe.GetWritePipe(); +        debugserver_args.AppendArgument(llvm::StringRef("--pipe")); +        debugserver_args.AppendArgument(llvm::to_string(write)); +        launch_info.AppendCloseFileAction(socket_pipe.GetReadFileDescriptor()); +#endif +      } else { +        // No host and port given, so lets listen on our end and make the +        // debugserver connect to us.. +        error = StartListenThread("127.0.0.1", 0); +        if (error.Fail()) { +          if (log) +            log->Printf("GDBRemoteCommunication::%s() unable to start listen " +                        "thread: %s", +                        __FUNCTION__, error.AsCString()); +          return error; +        } + +        ConnectionFileDescriptor *connection = +            (ConnectionFileDescriptor *)GetConnection(); +        // Wait for 10 seconds to resolve the bound port +        uint16_t port_ = connection->GetListeningPort(std::chrono::seconds(10)); +        if (port_ > 0) { +          char port_cstr[32]; +          snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%i", port_); +          // Send the host and port down that debugserver and specify an option +          // so that it connects back to the port we are listening to in this +          // process +          debugserver_args.AppendArgument(llvm::StringRef("--reverse-connect")); +          debugserver_args.AppendArgument(llvm::StringRef(port_cstr)); +          if (port) +            *port = port_; +        } else { +          error.SetErrorString("failed to bind to port 0 on 127.0.0.1"); +          if (log) +            log->Printf("GDBRemoteCommunication::%s() failed: %s", __FUNCTION__, +                        error.AsCString()); +          return error; +        } +      } +    } +    std::string env_debugserver_log_file = +        host_env.lookup("LLDB_DEBUGSERVER_LOG_FILE"); +    if (!env_debugserver_log_file.empty()) { +      debugserver_args.AppendArgument( +          llvm::formatv("--log-file={0}", env_debugserver_log_file).str()); +    } + +#if defined(__APPLE__) +    const char *env_debugserver_log_flags = +        getenv("LLDB_DEBUGSERVER_LOG_FLAGS"); +    if (env_debugserver_log_flags) { +      debugserver_args.AppendArgument( +          llvm::formatv("--log-flags={0}", env_debugserver_log_flags).str()); +    } +#else +    std::string env_debugserver_log_channels = +        host_env.lookup("LLDB_SERVER_LOG_CHANNELS"); +    if (!env_debugserver_log_channels.empty()) { +      debugserver_args.AppendArgument( +          llvm::formatv("--log-channels={0}", env_debugserver_log_channels) +              .str()); +    } +#endif + +    // Add additional args, starting with LLDB_DEBUGSERVER_EXTRA_ARG_1 until an +    // env var doesn't come back. +    uint32_t env_var_index = 1; +    bool has_env_var; +    do { +      char env_var_name[64]; +      snprintf(env_var_name, sizeof(env_var_name), +               "LLDB_DEBUGSERVER_EXTRA_ARG_%" PRIu32, env_var_index++); +      std::string extra_arg = host_env.lookup(env_var_name); +      has_env_var = !extra_arg.empty(); + +      if (has_env_var) { +        debugserver_args.AppendArgument(llvm::StringRef(extra_arg)); +        if (log) +          log->Printf("GDBRemoteCommunication::%s adding env var %s contents " +                      "to stub command line (%s)", +                      __FUNCTION__, env_var_name, extra_arg.c_str()); +      } +    } while (has_env_var); + +    if (inferior_args && inferior_args->GetArgumentCount() > 0) { +      debugserver_args.AppendArgument(llvm::StringRef("--")); +      debugserver_args.AppendArguments(*inferior_args); +    } + +    // Copy the current environment to the gdbserver/debugserver instance +    launch_info.GetEnvironment() = host_env; + +    // Close STDIN, STDOUT and STDERR. +    launch_info.AppendCloseFileAction(STDIN_FILENO); +    launch_info.AppendCloseFileAction(STDOUT_FILENO); +    launch_info.AppendCloseFileAction(STDERR_FILENO); + +    // Redirect STDIN, STDOUT and STDERR to "/dev/null". +    launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false); +    launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true); +    launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true); + +    if (log) { +      StreamString string_stream; +      Platform *const platform = nullptr; +      launch_info.Dump(string_stream, platform); +      log->Printf("launch info for gdb-remote stub:\n%s", +                  string_stream.GetData()); +    } +    error = Host::LaunchProcess(launch_info); + +    if (error.Success() && +        (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) && +        pass_comm_fd == -1) { +      if (named_pipe_path.size() > 0) { +        error = socket_pipe.OpenAsReader(named_pipe_path, false); +        if (error.Fail()) +          if (log) +            log->Printf("GDBRemoteCommunication::%s() " +                        "failed to open named pipe %s for reading: %s", +                        __FUNCTION__, named_pipe_path.c_str(), +                        error.AsCString()); +      } + +      if (socket_pipe.CanWrite()) +        socket_pipe.CloseWriteFileDescriptor(); +      if (socket_pipe.CanRead()) { +        char port_cstr[PATH_MAX] = {0}; +        port_cstr[0] = '\0'; +        size_t num_bytes = sizeof(port_cstr); +        // Read port from pipe with 10 second timeout. +        error = socket_pipe.ReadWithTimeout( +            port_cstr, num_bytes, std::chrono::seconds{10}, num_bytes); +        if (error.Success() && (port != nullptr)) { +          assert(num_bytes > 0 && port_cstr[num_bytes - 1] == '\0'); +          uint16_t child_port = StringConvert::ToUInt32(port_cstr, 0); +          if (*port == 0 || *port == child_port) { +            *port = child_port; +            if (log) +              log->Printf("GDBRemoteCommunication::%s() " +                          "debugserver listens %u port", +                          __FUNCTION__, *port); +          } else { +            if (log) +              log->Printf("GDBRemoteCommunication::%s() " +                          "debugserver listening on port " +                          "%d but requested port was %d", +                          __FUNCTION__, (uint32_t)child_port, +                          (uint32_t)(*port)); +          } +        } else { +          if (log) +            log->Printf("GDBRemoteCommunication::%s() " +                        "failed to read a port value from pipe %s: %s", +                        __FUNCTION__, named_pipe_path.c_str(), +                        error.AsCString()); +        } +        socket_pipe.Close(); +      } + +      if (named_pipe_path.size() > 0) { +        const auto err = socket_pipe.Delete(named_pipe_path); +        if (err.Fail()) { +          if (log) +            log->Printf( +                "GDBRemoteCommunication::%s failed to delete pipe %s: %s", +                __FUNCTION__, named_pipe_path.c_str(), err.AsCString()); +        } +      } + +      // Make sure we actually connect with the debugserver... +      JoinListenThread(); +    } +  } else { +    error.SetErrorStringWithFormat("unable to locate " DEBUGSERVER_BASENAME); +  } + +  if (error.Fail()) { +    if (log) +      log->Printf("GDBRemoteCommunication::%s() failed: %s", __FUNCTION__, +                  error.AsCString()); +  } + +  return error; +} + +void GDBRemoteCommunication::DumpHistory(Stream &strm) { m_history.Dump(strm); } + +void GDBRemoteCommunication::SetHistoryStream(llvm::raw_ostream *strm) { +  m_history.SetStream(strm); +} + +llvm::Error +GDBRemoteCommunication::ConnectLocally(GDBRemoteCommunication &client, +                                       GDBRemoteCommunication &server) { +  const bool child_processes_inherit = false; +  const int backlog = 5; +  TCPSocket listen_socket(true, child_processes_inherit); +  if (llvm::Error error = +          listen_socket.Listen("127.0.0.1:0", backlog).ToError()) +    return error; + +  Socket *accept_socket; +  std::future<Status> accept_status = std::async( +      std::launch::async, [&] { return listen_socket.Accept(accept_socket); }); + +  llvm::SmallString<32> remote_addr; +  llvm::raw_svector_ostream(remote_addr) +      << "connect://127.0.0.1:" << listen_socket.GetLocalPortNumber(); + +  std::unique_ptr<ConnectionFileDescriptor> conn_up( +      new ConnectionFileDescriptor()); +  Status status; +  if (conn_up->Connect(remote_addr, &status) != lldb::eConnectionStatusSuccess) +    return llvm::createStringError(llvm::inconvertibleErrorCode(), +                                   "Unable to connect: %s", status.AsCString()); + +  client.SetConnection(conn_up.release()); +  if (llvm::Error error = accept_status.get().ToError()) +    return error; + +  server.SetConnection(new ConnectionFileDescriptor(accept_socket)); +  return llvm::Error::success(); +} + +GDBRemoteCommunication::ScopedTimeout::ScopedTimeout( +    GDBRemoteCommunication &gdb_comm, std::chrono::seconds timeout) +    : m_gdb_comm(gdb_comm), m_timeout_modified(false) { +  auto curr_timeout = gdb_comm.GetPacketTimeout(); +  // Only update the timeout if the timeout is greater than the current +  // timeout. If the current timeout is larger, then just use that. +  if (curr_timeout < timeout) { +    m_timeout_modified = true; +    m_saved_timeout = m_gdb_comm.SetPacketTimeout(timeout); +  } +} + +GDBRemoteCommunication::ScopedTimeout::~ScopedTimeout() { +  // Only restore the timeout if we set it in the constructor. +  if (m_timeout_modified) +    m_gdb_comm.SetPacketTimeout(m_saved_timeout); +} + +// This function is called via the Communications class read thread when bytes +// become available for this connection. This function will consume all +// incoming bytes and try to parse whole packets as they become available. Full +// packets are placed in a queue, so that all packet requests can simply pop +// from this queue. Async notification packets will be dispatched immediately +// to the ProcessGDBRemote Async thread via an event. +void GDBRemoteCommunication::AppendBytesToCache(const uint8_t *bytes, +                                                size_t len, bool broadcast, +                                                lldb::ConnectionStatus status) { +  StringExtractorGDBRemote packet; + +  while (true) { +    PacketType type = CheckForPacket(bytes, len, packet); + +    // scrub the data so we do not pass it back to CheckForPacket on future +    // passes of the loop +    bytes = nullptr; +    len = 0; + +    // we may have received no packet so lets bail out +    if (type == PacketType::Invalid) +      break; + +    if (type == PacketType::Standard) { +      // scope for the mutex +      { +        // lock down the packet queue +        std::lock_guard<std::mutex> guard(m_packet_queue_mutex); +        // push a new packet into the queue +        m_packet_queue.push(packet); +        // Signal condition variable that we have a packet +        m_condition_queue_not_empty.notify_one(); +      } +    } + +    if (type == PacketType::Notify) { +      // put this packet into an event +      const char *pdata = packet.GetStringRef().c_str(); + +      // as the communication class, we are a broadcaster and the async thread +      // is tuned to listen to us +      BroadcastEvent(eBroadcastBitGdbReadThreadGotNotify, +                     new EventDataBytes(pdata)); +    } +  } +} + +void llvm::format_provider<GDBRemoteCommunication::PacketResult>::format( +    const GDBRemoteCommunication::PacketResult &result, raw_ostream &Stream, +    StringRef Style) { +  using PacketResult = GDBRemoteCommunication::PacketResult; + +  switch (result) { +  case PacketResult::Success: +    Stream << "Success"; +    break; +  case PacketResult::ErrorSendFailed: +    Stream << "ErrorSendFailed"; +    break; +  case PacketResult::ErrorSendAck: +    Stream << "ErrorSendAck"; +    break; +  case PacketResult::ErrorReplyFailed: +    Stream << "ErrorReplyFailed"; +    break; +  case PacketResult::ErrorReplyTimeout: +    Stream << "ErrorReplyTimeout"; +    break; +  case PacketResult::ErrorReplyInvalid: +    Stream << "ErrorReplyInvalid"; +    break; +  case PacketResult::ErrorReplyAck: +    Stream << "ErrorReplyAck"; +    break; +  case PacketResult::ErrorDisconnected: +    Stream << "ErrorDisconnected"; +    break; +  case PacketResult::ErrorNoSequenceLock: +    Stream << "ErrorNoSequenceLock"; +    break; +  } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h new file mode 100644 index 000000000000..bb777a5c26a7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -0,0 +1,238 @@ +//===-- GDBRemoteCommunication.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunication_h_ +#define liblldb_GDBRemoteCommunication_h_ + +#include "GDBRemoteCommunicationHistory.h" + +#include <condition_variable> +#include <mutex> +#include <queue> +#include <string> +#include <vector> + +#include "lldb/Core/Communication.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/Listener.h" +#include "lldb/Utility/Predicate.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" +#include "lldb/lldb-public.h" + +namespace lldb_private { +namespace process_gdb_remote { + +enum GDBStoppointType { +  eStoppointInvalid = -1, +  eBreakpointSoftware = 0, +  eBreakpointHardware, +  eWatchpointWrite, +  eWatchpointRead, +  eWatchpointReadWrite +}; + +enum class CompressionType { +  None = 0,    // no compression +  ZlibDeflate, // zlib's deflate compression scheme, requires zlib or Apple's +               // libcompression +  LZFSE,       // an Apple compression scheme, requires Apple's libcompression +  LZ4, // lz compression - called "lz4 raw" in libcompression terms, compat with +       // https://code.google.com/p/lz4/ +  LZMA, // Lempel–Ziv–Markov chain algorithm +}; + +class ProcessGDBRemote; + +class GDBRemoteCommunication : public Communication { +public: +  enum { +    eBroadcastBitRunPacketSent = kLoUserBroadcastBit, +    eBroadcastBitGdbReadThreadGotNotify = +        kLoUserBroadcastBit << 1 // Sent when we received a notify packet. +  }; + +  enum class PacketType { Invalid = 0, Standard, Notify }; + +  enum class PacketResult { +    Success = 0,        // Success +    ErrorSendFailed,    // Status sending the packet +    ErrorSendAck,       // Didn't get an ack back after sending a packet +    ErrorReplyFailed,   // Status getting the reply +    ErrorReplyTimeout,  // Timed out waiting for reply +    ErrorReplyInvalid,  // Got a reply but it wasn't valid for the packet that +                        // was sent +    ErrorReplyAck,      // Sending reply ack failed +    ErrorDisconnected,  // We were disconnected +    ErrorNoSequenceLock // We couldn't get the sequence lock for a multi-packet +                        // request +  }; + +  // Class to change the timeout for a given scope and restore it to the +  // original value when the +  // created ScopedTimeout object got out of scope +  class ScopedTimeout { +  public: +    ScopedTimeout(GDBRemoteCommunication &gdb_comm, +                  std::chrono::seconds timeout); +    ~ScopedTimeout(); + +  private: +    GDBRemoteCommunication &m_gdb_comm; +    std::chrono::seconds m_saved_timeout; +    // Don't ever reduce the timeout for a packet, only increase it. If the +    // requested timeout if less than the current timeout, we don't set it +    // and won't need to restore it. +    bool m_timeout_modified; +  }; + +  GDBRemoteCommunication(const char *comm_name, const char *listener_name); + +  ~GDBRemoteCommunication() override; + +  PacketResult GetAck(); + +  size_t SendAck(); + +  size_t SendNack(); + +  char CalculcateChecksum(llvm::StringRef payload); + +  PacketType CheckForPacket(const uint8_t *src, size_t src_len, +                            StringExtractorGDBRemote &packet); + +  bool GetSendAcks() { return m_send_acks; } + +  // Set the global packet timeout. +  // +  // For clients, this is the timeout that gets used when sending +  // packets and waiting for responses. For servers, this is used when waiting +  // for ACKs. +  std::chrono::seconds SetPacketTimeout(std::chrono::seconds packet_timeout) { +    const auto old_packet_timeout = m_packet_timeout; +    m_packet_timeout = packet_timeout; +    return old_packet_timeout; +  } + +  std::chrono::seconds GetPacketTimeout() const { return m_packet_timeout; } + +  // Start a debugserver instance on the current host using the +  // supplied connection URL. +  Status StartDebugserverProcess( +      const char *url, +      Platform *platform, // If non nullptr, then check with the platform for +                          // the GDB server binary if it can't be located +      ProcessLaunchInfo &launch_info, uint16_t *port, const Args *inferior_args, +      int pass_comm_fd); // Communication file descriptor to pass during +                         // fork/exec to avoid having to connect/accept + +  void DumpHistory(Stream &strm); +  void SetHistoryStream(llvm::raw_ostream *strm); + +  static llvm::Error ConnectLocally(GDBRemoteCommunication &client, +                                    GDBRemoteCommunication &server); + +protected: +  std::chrono::seconds m_packet_timeout; +  uint32_t m_echo_number; +  LazyBool m_supports_qEcho; +  GDBRemoteCommunicationHistory m_history; +  bool m_send_acks; +  bool m_is_platform; // Set to true if this class represents a platform, +                      // false if this class represents a debug session for +                      // a single process + +  CompressionType m_compression_type; + +  PacketResult SendPacketNoLock(llvm::StringRef payload); +  PacketResult SendRawPacketNoLock(llvm::StringRef payload, +                                   bool skip_ack = false); + +  PacketResult ReadPacket(StringExtractorGDBRemote &response, +                          Timeout<std::micro> timeout, bool sync_on_timeout); + +  PacketResult ReadPacketWithOutputSupport( +      StringExtractorGDBRemote &response, Timeout<std::micro> timeout, +      bool sync_on_timeout, +      llvm::function_ref<void(llvm::StringRef)> output_callback); + +  // Pop a packet from the queue in a thread safe manner +  PacketResult PopPacketFromQueue(StringExtractorGDBRemote &response, +                                  Timeout<std::micro> timeout); + +  PacketResult WaitForPacketNoLock(StringExtractorGDBRemote &response, +                                   Timeout<std::micro> timeout, +                                   bool sync_on_timeout); + +  bool CompressionIsEnabled() { +    return m_compression_type != CompressionType::None; +  } + +  // If compression is enabled, decompress the packet in m_bytes and update +  // m_bytes with the uncompressed version. +  // Returns 'true' packet was decompressed and m_bytes is the now-decompressed +  // text. +  // Returns 'false' if unable to decompress or if the checksum was invalid. +  // +  // NB: Once the packet has been decompressed, checksum cannot be computed +  // based +  // on m_bytes.  The checksum was for the compressed packet. +  bool DecompressPacket(); + +  Status StartListenThread(const char *hostname = "127.0.0.1", +                           uint16_t port = 0); + +  bool JoinListenThread(); + +  static lldb::thread_result_t ListenThread(lldb::thread_arg_t arg); + +  // GDB-Remote read thread +  //  . this thread constantly tries to read from the communication +  //    class and stores all packets received in a queue.  The usual +  //    threads read requests simply pop packets off the queue in the +  //    usual order. +  //    This setup allows us to intercept and handle async packets, such +  //    as the notify packet. + +  // This method is defined as part of communication.h +  // when the read thread gets any bytes it will pass them on to this function +  void AppendBytesToCache(const uint8_t *bytes, size_t len, bool broadcast, +                          lldb::ConnectionStatus status) override; + +private: +  std::queue<StringExtractorGDBRemote> m_packet_queue; // The packet queue +  std::mutex m_packet_queue_mutex; // Mutex for accessing queue +  std::condition_variable +      m_condition_queue_not_empty; // Condition variable to wait for packets + +  HostThread m_listen_thread; +  std::string m_listen_url; + +#if defined(HAVE_LIBCOMPRESSION) +  CompressionType m_decompression_scratch_type = CompressionType::None; +  void *m_decompression_scratch = nullptr; +#endif + +  DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunication); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +namespace llvm { +template <> +struct format_provider< +    lldb_private::process_gdb_remote::GDBRemoteCommunication::PacketResult> { +  static void format(const lldb_private::process_gdb_remote:: +                         GDBRemoteCommunication::PacketResult &state, +                     raw_ostream &Stream, StringRef Style); +}; +} // namespace llvm + +#endif // liblldb_GDBRemoteCommunication_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp new file mode 100644 index 000000000000..9797184026e0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -0,0 +1,4005 @@ +//===-- GDBRemoteCommunicationClient.cpp ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteCommunicationClient.h" + +#include <math.h> +#include <sys/stat.h> + +#include <numeric> +#include <sstream> + +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/XML.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/JSON.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" + +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "lldb/Host/Config.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +#include "llvm/ADT/StringSwitch.h" + +#if defined(__APPLE__) +#ifndef HAVE_LIBCOMPRESSION +#define HAVE_LIBCOMPRESSION +#endif +#include <compression.h> +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace std::chrono; + +// GDBRemoteCommunicationClient constructor +GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() +    : GDBRemoteClientBase("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), +      m_supports_vCont_all(eLazyBoolCalculate), +      m_supports_vCont_any(eLazyBoolCalculate), +      m_supports_vCont_c(eLazyBoolCalculate), +      m_supports_vCont_C(eLazyBoolCalculate), +      m_supports_vCont_s(eLazyBoolCalculate), +      m_supports_vCont_S(eLazyBoolCalculate), +      m_qHostInfo_is_valid(eLazyBoolCalculate), +      m_curr_pid_is_valid(eLazyBoolCalculate), +      m_qProcessInfo_is_valid(eLazyBoolCalculate), +      m_qGDBServerVersion_is_valid(eLazyBoolCalculate), +      m_supports_alloc_dealloc_memory(eLazyBoolCalculate), +      m_supports_memory_region_info(eLazyBoolCalculate), +      m_supports_watchpoint_support_info(eLazyBoolCalculate), +      m_supports_detach_stay_stopped(eLazyBoolCalculate), +      m_watchpoints_trigger_after_instruction(eLazyBoolCalculate), +      m_attach_or_wait_reply(eLazyBoolCalculate), +      m_prepare_for_reg_writing_reply(eLazyBoolCalculate), +      m_supports_p(eLazyBoolCalculate), m_supports_x(eLazyBoolCalculate), +      m_avoid_g_packets(eLazyBoolCalculate), +      m_supports_QSaveRegisterState(eLazyBoolCalculate), +      m_supports_qXfer_auxv_read(eLazyBoolCalculate), +      m_supports_qXfer_libraries_read(eLazyBoolCalculate), +      m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate), +      m_supports_qXfer_features_read(eLazyBoolCalculate), +      m_supports_qXfer_memory_map_read(eLazyBoolCalculate), +      m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate), +      m_supports_jThreadExtendedInfo(eLazyBoolCalculate), +      m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate), +      m_supports_jGetSharedCacheInfo(eLazyBoolCalculate), +      m_supports_QPassSignals(eLazyBoolCalculate), +      m_supports_error_string_reply(eLazyBoolCalculate), +      m_supports_qProcessInfoPID(true), m_supports_qfProcessInfo(true), +      m_supports_qUserName(true), m_supports_qGroupName(true), +      m_supports_qThreadStopInfo(true), m_supports_z0(true), +      m_supports_z1(true), m_supports_z2(true), m_supports_z3(true), +      m_supports_z4(true), m_supports_QEnvironment(true), +      m_supports_QEnvironmentHexEncoded(true), m_supports_qSymbol(true), +      m_qSymbol_requests_done(false), m_supports_qModuleInfo(true), +      m_supports_jThreadsInfo(true), m_supports_jModulesInfo(true), +      m_curr_pid(LLDB_INVALID_PROCESS_ID), m_curr_tid(LLDB_INVALID_THREAD_ID), +      m_curr_tid_run(LLDB_INVALID_THREAD_ID), +      m_num_supported_hardware_watchpoints(0), m_host_arch(), m_process_arch(), +      m_os_build(), m_os_kernel(), m_hostname(), m_gdb_server_name(), +      m_gdb_server_version(UINT32_MAX), m_default_packet_timeout(0), +      m_max_packet_size(0), m_qSupported_response(), +      m_supported_async_json_packets_is_valid(false), +      m_supported_async_json_packets_sp(), m_qXfer_memory_map(), +      m_qXfer_memory_map_loaded(false) {} + +// Destructor +GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() { +  if (IsConnected()) +    Disconnect(); +} + +bool GDBRemoteCommunicationClient::HandshakeWithServer(Status *error_ptr) { +  ResetDiscoverableSettings(false); + +  // Start the read thread after we send the handshake ack since if we fail to +  // send the handshake ack, there is no reason to continue... +  if (SendAck()) { +    // Wait for any responses that might have been queued up in the remote +    // GDB server and flush them all +    StringExtractorGDBRemote response; +    PacketResult packet_result = PacketResult::Success; +    while (packet_result == PacketResult::Success) +      packet_result = ReadPacket(response, milliseconds(10), false); + +    // The return value from QueryNoAckModeSupported() is true if the packet +    // was sent and _any_ response (including UNIMPLEMENTED) was received), or +    // false if no response was received. This quickly tells us if we have a +    // live connection to a remote GDB server... +    if (QueryNoAckModeSupported()) { +      return true; +    } else { +      if (error_ptr) +        error_ptr->SetErrorString("failed to get reply to handshake packet"); +    } +  } else { +    if (error_ptr) +      error_ptr->SetErrorString("failed to send the handshake ack"); +  } +  return false; +} + +bool GDBRemoteCommunicationClient::GetEchoSupported() { +  if (m_supports_qEcho == eLazyBoolCalculate) { +    GetRemoteQSupported(); +  } +  return m_supports_qEcho == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQPassSignalsSupported() { +  if (m_supports_QPassSignals == eLazyBoolCalculate) { +    GetRemoteQSupported(); +  } +  return m_supports_QPassSignals == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetAugmentedLibrariesSVR4ReadSupported() { +  if (m_supports_augmented_libraries_svr4_read == eLazyBoolCalculate) { +    GetRemoteQSupported(); +  } +  return m_supports_augmented_libraries_svr4_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQXferLibrariesSVR4ReadSupported() { +  if (m_supports_qXfer_libraries_svr4_read == eLazyBoolCalculate) { +    GetRemoteQSupported(); +  } +  return m_supports_qXfer_libraries_svr4_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQXferLibrariesReadSupported() { +  if (m_supports_qXfer_libraries_read == eLazyBoolCalculate) { +    GetRemoteQSupported(); +  } +  return m_supports_qXfer_libraries_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQXferAuxvReadSupported() { +  if (m_supports_qXfer_auxv_read == eLazyBoolCalculate) { +    GetRemoteQSupported(); +  } +  return m_supports_qXfer_auxv_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported() { +  if (m_supports_qXfer_features_read == eLazyBoolCalculate) { +    GetRemoteQSupported(); +  } +  return m_supports_qXfer_features_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQXferMemoryMapReadSupported() { +  if (m_supports_qXfer_memory_map_read == eLazyBoolCalculate) { +    GetRemoteQSupported(); +  } +  return m_supports_qXfer_memory_map_read == eLazyBoolYes; +} + +uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() { +  if (m_max_packet_size == 0) { +    GetRemoteQSupported(); +  } +  return m_max_packet_size; +} + +bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() { +  if (m_supports_not_sending_acks == eLazyBoolCalculate) { +    m_send_acks = true; +    m_supports_not_sending_acks = eLazyBoolNo; + +    // This is the first real packet that we'll send in a debug session and it +    // may take a little longer than normal to receive a reply.  Wait at least +    // 6 seconds for a reply to this packet. + +    ScopedTimeout timeout(*this, std::max(GetPacketTimeout(), seconds(6))); + +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse("QStartNoAckMode", response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) { +        m_send_acks = false; +        m_supports_not_sending_acks = eLazyBoolYes; +      } +      return true; +    } +  } +  return false; +} + +void GDBRemoteCommunicationClient::GetListThreadsInStopReplySupported() { +  if (m_supports_threads_in_stop_reply == eLazyBoolCalculate) { +    m_supports_threads_in_stop_reply = eLazyBoolNo; + +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse("QListThreadsInStopReply", response, +                                     false) == PacketResult::Success) { +      if (response.IsOKResponse()) +        m_supports_threads_in_stop_reply = eLazyBoolYes; +    } +  } +} + +bool GDBRemoteCommunicationClient::GetVAttachOrWaitSupported() { +  if (m_attach_or_wait_reply == eLazyBoolCalculate) { +    m_attach_or_wait_reply = eLazyBoolNo; + +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse("qVAttachOrWaitSupported", response, +                                     false) == PacketResult::Success) { +      if (response.IsOKResponse()) +        m_attach_or_wait_reply = eLazyBoolYes; +    } +  } +  return m_attach_or_wait_reply == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetSyncThreadStateSupported() { +  if (m_prepare_for_reg_writing_reply == eLazyBoolCalculate) { +    m_prepare_for_reg_writing_reply = eLazyBoolNo; + +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse("qSyncThreadStateSupported", response, +                                     false) == PacketResult::Success) { +      if (response.IsOKResponse()) +        m_prepare_for_reg_writing_reply = eLazyBoolYes; +    } +  } +  return m_prepare_for_reg_writing_reply == eLazyBoolYes; +} + +void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) { +  if (!did_exec) { +    // Hard reset everything, this is when we first connect to a GDB server +    m_supports_not_sending_acks = eLazyBoolCalculate; +    m_supports_thread_suffix = eLazyBoolCalculate; +    m_supports_threads_in_stop_reply = eLazyBoolCalculate; +    m_supports_vCont_c = eLazyBoolCalculate; +    m_supports_vCont_C = eLazyBoolCalculate; +    m_supports_vCont_s = eLazyBoolCalculate; +    m_supports_vCont_S = eLazyBoolCalculate; +    m_supports_p = eLazyBoolCalculate; +    m_supports_x = eLazyBoolCalculate; +    m_supports_QSaveRegisterState = eLazyBoolCalculate; +    m_qHostInfo_is_valid = eLazyBoolCalculate; +    m_curr_pid_is_valid = eLazyBoolCalculate; +    m_qGDBServerVersion_is_valid = eLazyBoolCalculate; +    m_supports_alloc_dealloc_memory = eLazyBoolCalculate; +    m_supports_memory_region_info = eLazyBoolCalculate; +    m_prepare_for_reg_writing_reply = eLazyBoolCalculate; +    m_attach_or_wait_reply = eLazyBoolCalculate; +    m_avoid_g_packets = eLazyBoolCalculate; +    m_supports_qXfer_auxv_read = eLazyBoolCalculate; +    m_supports_qXfer_libraries_read = eLazyBoolCalculate; +    m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; +    m_supports_qXfer_features_read = eLazyBoolCalculate; +    m_supports_qXfer_memory_map_read = eLazyBoolCalculate; +    m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; +    m_supports_qProcessInfoPID = true; +    m_supports_qfProcessInfo = true; +    m_supports_qUserName = true; +    m_supports_qGroupName = true; +    m_supports_qThreadStopInfo = true; +    m_supports_z0 = true; +    m_supports_z1 = true; +    m_supports_z2 = true; +    m_supports_z3 = true; +    m_supports_z4 = true; +    m_supports_QEnvironment = true; +    m_supports_QEnvironmentHexEncoded = true; +    m_supports_qSymbol = true; +    m_qSymbol_requests_done = false; +    m_supports_qModuleInfo = true; +    m_host_arch.Clear(); +    m_os_version = llvm::VersionTuple(); +    m_os_build.clear(); +    m_os_kernel.clear(); +    m_hostname.clear(); +    m_gdb_server_name.clear(); +    m_gdb_server_version = UINT32_MAX; +    m_default_packet_timeout = seconds(0); +    m_max_packet_size = 0; +    m_qSupported_response.clear(); +    m_supported_async_json_packets_is_valid = false; +    m_supported_async_json_packets_sp.reset(); +    m_supports_jModulesInfo = true; +  } + +  // These flags should be reset when we first connect to a GDB server and when +  // our inferior process execs +  m_qProcessInfo_is_valid = eLazyBoolCalculate; +  m_process_arch.Clear(); +} + +void GDBRemoteCommunicationClient::GetRemoteQSupported() { +  // Clear out any capabilities we expect to see in the qSupported response +  m_supports_qXfer_auxv_read = eLazyBoolNo; +  m_supports_qXfer_libraries_read = eLazyBoolNo; +  m_supports_qXfer_libraries_svr4_read = eLazyBoolNo; +  m_supports_augmented_libraries_svr4_read = eLazyBoolNo; +  m_supports_qXfer_features_read = eLazyBoolNo; +  m_supports_qXfer_memory_map_read = eLazyBoolNo; +  m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if +                                  // not, we assume no limit + +  // build the qSupported packet +  std::vector<std::string> features = {"xmlRegisters=i386,arm,mips"}; +  StreamString packet; +  packet.PutCString("qSupported"); +  for (uint32_t i = 0; i < features.size(); ++i) { +    packet.PutCString(i == 0 ? ":" : ";"); +    packet.PutCString(features[i]); +  } + +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(packet.GetString(), response, +                                   /*send_async=*/false) == +      PacketResult::Success) { +    const char *response_cstr = response.GetStringRef().c_str(); + +    // Hang on to the qSupported packet, so that platforms can do custom +    // configuration of the transport before attaching/launching the process. +    m_qSupported_response = response_cstr; + +    if (::strstr(response_cstr, "qXfer:auxv:read+")) +      m_supports_qXfer_auxv_read = eLazyBoolYes; +    if (::strstr(response_cstr, "qXfer:libraries-svr4:read+")) +      m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; +    if (::strstr(response_cstr, "augmented-libraries-svr4-read")) { +      m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; // implied +      m_supports_augmented_libraries_svr4_read = eLazyBoolYes; +    } +    if (::strstr(response_cstr, "qXfer:libraries:read+")) +      m_supports_qXfer_libraries_read = eLazyBoolYes; +    if (::strstr(response_cstr, "qXfer:features:read+")) +      m_supports_qXfer_features_read = eLazyBoolYes; +    if (::strstr(response_cstr, "qXfer:memory-map:read+")) +      m_supports_qXfer_memory_map_read = eLazyBoolYes; + +    // Look for a list of compressions in the features list e.g. +    // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib- +    // deflate,lzma +    const char *features_list = ::strstr(response_cstr, "qXfer:features:"); +    if (features_list) { +      const char *compressions = +          ::strstr(features_list, "SupportedCompressions="); +      if (compressions) { +        std::vector<std::string> supported_compressions; +        compressions += sizeof("SupportedCompressions=") - 1; +        const char *end_of_compressions = strchr(compressions, ';'); +        if (end_of_compressions == nullptr) { +          end_of_compressions = strchr(compressions, '\0'); +        } +        const char *current_compression = compressions; +        while (current_compression < end_of_compressions) { +          const char *next_compression_name = strchr(current_compression, ','); +          const char *end_of_this_word = next_compression_name; +          if (next_compression_name == nullptr || +              end_of_compressions < next_compression_name) { +            end_of_this_word = end_of_compressions; +          } + +          if (end_of_this_word) { +            if (end_of_this_word == current_compression) { +              current_compression++; +            } else { +              std::string this_compression( +                  current_compression, end_of_this_word - current_compression); +              supported_compressions.push_back(this_compression); +              current_compression = end_of_this_word + 1; +            } +          } else { +            supported_compressions.push_back(current_compression); +            current_compression = end_of_compressions; +          } +        } + +        if (supported_compressions.size() > 0) { +          MaybeEnableCompression(supported_compressions); +        } +      } +    } + +    if (::strstr(response_cstr, "qEcho")) +      m_supports_qEcho = eLazyBoolYes; +    else +      m_supports_qEcho = eLazyBoolNo; + +    if (::strstr(response_cstr, "QPassSignals+")) +      m_supports_QPassSignals = eLazyBoolYes; +    else +      m_supports_QPassSignals = eLazyBoolNo; + +    const char *packet_size_str = ::strstr(response_cstr, "PacketSize="); +    if (packet_size_str) { +      StringExtractorGDBRemote packet_response(packet_size_str + +                                               strlen("PacketSize=")); +      m_max_packet_size = +          packet_response.GetHexMaxU64(/*little_endian=*/false, UINT64_MAX); +      if (m_max_packet_size == 0) { +        m_max_packet_size = UINT64_MAX; // Must have been a garbled response +        Log *log( +            ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +        if (log) +          log->Printf("Garbled PacketSize spec in qSupported response"); +      } +    } +  } +} + +bool GDBRemoteCommunicationClient::GetThreadSuffixSupported() { +  if (m_supports_thread_suffix == eLazyBoolCalculate) { +    StringExtractorGDBRemote response; +    m_supports_thread_suffix = eLazyBoolNo; +    if (SendPacketAndWaitForResponse("QThreadSuffixSupported", response, +                                     false) == PacketResult::Success) { +      if (response.IsOKResponse()) +        m_supports_thread_suffix = eLazyBoolYes; +    } +  } +  return m_supports_thread_suffix; +} +bool GDBRemoteCommunicationClient::GetVContSupported(char flavor) { +  if (m_supports_vCont_c == eLazyBoolCalculate) { +    StringExtractorGDBRemote response; +    m_supports_vCont_any = eLazyBoolNo; +    m_supports_vCont_all = eLazyBoolNo; +    m_supports_vCont_c = eLazyBoolNo; +    m_supports_vCont_C = eLazyBoolNo; +    m_supports_vCont_s = eLazyBoolNo; +    m_supports_vCont_S = eLazyBoolNo; +    if (SendPacketAndWaitForResponse("vCont?", response, false) == +        PacketResult::Success) { +      const char *response_cstr = response.GetStringRef().c_str(); +      if (::strstr(response_cstr, ";c")) +        m_supports_vCont_c = eLazyBoolYes; + +      if (::strstr(response_cstr, ";C")) +        m_supports_vCont_C = eLazyBoolYes; + +      if (::strstr(response_cstr, ";s")) +        m_supports_vCont_s = eLazyBoolYes; + +      if (::strstr(response_cstr, ";S")) +        m_supports_vCont_S = eLazyBoolYes; + +      if (m_supports_vCont_c == eLazyBoolYes && +          m_supports_vCont_C == eLazyBoolYes && +          m_supports_vCont_s == eLazyBoolYes && +          m_supports_vCont_S == eLazyBoolYes) { +        m_supports_vCont_all = eLazyBoolYes; +      } + +      if (m_supports_vCont_c == eLazyBoolYes || +          m_supports_vCont_C == eLazyBoolYes || +          m_supports_vCont_s == eLazyBoolYes || +          m_supports_vCont_S == eLazyBoolYes) { +        m_supports_vCont_any = eLazyBoolYes; +      } +    } +  } + +  switch (flavor) { +  case 'a': +    return m_supports_vCont_any; +  case 'A': +    return m_supports_vCont_all; +  case 'c': +    return m_supports_vCont_c; +  case 'C': +    return m_supports_vCont_C; +  case 's': +    return m_supports_vCont_s; +  case 'S': +    return m_supports_vCont_S; +  default: +    break; +  } +  return false; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationClient::SendThreadSpecificPacketAndWaitForResponse( +    lldb::tid_t tid, StreamString &&payload, StringExtractorGDBRemote &response, +    bool send_async) { +  Lock lock(*this, send_async); +  if (!lock) { +    if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( +            GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)) +      log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex " +                  "for %s packet.", +                  __FUNCTION__, payload.GetData()); +    return PacketResult::ErrorNoSequenceLock; +  } + +  if (GetThreadSuffixSupported()) +    payload.Printf(";thread:%4.4" PRIx64 ";", tid); +  else { +    if (!SetCurrentThread(tid)) +      return PacketResult::ErrorSendFailed; +  } + +  return SendPacketAndWaitForResponseNoLock(payload.GetString(), response); +} + +// Check if the target supports 'p' packet. It sends out a 'p' packet and +// checks the response. A normal packet will tell us that support is available. +// +// Takes a valid thread ID because p needs to apply to a thread. +bool GDBRemoteCommunicationClient::GetpPacketSupported(lldb::tid_t tid) { +  if (m_supports_p == eLazyBoolCalculate) { +    m_supports_p = eLazyBoolNo; +    StreamString payload; +    payload.PutCString("p0"); +    StringExtractorGDBRemote response; +    if (SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), +                                                   response, false) == +            PacketResult::Success && +        response.IsNormalResponse()) { +      m_supports_p = eLazyBoolYes; +    } +  } +  return m_supports_p; +} + +StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() { +  // Get information on all threads at one using the "jThreadsInfo" packet +  StructuredData::ObjectSP object_sp; + +  if (m_supports_jThreadsInfo) { +    StringExtractorGDBRemote response; +    response.SetResponseValidatorToJSON(); +    if (SendPacketAndWaitForResponse("jThreadsInfo", response, false) == +        PacketResult::Success) { +      if (response.IsUnsupportedResponse()) { +        m_supports_jThreadsInfo = false; +      } else if (!response.Empty()) { +        object_sp = StructuredData::ParseJSON(response.GetStringRef()); +      } +    } +  } +  return object_sp; +} + +bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() { +  if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) { +    StringExtractorGDBRemote response; +    m_supports_jThreadExtendedInfo = eLazyBoolNo; +    if (SendPacketAndWaitForResponse("jThreadExtendedInfo:", response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) { +        m_supports_jThreadExtendedInfo = eLazyBoolYes; +      } +    } +  } +  return m_supports_jThreadExtendedInfo; +} + +void GDBRemoteCommunicationClient::EnableErrorStringInPacket() { +  if (m_supports_error_string_reply == eLazyBoolCalculate) { +    StringExtractorGDBRemote response; +    // We try to enable error strings in remote packets but if we fail, we just +    // work in the older way. +    m_supports_error_string_reply = eLazyBoolNo; +    if (SendPacketAndWaitForResponse("QEnableErrorStrings", response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) { +        m_supports_error_string_reply = eLazyBoolYes; +      } +    } +  } +} + +bool GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported() { +  if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) { +    StringExtractorGDBRemote response; +    m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolNo; +    if (SendPacketAndWaitForResponse("jGetLoadedDynamicLibrariesInfos:", +                                     response, +                                     false) == PacketResult::Success) { +      if (response.IsOKResponse()) { +        m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolYes; +      } +    } +  } +  return m_supports_jLoadedDynamicLibrariesInfos; +} + +bool GDBRemoteCommunicationClient::GetSharedCacheInfoSupported() { +  if (m_supports_jGetSharedCacheInfo == eLazyBoolCalculate) { +    StringExtractorGDBRemote response; +    m_supports_jGetSharedCacheInfo = eLazyBoolNo; +    if (SendPacketAndWaitForResponse("jGetSharedCacheInfo:", response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) { +        m_supports_jGetSharedCacheInfo = eLazyBoolYes; +      } +    } +  } +  return m_supports_jGetSharedCacheInfo; +} + +bool GDBRemoteCommunicationClient::GetxPacketSupported() { +  if (m_supports_x == eLazyBoolCalculate) { +    StringExtractorGDBRemote response; +    m_supports_x = eLazyBoolNo; +    char packet[256]; +    snprintf(packet, sizeof(packet), "x0,0"); +    if (SendPacketAndWaitForResponse(packet, response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) +        m_supports_x = eLazyBoolYes; +    } +  } +  return m_supports_x; +} + +GDBRemoteCommunicationClient::PacketResult +GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses( +    const char *payload_prefix, std::string &response_string) { +  Lock lock(*this, false); +  if (!lock) { +    Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | +                                                           GDBR_LOG_PACKETS)); +    if (log) +      log->Printf("error: failed to get packet sequence mutex, not sending " +                  "packets with prefix '%s'", +                  payload_prefix); +    return PacketResult::ErrorNoSequenceLock; +  } + +  response_string = ""; +  std::string payload_prefix_str(payload_prefix); +  unsigned int response_size = 0x1000; +  if (response_size > GetRemoteMaxPacketSize()) { // May send qSupported packet +    response_size = GetRemoteMaxPacketSize(); +  } + +  for (unsigned int offset = 0; true; offset += response_size) { +    StringExtractorGDBRemote this_response; +    // Construct payload +    char sizeDescriptor[128]; +    snprintf(sizeDescriptor, sizeof(sizeDescriptor), "%x,%x", offset, +             response_size); +    PacketResult result = SendPacketAndWaitForResponseNoLock( +        payload_prefix_str + sizeDescriptor, this_response); +    if (result != PacketResult::Success) +      return result; + +    const std::string &this_string = this_response.GetStringRef(); + +    // Check for m or l as first character; l seems to mean this is the last +    // chunk +    char first_char = *this_string.c_str(); +    if (first_char != 'm' && first_char != 'l') { +      return PacketResult::ErrorReplyInvalid; +    } +    // Concatenate the result so far (skipping 'm' or 'l') +    response_string.append(this_string, 1, std::string::npos); +    if (first_char == 'l') +      // We're done +      return PacketResult::Success; +  } +} + +lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) { +  if (allow_lazy && m_curr_pid_is_valid == eLazyBoolYes) +    return m_curr_pid; + +  // First try to retrieve the pid via the qProcessInfo request. +  GetCurrentProcessInfo(allow_lazy); +  if (m_curr_pid_is_valid == eLazyBoolYes) { +    // We really got it. +    return m_curr_pid; +  } else { +    // If we don't get a response for qProcessInfo, check if $qC gives us a +    // result. $qC only returns a real process id on older debugserver and +    // lldb-platform stubs. The gdb remote protocol documents $qC as returning +    // the thread id, which newer debugserver and lldb-gdbserver stubs return +    // correctly. +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse("qC", response, false) == +        PacketResult::Success) { +      if (response.GetChar() == 'Q') { +        if (response.GetChar() == 'C') { +          m_curr_pid = response.GetHexMaxU32(false, LLDB_INVALID_PROCESS_ID); +          if (m_curr_pid != LLDB_INVALID_PROCESS_ID) { +            m_curr_pid_is_valid = eLazyBoolYes; +            return m_curr_pid; +          } +        } +      } +    } + +    // If we don't get a response for $qC, check if $qfThreadID gives us a +    // result. +    if (m_curr_pid == LLDB_INVALID_PROCESS_ID) { +      std::vector<lldb::tid_t> thread_ids; +      bool sequence_mutex_unavailable; +      size_t size; +      size = GetCurrentThreadIDs(thread_ids, sequence_mutex_unavailable); +      if (size && !sequence_mutex_unavailable) { +        m_curr_pid = thread_ids.front(); +        m_curr_pid_is_valid = eLazyBoolYes; +        return m_curr_pid; +      } +    } +  } + +  return LLDB_INVALID_PROCESS_ID; +} + +bool GDBRemoteCommunicationClient::GetLaunchSuccess(std::string &error_str) { +  error_str.clear(); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse("qLaunchSuccess", response, false) == +      PacketResult::Success) { +    if (response.IsOKResponse()) +      return true; +    if (response.GetChar() == 'E') { +      // A string the describes what failed when launching... +      error_str = response.GetStringRef().substr(1); +    } else { +      error_str.assign("unknown error occurred launching process"); +    } +  } else { +    error_str.assign("timed out waiting for app to launch"); +  } +  return false; +} + +int GDBRemoteCommunicationClient::SendArgumentsPacket( +    const ProcessLaunchInfo &launch_info) { +  // Since we don't get the send argv0 separate from the executable path, we +  // need to make sure to use the actual executable path found in the +  // launch_info... +  std::vector<const char *> argv; +  FileSpec exe_file = launch_info.GetExecutableFile(); +  std::string exe_path; +  const char *arg = nullptr; +  const Args &launch_args = launch_info.GetArguments(); +  if (exe_file) +    exe_path = exe_file.GetPath(false); +  else { +    arg = launch_args.GetArgumentAtIndex(0); +    if (arg) +      exe_path = arg; +  } +  if (!exe_path.empty()) { +    argv.push_back(exe_path.c_str()); +    for (uint32_t i = 1; (arg = launch_args.GetArgumentAtIndex(i)) != nullptr; +         ++i) { +      if (arg) +        argv.push_back(arg); +    } +  } +  if (!argv.empty()) { +    StreamString packet; +    packet.PutChar('A'); +    for (size_t i = 0, n = argv.size(); i < n; ++i) { +      arg = argv[i]; +      const int arg_len = strlen(arg); +      if (i > 0) +        packet.PutChar(','); +      packet.Printf("%i,%i,", arg_len * 2, (int)i); +      packet.PutBytesAsRawHex8(arg, arg_len); +    } + +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) +        return 0; +      uint8_t error = response.GetError(); +      if (error) +        return error; +    } +  } +  return -1; +} + +int GDBRemoteCommunicationClient::SendEnvironment(const Environment &env) { +  for (const auto &KV : env) { +    int r = SendEnvironmentPacket(Environment::compose(KV).c_str()); +    if (r != 0) +      return r; +  } +  return 0; +} + +int GDBRemoteCommunicationClient::SendEnvironmentPacket( +    char const *name_equal_value) { +  if (name_equal_value && name_equal_value[0]) { +    StreamString packet; +    bool send_hex_encoding = false; +    for (const char *p = name_equal_value; *p != '\0' && !send_hex_encoding; +         ++p) { +      if (isprint(*p)) { +        switch (*p) { +        case '$': +        case '#': +        case '*': +        case '}': +          send_hex_encoding = true; +          break; +        default: +          break; +        } +      } else { +        // We have non printable characters, lets hex encode this... +        send_hex_encoding = true; +      } +    } + +    StringExtractorGDBRemote response; +    if (send_hex_encoding) { +      if (m_supports_QEnvironmentHexEncoded) { +        packet.PutCString("QEnvironmentHexEncoded:"); +        packet.PutBytesAsRawHex8(name_equal_value, strlen(name_equal_value)); +        if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == +            PacketResult::Success) { +          if (response.IsOKResponse()) +            return 0; +          uint8_t error = response.GetError(); +          if (error) +            return error; +          if (response.IsUnsupportedResponse()) +            m_supports_QEnvironmentHexEncoded = false; +        } +      } + +    } else if (m_supports_QEnvironment) { +      packet.Printf("QEnvironment:%s", name_equal_value); +      if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == +          PacketResult::Success) { +        if (response.IsOKResponse()) +          return 0; +        uint8_t error = response.GetError(); +        if (error) +          return error; +        if (response.IsUnsupportedResponse()) +          m_supports_QEnvironment = false; +      } +    } +  } +  return -1; +} + +int GDBRemoteCommunicationClient::SendLaunchArchPacket(char const *arch) { +  if (arch && arch[0]) { +    StreamString packet; +    packet.Printf("QLaunchArch:%s", arch); +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) +        return 0; +      uint8_t error = response.GetError(); +      if (error) +        return error; +    } +  } +  return -1; +} + +int GDBRemoteCommunicationClient::SendLaunchEventDataPacket( +    char const *data, bool *was_supported) { +  if (data && *data != '\0') { +    StreamString packet; +    packet.Printf("QSetProcessEvent:%s", data); +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) { +        if (was_supported) +          *was_supported = true; +        return 0; +      } else if (response.IsUnsupportedResponse()) { +        if (was_supported) +          *was_supported = false; +        return -1; +      } else { +        uint8_t error = response.GetError(); +        if (was_supported) +          *was_supported = true; +        if (error) +          return error; +      } +    } +  } +  return -1; +} + +llvm::VersionTuple GDBRemoteCommunicationClient::GetOSVersion() { +  GetHostInfo(); +  return m_os_version; +} + +bool GDBRemoteCommunicationClient::GetOSBuildString(std::string &s) { +  if (GetHostInfo()) { +    if (!m_os_build.empty()) { +      s = m_os_build; +      return true; +    } +  } +  s.clear(); +  return false; +} + +bool GDBRemoteCommunicationClient::GetOSKernelDescription(std::string &s) { +  if (GetHostInfo()) { +    if (!m_os_kernel.empty()) { +      s = m_os_kernel; +      return true; +    } +  } +  s.clear(); +  return false; +} + +bool GDBRemoteCommunicationClient::GetHostname(std::string &s) { +  if (GetHostInfo()) { +    if (!m_hostname.empty()) { +      s = m_hostname; +      return true; +    } +  } +  s.clear(); +  return false; +} + +ArchSpec GDBRemoteCommunicationClient::GetSystemArchitecture() { +  if (GetHostInfo()) +    return m_host_arch; +  return ArchSpec(); +} + +const lldb_private::ArchSpec & +GDBRemoteCommunicationClient::GetProcessArchitecture() { +  if (m_qProcessInfo_is_valid == eLazyBoolCalculate) +    GetCurrentProcessInfo(); +  return m_process_arch; +} + +bool GDBRemoteCommunicationClient::GetGDBServerVersion() { +  if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) { +    m_gdb_server_name.clear(); +    m_gdb_server_version = 0; +    m_qGDBServerVersion_is_valid = eLazyBoolNo; + +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse("qGDBServerVersion", response, false) == +        PacketResult::Success) { +      if (response.IsNormalResponse()) { +        llvm::StringRef name, value; +        bool success = false; +        while (response.GetNameColonValue(name, value)) { +          if (name.equals("name")) { +            success = true; +            m_gdb_server_name = value; +          } else if (name.equals("version")) { +            llvm::StringRef major, minor; +            std::tie(major, minor) = value.split('.'); +            if (!major.getAsInteger(0, m_gdb_server_version)) +              success = true; +          } +        } +        if (success) +          m_qGDBServerVersion_is_valid = eLazyBoolYes; +      } +    } +  } +  return m_qGDBServerVersion_is_valid == eLazyBoolYes; +} + +void GDBRemoteCommunicationClient::MaybeEnableCompression( +    std::vector<std::string> supported_compressions) { +  CompressionType avail_type = CompressionType::None; +  std::string avail_name; + +#if defined(HAVE_LIBCOMPRESSION) +  if (avail_type == CompressionType::None) { +    for (auto compression : supported_compressions) { +      if (compression == "lzfse") { +        avail_type = CompressionType::LZFSE; +        avail_name = compression; +        break; +      } +    } +  } +#endif + +#if defined(HAVE_LIBCOMPRESSION) +  if (avail_type == CompressionType::None) { +    for (auto compression : supported_compressions) { +      if (compression == "zlib-deflate") { +        avail_type = CompressionType::ZlibDeflate; +        avail_name = compression; +        break; +      } +    } +  } +#endif + +#if defined(HAVE_LIBZ) +  if (avail_type == CompressionType::None) { +    for (auto compression : supported_compressions) { +      if (compression == "zlib-deflate") { +        avail_type = CompressionType::ZlibDeflate; +        avail_name = compression; +        break; +      } +    } +  } +#endif + +#if defined(HAVE_LIBCOMPRESSION) +  if (avail_type == CompressionType::None) { +    for (auto compression : supported_compressions) { +      if (compression == "lz4") { +        avail_type = CompressionType::LZ4; +        avail_name = compression; +        break; +      } +    } +  } +#endif + +#if defined(HAVE_LIBCOMPRESSION) +  if (avail_type == CompressionType::None) { +    for (auto compression : supported_compressions) { +      if (compression == "lzma") { +        avail_type = CompressionType::LZMA; +        avail_name = compression; +        break; +      } +    } +  } +#endif + +  if (avail_type != CompressionType::None) { +    StringExtractorGDBRemote response; +    std::string packet = "QEnableCompression:type:" + avail_name + ";"; +    if (SendPacketAndWaitForResponse(packet, response, false) != +        PacketResult::Success) +      return; + +    if (response.IsOKResponse()) { +      m_compression_type = avail_type; +    } +  } +} + +const char *GDBRemoteCommunicationClient::GetGDBServerProgramName() { +  if (GetGDBServerVersion()) { +    if (!m_gdb_server_name.empty()) +      return m_gdb_server_name.c_str(); +  } +  return nullptr; +} + +uint32_t GDBRemoteCommunicationClient::GetGDBServerProgramVersion() { +  if (GetGDBServerVersion()) +    return m_gdb_server_version; +  return 0; +} + +bool GDBRemoteCommunicationClient::GetDefaultThreadId(lldb::tid_t &tid) { +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse("qC", response, false) != +      PacketResult::Success) +    return false; + +  if (!response.IsNormalResponse()) +    return false; + +  if (response.GetChar() == 'Q' && response.GetChar() == 'C') +    tid = response.GetHexMaxU32(true, -1); + +  return true; +} + +bool GDBRemoteCommunicationClient::GetHostInfo(bool force) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS)); + +  if (force || m_qHostInfo_is_valid == eLazyBoolCalculate) { +    // host info computation can require DNS traffic and shelling out to external processes. +    // Increase the timeout to account for that. +    ScopedTimeout timeout(*this, seconds(10)); +    m_qHostInfo_is_valid = eLazyBoolNo; +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse("qHostInfo", response, false) == +        PacketResult::Success) { +      if (response.IsNormalResponse()) { +        llvm::StringRef name; +        llvm::StringRef value; +        uint32_t cpu = LLDB_INVALID_CPUTYPE; +        uint32_t sub = 0; +        std::string arch_name; +        std::string os_name; +        std::string vendor_name; +        std::string triple; +        std::string distribution_id; +        uint32_t pointer_byte_size = 0; +        ByteOrder byte_order = eByteOrderInvalid; +        uint32_t num_keys_decoded = 0; +        while (response.GetNameColonValue(name, value)) { +          if (name.equals("cputype")) { +            // exception type in big endian hex +            if (!value.getAsInteger(0, cpu)) +              ++num_keys_decoded; +          } else if (name.equals("cpusubtype")) { +            // exception count in big endian hex +            if (!value.getAsInteger(0, sub)) +              ++num_keys_decoded; +          } else if (name.equals("arch")) { +            arch_name = value; +            ++num_keys_decoded; +          } else if (name.equals("triple")) { +            StringExtractor extractor(value); +            extractor.GetHexByteString(triple); +            ++num_keys_decoded; +          } else if (name.equals("distribution_id")) { +            StringExtractor extractor(value); +            extractor.GetHexByteString(distribution_id); +            ++num_keys_decoded; +          } else if (name.equals("os_build")) { +            StringExtractor extractor(value); +            extractor.GetHexByteString(m_os_build); +            ++num_keys_decoded; +          } else if (name.equals("hostname")) { +            StringExtractor extractor(value); +            extractor.GetHexByteString(m_hostname); +            ++num_keys_decoded; +          } else if (name.equals("os_kernel")) { +            StringExtractor extractor(value); +            extractor.GetHexByteString(m_os_kernel); +            ++num_keys_decoded; +          } else if (name.equals("ostype")) { +            os_name = value; +            ++num_keys_decoded; +          } else if (name.equals("vendor")) { +            vendor_name = value; +            ++num_keys_decoded; +          } else if (name.equals("endian")) { +            byte_order = llvm::StringSwitch<lldb::ByteOrder>(value) +                             .Case("little", eByteOrderLittle) +                             .Case("big", eByteOrderBig) +                             .Case("pdp", eByteOrderPDP) +                             .Default(eByteOrderInvalid); +            if (byte_order != eByteOrderInvalid) +              ++num_keys_decoded; +          } else if (name.equals("ptrsize")) { +            if (!value.getAsInteger(0, pointer_byte_size)) +              ++num_keys_decoded; +          } else if (name.equals("os_version") || +                     name.equals( +                         "version")) // Older debugserver binaries used the +                                     // "version" key instead of +                                     // "os_version"... +          { +            if (!m_os_version.tryParse(value)) +              ++num_keys_decoded; +          } else if (name.equals("watchpoint_exceptions_received")) { +            m_watchpoints_trigger_after_instruction = +                llvm::StringSwitch<LazyBool>(value) +                    .Case("before", eLazyBoolNo) +                    .Case("after", eLazyBoolYes) +                    .Default(eLazyBoolCalculate); +            if (m_watchpoints_trigger_after_instruction != eLazyBoolCalculate) +              ++num_keys_decoded; +          } else if (name.equals("default_packet_timeout")) { +            uint32_t timeout_seconds; +            if (!value.getAsInteger(0, timeout_seconds)) { +              m_default_packet_timeout = seconds(timeout_seconds); +              SetPacketTimeout(m_default_packet_timeout); +              ++num_keys_decoded; +            } +          } +        } + +        if (num_keys_decoded > 0) +          m_qHostInfo_is_valid = eLazyBoolYes; + +        if (triple.empty()) { +          if (arch_name.empty()) { +            if (cpu != LLDB_INVALID_CPUTYPE) { +              m_host_arch.SetArchitecture(eArchTypeMachO, cpu, sub); +              if (pointer_byte_size) { +                assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); +              } +              if (byte_order != eByteOrderInvalid) { +                assert(byte_order == m_host_arch.GetByteOrder()); +              } + +              if (!vendor_name.empty()) +                m_host_arch.GetTriple().setVendorName( +                    llvm::StringRef(vendor_name)); +              if (!os_name.empty()) +                m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name)); +            } +          } else { +            std::string triple; +            triple += arch_name; +            if (!vendor_name.empty() || !os_name.empty()) { +              triple += '-'; +              if (vendor_name.empty()) +                triple += "unknown"; +              else +                triple += vendor_name; +              triple += '-'; +              if (os_name.empty()) +                triple += "unknown"; +              else +                triple += os_name; +            } +            m_host_arch.SetTriple(triple.c_str()); + +            llvm::Triple &host_triple = m_host_arch.GetTriple(); +            if (host_triple.getVendor() == llvm::Triple::Apple && +                host_triple.getOS() == llvm::Triple::Darwin) { +              switch (m_host_arch.GetMachine()) { +              case llvm::Triple::aarch64: +              case llvm::Triple::arm: +              case llvm::Triple::thumb: +                host_triple.setOS(llvm::Triple::IOS); +                break; +              default: +                host_triple.setOS(llvm::Triple::MacOSX); +                break; +              } +            } +            if (pointer_byte_size) { +              assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); +            } +            if (byte_order != eByteOrderInvalid) { +              assert(byte_order == m_host_arch.GetByteOrder()); +            } +          } +        } else { +          m_host_arch.SetTriple(triple.c_str()); +          if (pointer_byte_size) { +            assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); +          } +          if (byte_order != eByteOrderInvalid) { +            assert(byte_order == m_host_arch.GetByteOrder()); +          } + +          if (log) +            log->Printf("GDBRemoteCommunicationClient::%s parsed host " +                        "architecture as %s, triple as %s from triple text %s", +                        __FUNCTION__, m_host_arch.GetArchitectureName() +                                          ? m_host_arch.GetArchitectureName() +                                          : "<null-arch-name>", +                        m_host_arch.GetTriple().getTriple().c_str(), +                        triple.c_str()); +        } +        if (!distribution_id.empty()) +          m_host_arch.SetDistributionId(distribution_id.c_str()); +      } +    } +  } +  return m_qHostInfo_is_valid == eLazyBoolYes; +} + +int GDBRemoteCommunicationClient::SendAttach( +    lldb::pid_t pid, StringExtractorGDBRemote &response) { +  if (pid != LLDB_INVALID_PROCESS_ID) { +    char packet[64]; +    const int packet_len = +        ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, pid); +    UNUSED_IF_ASSERT_DISABLED(packet_len); +    assert(packet_len < (int)sizeof(packet)); +    if (SendPacketAndWaitForResponse(packet, response, false) == +        PacketResult::Success) { +      if (response.IsErrorResponse()) +        return response.GetError(); +      return 0; +    } +  } +  return -1; +} + +int GDBRemoteCommunicationClient::SendStdinNotification(const char *data, +                                                        size_t data_len) { +  StreamString packet; +  packet.PutCString("I"); +  packet.PutBytesAsRawHex8(data, data_len); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == +      PacketResult::Success) { +    return 0; +  } +  return response.GetError(); +} + +const lldb_private::ArchSpec & +GDBRemoteCommunicationClient::GetHostArchitecture() { +  if (m_qHostInfo_is_valid == eLazyBoolCalculate) +    GetHostInfo(); +  return m_host_arch; +} + +seconds GDBRemoteCommunicationClient::GetHostDefaultPacketTimeout() { +  if (m_qHostInfo_is_valid == eLazyBoolCalculate) +    GetHostInfo(); +  return m_default_packet_timeout; +} + +addr_t GDBRemoteCommunicationClient::AllocateMemory(size_t size, +                                                    uint32_t permissions) { +  if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { +    m_supports_alloc_dealloc_memory = eLazyBoolYes; +    char packet[64]; +    const int packet_len = ::snprintf( +        packet, sizeof(packet), "_M%" PRIx64 ",%s%s%s", (uint64_t)size, +        permissions & lldb::ePermissionsReadable ? "r" : "", +        permissions & lldb::ePermissionsWritable ? "w" : "", +        permissions & lldb::ePermissionsExecutable ? "x" : ""); +    assert(packet_len < (int)sizeof(packet)); +    UNUSED_IF_ASSERT_DISABLED(packet_len); +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet, response, false) == +        PacketResult::Success) { +      if (response.IsUnsupportedResponse()) +        m_supports_alloc_dealloc_memory = eLazyBoolNo; +      else if (!response.IsErrorResponse()) +        return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); +    } else { +      m_supports_alloc_dealloc_memory = eLazyBoolNo; +    } +  } +  return LLDB_INVALID_ADDRESS; +} + +bool GDBRemoteCommunicationClient::DeallocateMemory(addr_t addr) { +  if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { +    m_supports_alloc_dealloc_memory = eLazyBoolYes; +    char packet[64]; +    const int packet_len = +        ::snprintf(packet, sizeof(packet), "_m%" PRIx64, (uint64_t)addr); +    assert(packet_len < (int)sizeof(packet)); +    UNUSED_IF_ASSERT_DISABLED(packet_len); +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet, response, false) == +        PacketResult::Success) { +      if (response.IsUnsupportedResponse()) +        m_supports_alloc_dealloc_memory = eLazyBoolNo; +      else if (response.IsOKResponse()) +        return true; +    } else { +      m_supports_alloc_dealloc_memory = eLazyBoolNo; +    } +  } +  return false; +} + +Status GDBRemoteCommunicationClient::Detach(bool keep_stopped) { +  Status error; + +  if (keep_stopped) { +    if (m_supports_detach_stay_stopped == eLazyBoolCalculate) { +      char packet[64]; +      const int packet_len = +          ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:"); +      assert(packet_len < (int)sizeof(packet)); +      UNUSED_IF_ASSERT_DISABLED(packet_len); +      StringExtractorGDBRemote response; +      if (SendPacketAndWaitForResponse(packet, response, false) == +              PacketResult::Success && +          response.IsOKResponse()) { +        m_supports_detach_stay_stopped = eLazyBoolYes; +      } else { +        m_supports_detach_stay_stopped = eLazyBoolNo; +      } +    } + +    if (m_supports_detach_stay_stopped == eLazyBoolNo) { +      error.SetErrorString("Stays stopped not supported by this target."); +      return error; +    } else { +      StringExtractorGDBRemote response; +      PacketResult packet_result = +          SendPacketAndWaitForResponse("D1", response, false); +      if (packet_result != PacketResult::Success) +        error.SetErrorString("Sending extended disconnect packet failed."); +    } +  } else { +    StringExtractorGDBRemote response; +    PacketResult packet_result = +        SendPacketAndWaitForResponse("D", response, false); +    if (packet_result != PacketResult::Success) +      error.SetErrorString("Sending disconnect packet failed."); +  } +  return error; +} + +Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( +    lldb::addr_t addr, lldb_private::MemoryRegionInfo ®ion_info) { +  Status error; +  region_info.Clear(); + +  if (m_supports_memory_region_info != eLazyBoolNo) { +    m_supports_memory_region_info = eLazyBoolYes; +    char packet[64]; +    const int packet_len = ::snprintf( +        packet, sizeof(packet), "qMemoryRegionInfo:%" PRIx64, (uint64_t)addr); +    assert(packet_len < (int)sizeof(packet)); +    UNUSED_IF_ASSERT_DISABLED(packet_len); +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet, response, false) == +            PacketResult::Success && +        response.GetResponseType() == StringExtractorGDBRemote::eResponse) { +      llvm::StringRef name; +      llvm::StringRef value; +      addr_t addr_value = LLDB_INVALID_ADDRESS; +      bool success = true; +      bool saw_permissions = false; +      while (success && response.GetNameColonValue(name, value)) { +        if (name.equals("start")) { +          if (!value.getAsInteger(16, addr_value)) +            region_info.GetRange().SetRangeBase(addr_value); +        } else if (name.equals("size")) { +          if (!value.getAsInteger(16, addr_value)) +            region_info.GetRange().SetByteSize(addr_value); +        } else if (name.equals("permissions") && +                   region_info.GetRange().IsValid()) { +          saw_permissions = true; +          if (region_info.GetRange().Contains(addr)) { +            if (value.find('r') != llvm::StringRef::npos) +              region_info.SetReadable(MemoryRegionInfo::eYes); +            else +              region_info.SetReadable(MemoryRegionInfo::eNo); + +            if (value.find('w') != llvm::StringRef::npos) +              region_info.SetWritable(MemoryRegionInfo::eYes); +            else +              region_info.SetWritable(MemoryRegionInfo::eNo); + +            if (value.find('x') != llvm::StringRef::npos) +              region_info.SetExecutable(MemoryRegionInfo::eYes); +            else +              region_info.SetExecutable(MemoryRegionInfo::eNo); + +            region_info.SetMapped(MemoryRegionInfo::eYes); +          } else { +            // The reported region does not contain this address -- we're +            // looking at an unmapped page +            region_info.SetReadable(MemoryRegionInfo::eNo); +            region_info.SetWritable(MemoryRegionInfo::eNo); +            region_info.SetExecutable(MemoryRegionInfo::eNo); +            region_info.SetMapped(MemoryRegionInfo::eNo); +          } +        } else if (name.equals("name")) { +          StringExtractorGDBRemote name_extractor(value); +          std::string name; +          name_extractor.GetHexByteString(name); +          region_info.SetName(name.c_str()); +        } else if (name.equals("error")) { +          StringExtractorGDBRemote error_extractor(value); +          std::string error_string; +          // Now convert the HEX bytes into a string value +          error_extractor.GetHexByteString(error_string); +          error.SetErrorString(error_string.c_str()); +        } +      } + +      if (region_info.GetRange().IsValid()) { +        // We got a valid address range back but no permissions -- which means +        // this is an unmapped page +        if (!saw_permissions) { +          region_info.SetReadable(MemoryRegionInfo::eNo); +          region_info.SetWritable(MemoryRegionInfo::eNo); +          region_info.SetExecutable(MemoryRegionInfo::eNo); +          region_info.SetMapped(MemoryRegionInfo::eNo); +        } +      } else { +        // We got an invalid address range back +        error.SetErrorString("Server returned invalid range"); +      } +    } else { +      m_supports_memory_region_info = eLazyBoolNo; +    } +  } + +  if (m_supports_memory_region_info == eLazyBoolNo) { +    error.SetErrorString("qMemoryRegionInfo is not supported"); +  } + +  // Try qXfer:memory-map:read to get region information not included in +  // qMemoryRegionInfo +  MemoryRegionInfo qXfer_region_info; +  Status qXfer_error = GetQXferMemoryMapRegionInfo(addr, qXfer_region_info); + +  if (error.Fail()) { +    // If qMemoryRegionInfo failed, but qXfer:memory-map:read succeeded, use +    // the qXfer result as a fallback +    if (qXfer_error.Success()) { +      region_info = qXfer_region_info; +      error.Clear(); +    } else { +      region_info.Clear(); +    } +  } else if (qXfer_error.Success()) { +    // If both qMemoryRegionInfo and qXfer:memory-map:read succeeded, and if +    // both regions are the same range, update the result to include the flash- +    // memory information that is specific to the qXfer result. +    if (region_info.GetRange() == qXfer_region_info.GetRange()) { +      region_info.SetFlash(qXfer_region_info.GetFlash()); +      region_info.SetBlocksize(qXfer_region_info.GetBlocksize()); +    } +  } +  return error; +} + +Status GDBRemoteCommunicationClient::GetQXferMemoryMapRegionInfo( +    lldb::addr_t addr, MemoryRegionInfo ®ion) { +  Status error = LoadQXferMemoryMap(); +  if (!error.Success()) +    return error; +  for (const auto &map_region : m_qXfer_memory_map) { +    if (map_region.GetRange().Contains(addr)) { +      region = map_region; +      return error; +    } +  } +  error.SetErrorString("Region not found"); +  return error; +} + +Status GDBRemoteCommunicationClient::LoadQXferMemoryMap() { + +  Status error; + +  if (m_qXfer_memory_map_loaded) +    // Already loaded, return success +    return error; + +  if (!XMLDocument::XMLEnabled()) { +    error.SetErrorString("XML is not supported"); +    return error; +  } + +  if (!GetQXferMemoryMapReadSupported()) { +    error.SetErrorString("Memory map is not supported"); +    return error; +  } + +  std::string xml; +  lldb_private::Status lldberr; +  if (!ReadExtFeature(ConstString("memory-map"), ConstString(""), xml, +                      lldberr)) { +    error.SetErrorString("Failed to read memory map"); +    return error; +  } + +  XMLDocument xml_document; + +  if (!xml_document.ParseMemory(xml.c_str(), xml.size())) { +    error.SetErrorString("Failed to parse memory map xml"); +    return error; +  } + +  XMLNode map_node = xml_document.GetRootElement("memory-map"); +  if (!map_node) { +    error.SetErrorString("Invalid root node in memory map xml"); +    return error; +  } + +  m_qXfer_memory_map.clear(); + +  map_node.ForEachChildElement([this](const XMLNode &memory_node) -> bool { +    if (!memory_node.IsElement()) +      return true; +    if (memory_node.GetName() != "memory") +      return true; +    auto type = memory_node.GetAttributeValue("type", ""); +    uint64_t start; +    uint64_t length; +    if (!memory_node.GetAttributeValueAsUnsigned("start", start)) +      return true; +    if (!memory_node.GetAttributeValueAsUnsigned("length", length)) +      return true; +    MemoryRegionInfo region; +    region.GetRange().SetRangeBase(start); +    region.GetRange().SetByteSize(length); +    if (type == "rom") { +      region.SetReadable(MemoryRegionInfo::eYes); +      this->m_qXfer_memory_map.push_back(region); +    } else if (type == "ram") { +      region.SetReadable(MemoryRegionInfo::eYes); +      region.SetWritable(MemoryRegionInfo::eYes); +      this->m_qXfer_memory_map.push_back(region); +    } else if (type == "flash") { +      region.SetFlash(MemoryRegionInfo::eYes); +      memory_node.ForEachChildElement( +          [®ion](const XMLNode &prop_node) -> bool { +            if (!prop_node.IsElement()) +              return true; +            if (prop_node.GetName() != "property") +              return true; +            auto propname = prop_node.GetAttributeValue("name", ""); +            if (propname == "blocksize") { +              uint64_t blocksize; +              if (prop_node.GetElementTextAsUnsigned(blocksize)) +                region.SetBlocksize(blocksize); +            } +            return true; +          }); +      this->m_qXfer_memory_map.push_back(region); +    } +    return true; +  }); + +  m_qXfer_memory_map_loaded = true; + +  return error; +} + +Status GDBRemoteCommunicationClient::GetWatchpointSupportInfo(uint32_t &num) { +  Status error; + +  if (m_supports_watchpoint_support_info == eLazyBoolYes) { +    num = m_num_supported_hardware_watchpoints; +    return error; +  } + +  // Set num to 0 first. +  num = 0; +  if (m_supports_watchpoint_support_info != eLazyBoolNo) { +    char packet[64]; +    const int packet_len = +        ::snprintf(packet, sizeof(packet), "qWatchpointSupportInfo:"); +    assert(packet_len < (int)sizeof(packet)); +    UNUSED_IF_ASSERT_DISABLED(packet_len); +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet, response, false) == +        PacketResult::Success) { +      m_supports_watchpoint_support_info = eLazyBoolYes; +      llvm::StringRef name; +      llvm::StringRef value; +      bool found_num_field = false; +      while (response.GetNameColonValue(name, value)) { +        if (name.equals("num")) { +          value.getAsInteger(0, m_num_supported_hardware_watchpoints); +          num = m_num_supported_hardware_watchpoints; +          found_num_field = true; +        } +      } +      if (!found_num_field) { +        m_supports_watchpoint_support_info = eLazyBoolNo; +      } +    } else { +      m_supports_watchpoint_support_info = eLazyBoolNo; +    } +  } + +  if (m_supports_watchpoint_support_info == eLazyBoolNo) { +    error.SetErrorString("qWatchpointSupportInfo is not supported"); +  } +  return error; +} + +lldb_private::Status GDBRemoteCommunicationClient::GetWatchpointSupportInfo( +    uint32_t &num, bool &after, const ArchSpec &arch) { +  Status error(GetWatchpointSupportInfo(num)); +  if (error.Success()) +    error = GetWatchpointsTriggerAfterInstruction(after, arch); +  return error; +} + +lldb_private::Status +GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction( +    bool &after, const ArchSpec &arch) { +  Status error; +  llvm::Triple triple = arch.GetTriple(); + +  // we assume watchpoints will happen after running the relevant opcode and we +  // only want to override this behavior if we have explicitly received a +  // qHostInfo telling us otherwise +  if (m_qHostInfo_is_valid != eLazyBoolYes) { +    // On targets like MIPS and ppc64, watchpoint exceptions are always +    // generated before the instruction is executed. The connected target may +    // not support qHostInfo or qWatchpointSupportInfo packets. +    after = !(triple.isMIPS() || triple.isPPC64()); +  } else { +    // For MIPS and ppc64, set m_watchpoints_trigger_after_instruction to +    // eLazyBoolNo if it is not calculated before. +    if (m_watchpoints_trigger_after_instruction == eLazyBoolCalculate && +        (triple.isMIPS() || triple.isPPC64())) +      m_watchpoints_trigger_after_instruction = eLazyBoolNo; + +    after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo); +  } +  return error; +} + +int GDBRemoteCommunicationClient::SetSTDIN(const FileSpec &file_spec) { +  if (file_spec) { +    std::string path{file_spec.GetPath(false)}; +    StreamString packet; +    packet.PutCString("QSetSTDIN:"); +    packet.PutStringAsRawHex8(path); + +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) +        return 0; +      uint8_t error = response.GetError(); +      if (error) +        return error; +    } +  } +  return -1; +} + +int GDBRemoteCommunicationClient::SetSTDOUT(const FileSpec &file_spec) { +  if (file_spec) { +    std::string path{file_spec.GetPath(false)}; +    StreamString packet; +    packet.PutCString("QSetSTDOUT:"); +    packet.PutStringAsRawHex8(path); + +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) +        return 0; +      uint8_t error = response.GetError(); +      if (error) +        return error; +    } +  } +  return -1; +} + +int GDBRemoteCommunicationClient::SetSTDERR(const FileSpec &file_spec) { +  if (file_spec) { +    std::string path{file_spec.GetPath(false)}; +    StreamString packet; +    packet.PutCString("QSetSTDERR:"); +    packet.PutStringAsRawHex8(path); + +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) +        return 0; +      uint8_t error = response.GetError(); +      if (error) +        return error; +    } +  } +  return -1; +} + +bool GDBRemoteCommunicationClient::GetWorkingDir(FileSpec &working_dir) { +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse("qGetWorkingDir", response, false) == +      PacketResult::Success) { +    if (response.IsUnsupportedResponse()) +      return false; +    if (response.IsErrorResponse()) +      return false; +    std::string cwd; +    response.GetHexByteString(cwd); +    working_dir.SetFile(cwd, GetHostArchitecture().GetTriple()); +    return !cwd.empty(); +  } +  return false; +} + +int GDBRemoteCommunicationClient::SetWorkingDir(const FileSpec &working_dir) { +  if (working_dir) { +    std::string path{working_dir.GetPath(false)}; +    StreamString packet; +    packet.PutCString("QSetWorkingDir:"); +    packet.PutStringAsRawHex8(path); + +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == +        PacketResult::Success) { +      if (response.IsOKResponse()) +        return 0; +      uint8_t error = response.GetError(); +      if (error) +        return error; +    } +  } +  return -1; +} + +int GDBRemoteCommunicationClient::SetDisableASLR(bool enable) { +  char packet[32]; +  const int packet_len = +      ::snprintf(packet, sizeof(packet), "QSetDisableASLR:%i", enable ? 1 : 0); +  assert(packet_len < (int)sizeof(packet)); +  UNUSED_IF_ASSERT_DISABLED(packet_len); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(packet, response, false) == +      PacketResult::Success) { +    if (response.IsOKResponse()) +      return 0; +    uint8_t error = response.GetError(); +    if (error) +      return error; +  } +  return -1; +} + +int GDBRemoteCommunicationClient::SetDetachOnError(bool enable) { +  char packet[32]; +  const int packet_len = ::snprintf(packet, sizeof(packet), +                                    "QSetDetachOnError:%i", enable ? 1 : 0); +  assert(packet_len < (int)sizeof(packet)); +  UNUSED_IF_ASSERT_DISABLED(packet_len); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(packet, response, false) == +      PacketResult::Success) { +    if (response.IsOKResponse()) +      return 0; +    uint8_t error = response.GetError(); +    if (error) +      return error; +  } +  return -1; +} + +bool GDBRemoteCommunicationClient::DecodeProcessInfoResponse( +    StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info) { +  if (response.IsNormalResponse()) { +    llvm::StringRef name; +    llvm::StringRef value; +    StringExtractor extractor; + +    uint32_t cpu = LLDB_INVALID_CPUTYPE; +    uint32_t sub = 0; +    std::string vendor; +    std::string os_type; + +    while (response.GetNameColonValue(name, value)) { +      if (name.equals("pid")) { +        lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; +        value.getAsInteger(0, pid); +        process_info.SetProcessID(pid); +      } else if (name.equals("ppid")) { +        lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; +        value.getAsInteger(0, pid); +        process_info.SetParentProcessID(pid); +      } else if (name.equals("uid")) { +        uint32_t uid = UINT32_MAX; +        value.getAsInteger(0, uid); +        process_info.SetUserID(uid); +      } else if (name.equals("euid")) { +        uint32_t uid = UINT32_MAX; +        value.getAsInteger(0, uid); +        process_info.SetEffectiveGroupID(uid); +      } else if (name.equals("gid")) { +        uint32_t gid = UINT32_MAX; +        value.getAsInteger(0, gid); +        process_info.SetGroupID(gid); +      } else if (name.equals("egid")) { +        uint32_t gid = UINT32_MAX; +        value.getAsInteger(0, gid); +        process_info.SetEffectiveGroupID(gid); +      } else if (name.equals("triple")) { +        StringExtractor extractor(value); +        std::string triple; +        extractor.GetHexByteString(triple); +        process_info.GetArchitecture().SetTriple(triple.c_str()); +      } else if (name.equals("name")) { +        StringExtractor extractor(value); +        // The process name from ASCII hex bytes since we can't control the +        // characters in a process name +        std::string name; +        extractor.GetHexByteString(name); +        process_info.GetExecutableFile().SetFile(name, FileSpec::Style::native); +      } else if (name.equals("cputype")) { +        value.getAsInteger(0, cpu); +      } else if (name.equals("cpusubtype")) { +        value.getAsInteger(0, sub); +      } else if (name.equals("vendor")) { +        vendor = value; +      } else if (name.equals("ostype")) { +        os_type = value; +      } +    } + +    if (cpu != LLDB_INVALID_CPUTYPE && !vendor.empty() && !os_type.empty()) { +      if (vendor == "apple") { +        process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu, +                                                       sub); +        process_info.GetArchitecture().GetTriple().setVendorName( +            llvm::StringRef(vendor)); +        process_info.GetArchitecture().GetTriple().setOSName( +            llvm::StringRef(os_type)); +      } +    } + +    if (process_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) +      return true; +  } +  return false; +} + +bool GDBRemoteCommunicationClient::GetProcessInfo( +    lldb::pid_t pid, ProcessInstanceInfo &process_info) { +  process_info.Clear(); + +  if (m_supports_qProcessInfoPID) { +    char packet[32]; +    const int packet_len = +        ::snprintf(packet, sizeof(packet), "qProcessInfoPID:%" PRIu64, pid); +    assert(packet_len < (int)sizeof(packet)); +    UNUSED_IF_ASSERT_DISABLED(packet_len); +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet, response, false) == +        PacketResult::Success) { +      return DecodeProcessInfoResponse(response, process_info); +    } else { +      m_supports_qProcessInfoPID = false; +      return false; +    } +  } +  return false; +} + +bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | +                                                         GDBR_LOG_PACKETS)); + +  if (allow_lazy) { +    if (m_qProcessInfo_is_valid == eLazyBoolYes) +      return true; +    if (m_qProcessInfo_is_valid == eLazyBoolNo) +      return false; +  } + +  GetHostInfo(); + +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse("qProcessInfo", response, false) == +      PacketResult::Success) { +    if (response.IsNormalResponse()) { +      llvm::StringRef name; +      llvm::StringRef value; +      uint32_t cpu = LLDB_INVALID_CPUTYPE; +      uint32_t sub = 0; +      std::string arch_name; +      std::string os_name; +      std::string vendor_name; +      std::string triple; +      std::string elf_abi; +      uint32_t pointer_byte_size = 0; +      StringExtractor extractor; +      ByteOrder byte_order = eByteOrderInvalid; +      uint32_t num_keys_decoded = 0; +      lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; +      while (response.GetNameColonValue(name, value)) { +        if (name.equals("cputype")) { +          if (!value.getAsInteger(16, cpu)) +            ++num_keys_decoded; +        } else if (name.equals("cpusubtype")) { +          if (!value.getAsInteger(16, sub)) +            ++num_keys_decoded; +        } else if (name.equals("triple")) { +          StringExtractor extractor(value); +          extractor.GetHexByteString(triple); +          ++num_keys_decoded; +        } else if (name.equals("ostype")) { +          os_name = value; +          ++num_keys_decoded; +        } else if (name.equals("vendor")) { +          vendor_name = value; +          ++num_keys_decoded; +        } else if (name.equals("endian")) { +          byte_order = llvm::StringSwitch<lldb::ByteOrder>(value) +                           .Case("little", eByteOrderLittle) +                           .Case("big", eByteOrderBig) +                           .Case("pdp", eByteOrderPDP) +                           .Default(eByteOrderInvalid); +          if (byte_order != eByteOrderInvalid) +            ++num_keys_decoded; +        } else if (name.equals("ptrsize")) { +          if (!value.getAsInteger(16, pointer_byte_size)) +            ++num_keys_decoded; +        } else if (name.equals("pid")) { +          if (!value.getAsInteger(16, pid)) +            ++num_keys_decoded; +        } else if (name.equals("elf_abi")) { +          elf_abi = value; +          ++num_keys_decoded; +        } +      } +      if (num_keys_decoded > 0) +        m_qProcessInfo_is_valid = eLazyBoolYes; +      if (pid != LLDB_INVALID_PROCESS_ID) { +        m_curr_pid_is_valid = eLazyBoolYes; +        m_curr_pid = pid; +      } + +      // Set the ArchSpec from the triple if we have it. +      if (!triple.empty()) { +        m_process_arch.SetTriple(triple.c_str()); +        m_process_arch.SetFlags(elf_abi); +        if (pointer_byte_size) { +          assert(pointer_byte_size == m_process_arch.GetAddressByteSize()); +        } +      } else if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() && +                 !vendor_name.empty()) { +        llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name); + +        assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat); +        assert(triple.getObjectFormat() != llvm::Triple::Wasm); +        assert(triple.getObjectFormat() != llvm::Triple::XCOFF); +        switch (triple.getObjectFormat()) { +        case llvm::Triple::MachO: +          m_process_arch.SetArchitecture(eArchTypeMachO, cpu, sub); +          break; +        case llvm::Triple::ELF: +          m_process_arch.SetArchitecture(eArchTypeELF, cpu, sub); +          break; +        case llvm::Triple::COFF: +          m_process_arch.SetArchitecture(eArchTypeCOFF, cpu, sub); +          break; +        case llvm::Triple::Wasm: +        case llvm::Triple::XCOFF: +          if (log) +            log->Printf("error: not supported target architecture"); +          return false; +        case llvm::Triple::UnknownObjectFormat: +          if (log) +            log->Printf("error: failed to determine target architecture"); +          return false; +        } + +        if (pointer_byte_size) { +          assert(pointer_byte_size == m_process_arch.GetAddressByteSize()); +        } +        if (byte_order != eByteOrderInvalid) { +          assert(byte_order == m_process_arch.GetByteOrder()); +        } +        m_process_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name)); +        m_process_arch.GetTriple().setOSName(llvm::StringRef(os_name)); +        m_host_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name)); +        m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name)); +      } +      return true; +    } +  } else { +    m_qProcessInfo_is_valid = eLazyBoolNo; +  } + +  return false; +} + +uint32_t GDBRemoteCommunicationClient::FindProcesses( +    const ProcessInstanceInfoMatch &match_info, +    ProcessInstanceInfoList &process_infos) { +  process_infos.Clear(); + +  if (m_supports_qfProcessInfo) { +    StreamString packet; +    packet.PutCString("qfProcessInfo"); +    if (!match_info.MatchAllProcesses()) { +      packet.PutChar(':'); +      const char *name = match_info.GetProcessInfo().GetName(); +      bool has_name_match = false; +      if (name && name[0]) { +        has_name_match = true; +        NameMatch name_match_type = match_info.GetNameMatchType(); +        switch (name_match_type) { +        case NameMatch::Ignore: +          has_name_match = false; +          break; + +        case NameMatch::Equals: +          packet.PutCString("name_match:equals;"); +          break; + +        case NameMatch::Contains: +          packet.PutCString("name_match:contains;"); +          break; + +        case NameMatch::StartsWith: +          packet.PutCString("name_match:starts_with;"); +          break; + +        case NameMatch::EndsWith: +          packet.PutCString("name_match:ends_with;"); +          break; + +        case NameMatch::RegularExpression: +          packet.PutCString("name_match:regex;"); +          break; +        } +        if (has_name_match) { +          packet.PutCString("name:"); +          packet.PutBytesAsRawHex8(name, ::strlen(name)); +          packet.PutChar(';'); +        } +      } + +      if (match_info.GetProcessInfo().ProcessIDIsValid()) +        packet.Printf("pid:%" PRIu64 ";", +                      match_info.GetProcessInfo().GetProcessID()); +      if (match_info.GetProcessInfo().ParentProcessIDIsValid()) +        packet.Printf("parent_pid:%" PRIu64 ";", +                      match_info.GetProcessInfo().GetParentProcessID()); +      if (match_info.GetProcessInfo().UserIDIsValid()) +        packet.Printf("uid:%u;", match_info.GetProcessInfo().GetUserID()); +      if (match_info.GetProcessInfo().GroupIDIsValid()) +        packet.Printf("gid:%u;", match_info.GetProcessInfo().GetGroupID()); +      if (match_info.GetProcessInfo().EffectiveUserIDIsValid()) +        packet.Printf("euid:%u;", +                      match_info.GetProcessInfo().GetEffectiveUserID()); +      if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) +        packet.Printf("egid:%u;", +                      match_info.GetProcessInfo().GetEffectiveGroupID()); +      if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) +        packet.Printf("all_users:%u;", match_info.GetMatchAllUsers() ? 1 : 0); +      if (match_info.GetProcessInfo().GetArchitecture().IsValid()) { +        const ArchSpec &match_arch = +            match_info.GetProcessInfo().GetArchitecture(); +        const llvm::Triple &triple = match_arch.GetTriple(); +        packet.PutCString("triple:"); +        packet.PutCString(triple.getTriple()); +        packet.PutChar(';'); +      } +    } +    StringExtractorGDBRemote response; +    // Increase timeout as the first qfProcessInfo packet takes a long time on +    // Android. The value of 1min was arrived at empirically. +    ScopedTimeout timeout(*this, minutes(1)); +    if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == +        PacketResult::Success) { +      do { +        ProcessInstanceInfo process_info; +        if (!DecodeProcessInfoResponse(response, process_info)) +          break; +        process_infos.Append(process_info); +        response.GetStringRef().clear(); +        response.SetFilePos(0); +      } while (SendPacketAndWaitForResponse("qsProcessInfo", response, false) == +               PacketResult::Success); +    } else { +      m_supports_qfProcessInfo = false; +      return 0; +    } +  } +  return process_infos.GetSize(); +} + +bool GDBRemoteCommunicationClient::GetUserName(uint32_t uid, +                                               std::string &name) { +  if (m_supports_qUserName) { +    char packet[32]; +    const int packet_len = +        ::snprintf(packet, sizeof(packet), "qUserName:%i", uid); +    assert(packet_len < (int)sizeof(packet)); +    UNUSED_IF_ASSERT_DISABLED(packet_len); +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet, response, false) == +        PacketResult::Success) { +      if (response.IsNormalResponse()) { +        // Make sure we parsed the right number of characters. The response is +        // the hex encoded user name and should make up the entire packet. If +        // there are any non-hex ASCII bytes, the length won't match below.. +        if (response.GetHexByteString(name) * 2 == +            response.GetStringRef().size()) +          return true; +      } +    } else { +      m_supports_qUserName = false; +      return false; +    } +  } +  return false; +} + +bool GDBRemoteCommunicationClient::GetGroupName(uint32_t gid, +                                                std::string &name) { +  if (m_supports_qGroupName) { +    char packet[32]; +    const int packet_len = +        ::snprintf(packet, sizeof(packet), "qGroupName:%i", gid); +    assert(packet_len < (int)sizeof(packet)); +    UNUSED_IF_ASSERT_DISABLED(packet_len); +    StringExtractorGDBRemote response; +    if (SendPacketAndWaitForResponse(packet, response, false) == +        PacketResult::Success) { +      if (response.IsNormalResponse()) { +        // Make sure we parsed the right number of characters. The response is +        // the hex encoded group name and should make up the entire packet. If +        // there are any non-hex ASCII bytes, the length won't match below.. +        if (response.GetHexByteString(name) * 2 == +            response.GetStringRef().size()) +          return true; +      } +    } else { +      m_supports_qGroupName = false; +      return false; +    } +  } +  return false; +} + +bool GDBRemoteCommunicationClient::SetNonStopMode(const bool enable) { +  // Form non-stop packet request +  char packet[32]; +  const int packet_len = +      ::snprintf(packet, sizeof(packet), "QNonStop:%1d", (int)enable); +  assert(packet_len < (int)sizeof(packet)); +  UNUSED_IF_ASSERT_DISABLED(packet_len); + +  StringExtractorGDBRemote response; +  // Send to target +  if (SendPacketAndWaitForResponse(packet, response, false) == +      PacketResult::Success) +    if (response.IsOKResponse()) +      return true; + +  // Failed or not supported +  return false; +} + +static void MakeSpeedTestPacket(StreamString &packet, uint32_t send_size, +                                uint32_t recv_size) { +  packet.Clear(); +  packet.Printf("qSpeedTest:response_size:%i;data:", recv_size); +  uint32_t bytes_left = send_size; +  while (bytes_left > 0) { +    if (bytes_left >= 26) { +      packet.PutCString("abcdefghijklmnopqrstuvwxyz"); +      bytes_left -= 26; +    } else { +      packet.Printf("%*.*s;", bytes_left, bytes_left, +                    "abcdefghijklmnopqrstuvwxyz"); +      bytes_left = 0; +    } +  } +} + +duration<float> +calculate_standard_deviation(const std::vector<duration<float>> &v) { +  using Dur = duration<float>; +  Dur sum = std::accumulate(std::begin(v), std::end(v), Dur()); +  Dur mean = sum / v.size(); +  float accum = 0; +  for (auto d : v) { +    float delta = (d - mean).count(); +    accum += delta * delta; +  }; + +  return Dur(sqrtf(accum / (v.size() - 1))); +} + +void GDBRemoteCommunicationClient::TestPacketSpeed(const uint32_t num_packets, +                                                   uint32_t max_send, +                                                   uint32_t max_recv, +                                                   uint64_t recv_amount, +                                                   bool json, Stream &strm) { +  uint32_t i; +  if (SendSpeedTestPacket(0, 0)) { +    StreamString packet; +    if (json) +      strm.Printf("{ \"packet_speeds\" : {\n    \"num_packets\" : %u,\n    " +                  "\"results\" : [", +                  num_packets); +    else +      strm.Printf("Testing sending %u packets of various sizes:\n", +                  num_packets); +    strm.Flush(); + +    uint32_t result_idx = 0; +    uint32_t send_size; +    std::vector<duration<float>> packet_times; + +    for (send_size = 0; send_size <= max_send; +         send_size ? send_size *= 2 : send_size = 4) { +      for (uint32_t recv_size = 0; recv_size <= max_recv; +           recv_size ? recv_size *= 2 : recv_size = 4) { +        MakeSpeedTestPacket(packet, send_size, recv_size); + +        packet_times.clear(); +        // Test how long it takes to send 'num_packets' packets +        const auto start_time = steady_clock::now(); +        for (i = 0; i < num_packets; ++i) { +          const auto packet_start_time = steady_clock::now(); +          StringExtractorGDBRemote response; +          SendPacketAndWaitForResponse(packet.GetString(), response, false); +          const auto packet_end_time = steady_clock::now(); +          packet_times.push_back(packet_end_time - packet_start_time); +        } +        const auto end_time = steady_clock::now(); +        const auto total_time = end_time - start_time; + +        float packets_per_second = +            ((float)num_packets) / duration<float>(total_time).count(); +        auto average_per_packet = total_time / num_packets; +        const duration<float> standard_deviation = +            calculate_standard_deviation(packet_times); +        if (json) { +          strm.Format("{0}\n     {{\"send_size\" : {1,6}, \"recv_size\" : " +                      "{2,6}, \"total_time_nsec\" : {3,12:ns-}, " +                      "\"standard_deviation_nsec\" : {4,9:ns-f0}}", +                      result_idx > 0 ? "," : "", send_size, recv_size, +                      total_time, standard_deviation); +          ++result_idx; +        } else { +          strm.Format("qSpeedTest(send={0,7}, recv={1,7}) in {2:s+f9} for " +                      "{3,9:f2} packets/s ({4,10:ms+f6} per packet) with " +                      "standard deviation of {5,10:ms+f6}\n", +                      send_size, recv_size, duration<float>(total_time), +                      packets_per_second, duration<float>(average_per_packet), +                      standard_deviation); +        } +        strm.Flush(); +      } +    } + +    const float k_recv_amount_mb = (float)recv_amount / (1024.0f * 1024.0f); +    if (json) +      strm.Printf("\n    ]\n  },\n  \"download_speed\" : {\n    \"byte_size\" " +                  ": %" PRIu64 ",\n    \"results\" : [", +                  recv_amount); +    else +      strm.Printf("Testing receiving %2.1fMB of data using varying receive " +                  "packet sizes:\n", +                  k_recv_amount_mb); +    strm.Flush(); +    send_size = 0; +    result_idx = 0; +    for (uint32_t recv_size = 32; recv_size <= max_recv; recv_size *= 2) { +      MakeSpeedTestPacket(packet, send_size, recv_size); + +      // If we have a receive size, test how long it takes to receive 4MB of +      // data +      if (recv_size > 0) { +        const auto start_time = steady_clock::now(); +        uint32_t bytes_read = 0; +        uint32_t packet_count = 0; +        while (bytes_read < recv_amount) { +          StringExtractorGDBRemote response; +          SendPacketAndWaitForResponse(packet.GetString(), response, false); +          bytes_read += recv_size; +          ++packet_count; +        } +        const auto end_time = steady_clock::now(); +        const auto total_time = end_time - start_time; +        float mb_second = ((float)recv_amount) / +                          duration<float>(total_time).count() / +                          (1024.0 * 1024.0); +        float packets_per_second = +            ((float)packet_count) / duration<float>(total_time).count(); +        const auto average_per_packet = total_time / packet_count; + +        if (json) { +          strm.Format("{0}\n     {{\"send_size\" : {1,6}, \"recv_size\" : " +                      "{2,6}, \"total_time_nsec\" : {3,12:ns-}}", +                      result_idx > 0 ? "," : "", send_size, recv_size, +                      total_time); +          ++result_idx; +        } else { +          strm.Format("qSpeedTest(send={0,7}, recv={1,7}) {2,6} packets needed " +                      "to receive {3:f1}MB in {4:s+f9} for {5} MB/sec for " +                      "{6,9:f2} packets/sec ({7,10:ms+f6} per packet)\n", +                      send_size, recv_size, packet_count, k_recv_amount_mb, +                      duration<float>(total_time), mb_second, +                      packets_per_second, duration<float>(average_per_packet)); +        } +        strm.Flush(); +      } +    } +    if (json) +      strm.Printf("\n    ]\n  }\n}\n"); +    else +      strm.EOL(); +  } +} + +bool GDBRemoteCommunicationClient::SendSpeedTestPacket(uint32_t send_size, +                                                       uint32_t recv_size) { +  StreamString packet; +  packet.Printf("qSpeedTest:response_size:%i;data:", recv_size); +  uint32_t bytes_left = send_size; +  while (bytes_left > 0) { +    if (bytes_left >= 26) { +      packet.PutCString("abcdefghijklmnopqrstuvwxyz"); +      bytes_left -= 26; +    } else { +      packet.Printf("%*.*s;", bytes_left, bytes_left, +                    "abcdefghijklmnopqrstuvwxyz"); +      bytes_left = 0; +    } +  } + +  StringExtractorGDBRemote response; +  return SendPacketAndWaitForResponse(packet.GetString(), response, false) == +         PacketResult::Success; +} + +bool GDBRemoteCommunicationClient::LaunchGDBServer( +    const char *remote_accept_hostname, lldb::pid_t &pid, uint16_t &port, +    std::string &socket_name) { +  pid = LLDB_INVALID_PROCESS_ID; +  port = 0; +  socket_name.clear(); + +  StringExtractorGDBRemote response; +  StreamString stream; +  stream.PutCString("qLaunchGDBServer;"); +  std::string hostname; +  if (remote_accept_hostname && remote_accept_hostname[0]) +    hostname = remote_accept_hostname; +  else { +    if (HostInfo::GetHostname(hostname)) { +      // Make the GDB server we launch only accept connections from this host +      stream.Printf("host:%s;", hostname.c_str()); +    } else { +      // Make the GDB server we launch accept connections from any host since +      // we can't figure out the hostname +      stream.Printf("host:*;"); +    } +  } +  // give the process a few seconds to startup +  ScopedTimeout timeout(*this, seconds(10)); + +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    llvm::StringRef name; +    llvm::StringRef value; +    while (response.GetNameColonValue(name, value)) { +      if (name.equals("port")) +        value.getAsInteger(0, port); +      else if (name.equals("pid")) +        value.getAsInteger(0, pid); +      else if (name.compare("socket_name") == 0) { +        StringExtractor extractor(value); +        extractor.GetHexByteString(socket_name); +      } +    } +    return true; +  } +  return false; +} + +size_t GDBRemoteCommunicationClient::QueryGDBServer( +    std::vector<std::pair<uint16_t, std::string>> &connection_urls) { +  connection_urls.clear(); + +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse("qQueryGDBServer", response, false) != +      PacketResult::Success) +    return 0; + +  StructuredData::ObjectSP data = +      StructuredData::ParseJSON(response.GetStringRef()); +  if (!data) +    return 0; + +  StructuredData::Array *array = data->GetAsArray(); +  if (!array) +    return 0; + +  for (size_t i = 0, count = array->GetSize(); i < count; ++i) { +    StructuredData::Dictionary *element = nullptr; +    if (!array->GetItemAtIndexAsDictionary(i, element)) +      continue; + +    uint16_t port = 0; +    if (StructuredData::ObjectSP port_osp = +            element->GetValueForKey(llvm::StringRef("port"))) +      port = port_osp->GetIntegerValue(0); + +    std::string socket_name; +    if (StructuredData::ObjectSP socket_name_osp = +            element->GetValueForKey(llvm::StringRef("socket_name"))) +      socket_name = socket_name_osp->GetStringValue(); + +    if (port != 0 || !socket_name.empty()) +      connection_urls.emplace_back(port, socket_name); +  } +  return connection_urls.size(); +} + +bool GDBRemoteCommunicationClient::KillSpawnedProcess(lldb::pid_t pid) { +  StreamString stream; +  stream.Printf("qKillSpawnedProcess:%" PRId64, pid); + +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    if (response.IsOKResponse()) +      return true; +  } +  return false; +} + +bool GDBRemoteCommunicationClient::SetCurrentThread(uint64_t tid) { +  if (m_curr_tid == tid) +    return true; + +  char packet[32]; +  int packet_len; +  if (tid == UINT64_MAX) +    packet_len = ::snprintf(packet, sizeof(packet), "Hg-1"); +  else +    packet_len = ::snprintf(packet, sizeof(packet), "Hg%" PRIx64, tid); +  assert(packet_len + 1 < (int)sizeof(packet)); +  UNUSED_IF_ASSERT_DISABLED(packet_len); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(packet, response, false) == +      PacketResult::Success) { +    if (response.IsOKResponse()) { +      m_curr_tid = tid; +      return true; +    } + +    /* +     * Connected bare-iron target (like YAMON gdb-stub) may not have support for +     * Hg packet. +     * The reply from '?' packet could be as simple as 'S05'. There is no packet +     * which can +     * give us pid and/or tid. Assume pid=tid=1 in such cases. +    */ +    if (response.IsUnsupportedResponse() && IsConnected()) { +      m_curr_tid = 1; +      return true; +    } +  } +  return false; +} + +bool GDBRemoteCommunicationClient::SetCurrentThreadForRun(uint64_t tid) { +  if (m_curr_tid_run == tid) +    return true; + +  char packet[32]; +  int packet_len; +  if (tid == UINT64_MAX) +    packet_len = ::snprintf(packet, sizeof(packet), "Hc-1"); +  else +    packet_len = ::snprintf(packet, sizeof(packet), "Hc%" PRIx64, tid); + +  assert(packet_len + 1 < (int)sizeof(packet)); +  UNUSED_IF_ASSERT_DISABLED(packet_len); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(packet, response, false) == +      PacketResult::Success) { +    if (response.IsOKResponse()) { +      m_curr_tid_run = tid; +      return true; +    } + +    /* +     * Connected bare-iron target (like YAMON gdb-stub) may not have support for +     * Hc packet. +     * The reply from '?' packet could be as simple as 'S05'. There is no packet +     * which can +     * give us pid and/or tid. Assume pid=tid=1 in such cases. +    */ +    if (response.IsUnsupportedResponse() && IsConnected()) { +      m_curr_tid_run = 1; +      return true; +    } +  } +  return false; +} + +bool GDBRemoteCommunicationClient::GetStopReply( +    StringExtractorGDBRemote &response) { +  if (SendPacketAndWaitForResponse("?", response, false) == +      PacketResult::Success) +    return response.IsNormalResponse(); +  return false; +} + +bool GDBRemoteCommunicationClient::GetThreadStopInfo( +    lldb::tid_t tid, StringExtractorGDBRemote &response) { +  if (m_supports_qThreadStopInfo) { +    char packet[256]; +    int packet_len = +        ::snprintf(packet, sizeof(packet), "qThreadStopInfo%" PRIx64, tid); +    assert(packet_len < (int)sizeof(packet)); +    UNUSED_IF_ASSERT_DISABLED(packet_len); +    if (SendPacketAndWaitForResponse(packet, response, false) == +        PacketResult::Success) { +      if (response.IsUnsupportedResponse()) +        m_supports_qThreadStopInfo = false; +      else if (response.IsNormalResponse()) +        return true; +      else +        return false; +    } else { +      m_supports_qThreadStopInfo = false; +    } +  } +  return false; +} + +uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket( +    GDBStoppointType type, bool insert, addr_t addr, uint32_t length) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); +  if (log) +    log->Printf("GDBRemoteCommunicationClient::%s() %s at addr = 0x%" PRIx64, +                __FUNCTION__, insert ? "add" : "remove", addr); + +  // Check if the stub is known not to support this breakpoint type +  if (!SupportsGDBStoppointPacket(type)) +    return UINT8_MAX; +  // Construct the breakpoint packet +  char packet[64]; +  const int packet_len = +      ::snprintf(packet, sizeof(packet), "%c%i,%" PRIx64 ",%x", +                 insert ? 'Z' : 'z', type, addr, length); +  // Check we haven't overwritten the end of the packet buffer +  assert(packet_len + 1 < (int)sizeof(packet)); +  UNUSED_IF_ASSERT_DISABLED(packet_len); +  StringExtractorGDBRemote response; +  // Make sure the response is either "OK", "EXX" where XX are two hex digits, +  // or "" (unsupported) +  response.SetResponseValidatorToOKErrorNotSupported(); +  // Try to send the breakpoint packet, and check that it was correctly sent +  if (SendPacketAndWaitForResponse(packet, response, true) == +      PacketResult::Success) { +    // Receive and OK packet when the breakpoint successfully placed +    if (response.IsOKResponse()) +      return 0; + +    // Status while setting breakpoint, send back specific error +    if (response.IsErrorResponse()) +      return response.GetError(); + +    // Empty packet informs us that breakpoint is not supported +    if (response.IsUnsupportedResponse()) { +      // Disable this breakpoint type since it is unsupported +      switch (type) { +      case eBreakpointSoftware: +        m_supports_z0 = false; +        break; +      case eBreakpointHardware: +        m_supports_z1 = false; +        break; +      case eWatchpointWrite: +        m_supports_z2 = false; +        break; +      case eWatchpointRead: +        m_supports_z3 = false; +        break; +      case eWatchpointReadWrite: +        m_supports_z4 = false; +        break; +      case eStoppointInvalid: +        return UINT8_MAX; +      } +    } +  } +  // Signal generic failure +  return UINT8_MAX; +} + +size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs( +    std::vector<lldb::tid_t> &thread_ids, bool &sequence_mutex_unavailable) { +  thread_ids.clear(); + +  Lock lock(*this, false); +  if (lock) { +    sequence_mutex_unavailable = false; +    StringExtractorGDBRemote response; + +    PacketResult packet_result; +    for (packet_result = +             SendPacketAndWaitForResponseNoLock("qfThreadInfo", response); +         packet_result == PacketResult::Success && response.IsNormalResponse(); +         packet_result = +             SendPacketAndWaitForResponseNoLock("qsThreadInfo", response)) { +      char ch = response.GetChar(); +      if (ch == 'l') +        break; +      if (ch == 'm') { +        do { +          tid_t tid = response.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID); + +          if (tid != LLDB_INVALID_THREAD_ID) { +            thread_ids.push_back(tid); +          } +          ch = response.GetChar(); // Skip the command separator +        } while (ch == ',');       // Make sure we got a comma separator +      } +    } + +    /* +     * Connected bare-iron target (like YAMON gdb-stub) may not have support for +     * qProcessInfo, qC and qfThreadInfo packets. The reply from '?' packet +     * could +     * be as simple as 'S05'. There is no packet which can give us pid and/or +     * tid. +     * Assume pid=tid=1 in such cases. +    */ +    if ((response.IsUnsupportedResponse() || response.IsNormalResponse()) && +        thread_ids.size() == 0 && IsConnected()) { +      thread_ids.push_back(1); +    } +  } else { +#if !defined(LLDB_CONFIGURATION_DEBUG) +    Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | +                                                           GDBR_LOG_PACKETS)); +    if (log) +      log->Printf("error: failed to get packet sequence mutex, not sending " +                  "packet 'qfThreadInfo'"); +#endif +    sequence_mutex_unavailable = true; +  } +  return thread_ids.size(); +} + +lldb::addr_t GDBRemoteCommunicationClient::GetShlibInfoAddr() { +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse("qShlibInfoAddr", response, false) != +          PacketResult::Success || +      !response.IsNormalResponse()) +    return LLDB_INVALID_ADDRESS; +  return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); +} + +lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand( +    const char *command, // Shouldn't be NULL +    const FileSpec & +        working_dir, // Pass empty FileSpec to use the current working directory +    int *status_ptr, // Pass NULL if you don't want the process exit status +    int *signo_ptr,  // Pass NULL if you don't want the signal that caused the +                     // process to exit +    std::string +        *command_output, // Pass NULL if you don't want the command output +    const Timeout<std::micro> &timeout) { +  lldb_private::StreamString stream; +  stream.PutCString("qPlatform_shell:"); +  stream.PutBytesAsRawHex8(command, strlen(command)); +  stream.PutChar(','); +  uint32_t timeout_sec = UINT32_MAX; +  if (timeout) { +    // TODO: Use chrono version of std::ceil once c++17 is available. +    timeout_sec = std::ceil(std::chrono::duration<double>(*timeout).count()); +  } +  stream.PutHex32(timeout_sec); +  if (working_dir) { +    std::string path{working_dir.GetPath(false)}; +    stream.PutChar(','); +    stream.PutStringAsRawHex8(path); +  } +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    if (response.GetChar() != 'F') +      return Status("malformed reply"); +    if (response.GetChar() != ',') +      return Status("malformed reply"); +    uint32_t exitcode = response.GetHexMaxU32(false, UINT32_MAX); +    if (exitcode == UINT32_MAX) +      return Status("unable to run remote process"); +    else if (status_ptr) +      *status_ptr = exitcode; +    if (response.GetChar() != ',') +      return Status("malformed reply"); +    uint32_t signo = response.GetHexMaxU32(false, UINT32_MAX); +    if (signo_ptr) +      *signo_ptr = signo; +    if (response.GetChar() != ',') +      return Status("malformed reply"); +    std::string output; +    response.GetEscapedBinaryData(output); +    if (command_output) +      command_output->assign(output); +    return Status(); +  } +  return Status("unable to send packet"); +} + +Status GDBRemoteCommunicationClient::MakeDirectory(const FileSpec &file_spec, +                                                   uint32_t file_permissions) { +  std::string path{file_spec.GetPath(false)}; +  lldb_private::StreamString stream; +  stream.PutCString("qPlatform_mkdir:"); +  stream.PutHex32(file_permissions); +  stream.PutChar(','); +  stream.PutStringAsRawHex8(path); +  llvm::StringRef packet = stream.GetString(); +  StringExtractorGDBRemote response; + +  if (SendPacketAndWaitForResponse(packet, response, false) != +      PacketResult::Success) +    return Status("failed to send '%s' packet", packet.str().c_str()); + +  if (response.GetChar() != 'F') +    return Status("invalid response to '%s' packet", packet.str().c_str()); + +  return Status(response.GetU32(UINT32_MAX), eErrorTypePOSIX); +} + +Status +GDBRemoteCommunicationClient::SetFilePermissions(const FileSpec &file_spec, +                                                 uint32_t file_permissions) { +  std::string path{file_spec.GetPath(false)}; +  lldb_private::StreamString stream; +  stream.PutCString("qPlatform_chmod:"); +  stream.PutHex32(file_permissions); +  stream.PutChar(','); +  stream.PutStringAsRawHex8(path); +  llvm::StringRef packet = stream.GetString(); +  StringExtractorGDBRemote response; + +  if (SendPacketAndWaitForResponse(packet, response, false) != +      PacketResult::Success) +    return Status("failed to send '%s' packet", stream.GetData()); + +  if (response.GetChar() != 'F') +    return Status("invalid response to '%s' packet", stream.GetData()); + +  return Status(response.GetU32(UINT32_MAX), eErrorTypePOSIX); +} + +static uint64_t ParseHostIOPacketResponse(StringExtractorGDBRemote &response, +                                          uint64_t fail_result, Status &error) { +  response.SetFilePos(0); +  if (response.GetChar() != 'F') +    return fail_result; +  int32_t result = response.GetS32(-2); +  if (result == -2) +    return fail_result; +  if (response.GetChar() == ',') { +    int result_errno = response.GetS32(-2); +    if (result_errno != -2) +      error.SetError(result_errno, eErrorTypePOSIX); +    else +      error.SetError(-1, eErrorTypeGeneric); +  } else +    error.Clear(); +  return result; +} +lldb::user_id_t +GDBRemoteCommunicationClient::OpenFile(const lldb_private::FileSpec &file_spec, +                                       uint32_t flags, mode_t mode, +                                       Status &error) { +  std::string path(file_spec.GetPath(false)); +  lldb_private::StreamString stream; +  stream.PutCString("vFile:open:"); +  if (path.empty()) +    return UINT64_MAX; +  stream.PutStringAsRawHex8(path); +  stream.PutChar(','); +  stream.PutHex32(flags); +  stream.PutChar(','); +  stream.PutHex32(mode); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    return ParseHostIOPacketResponse(response, UINT64_MAX, error); +  } +  return UINT64_MAX; +} + +bool GDBRemoteCommunicationClient::CloseFile(lldb::user_id_t fd, +                                             Status &error) { +  lldb_private::StreamString stream; +  stream.Printf("vFile:close:%i", (int)fd); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    return ParseHostIOPacketResponse(response, -1, error) == 0; +  } +  return false; +} + +// Extension of host I/O packets to get the file size. +lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize( +    const lldb_private::FileSpec &file_spec) { +  std::string path(file_spec.GetPath(false)); +  lldb_private::StreamString stream; +  stream.PutCString("vFile:size:"); +  stream.PutStringAsRawHex8(path); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    if (response.GetChar() != 'F') +      return UINT64_MAX; +    uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX); +    return retcode; +  } +  return UINT64_MAX; +} + +Status +GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec, +                                                 uint32_t &file_permissions) { +  std::string path{file_spec.GetPath(false)}; +  Status error; +  lldb_private::StreamString stream; +  stream.PutCString("vFile:mode:"); +  stream.PutStringAsRawHex8(path); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    if (response.GetChar() != 'F') { +      error.SetErrorStringWithFormat("invalid response to '%s' packet", +                                     stream.GetData()); +    } else { +      const uint32_t mode = response.GetS32(-1); +      if (static_cast<int32_t>(mode) == -1) { +        if (response.GetChar() == ',') { +          int response_errno = response.GetS32(-1); +          if (response_errno > 0) +            error.SetError(response_errno, lldb::eErrorTypePOSIX); +          else +            error.SetErrorToGenericError(); +        } else +          error.SetErrorToGenericError(); +      } else { +        file_permissions = mode & (S_IRWXU | S_IRWXG | S_IRWXO); +      } +    } +  } else { +    error.SetErrorStringWithFormat("failed to send '%s' packet", +                                   stream.GetData()); +  } +  return error; +} + +uint64_t GDBRemoteCommunicationClient::ReadFile(lldb::user_id_t fd, +                                                uint64_t offset, void *dst, +                                                uint64_t dst_len, +                                                Status &error) { +  lldb_private::StreamString stream; +  stream.Printf("vFile:pread:%i,%" PRId64 ",%" PRId64, (int)fd, dst_len, +                offset); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    if (response.GetChar() != 'F') +      return 0; +    uint32_t retcode = response.GetHexMaxU32(false, UINT32_MAX); +    if (retcode == UINT32_MAX) +      return retcode; +    const char next = (response.Peek() ? *response.Peek() : 0); +    if (next == ',') +      return 0; +    if (next == ';') { +      response.GetChar(); // skip the semicolon +      std::string buffer; +      if (response.GetEscapedBinaryData(buffer)) { +        const uint64_t data_to_write = +            std::min<uint64_t>(dst_len, buffer.size()); +        if (data_to_write > 0) +          memcpy(dst, &buffer[0], data_to_write); +        return data_to_write; +      } +    } +  } +  return 0; +} + +uint64_t GDBRemoteCommunicationClient::WriteFile(lldb::user_id_t fd, +                                                 uint64_t offset, +                                                 const void *src, +                                                 uint64_t src_len, +                                                 Status &error) { +  lldb_private::StreamGDBRemote stream; +  stream.Printf("vFile:pwrite:%i,%" PRId64 ",", (int)fd, offset); +  stream.PutEscapedBytes(src, src_len); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    if (response.GetChar() != 'F') { +      error.SetErrorStringWithFormat("write file failed"); +      return 0; +    } +    uint64_t bytes_written = response.GetU64(UINT64_MAX); +    if (bytes_written == UINT64_MAX) { +      error.SetErrorToGenericError(); +      if (response.GetChar() == ',') { +        int response_errno = response.GetS32(-1); +        if (response_errno > 0) +          error.SetError(response_errno, lldb::eErrorTypePOSIX); +      } +      return 0; +    } +    return bytes_written; +  } else { +    error.SetErrorString("failed to send vFile:pwrite packet"); +  } +  return 0; +} + +Status GDBRemoteCommunicationClient::CreateSymlink(const FileSpec &src, +                                                   const FileSpec &dst) { +  std::string src_path{src.GetPath(false)}, dst_path{dst.GetPath(false)}; +  Status error; +  lldb_private::StreamGDBRemote stream; +  stream.PutCString("vFile:symlink:"); +  // the unix symlink() command reverses its parameters where the dst if first, +  // so we follow suit here +  stream.PutStringAsRawHex8(dst_path); +  stream.PutChar(','); +  stream.PutStringAsRawHex8(src_path); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    if (response.GetChar() == 'F') { +      uint32_t result = response.GetU32(UINT32_MAX); +      if (result != 0) { +        error.SetErrorToGenericError(); +        if (response.GetChar() == ',') { +          int response_errno = response.GetS32(-1); +          if (response_errno > 0) +            error.SetError(response_errno, lldb::eErrorTypePOSIX); +        } +      } +    } else { +      // Should have returned with 'F<result>[,<errno>]' +      error.SetErrorStringWithFormat("symlink failed"); +    } +  } else { +    error.SetErrorString("failed to send vFile:symlink packet"); +  } +  return error; +} + +Status GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) { +  std::string path{file_spec.GetPath(false)}; +  Status error; +  lldb_private::StreamGDBRemote stream; +  stream.PutCString("vFile:unlink:"); +  // the unix symlink() command reverses its parameters where the dst if first, +  // so we follow suit here +  stream.PutStringAsRawHex8(path); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    if (response.GetChar() == 'F') { +      uint32_t result = response.GetU32(UINT32_MAX); +      if (result != 0) { +        error.SetErrorToGenericError(); +        if (response.GetChar() == ',') { +          int response_errno = response.GetS32(-1); +          if (response_errno > 0) +            error.SetError(response_errno, lldb::eErrorTypePOSIX); +        } +      } +    } else { +      // Should have returned with 'F<result>[,<errno>]' +      error.SetErrorStringWithFormat("unlink failed"); +    } +  } else { +    error.SetErrorString("failed to send vFile:unlink packet"); +  } +  return error; +} + +// Extension of host I/O packets to get whether a file exists. +bool GDBRemoteCommunicationClient::GetFileExists( +    const lldb_private::FileSpec &file_spec) { +  std::string path(file_spec.GetPath(false)); +  lldb_private::StreamString stream; +  stream.PutCString("vFile:exists:"); +  stream.PutStringAsRawHex8(path); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    if (response.GetChar() != 'F') +      return false; +    if (response.GetChar() != ',') +      return false; +    bool retcode = (response.GetChar() != '0'); +    return retcode; +  } +  return false; +} + +bool GDBRemoteCommunicationClient::CalculateMD5( +    const lldb_private::FileSpec &file_spec, uint64_t &high, uint64_t &low) { +  std::string path(file_spec.GetPath(false)); +  lldb_private::StreamString stream; +  stream.PutCString("vFile:MD5:"); +  stream.PutStringAsRawHex8(path); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == +      PacketResult::Success) { +    if (response.GetChar() != 'F') +      return false; +    if (response.GetChar() != ',') +      return false; +    if (response.Peek() && *response.Peek() == 'x') +      return false; +    low = response.GetHexMaxU64(false, UINT64_MAX); +    high = response.GetHexMaxU64(false, UINT64_MAX); +    return true; +  } +  return false; +} + +bool GDBRemoteCommunicationClient::AvoidGPackets(ProcessGDBRemote *process) { +  // Some targets have issues with g/G packets and we need to avoid using them +  if (m_avoid_g_packets == eLazyBoolCalculate) { +    if (process) { +      m_avoid_g_packets = eLazyBoolNo; +      const ArchSpec &arch = process->GetTarget().GetArchitecture(); +      if (arch.IsValid() && +          arch.GetTriple().getVendor() == llvm::Triple::Apple && +          arch.GetTriple().getOS() == llvm::Triple::IOS && +          arch.GetTriple().getArch() == llvm::Triple::aarch64) { +        m_avoid_g_packets = eLazyBoolYes; +        uint32_t gdb_server_version = GetGDBServerProgramVersion(); +        if (gdb_server_version != 0) { +          const char *gdb_server_name = GetGDBServerProgramName(); +          if (gdb_server_name && strcmp(gdb_server_name, "debugserver") == 0) { +            if (gdb_server_version >= 310) +              m_avoid_g_packets = eLazyBoolNo; +          } +        } +      } +    } +  } +  return m_avoid_g_packets == eLazyBoolYes; +} + +DataBufferSP GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid, +                                                        uint32_t reg) { +  StreamString payload; +  payload.Printf("p%x", reg); +  StringExtractorGDBRemote response; +  if (SendThreadSpecificPacketAndWaitForResponse( +          tid, std::move(payload), response, false) != PacketResult::Success || +      !response.IsNormalResponse()) +    return nullptr; + +  DataBufferSP buffer_sp( +      new DataBufferHeap(response.GetStringRef().size() / 2, 0)); +  response.GetHexBytes(buffer_sp->GetData(), '\xcc'); +  return buffer_sp; +} + +DataBufferSP GDBRemoteCommunicationClient::ReadAllRegisters(lldb::tid_t tid) { +  StreamString payload; +  payload.PutChar('g'); +  StringExtractorGDBRemote response; +  if (SendThreadSpecificPacketAndWaitForResponse( +          tid, std::move(payload), response, false) != PacketResult::Success || +      !response.IsNormalResponse()) +    return nullptr; + +  DataBufferSP buffer_sp( +      new DataBufferHeap(response.GetStringRef().size() / 2, 0)); +  response.GetHexBytes(buffer_sp->GetData(), '\xcc'); +  return buffer_sp; +} + +bool GDBRemoteCommunicationClient::WriteRegister(lldb::tid_t tid, +                                                 uint32_t reg_num, +                                                 llvm::ArrayRef<uint8_t> data) { +  StreamString payload; +  payload.Printf("P%x=", reg_num); +  payload.PutBytesAsRawHex8(data.data(), data.size(), +                            endian::InlHostByteOrder(), +                            endian::InlHostByteOrder()); +  StringExtractorGDBRemote response; +  return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), +                                                    response, false) == +             PacketResult::Success && +         response.IsOKResponse(); +} + +bool GDBRemoteCommunicationClient::WriteAllRegisters( +    lldb::tid_t tid, llvm::ArrayRef<uint8_t> data) { +  StreamString payload; +  payload.PutChar('G'); +  payload.PutBytesAsRawHex8(data.data(), data.size(), +                            endian::InlHostByteOrder(), +                            endian::InlHostByteOrder()); +  StringExtractorGDBRemote response; +  return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), +                                                    response, false) == +             PacketResult::Success && +         response.IsOKResponse(); +} + +bool GDBRemoteCommunicationClient::SaveRegisterState(lldb::tid_t tid, +                                                     uint32_t &save_id) { +  save_id = 0; // Set to invalid save ID +  if (m_supports_QSaveRegisterState == eLazyBoolNo) +    return false; + +  m_supports_QSaveRegisterState = eLazyBoolYes; +  StreamString payload; +  payload.PutCString("QSaveRegisterState"); +  StringExtractorGDBRemote response; +  if (SendThreadSpecificPacketAndWaitForResponse( +          tid, std::move(payload), response, false) != PacketResult::Success) +    return false; + +  if (response.IsUnsupportedResponse()) +    m_supports_QSaveRegisterState = eLazyBoolNo; + +  const uint32_t response_save_id = response.GetU32(0); +  if (response_save_id == 0) +    return false; + +  save_id = response_save_id; +  return true; +} + +bool GDBRemoteCommunicationClient::RestoreRegisterState(lldb::tid_t tid, +                                                        uint32_t save_id) { +  // We use the "m_supports_QSaveRegisterState" variable here because the +  // QSaveRegisterState and QRestoreRegisterState packets must both be +  // supported in order to be useful +  if (m_supports_QSaveRegisterState == eLazyBoolNo) +    return false; + +  StreamString payload; +  payload.Printf("QRestoreRegisterState:%u", save_id); +  StringExtractorGDBRemote response; +  if (SendThreadSpecificPacketAndWaitForResponse( +          tid, std::move(payload), response, false) != PacketResult::Success) +    return false; + +  if (response.IsOKResponse()) +    return true; + +  if (response.IsUnsupportedResponse()) +    m_supports_QSaveRegisterState = eLazyBoolNo; +  return false; +} + +bool GDBRemoteCommunicationClient::SyncThreadState(lldb::tid_t tid) { +  if (!GetSyncThreadStateSupported()) +    return false; + +  StreamString packet; +  StringExtractorGDBRemote response; +  packet.Printf("QSyncThreadState:%4.4" PRIx64 ";", tid); +  return SendPacketAndWaitForResponse(packet.GetString(), response, false) == +             GDBRemoteCommunication::PacketResult::Success && +         response.IsOKResponse(); +} + +lldb::user_id_t +GDBRemoteCommunicationClient::SendStartTracePacket(const TraceOptions &options, +                                                   Status &error) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  lldb::user_id_t ret_uid = LLDB_INVALID_UID; + +  StreamGDBRemote escaped_packet; +  escaped_packet.PutCString("jTraceStart:"); + +  StructuredData::Dictionary json_packet; +  json_packet.AddIntegerItem("type", options.getType()); +  json_packet.AddIntegerItem("buffersize", options.getTraceBufferSize()); +  json_packet.AddIntegerItem("metabuffersize", options.getMetaDataBufferSize()); + +  if (options.getThreadID() != LLDB_INVALID_THREAD_ID) +    json_packet.AddIntegerItem("threadid", options.getThreadID()); + +  StructuredData::DictionarySP custom_params = options.getTraceParams(); +  if (custom_params) +    json_packet.AddItem("params", custom_params); + +  StreamString json_string; +  json_packet.Dump(json_string, false); +  escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize()); + +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, +                                   true) == +      GDBRemoteCommunication::PacketResult::Success) { +    if (!response.IsNormalResponse()) { +      error = response.GetStatus(); +      LLDB_LOG(log, "Target does not support Tracing , error {0}", error); +    } else { +      ret_uid = response.GetHexMaxU64(false, LLDB_INVALID_UID); +    } +  } else { +    LLDB_LOG(log, "failed to send packet"); +    error.SetErrorStringWithFormat("failed to send packet: '%s'", +                                   escaped_packet.GetData()); +  } +  return ret_uid; +} + +Status +GDBRemoteCommunicationClient::SendStopTracePacket(lldb::user_id_t uid, +                                                  lldb::tid_t thread_id) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  StringExtractorGDBRemote response; +  Status error; + +  StructuredData::Dictionary json_packet; +  StreamGDBRemote escaped_packet; +  StreamString json_string; +  escaped_packet.PutCString("jTraceStop:"); + +  json_packet.AddIntegerItem("traceid", uid); + +  if (thread_id != LLDB_INVALID_THREAD_ID) +    json_packet.AddIntegerItem("threadid", thread_id); + +  json_packet.Dump(json_string, false); + +  escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize()); + +  if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, +                                   true) == +      GDBRemoteCommunication::PacketResult::Success) { +    if (!response.IsOKResponse()) { +      error = response.GetStatus(); +      LLDB_LOG(log, "stop tracing failed"); +    } +  } else { +    LLDB_LOG(log, "failed to send packet"); +    error.SetErrorStringWithFormat( +        "failed to send packet: '%s' with error '%d'", escaped_packet.GetData(), +        response.GetError()); +  } +  return error; +} + +Status GDBRemoteCommunicationClient::SendGetDataPacket( +    lldb::user_id_t uid, lldb::tid_t thread_id, +    llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) { + +  StreamGDBRemote escaped_packet; +  escaped_packet.PutCString("jTraceBufferRead:"); +  return SendGetTraceDataPacket(escaped_packet, uid, thread_id, buffer, offset); +} + +Status GDBRemoteCommunicationClient::SendGetMetaDataPacket( +    lldb::user_id_t uid, lldb::tid_t thread_id, +    llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) { + +  StreamGDBRemote escaped_packet; +  escaped_packet.PutCString("jTraceMetaRead:"); +  return SendGetTraceDataPacket(escaped_packet, uid, thread_id, buffer, offset); +} + +Status +GDBRemoteCommunicationClient::SendGetTraceConfigPacket(lldb::user_id_t uid, +                                                       TraceOptions &options) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  StringExtractorGDBRemote response; +  Status error; + +  StreamString json_string; +  StreamGDBRemote escaped_packet; +  escaped_packet.PutCString("jTraceConfigRead:"); + +  StructuredData::Dictionary json_packet; +  json_packet.AddIntegerItem("traceid", uid); + +  if (options.getThreadID() != LLDB_INVALID_THREAD_ID) +    json_packet.AddIntegerItem("threadid", options.getThreadID()); + +  json_packet.Dump(json_string, false); +  escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize()); + +  if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, +                                   true) == +      GDBRemoteCommunication::PacketResult::Success) { +    if (response.IsNormalResponse()) { +      uint64_t type = std::numeric_limits<uint64_t>::max(); +      uint64_t buffersize = std::numeric_limits<uint64_t>::max(); +      uint64_t metabuffersize = std::numeric_limits<uint64_t>::max(); + +      auto json_object = StructuredData::ParseJSON(response.Peek()); + +      if (!json_object || +          json_object->GetType() != lldb::eStructuredDataTypeDictionary) { +        error.SetErrorString("Invalid Configuration obtained"); +        return error; +      } + +      auto json_dict = json_object->GetAsDictionary(); + +      json_dict->GetValueForKeyAsInteger<uint64_t>("metabuffersize", +                                                   metabuffersize); +      options.setMetaDataBufferSize(metabuffersize); + +      json_dict->GetValueForKeyAsInteger<uint64_t>("buffersize", buffersize); +      options.setTraceBufferSize(buffersize); + +      json_dict->GetValueForKeyAsInteger<uint64_t>("type", type); +      options.setType(static_cast<lldb::TraceType>(type)); + +      StructuredData::ObjectSP custom_params_sp = +          json_dict->GetValueForKey("params"); +      if (custom_params_sp) { +        if (custom_params_sp->GetType() != +            lldb::eStructuredDataTypeDictionary) { +          error.SetErrorString("Invalid Configuration obtained"); +          return error; +        } else +          options.setTraceParams( +              static_pointer_cast<StructuredData::Dictionary>( +                  custom_params_sp)); +      } +    } else { +      error = response.GetStatus(); +    } +  } else { +    LLDB_LOG(log, "failed to send packet"); +    error.SetErrorStringWithFormat("failed to send packet: '%s'", +                                   escaped_packet.GetData()); +  } +  return error; +} + +Status GDBRemoteCommunicationClient::SendGetTraceDataPacket( +    StreamGDBRemote &packet, lldb::user_id_t uid, lldb::tid_t thread_id, +    llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  Status error; + +  StructuredData::Dictionary json_packet; + +  json_packet.AddIntegerItem("traceid", uid); +  json_packet.AddIntegerItem("offset", offset); +  json_packet.AddIntegerItem("buffersize", buffer.size()); + +  if (thread_id != LLDB_INVALID_THREAD_ID) +    json_packet.AddIntegerItem("threadid", thread_id); + +  StreamString json_string; +  json_packet.Dump(json_string, false); + +  packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize()); +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(packet.GetString(), response, true) == +      GDBRemoteCommunication::PacketResult::Success) { +    if (response.IsNormalResponse()) { +      size_t filled_size = response.GetHexBytesAvail(buffer); +      buffer = llvm::MutableArrayRef<uint8_t>(buffer.data(), filled_size); +    } else { +      error = response.GetStatus(); +      buffer = buffer.slice(buffer.size()); +    } +  } else { +    LLDB_LOG(log, "failed to send packet"); +    error.SetErrorStringWithFormat("failed to send packet: '%s'", +                                   packet.GetData()); +    buffer = buffer.slice(buffer.size()); +  } +  return error; +} + +bool GDBRemoteCommunicationClient::GetModuleInfo( +    const FileSpec &module_file_spec, const lldb_private::ArchSpec &arch_spec, +    ModuleSpec &module_spec) { +  if (!m_supports_qModuleInfo) +    return false; + +  std::string module_path = module_file_spec.GetPath(false); +  if (module_path.empty()) +    return false; + +  StreamString packet; +  packet.PutCString("qModuleInfo:"); +  packet.PutStringAsRawHex8(module_path); +  packet.PutCString(";"); +  const auto &triple = arch_spec.GetTriple().getTriple(); +  packet.PutStringAsRawHex8(triple); + +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(packet.GetString(), response, false) != +      PacketResult::Success) +    return false; + +  if (response.IsErrorResponse()) +    return false; + +  if (response.IsUnsupportedResponse()) { +    m_supports_qModuleInfo = false; +    return false; +  } + +  llvm::StringRef name; +  llvm::StringRef value; + +  module_spec.Clear(); +  module_spec.GetFileSpec() = module_file_spec; + +  while (response.GetNameColonValue(name, value)) { +    if (name == "uuid" || name == "md5") { +      StringExtractor extractor(value); +      std::string uuid; +      extractor.GetHexByteString(uuid); +      module_spec.GetUUID().SetFromStringRef(uuid, uuid.size() / 2); +    } else if (name == "triple") { +      StringExtractor extractor(value); +      std::string triple; +      extractor.GetHexByteString(triple); +      module_spec.GetArchitecture().SetTriple(triple.c_str()); +    } else if (name == "file_offset") { +      uint64_t ival = 0; +      if (!value.getAsInteger(16, ival)) +        module_spec.SetObjectOffset(ival); +    } else if (name == "file_size") { +      uint64_t ival = 0; +      if (!value.getAsInteger(16, ival)) +        module_spec.SetObjectSize(ival); +    } else if (name == "file_path") { +      StringExtractor extractor(value); +      std::string path; +      extractor.GetHexByteString(path); +      module_spec.GetFileSpec() = FileSpec(path, arch_spec.GetTriple()); +    } +  } + +  return true; +} + +static llvm::Optional<ModuleSpec> +ParseModuleSpec(StructuredData::Dictionary *dict) { +  ModuleSpec result; +  if (!dict) +    return llvm::None; + +  llvm::StringRef string; +  uint64_t integer; + +  if (!dict->GetValueForKeyAsString("uuid", string)) +    return llvm::None; +  if (result.GetUUID().SetFromStringRef(string, string.size() / 2) != +      string.size()) +    return llvm::None; + +  if (!dict->GetValueForKeyAsInteger("file_offset", integer)) +    return llvm::None; +  result.SetObjectOffset(integer); + +  if (!dict->GetValueForKeyAsInteger("file_size", integer)) +    return llvm::None; +  result.SetObjectSize(integer); + +  if (!dict->GetValueForKeyAsString("triple", string)) +    return llvm::None; +  result.GetArchitecture().SetTriple(string); + +  if (!dict->GetValueForKeyAsString("file_path", string)) +    return llvm::None; +  result.GetFileSpec() = FileSpec(string, result.GetArchitecture().GetTriple()); + +  return result; +} + +llvm::Optional<std::vector<ModuleSpec>> +GDBRemoteCommunicationClient::GetModulesInfo( +    llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple) { +  if (!m_supports_jModulesInfo) +    return llvm::None; + +  JSONArray::SP module_array_sp = std::make_shared<JSONArray>(); +  for (const FileSpec &module_file_spec : module_file_specs) { +    JSONObject::SP module_sp = std::make_shared<JSONObject>(); +    module_array_sp->AppendObject(module_sp); +    module_sp->SetObject( +        "file", std::make_shared<JSONString>(module_file_spec.GetPath(false))); +    module_sp->SetObject("triple", +                         std::make_shared<JSONString>(triple.getTriple())); +  } +  StreamString unescaped_payload; +  unescaped_payload.PutCString("jModulesInfo:"); +  module_array_sp->Write(unescaped_payload); +  StreamGDBRemote payload; +  payload.PutEscapedBytes(unescaped_payload.GetString().data(), +                          unescaped_payload.GetSize()); + +  // Increase the timeout for jModulesInfo since this packet can take longer. +  ScopedTimeout timeout(*this, std::chrono::seconds(10)); + +  StringExtractorGDBRemote response; +  if (SendPacketAndWaitForResponse(payload.GetString(), response, false) != +          PacketResult::Success || +      response.IsErrorResponse()) +    return llvm::None; + +  if (response.IsUnsupportedResponse()) { +    m_supports_jModulesInfo = false; +    return llvm::None; +  } + +  StructuredData::ObjectSP response_object_sp = +      StructuredData::ParseJSON(response.GetStringRef()); +  if (!response_object_sp) +    return llvm::None; + +  StructuredData::Array *response_array = response_object_sp->GetAsArray(); +  if (!response_array) +    return llvm::None; + +  std::vector<ModuleSpec> result; +  for (size_t i = 0; i < response_array->GetSize(); ++i) { +    if (llvm::Optional<ModuleSpec> module_spec = ParseModuleSpec( +            response_array->GetItemAtIndex(i)->GetAsDictionary())) +      result.push_back(*module_spec); +  } + +  return result; +} + +// query the target remote for extended information using the qXfer packet +// +// example: object='features', annex='target.xml', out=<xml output> return: +// 'true'  on success +//          'false' on failure (err set) +bool GDBRemoteCommunicationClient::ReadExtFeature( +    const lldb_private::ConstString object, +    const lldb_private::ConstString annex, std::string &out, +    lldb_private::Status &err) { + +  std::stringstream output; +  StringExtractorGDBRemote chunk; + +  uint64_t size = GetRemoteMaxPacketSize(); +  if (size == 0) +    size = 0x1000; +  size = size - 1; // Leave space for the 'm' or 'l' character in the response +  int offset = 0; +  bool active = true; + +  // loop until all data has been read +  while (active) { + +    // send query extended feature packet +    std::stringstream packet; +    packet << "qXfer:" << object.AsCString("") +           << ":read:" << annex.AsCString("") << ":" << std::hex << offset +           << "," << std::hex << size; + +    GDBRemoteCommunication::PacketResult res = +        SendPacketAndWaitForResponse(packet.str(), chunk, false); + +    if (res != GDBRemoteCommunication::PacketResult::Success) { +      err.SetErrorString("Error sending $qXfer packet"); +      return false; +    } + +    const std::string &str = chunk.GetStringRef(); +    if (str.length() == 0) { +      // should have some data in chunk +      err.SetErrorString("Empty response from $qXfer packet"); +      return false; +    } + +    // check packet code +    switch (str[0]) { +    // last chunk +    case ('l'): +      active = false; +      LLVM_FALLTHROUGH; + +    // more chunks +    case ('m'): +      if (str.length() > 1) +        output << &str[1]; +      offset += size; +      break; + +    // unknown chunk +    default: +      err.SetErrorString("Invalid continuation code from $qXfer packet"); +      return false; +    } +  } + +  out = output.str(); +  err.Success(); +  return true; +} + +// Notify the target that gdb is prepared to serve symbol lookup requests. +//  packet: "qSymbol::" +//  reply: +//  OK                  The target does not need to look up any (more) symbols. +//  qSymbol:<sym_name>  The target requests the value of symbol sym_name (hex +//  encoded). +//                      LLDB may provide the value by sending another qSymbol +//                      packet +//                      in the form of"qSymbol:<sym_value>:<sym_name>". +// +//  Three examples: +// +//  lldb sends:    qSymbol:: +//  lldb receives: OK +//     Remote gdb stub does not need to know the addresses of any symbols, lldb +//     does not +//     need to ask again in this session. +// +//  lldb sends:    qSymbol:: +//  lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473 +//  lldb sends:    qSymbol::64697370617463685f71756575655f6f666673657473 +//  lldb receives: OK +//     Remote gdb stub asks for address of 'dispatch_queue_offsets'.  lldb does +//     not know +//     the address at this time.  lldb needs to send qSymbol:: again when it has +//     more +//     solibs loaded. +// +//  lldb sends:    qSymbol:: +//  lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473 +//  lldb sends:    qSymbol:2bc97554:64697370617463685f71756575655f6f666673657473 +//  lldb receives: OK +//     Remote gdb stub asks for address of 'dispatch_queue_offsets'.  lldb says +//     that it +//     is at address 0x2bc97554.  Remote gdb stub sends 'OK' indicating that it +//     does not +//     need any more symbols.  lldb does not need to ask again in this session. + +void GDBRemoteCommunicationClient::ServeSymbolLookups( +    lldb_private::Process *process) { +  // Set to true once we've resolved a symbol to an address for the remote +  // stub. If we get an 'OK' response after this, the remote stub doesn't need +  // any more symbols and we can stop asking. +  bool symbol_response_provided = false; + +  // Is this the initial qSymbol:: packet? +  bool first_qsymbol_query = true; + +  if (m_supports_qSymbol && !m_qSymbol_requests_done) { +    Lock lock(*this, false); +    if (lock) { +      StreamString packet; +      packet.PutCString("qSymbol::"); +      StringExtractorGDBRemote response; +      while (SendPacketAndWaitForResponseNoLock(packet.GetString(), response) == +             PacketResult::Success) { +        if (response.IsOKResponse()) { +          if (symbol_response_provided || first_qsymbol_query) { +            m_qSymbol_requests_done = true; +          } + +          // We are done serving symbols requests +          return; +        } +        first_qsymbol_query = false; + +        if (response.IsUnsupportedResponse()) { +          // qSymbol is not supported by the current GDB server we are +          // connected to +          m_supports_qSymbol = false; +          return; +        } else { +          llvm::StringRef response_str(response.GetStringRef()); +          if (response_str.startswith("qSymbol:")) { +            response.SetFilePos(strlen("qSymbol:")); +            std::string symbol_name; +            if (response.GetHexByteString(symbol_name)) { +              if (symbol_name.empty()) +                return; + +              addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; +              lldb_private::SymbolContextList sc_list; +              if (process->GetTarget().GetImages().FindSymbolsWithNameAndType( +                      ConstString(symbol_name), eSymbolTypeAny, sc_list)) { +                const size_t num_scs = sc_list.GetSize(); +                for (size_t sc_idx = 0; +                     sc_idx < num_scs && +                     symbol_load_addr == LLDB_INVALID_ADDRESS; +                     ++sc_idx) { +                  SymbolContext sc; +                  if (sc_list.GetContextAtIndex(sc_idx, sc)) { +                    if (sc.symbol) { +                      switch (sc.symbol->GetType()) { +                      case eSymbolTypeInvalid: +                      case eSymbolTypeAbsolute: +                      case eSymbolTypeUndefined: +                      case eSymbolTypeSourceFile: +                      case eSymbolTypeHeaderFile: +                      case eSymbolTypeObjectFile: +                      case eSymbolTypeCommonBlock: +                      case eSymbolTypeBlock: +                      case eSymbolTypeLocal: +                      case eSymbolTypeParam: +                      case eSymbolTypeVariable: +                      case eSymbolTypeVariableType: +                      case eSymbolTypeLineEntry: +                      case eSymbolTypeLineHeader: +                      case eSymbolTypeScopeBegin: +                      case eSymbolTypeScopeEnd: +                      case eSymbolTypeAdditional: +                      case eSymbolTypeCompiler: +                      case eSymbolTypeInstrumentation: +                      case eSymbolTypeTrampoline: +                        break; + +                      case eSymbolTypeCode: +                      case eSymbolTypeResolver: +                      case eSymbolTypeData: +                      case eSymbolTypeRuntime: +                      case eSymbolTypeException: +                      case eSymbolTypeObjCClass: +                      case eSymbolTypeObjCMetaClass: +                      case eSymbolTypeObjCIVar: +                      case eSymbolTypeReExported: +                        symbol_load_addr = +                            sc.symbol->GetLoadAddress(&process->GetTarget()); +                        break; +                      } +                    } +                  } +                } +              } +              // This is the normal path where our symbol lookup was successful +              // and we want to send a packet with the new symbol value and see +              // if another lookup needs to be done. + +              // Change "packet" to contain the requested symbol value and name +              packet.Clear(); +              packet.PutCString("qSymbol:"); +              if (symbol_load_addr != LLDB_INVALID_ADDRESS) { +                packet.Printf("%" PRIx64, symbol_load_addr); +                symbol_response_provided = true; +              } else { +                symbol_response_provided = false; +              } +              packet.PutCString(":"); +              packet.PutBytesAsRawHex8(symbol_name.data(), symbol_name.size()); +              continue; // go back to the while loop and send "packet" and wait +                        // for another response +            } +          } +        } +      } +      // If we make it here, the symbol request packet response wasn't valid or +      // our symbol lookup failed so we must abort +      return; + +    } else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( +                   GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)) { +      log->Printf( +          "GDBRemoteCommunicationClient::%s: Didn't get sequence mutex.", +          __FUNCTION__); +    } +  } +} + +StructuredData::Array * +GDBRemoteCommunicationClient::GetSupportedStructuredDataPlugins() { +  if (!m_supported_async_json_packets_is_valid) { +    // Query the server for the array of supported asynchronous JSON packets. +    m_supported_async_json_packets_is_valid = true; + +    Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + +    // Poll it now. +    StringExtractorGDBRemote response; +    const bool send_async = false; +    if (SendPacketAndWaitForResponse("qStructuredDataPlugins", response, +                                     send_async) == PacketResult::Success) { +      m_supported_async_json_packets_sp = +          StructuredData::ParseJSON(response.GetStringRef()); +      if (m_supported_async_json_packets_sp && +          !m_supported_async_json_packets_sp->GetAsArray()) { +        // We were returned something other than a JSON array.  This is +        // invalid.  Clear it out. +        if (log) +          log->Printf("GDBRemoteCommunicationClient::%s(): " +                      "QSupportedAsyncJSONPackets returned invalid " +                      "result: %s", +                      __FUNCTION__, response.GetStringRef().c_str()); +        m_supported_async_json_packets_sp.reset(); +      } +    } else { +      if (log) +        log->Printf("GDBRemoteCommunicationClient::%s(): " +                    "QSupportedAsyncJSONPackets unsupported", +                    __FUNCTION__); +    } + +    if (log && m_supported_async_json_packets_sp) { +      StreamString stream; +      m_supported_async_json_packets_sp->Dump(stream); +      log->Printf("GDBRemoteCommunicationClient::%s(): supported async " +                  "JSON packets: %s", +                  __FUNCTION__, stream.GetData()); +    } +  } + +  return m_supported_async_json_packets_sp +             ? m_supported_async_json_packets_sp->GetAsArray() +             : nullptr; +} + +Status GDBRemoteCommunicationClient::SendSignalsToIgnore( +    llvm::ArrayRef<int32_t> signals) { +  // Format packet: +  // QPassSignals:<hex_sig1>;<hex_sig2>...;<hex_sigN> +  auto range = llvm::make_range(signals.begin(), signals.end()); +  std::string packet = formatv("QPassSignals:{0:$[;]@(x-2)}", range).str(); + +  StringExtractorGDBRemote response; +  auto send_status = SendPacketAndWaitForResponse(packet, response, false); + +  if (send_status != GDBRemoteCommunication::PacketResult::Success) +    return Status("Sending QPassSignals packet failed"); + +  if (response.IsOKResponse()) { +    return Status(); +  } else { +    return Status("Unknown error happened during sending QPassSignals packet."); +  } +} + +Status GDBRemoteCommunicationClient::ConfigureRemoteStructuredData( +    ConstString type_name, const StructuredData::ObjectSP &config_sp) { +  Status error; + +  if (type_name.GetLength() == 0) { +    error.SetErrorString("invalid type_name argument"); +    return error; +  } + +  // Build command: Configure{type_name}: serialized config data. +  StreamGDBRemote stream; +  stream.PutCString("QConfigure"); +  stream.PutCString(type_name.AsCString()); +  stream.PutChar(':'); +  if (config_sp) { +    // Gather the plain-text version of the configuration data. +    StreamString unescaped_stream; +    config_sp->Dump(unescaped_stream); +    unescaped_stream.Flush(); + +    // Add it to the stream in escaped fashion. +    stream.PutEscapedBytes(unescaped_stream.GetString().data(), +                           unescaped_stream.GetSize()); +  } +  stream.Flush(); + +  // Send the packet. +  const bool send_async = false; +  StringExtractorGDBRemote response; +  auto result = +      SendPacketAndWaitForResponse(stream.GetString(), response, send_async); +  if (result == PacketResult::Success) { +    // We failed if the config result comes back other than OK. +    if (strcmp(response.GetStringRef().c_str(), "OK") == 0) { +      // Okay! +      error.Clear(); +    } else { +      error.SetErrorStringWithFormat("configuring StructuredData feature " +                                     "%s failed with error %s", +                                     type_name.AsCString(), +                                     response.GetStringRef().c_str()); +    } +  } else { +    // Can we get more data here on the failure? +    error.SetErrorStringWithFormat("configuring StructuredData feature %s " +                                   "failed when sending packet: " +                                   "PacketResult=%d", +                                   type_name.AsCString(), (int)result); +  } +  return error; +} + +void GDBRemoteCommunicationClient::OnRunPacketSent(bool first) { +  GDBRemoteClientBase::OnRunPacketSent(first); +  m_curr_tid = LLDB_INVALID_THREAD_ID; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h new file mode 100644 index 000000000000..de85c9f8b67b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -0,0 +1,602 @@ +//===-- GDBRemoteCommunicationClient.h --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationClient_h_ +#define liblldb_GDBRemoteCommunicationClient_h_ + +#include "GDBRemoteClientBase.h" + +#include <chrono> +#include <map> +#include <mutex> +#include <string> +#include <vector> + +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/StreamGDBRemote.h" +#include "lldb/Utility/StructuredData.h" +#if defined(_WIN32) +#include "lldb/Host/windows/PosixApi.h" +#endif + +#include "llvm/ADT/Optional.h" +#include "llvm/Support/VersionTuple.h" + +namespace lldb_private { +namespace process_gdb_remote { + +class GDBRemoteCommunicationClient : public GDBRemoteClientBase { +public: +  GDBRemoteCommunicationClient(); + +  ~GDBRemoteCommunicationClient() override; + +  // After connecting, send the handshake to the server to make sure +  // we are communicating with it. +  bool HandshakeWithServer(Status *error_ptr); + +  // 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> +  // <prefix><size>,<size> +  // <prefix><size>*2,<size> +  // <prefix><size>*3,<size> +  // ... +  // until a "$l..." packet is received, indicating the end. +  // (size is in hex; this format is used by a standard gdbserver to +  // return the given portion of the output specified by <prefix>; +  // for example, "qXfer:libraries-svr4:read::fff,1000" means +  // "return a chunk of the xml description file for shared +  // library load addresses, where the chunk starts at offset 0xfff +  // and continues for 0x1000 bytes"). +  // Concatenate the resulting server response packets together and +  // return in response_string.  If any packet fails, the return value +  // indicates that failure and the returned string value is undefined. +  PacketResult +  SendPacketsAndConcatenateResponses(const char *send_payload_prefix, +                                     std::string &response_string); + +  bool GetThreadSuffixSupported(); + +  // This packet is usually sent first and the boolean return value +  // indicates if the packet was send and any response was received +  // even in the response is UNIMPLEMENTED. If the packet failed to +  // get a response, then false is returned. This quickly tells us +  // if we were able to connect and communicate with the remote GDB +  // server +  bool QueryNoAckModeSupported(); + +  void GetListThreadsInStopReplySupported(); + +  lldb::pid_t GetCurrentProcessID(bool allow_lazy = true); + +  bool GetLaunchSuccess(std::string &error_str); + +  bool LaunchGDBServer(const char *remote_accept_hostname, lldb::pid_t &pid, +                       uint16_t &port, std::string &socket_name); + +  size_t QueryGDBServer( +      std::vector<std::pair<uint16_t, std::string>> &connection_urls); + +  bool KillSpawnedProcess(lldb::pid_t pid); + +  /// Sends a GDB remote protocol 'A' packet that delivers program +  /// arguments to the remote server. +  /// +  /// \param[in] argv +  ///     A NULL terminated array of const C strings to use as the +  ///     arguments. +  /// +  /// \return +  ///     Zero if the response was "OK", a positive value if the +  ///     the response was "Exx" where xx are two hex digits, or +  ///     -1 if the call is unsupported or any other unexpected +  ///     response was received. +  int SendArgumentsPacket(const ProcessLaunchInfo &launch_info); + +  /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the +  /// environment that will get used when launching an application +  /// in conjunction with the 'A' packet. This function can be called +  /// multiple times in a row in order to pass on the desired +  /// environment that the inferior should be launched with. +  /// +  /// \param[in] name_equal_value +  ///     A NULL terminated C string that contains a single environment +  ///     in the format "NAME=VALUE". +  /// +  /// \return +  ///     Zero if the response was "OK", a positive value if the +  ///     the response was "Exx" where xx are two hex digits, or +  ///     -1 if the call is unsupported or any other unexpected +  ///     response was received. +  int SendEnvironmentPacket(char const *name_equal_value); +  int SendEnvironment(const Environment &env); + +  int SendLaunchArchPacket(const char *arch); + +  int SendLaunchEventDataPacket(const char *data, +                                bool *was_supported = nullptr); + +  /// Sends a "vAttach:PID" where PID is in hex. +  /// +  /// \param[in] pid +  ///     A process ID for the remote gdb server to attach to. +  /// +  /// \param[out] response +  ///     The response received from the gdb server. If the return +  ///     value is zero, \a response will contain a stop reply +  ///     packet. +  /// +  /// \return +  ///     Zero if the attach was successful, or an error indicating +  ///     an error code. +  int SendAttach(lldb::pid_t pid, StringExtractorGDBRemote &response); + +  /// Sends a GDB remote protocol 'I' packet that delivers stdin +  /// data to the remote process. +  /// +  /// \param[in] data +  ///     A pointer to stdin data. +  /// +  /// \param[in] data_len +  ///     The number of bytes available at \a data. +  /// +  /// \return +  ///     Zero if the attach was successful, or an error indicating +  ///     an error code. +  int SendStdinNotification(const char *data, size_t data_len); + +  /// Sets the path to use for stdin/out/err for a process +  /// that will be launched with the 'A' packet. +  /// +  /// \param[in] path +  ///     The path to use for stdin/out/err +  /// +  /// \return +  ///     Zero if the for success, or an error code for failure. +  int SetSTDIN(const FileSpec &file_spec); +  int SetSTDOUT(const FileSpec &file_spec); +  int SetSTDERR(const FileSpec &file_spec); + +  /// Sets the disable ASLR flag to \a enable for a process that will +  /// be launched with the 'A' packet. +  /// +  /// \param[in] enable +  ///     A boolean value indicating whether to disable ASLR or not. +  /// +  /// \return +  ///     Zero if the for success, or an error code for failure. +  int SetDisableASLR(bool enable); + +  /// Sets the DetachOnError flag to \a enable for the process controlled by the +  /// stub. +  /// +  /// \param[in] enable +  ///     A boolean value indicating whether to detach on error or not. +  /// +  /// \return +  ///     Zero if the for success, or an error code for failure. +  int SetDetachOnError(bool enable); + +  /// Sets the working directory to \a path for a process that will +  /// be launched with the 'A' packet for non platform based +  /// connections. If this packet is sent to a GDB server that +  /// implements the platform, it will change the current working +  /// directory for the platform process. +  /// +  /// \param[in] working_dir +  ///     The path to a directory to use when launching our process +  /// +  /// \return +  ///     Zero if the for success, or an error code for failure. +  int SetWorkingDir(const FileSpec &working_dir); + +  /// Gets the current working directory of a remote platform GDB +  /// server. +  /// +  /// \param[out] working_dir +  ///     The current working directory on the remote platform. +  /// +  /// \return +  ///     Boolean for success +  bool GetWorkingDir(FileSpec &working_dir); + +  lldb::addr_t AllocateMemory(size_t size, uint32_t permissions); + +  bool DeallocateMemory(lldb::addr_t addr); + +  Status Detach(bool keep_stopped); + +  Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info); + +  Status GetWatchpointSupportInfo(uint32_t &num); + +  Status GetWatchpointSupportInfo(uint32_t &num, bool &after, +                                  const ArchSpec &arch); + +  Status GetWatchpointsTriggerAfterInstruction(bool &after, +                                               const ArchSpec &arch); + +  const ArchSpec &GetHostArchitecture(); + +  std::chrono::seconds GetHostDefaultPacketTimeout(); + +  const ArchSpec &GetProcessArchitecture(); + +  void GetRemoteQSupported(); + +  bool GetVContSupported(char flavor); + +  bool GetpPacketSupported(lldb::tid_t tid); + +  bool GetxPacketSupported(); + +  bool GetVAttachOrWaitSupported(); + +  bool GetSyncThreadStateSupported(); + +  void ResetDiscoverableSettings(bool did_exec); + +  bool GetHostInfo(bool force = false); + +  bool GetDefaultThreadId(lldb::tid_t &tid); + +  llvm::VersionTuple GetOSVersion(); + +  bool GetOSBuildString(std::string &s); + +  bool GetOSKernelDescription(std::string &s); + +  ArchSpec GetSystemArchitecture(); + +  bool GetHostname(std::string &s); + +  lldb::addr_t GetShlibInfoAddr(); + +  bool GetSupportsThreadSuffix(); + +  bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info); + +  uint32_t FindProcesses(const ProcessInstanceInfoMatch &process_match_info, +                         ProcessInstanceInfoList &process_infos); + +  bool GetUserName(uint32_t uid, std::string &name); + +  bool GetGroupName(uint32_t gid, std::string &name); + +  bool HasFullVContSupport() { return GetVContSupported('A'); } + +  bool HasAnyVContSupport() { return GetVContSupported('a'); } + +  bool GetStopReply(StringExtractorGDBRemote &response); + +  bool GetThreadStopInfo(lldb::tid_t tid, StringExtractorGDBRemote &response); + +  bool SupportsGDBStoppointPacket(GDBStoppointType type) { +    switch (type) { +    case eBreakpointSoftware: +      return m_supports_z0; +    case eBreakpointHardware: +      return m_supports_z1; +    case eWatchpointWrite: +      return m_supports_z2; +    case eWatchpointRead: +      return m_supports_z3; +    case eWatchpointReadWrite: +      return m_supports_z4; +    default: +      return false; +    } +  } + +  uint8_t SendGDBStoppointTypePacket( +      GDBStoppointType type, // Type of breakpoint or watchpoint +      bool insert,           // Insert or remove? +      lldb::addr_t addr,     // Address of breakpoint or watchpoint +      uint32_t length);      // Byte Size of breakpoint or watchpoint + +  bool SetNonStopMode(const bool enable); + +  void TestPacketSpeed(const uint32_t num_packets, uint32_t max_send, +                       uint32_t max_recv, uint64_t recv_amount, bool json, +                       Stream &strm); + +  // This packet is for testing the speed of the interface only. Both +  // the client and server need to support it, but this allows us to +  // measure the packet speed without any other work being done on the +  // other end and avoids any of that work affecting the packet send +  // and response times. +  bool SendSpeedTestPacket(uint32_t send_size, uint32_t recv_size); + +  bool SetCurrentThread(uint64_t tid); + +  bool SetCurrentThreadForRun(uint64_t tid); + +  bool GetQXferAuxvReadSupported(); + +  void EnableErrorStringInPacket(); + +  bool GetQXferLibrariesReadSupported(); + +  bool GetQXferLibrariesSVR4ReadSupported(); + +  uint64_t GetRemoteMaxPacketSize(); + +  bool GetEchoSupported(); + +  bool GetQPassSignalsSupported(); + +  bool GetAugmentedLibrariesSVR4ReadSupported(); + +  bool GetQXferFeaturesReadSupported(); + +  bool GetQXferMemoryMapReadSupported(); + +  LazyBool SupportsAllocDeallocMemory() // const +  { +    // Uncomment this to have lldb pretend the debug server doesn't respond to +    // alloc/dealloc memory packets. +    // m_supports_alloc_dealloc_memory = lldb_private::eLazyBoolNo; +    return m_supports_alloc_dealloc_memory; +  } + +  size_t GetCurrentThreadIDs(std::vector<lldb::tid_t> &thread_ids, +                             bool &sequence_mutex_unavailable); + +  lldb::user_id_t OpenFile(const FileSpec &file_spec, uint32_t flags, +                           mode_t mode, Status &error); + +  bool CloseFile(lldb::user_id_t fd, Status &error); + +  lldb::user_id_t GetFileSize(const FileSpec &file_spec); + +  Status GetFilePermissions(const FileSpec &file_spec, +                            uint32_t &file_permissions); + +  Status SetFilePermissions(const FileSpec &file_spec, +                            uint32_t file_permissions); + +  uint64_t ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst, +                    uint64_t dst_len, Status &error); + +  uint64_t WriteFile(lldb::user_id_t fd, uint64_t offset, const void *src, +                     uint64_t src_len, Status &error); + +  Status CreateSymlink(const FileSpec &src, const FileSpec &dst); + +  Status Unlink(const FileSpec &file_spec); + +  Status MakeDirectory(const FileSpec &file_spec, uint32_t mode); + +  bool GetFileExists(const FileSpec &file_spec); + +  Status RunShellCommand( +      const char *command,         // Shouldn't be nullptr +      const FileSpec &working_dir, // Pass empty FileSpec to use the current +                                   // working directory +      int *status_ptr, // Pass nullptr if you don't want the process exit status +      int *signo_ptr,  // Pass nullptr if you don't want the signal that caused +                       // the process to exit +      std::string +          *command_output, // Pass nullptr if you don't want the command output +      const Timeout<std::micro> &timeout); + +  bool CalculateMD5(const FileSpec &file_spec, uint64_t &high, uint64_t &low); + +  lldb::DataBufferSP ReadRegister( +      lldb::tid_t tid, +      uint32_t +          reg_num); // Must be the eRegisterKindProcessPlugin register number + +  lldb::DataBufferSP ReadAllRegisters(lldb::tid_t tid); + +  bool +  WriteRegister(lldb::tid_t tid, +                uint32_t reg_num, // eRegisterKindProcessPlugin register number +                llvm::ArrayRef<uint8_t> data); + +  bool WriteAllRegisters(lldb::tid_t tid, llvm::ArrayRef<uint8_t> data); + +  bool SaveRegisterState(lldb::tid_t tid, uint32_t &save_id); + +  bool RestoreRegisterState(lldb::tid_t tid, uint32_t save_id); + +  bool SyncThreadState(lldb::tid_t tid); + +  const char *GetGDBServerProgramName(); + +  uint32_t GetGDBServerProgramVersion(); + +  bool AvoidGPackets(ProcessGDBRemote *process); + +  StructuredData::ObjectSP GetThreadsInfo(); + +  bool GetThreadExtendedInfoSupported(); + +  bool GetLoadedDynamicLibrariesInfosSupported(); + +  bool GetSharedCacheInfoSupported(); + +  bool GetModuleInfo(const FileSpec &module_file_spec, +                     const ArchSpec &arch_spec, ModuleSpec &module_spec); + +  llvm::Optional<std::vector<ModuleSpec>> +  GetModulesInfo(llvm::ArrayRef<FileSpec> module_file_specs, +                 const llvm::Triple &triple); + +  bool ReadExtFeature(const lldb_private::ConstString object, +                      const lldb_private::ConstString annex, std::string &out, +                      lldb_private::Status &err); + +  void ServeSymbolLookups(lldb_private::Process *process); + +  // Sends QPassSignals packet to the server with given signals to ignore. +  Status SendSignalsToIgnore(llvm::ArrayRef<int32_t> signals); + +  /// Return the feature set supported by the gdb-remote server. +  /// +  /// This method returns the remote side's response to the qSupported +  /// packet.  The response is the complete string payload returned +  /// to the client. +  /// +  /// \return +  ///     The string returned by the server to the qSupported query. +  const std::string &GetServerSupportedFeatures() const { +    return m_qSupported_response; +  } + +  /// Return the array of async JSON packet types supported by the remote. +  /// +  /// This method returns the remote side's array of supported JSON +  /// packet types as a list of type names.  Each of the results are +  /// expected to have an Enable{type_name} command to enable and configure +  /// the related feature.  Each type_name for an enabled feature will +  /// possibly send async-style packets that contain a payload of a +  /// binhex-encoded JSON dictionary.  The dictionary will have a +  /// string field named 'type', that contains the type_name of the +  /// supported packet type. +  /// +  /// There is a Plugin category called structured-data plugins. +  /// A plugin indicates whether it knows how to handle a type_name. +  /// If so, it can be used to process the async JSON packet. +  /// +  /// \return +  ///     The string returned by the server to the qSupported query. +  lldb_private::StructuredData::Array *GetSupportedStructuredDataPlugins(); + +  /// Configure a StructuredData feature on the remote end. +  /// +  /// \see \b Process::ConfigureStructuredData(...) for details. +  Status +  ConfigureRemoteStructuredData(ConstString type_name, +                                const StructuredData::ObjectSP &config_sp); + +  lldb::user_id_t SendStartTracePacket(const TraceOptions &options, +                                       Status &error); + +  Status SendStopTracePacket(lldb::user_id_t uid, lldb::tid_t thread_id); + +  Status SendGetDataPacket(lldb::user_id_t uid, lldb::tid_t thread_id, +                           llvm::MutableArrayRef<uint8_t> &buffer, +                           size_t offset = 0); + +  Status SendGetMetaDataPacket(lldb::user_id_t uid, lldb::tid_t thread_id, +                               llvm::MutableArrayRef<uint8_t> &buffer, +                               size_t offset = 0); + +  Status SendGetTraceConfigPacket(lldb::user_id_t uid, TraceOptions &options); + +protected: +  LazyBool m_supports_not_sending_acks; +  LazyBool m_supports_thread_suffix; +  LazyBool m_supports_threads_in_stop_reply; +  LazyBool m_supports_vCont_all; +  LazyBool m_supports_vCont_any; +  LazyBool m_supports_vCont_c; +  LazyBool m_supports_vCont_C; +  LazyBool m_supports_vCont_s; +  LazyBool m_supports_vCont_S; +  LazyBool m_qHostInfo_is_valid; +  LazyBool m_curr_pid_is_valid; +  LazyBool m_qProcessInfo_is_valid; +  LazyBool m_qGDBServerVersion_is_valid; +  LazyBool m_supports_alloc_dealloc_memory; +  LazyBool m_supports_memory_region_info; +  LazyBool m_supports_watchpoint_support_info; +  LazyBool m_supports_detach_stay_stopped; +  LazyBool m_watchpoints_trigger_after_instruction; +  LazyBool m_attach_or_wait_reply; +  LazyBool m_prepare_for_reg_writing_reply; +  LazyBool m_supports_p; +  LazyBool m_supports_x; +  LazyBool m_avoid_g_packets; +  LazyBool m_supports_QSaveRegisterState; +  LazyBool m_supports_qXfer_auxv_read; +  LazyBool m_supports_qXfer_libraries_read; +  LazyBool m_supports_qXfer_libraries_svr4_read; +  LazyBool m_supports_qXfer_features_read; +  LazyBool m_supports_qXfer_memory_map_read; +  LazyBool m_supports_augmented_libraries_svr4_read; +  LazyBool m_supports_jThreadExtendedInfo; +  LazyBool m_supports_jLoadedDynamicLibrariesInfos; +  LazyBool m_supports_jGetSharedCacheInfo; +  LazyBool m_supports_QPassSignals; +  LazyBool m_supports_error_string_reply; + +  bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1, +      m_supports_qUserName : 1, m_supports_qGroupName : 1, +      m_supports_qThreadStopInfo : 1, m_supports_z0 : 1, m_supports_z1 : 1, +      m_supports_z2 : 1, m_supports_z3 : 1, m_supports_z4 : 1, +      m_supports_QEnvironment : 1, m_supports_QEnvironmentHexEncoded : 1, +      m_supports_qSymbol : 1, m_qSymbol_requests_done : 1, +      m_supports_qModuleInfo : 1, m_supports_jThreadsInfo : 1, +      m_supports_jModulesInfo : 1; + +  lldb::pid_t m_curr_pid; +  lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all +                          // other operations +  lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for +                              // continue, step, etc + +  uint32_t m_num_supported_hardware_watchpoints; + +  ArchSpec m_host_arch; +  ArchSpec m_process_arch; +  llvm::VersionTuple m_os_version; +  std::string m_os_build; +  std::string m_os_kernel; +  std::string m_hostname; +  std::string m_gdb_server_name; // from reply to qGDBServerVersion, empty if +                                 // qGDBServerVersion is not supported +  uint32_t m_gdb_server_version; // from reply to qGDBServerVersion, zero if +                                 // qGDBServerVersion is not supported +  std::chrono::seconds m_default_packet_timeout; +  uint64_t m_max_packet_size;        // as returned by qSupported +  std::string m_qSupported_response; // the complete response to qSupported + +  bool m_supported_async_json_packets_is_valid; +  lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp; + +  std::vector<MemoryRegionInfo> m_qXfer_memory_map; +  bool m_qXfer_memory_map_loaded; + +  bool GetCurrentProcessInfo(bool allow_lazy_pid = true); + +  bool GetGDBServerVersion(); + +  // Given the list of compression types that the remote debug stub can support, +  // possibly enable compression if we find an encoding we can handle. +  void MaybeEnableCompression(std::vector<std::string> supported_compressions); + +  bool DecodeProcessInfoResponse(StringExtractorGDBRemote &response, +                                 ProcessInstanceInfo &process_info); + +  void OnRunPacketSent(bool first) override; + +  PacketResult SendThreadSpecificPacketAndWaitForResponse( +      lldb::tid_t tid, StreamString &&payload, +      StringExtractorGDBRemote &response, bool send_async); + +  Status SendGetTraceDataPacket(StreamGDBRemote &packet, lldb::user_id_t uid, +                                lldb::tid_t thread_id, +                                llvm::MutableArrayRef<uint8_t> &buffer, +                                size_t offset); + +  Status LoadQXferMemoryMap(); + +  Status GetQXferMemoryMapRegionInfo(lldb::addr_t addr, +                                     MemoryRegionInfo ®ion); + +private: +  DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationClient); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_GDBRemoteCommunicationClient_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp new file mode 100644 index 000000000000..bcddb4faf863 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp @@ -0,0 +1,142 @@ +//===-- GDBRemoteCommunicationHistory.cpp -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteCommunicationHistory.h" + +// Other libraries and framework includes +#include "lldb/Core/StreamFile.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +void GDBRemoteCommunicationHistory::Entry::Serialize(raw_ostream &strm) const { +  yaml::Output yout(strm); +  yout << const_cast<GDBRemoteCommunicationHistory::Entry &>(*this); +  strm.flush(); +} + +GDBRemoteCommunicationHistory::GDBRemoteCommunicationHistory(uint32_t size) +    : m_packets(), m_curr_idx(0), m_total_packet_count(0), +      m_dumped_to_log(false) { +  if (size) +    m_packets.resize(size); +} + +GDBRemoteCommunicationHistory::~GDBRemoteCommunicationHistory() {} + +void GDBRemoteCommunicationHistory::AddPacket(char packet_char, PacketType type, +                                              uint32_t bytes_transmitted) { +  const size_t size = m_packets.size(); +  if (size == 0) +    return; + +  const uint32_t idx = GetNextIndex(); +  m_packets[idx].packet.data.assign(1, packet_char); +  m_packets[idx].type = type; +  m_packets[idx].bytes_transmitted = bytes_transmitted; +  m_packets[idx].packet_idx = m_total_packet_count; +  m_packets[idx].tid = llvm::get_threadid(); +  if (m_stream) +    m_packets[idx].Serialize(*m_stream); +} + +void GDBRemoteCommunicationHistory::AddPacket(const std::string &src, +                                              uint32_t src_len, PacketType type, +                                              uint32_t bytes_transmitted) { +  const size_t size = m_packets.size(); +  if (size == 0) +    return; + +  const uint32_t idx = GetNextIndex(); +  m_packets[idx].packet.data.assign(src, 0, src_len); +  m_packets[idx].type = type; +  m_packets[idx].bytes_transmitted = bytes_transmitted; +  m_packets[idx].packet_idx = m_total_packet_count; +  m_packets[idx].tid = llvm::get_threadid(); +  if (m_stream) +    m_packets[idx].Serialize(*m_stream); +} + +void GDBRemoteCommunicationHistory::Dump(Stream &strm) const { +  const uint32_t size = GetNumPacketsInHistory(); +  const uint32_t first_idx = GetFirstSavedPacketIndex(); +  const uint32_t stop_idx = m_curr_idx + size; +  for (uint32_t i = first_idx; i < stop_idx; ++i) { +    const uint32_t idx = NormalizeIndex(i); +    const Entry &entry = m_packets[idx]; +    if (entry.type == ePacketTypeInvalid || entry.packet.data.empty()) +      break; +    strm.Printf("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s\n", +                entry.packet_idx, entry.tid, entry.bytes_transmitted, +                (entry.type == ePacketTypeSend) ? "send" : "read", +                entry.packet.data.c_str()); +  } +} + +void GDBRemoteCommunicationHistory::Dump(Log *log) const { +  if (!log || m_dumped_to_log) +    return; + +  m_dumped_to_log = true; +  const uint32_t size = GetNumPacketsInHistory(); +  const uint32_t first_idx = GetFirstSavedPacketIndex(); +  const uint32_t stop_idx = m_curr_idx + size; +  for (uint32_t i = first_idx; i < stop_idx; ++i) { +    const uint32_t idx = NormalizeIndex(i); +    const Entry &entry = m_packets[idx]; +    if (entry.type == ePacketTypeInvalid || entry.packet.data.empty()) +      break; +    log->Printf("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s", +                entry.packet_idx, entry.tid, entry.bytes_transmitted, +                (entry.type == ePacketTypeSend) ? "send" : "read", +                entry.packet.data.c_str()); +  } +} + +void yaml::ScalarEnumerationTraits<GDBRemoteCommunicationHistory::PacketType>:: +    enumeration(IO &io, GDBRemoteCommunicationHistory::PacketType &value) { +  io.enumCase(value, "Invalid", +              GDBRemoteCommunicationHistory::ePacketTypeInvalid); +  io.enumCase(value, "Send", GDBRemoteCommunicationHistory::ePacketTypeSend); +  io.enumCase(value, "Recv", GDBRemoteCommunicationHistory::ePacketTypeRecv); +} + +void yaml::ScalarTraits<GDBRemoteCommunicationHistory::Entry::BinaryData>:: +    output(const GDBRemoteCommunicationHistory::Entry::BinaryData &Val, void *, +           raw_ostream &Out) { +  Out << toHex(Val.data); +} + +StringRef +yaml::ScalarTraits<GDBRemoteCommunicationHistory::Entry::BinaryData>::input( +    StringRef Scalar, void *, +    GDBRemoteCommunicationHistory::Entry::BinaryData &Val) { +  Val.data = fromHex(Scalar); +  return {}; +} + +void yaml::MappingTraits<GDBRemoteCommunicationHistory::Entry>::mapping( +    IO &io, GDBRemoteCommunicationHistory::Entry &Entry) { +  io.mapRequired("packet", Entry.packet); +  io.mapRequired("type", Entry.type); +  io.mapRequired("bytes", Entry.bytes_transmitted); +  io.mapRequired("index", Entry.packet_idx); +  io.mapRequired("tid", Entry.tid); +} + +StringRef yaml::MappingTraits<GDBRemoteCommunicationHistory::Entry>::validate( +    IO &io, GDBRemoteCommunicationHistory::Entry &Entry) { +  if (Entry.bytes_transmitted != Entry.packet.data.size()) +    return "BinaryData size doesn't match bytes transmitted"; + +  return {}; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h new file mode 100644 index 000000000000..85f112b50623 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h @@ -0,0 +1,155 @@ +//===-- GDBRemoteCommunicationHistory.h--------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationHistory_h_ +#define liblldb_GDBRemoteCommunicationHistory_h_ + +#include <string> +#include <vector> + +#include "lldb/lldb-public.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" + +namespace lldb_private { +namespace process_gdb_remote { + +/// The history keeps a circular buffer of GDB remote packets. The history is +/// used for logging and replaying GDB remote packets. +class GDBRemoteCommunicationHistory { +public: +  friend llvm::yaml::MappingTraits<GDBRemoteCommunicationHistory>; + +  enum PacketType { ePacketTypeInvalid = 0, ePacketTypeSend, ePacketTypeRecv }; + +  /// Entry in the ring buffer containing the packet data, its type, size and +  /// index. Entries can be serialized to file. +  struct Entry { +    Entry() +        : packet(), type(ePacketTypeInvalid), bytes_transmitted(0), +          packet_idx(0), tid(LLDB_INVALID_THREAD_ID) {} + +    void Clear() { +      packet.data.clear(); +      type = ePacketTypeInvalid; +      bytes_transmitted = 0; +      packet_idx = 0; +      tid = LLDB_INVALID_THREAD_ID; +    } + +    struct BinaryData { +      std::string data; +    }; + +    void Serialize(llvm::raw_ostream &strm) const; + +    BinaryData packet; +    PacketType type; +    uint32_t bytes_transmitted; +    uint32_t packet_idx; +    lldb::tid_t tid; +  }; + +  GDBRemoteCommunicationHistory(uint32_t size = 0); + +  ~GDBRemoteCommunicationHistory(); + +  // For single char packets for ack, nack and /x03 +  void AddPacket(char packet_char, PacketType type, uint32_t bytes_transmitted); + +  void AddPacket(const std::string &src, uint32_t src_len, PacketType type, +                 uint32_t bytes_transmitted); + +  void Dump(Stream &strm) const; +  void Dump(Log *log) const; +  bool DidDumpToLog() const { return m_dumped_to_log; } + +  void SetStream(llvm::raw_ostream *strm) { m_stream = strm; } + +private: +  uint32_t GetFirstSavedPacketIndex() const { +    if (m_total_packet_count < m_packets.size()) +      return 0; +    else +      return m_curr_idx + 1; +  } + +  uint32_t GetNumPacketsInHistory() const { +    if (m_total_packet_count < m_packets.size()) +      return m_total_packet_count; +    else +      return (uint32_t)m_packets.size(); +  } + +  uint32_t GetNextIndex() { +    ++m_total_packet_count; +    const uint32_t idx = m_curr_idx; +    m_curr_idx = NormalizeIndex(idx + 1); +    return idx; +  } + +  uint32_t NormalizeIndex(uint32_t i) const { +    return m_packets.empty() ? 0 : i % m_packets.size(); +  } + +  std::vector<Entry> m_packets; +  uint32_t m_curr_idx; +  uint32_t m_total_packet_count; +  mutable bool m_dumped_to_log; +  llvm::raw_ostream *m_stream = nullptr; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +LLVM_YAML_IS_DOCUMENT_LIST_VECTOR( +    lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry) + +namespace llvm { +namespace yaml { + +template <> +struct ScalarEnumerationTraits<lldb_private::process_gdb_remote:: +                                   GDBRemoteCommunicationHistory::PacketType> { +  static void enumeration(IO &io, +                          lldb_private::process_gdb_remote:: +                              GDBRemoteCommunicationHistory::PacketType &value); +}; + +template <> +struct ScalarTraits<lldb_private::process_gdb_remote:: +                        GDBRemoteCommunicationHistory::Entry::BinaryData> { +  static void output(const lldb_private::process_gdb_remote:: +                         GDBRemoteCommunicationHistory::Entry::BinaryData &, +                     void *, raw_ostream &); + +  static StringRef +  input(StringRef, void *, +        lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry:: +            BinaryData &); + +  static QuotingType mustQuote(StringRef S) { return QuotingType::None; } +}; + +template <> +struct MappingTraits< +    lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry> { +  static void +  mapping(IO &io, +          lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry +              &Entry); + +  static StringRef validate( +      IO &io, +      lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry &); +}; + +} // namespace yaml +} // namespace llvm + +#endif // liblldb_GDBRemoteCommunicationHistory_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp new file mode 100644 index 000000000000..417f5737a30f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp @@ -0,0 +1,282 @@ +//===-- GDBRemoteCommunicationReplayServer.cpp ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <errno.h> + +#include "lldb/Host/Config.h" + +#include "GDBRemoteCommunicationReplayServer.h" +#include "ProcessGDBRemoteLog.h" + +// C Includes +// C++ Includes +#include <cstring> + +// Project includes +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +/// Check if the given expected packet matches the actual packet. +static bool unexpected(llvm::StringRef expected, llvm::StringRef actual) { +  // The 'expected' string contains the raw data, including the leading $ and +  // trailing checksum. The 'actual' string contains only the packet's content. +  if (expected.contains(actual)) +    return false; +  // Contains a PID which might be different. +  if (expected.contains("vAttach")) +    return false; +  // Contains a ascii-hex-path. +  if (expected.contains("QSetSTD")) +    return false; +  // Contains environment values. +  if (expected.contains("QEnvironment")) +    return false; + +  return true; +} + +/// Check if we should reply to the given packet. +static bool skip(llvm::StringRef data) { +  assert(!data.empty() && "Empty packet?"); + +  // We've already acknowledge the '+' packet so we're done here. +  if (data == "+") +    return true; + +  /// Don't 't reply to ^C. We need this because of stop reply packets, which +  /// are only returned when the target halts. Reproducers synchronize these +  /// 'asynchronous' replies, by recording them as a regular replies to the +  /// previous packet (e.g. vCont). As a result, we should ignore real +  /// asynchronous requests. +  if (data.data()[0] == 0x03) +    return true; + +  return false; +} + +GDBRemoteCommunicationReplayServer::GDBRemoteCommunicationReplayServer() +    : GDBRemoteCommunication("gdb-replay", "gdb-replay.rx_packet"), +      m_async_broadcaster(nullptr, "lldb.gdb-replay.async-broadcaster"), +      m_async_listener_sp( +          Listener::MakeListener("lldb.gdb-replay.async-listener")), +      m_async_thread_state_mutex(), m_skip_acks(false) { +  m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, +                                   "async thread continue"); +  m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, +                                   "async thread should exit"); + +  const uint32_t async_event_mask = +      eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; +  m_async_listener_sp->StartListeningForEvents(&m_async_broadcaster, +                                               async_event_mask); +} + +GDBRemoteCommunicationReplayServer::~GDBRemoteCommunicationReplayServer() { +  StopAsyncThread(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationReplayServer::GetPacketAndSendResponse( +    Timeout<std::micro> timeout, Status &error, bool &interrupt, bool &quit) { +  std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); + +  StringExtractorGDBRemote packet; +  PacketResult packet_result = WaitForPacketNoLock(packet, timeout, false); + +  if (packet_result != PacketResult::Success) { +    if (!IsConnected()) { +      error.SetErrorString("lost connection"); +      quit = true; +    } else { +      error.SetErrorString("timeout"); +    } +    return packet_result; +  } + +  m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue); + +  // Check if we should reply to this packet. +  if (skip(packet.GetStringRef())) +    return PacketResult::Success; + +  // This completes the handshake. Since m_send_acks was true, we can unset it +  // already. +  if (packet.GetStringRef() == "QStartNoAckMode") +    m_send_acks = false; + +  // A QEnvironment packet is sent for every environment variable. If the +  // number of environment variables is different during replay, the replies +  // become out of sync. +  if (packet.GetStringRef().find("QEnvironment") == 0) +    return SendRawPacketNoLock("$OK#9a"); + +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  while (!m_packet_history.empty()) { +    // Pop last packet from the history. +    GDBRemoteCommunicationHistory::Entry entry = m_packet_history.back(); +    m_packet_history.pop_back(); + +    // We've handled the handshake implicitly before. Skip the packet and move +    // on. +    if (entry.packet.data == "+") +      continue; + +    if (entry.type == GDBRemoteCommunicationHistory::ePacketTypeSend) { +      if (unexpected(entry.packet.data, packet.GetStringRef())) { +        LLDB_LOG(log, +                 "GDBRemoteCommunicationReplayServer expected packet: '{0}'", +                 entry.packet.data); +        LLDB_LOG(log, "GDBRemoteCommunicationReplayServer actual packet: '{0}'", +                 packet.GetStringRef()); +        assert(false && "Encountered unexpected packet during replay"); +        return PacketResult::ErrorSendFailed; +      } + +      // Ignore QEnvironment packets as they're handled earlier. +      if (entry.packet.data.find("QEnvironment") == 1) { +        assert(m_packet_history.back().type == +               GDBRemoteCommunicationHistory::ePacketTypeRecv); +        m_packet_history.pop_back(); +      } + +      continue; +    } + +    if (entry.type == GDBRemoteCommunicationHistory::ePacketTypeInvalid) { +      LLDB_LOG( +          log, +          "GDBRemoteCommunicationReplayServer skipped invalid packet: '{0}'", +          packet.GetStringRef()); +      continue; +    } + +    LLDB_LOG(log, +             "GDBRemoteCommunicationReplayServer replied to '{0}' with '{1}'", +             packet.GetStringRef(), entry.packet.data); +    return SendRawPacketNoLock(entry.packet.data); +  } + +  quit = true; + +  return packet_result; +} + +LLVM_YAML_IS_DOCUMENT_LIST_VECTOR( +    std::vector< +        lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry>) + +llvm::Error +GDBRemoteCommunicationReplayServer::LoadReplayHistory(const FileSpec &path) { +  auto error_or_file = MemoryBuffer::getFile(path.GetPath()); +  if (auto err = error_or_file.getError()) +    return errorCodeToError(err); + +  yaml::Input yin((*error_or_file)->getBuffer()); +  yin >> m_packet_history; + +  if (auto err = yin.error()) +    return errorCodeToError(err); + +  // We want to manipulate the vector like a stack so we need to reverse the +  // order of the packets to have the oldest on at the back. +  std::reverse(m_packet_history.begin(), m_packet_history.end()); + +  return Error::success(); +} + +bool GDBRemoteCommunicationReplayServer::StartAsyncThread() { +  std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); +  if (!m_async_thread.IsJoinable()) { +    // Create a thread that watches our internal state and controls which +    // events make it to clients (into the DCProcess event queue). +    llvm::Expected<HostThread> async_thread = ThreadLauncher::LaunchThread( +        "<lldb.gdb-replay.async>", +        GDBRemoteCommunicationReplayServer::AsyncThread, this); +    if (!async_thread) { +      LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), +               "failed to launch host thread: {}", +               llvm::toString(async_thread.takeError())); +      return false; +    } +    m_async_thread = *async_thread; +  } + +  // Wait for handshake. +  m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue); + +  return m_async_thread.IsJoinable(); +} + +void GDBRemoteCommunicationReplayServer::StopAsyncThread() { +  std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); + +  if (!m_async_thread.IsJoinable()) +    return; + +  // Request thread to stop. +  m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit); + +  // Disconnect client. +  Disconnect(); + +  // Stop the thread. +  m_async_thread.Join(nullptr); +  m_async_thread.Reset(); +} + +void GDBRemoteCommunicationReplayServer::ReceivePacket( +    GDBRemoteCommunicationReplayServer &server, bool &done) { +  Status error; +  bool interrupt; +  auto packet_result = server.GetPacketAndSendResponse(std::chrono::seconds(1), +                                                       error, interrupt, done); +  if (packet_result != GDBRemoteCommunication::PacketResult::Success && +      packet_result != +          GDBRemoteCommunication::PacketResult::ErrorReplyTimeout) { +    done = true; +  } else { +    server.m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue); +  } +} + +thread_result_t GDBRemoteCommunicationReplayServer::AsyncThread(void *arg) { +  GDBRemoteCommunicationReplayServer *server = +      (GDBRemoteCommunicationReplayServer *)arg; + +  EventSP event_sp; +  bool done = false; + +  while (true) { +    if (server->m_async_listener_sp->GetEvent(event_sp, llvm::None)) { +      const uint32_t event_type = event_sp->GetType(); +      if (event_sp->BroadcasterIs(&server->m_async_broadcaster)) { +        switch (event_type) { +        case eBroadcastBitAsyncContinue: +          ReceivePacket(*server, done); +          if (done) +            return {}; +          break; +        case eBroadcastBitAsyncThreadShouldExit: +        default: +          return {}; +        } +      } +    } +  } + +  return {}; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h new file mode 100644 index 000000000000..26d65e265463 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h @@ -0,0 +1,82 @@ +//===-- GDBRemoteCommunicationReplayServer.h --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationReplayServer_h_ +#define liblldb_GDBRemoteCommunicationReplayServer_h_ + +// Other libraries and framework includes +#include "GDBRemoteCommunication.h" +#include "GDBRemoteCommunicationHistory.h" + +// Project includes +#include "lldb/Host/HostThread.h" +#include "lldb/Utility/Broadcaster.h" +#include "lldb/lldb-private-forward.h" +#include "llvm/Support/Error.h" + +// C Includes +// C++ Includes +#include <functional> +#include <map> +#include <thread> + +class StringExtractorGDBRemote; + +namespace lldb_private { +namespace process_gdb_remote { + +class ProcessGDBRemote; + +/// Dummy GDB server that replays packets from the GDB Remote Communication +/// history. This is used to replay GDB packets. +class GDBRemoteCommunicationReplayServer : public GDBRemoteCommunication { +public: +  GDBRemoteCommunicationReplayServer(); + +  ~GDBRemoteCommunicationReplayServer() override; + +  PacketResult GetPacketAndSendResponse(Timeout<std::micro> timeout, +                                        Status &error, bool &interrupt, +                                        bool &quit); + +  bool HandshakeWithClient() { return GetAck() == PacketResult::Success; } + +  llvm::Error LoadReplayHistory(const FileSpec &path); + +  bool StartAsyncThread(); +  void StopAsyncThread(); + +protected: +  enum { +    eBroadcastBitAsyncContinue = (1 << 0), +    eBroadcastBitAsyncThreadShouldExit = (1 << 1), +  }; + +  static void ReceivePacket(GDBRemoteCommunicationReplayServer &server, +                            bool &done); +  static lldb::thread_result_t AsyncThread(void *arg); + +  /// Replay history with the oldest packet at the end. +  std::vector<GDBRemoteCommunicationHistory::Entry> m_packet_history; + +  /// Server thread. +  Broadcaster m_async_broadcaster; +  lldb::ListenerSP m_async_listener_sp; +  HostThread m_async_thread; +  std::recursive_mutex m_async_thread_state_mutex; + +  bool m_skip_acks; + +private: +  DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationReplayServer); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_GDBRemoteCommunicationReplayServer_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp new file mode 100644 index 000000000000..49cbeb023fd5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -0,0 +1,158 @@ +//===-- GDBRemoteCommunicationServer.cpp ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <errno.h> + +#include "lldb/Host/Config.h" + +#include "GDBRemoteCommunicationServer.h" + +#include <cstring> + +#include "ProcessGDBRemoteLog.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +GDBRemoteCommunicationServer::GDBRemoteCommunicationServer( +    const char *comm_name, const char *listener_name) +    : GDBRemoteCommunication(comm_name, listener_name), m_exit_now(false) { +  RegisterPacketHandler( +      StringExtractorGDBRemote::eServerPacketType_QEnableErrorStrings, +      [this](StringExtractorGDBRemote packet, Status &error, bool &interrupt, +             bool &quit) { return this->Handle_QErrorStringEnable(packet); }); +} + +GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer() {} + +void GDBRemoteCommunicationServer::RegisterPacketHandler( +    StringExtractorGDBRemote::ServerPacketType packet_type, +    PacketHandler handler) { +  m_packet_handlers[packet_type] = std::move(handler); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::GetPacketAndSendResponse( +    Timeout<std::micro> timeout, Status &error, bool &interrupt, bool &quit) { +  StringExtractorGDBRemote packet; + +  PacketResult packet_result = WaitForPacketNoLock(packet, timeout, false); +  if (packet_result == PacketResult::Success) { +    const StringExtractorGDBRemote::ServerPacketType packet_type = +        packet.GetServerPacketType(); +    switch (packet_type) { +    case StringExtractorGDBRemote::eServerPacketType_nack: +    case StringExtractorGDBRemote::eServerPacketType_ack: +      break; + +    case StringExtractorGDBRemote::eServerPacketType_invalid: +      error.SetErrorString("invalid packet"); +      quit = true; +      break; + +    case StringExtractorGDBRemote::eServerPacketType_unimplemented: +      packet_result = SendUnimplementedResponse(packet.GetStringRef().c_str()); +      break; + +    default: +      auto handler_it = m_packet_handlers.find(packet_type); +      if (handler_it == m_packet_handlers.end()) +        packet_result = +            SendUnimplementedResponse(packet.GetStringRef().c_str()); +      else +        packet_result = handler_it->second(packet, error, interrupt, quit); +      break; +    } +  } else { +    if (!IsConnected()) { +      error.SetErrorString("lost connection"); +      quit = true; +    } else { +      error.SetErrorString("timeout"); +    } +  } + +  // Check if anything occurred that would force us to want to exit. +  if (m_exit_now) +    quit = true; + +  return packet_result; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendUnimplementedResponse(const char *) { +  // TODO: Log the packet we aren't handling... +  return SendPacketNoLock(""); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendErrorResponse(uint8_t err) { +  char packet[16]; +  int packet_len = ::snprintf(packet, sizeof(packet), "E%2.2x", err); +  assert(packet_len < (int)sizeof(packet)); +  return SendPacketNoLock(llvm::StringRef(packet, packet_len)); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendErrorResponse(const Status &error) { +  if (m_send_error_strings) { +    lldb_private::StreamString packet; +    packet.Printf("E%2.2x;", static_cast<uint8_t>(error.GetError())); +    packet.PutStringAsRawHex8(error.AsCString()); +    return SendPacketNoLock(packet.GetString()); +  } else +    return SendErrorResponse(error.GetError()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendErrorResponse(llvm::Error error) { +  std::unique_ptr<llvm::ErrorInfoBase> EIB; +  std::unique_ptr<PacketUnimplementedError> PUE; +  llvm::handleAllErrors( +      std::move(error), +      [&](std::unique_ptr<PacketUnimplementedError> E) { PUE = std::move(E); }, +      [&](std::unique_ptr<llvm::ErrorInfoBase> E) { EIB = std::move(E); }); + +  if (EIB) +    return SendErrorResponse(Status(llvm::Error(std::move(EIB)))); +  if (PUE) +    return SendUnimplementedResponse(PUE->message().c_str()); +  return SendErrorResponse(Status("Unknown Error")); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::Handle_QErrorStringEnable( +    StringExtractorGDBRemote &packet) { +  m_send_error_strings = true; +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendIllFormedResponse( +    const StringExtractorGDBRemote &failed_packet, const char *message) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS)); +  if (log) +    log->Printf("GDBRemoteCommunicationServer::%s: ILLFORMED: '%s' (%s)", +                __FUNCTION__, failed_packet.GetStringRef().c_str(), +                message ? message : ""); +  return SendErrorResponse(0x03); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendOKResponse() { +  return SendPacketNoLock("OK"); +} + +bool GDBRemoteCommunicationServer::HandshakeWithClient() { +  return GetAck() == PacketResult::Success; +} + +char PacketUnimplementedError::ID; diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h new file mode 100644 index 000000000000..86f0abf45e06 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -0,0 +1,95 @@ +//===-- GDBRemoteCommunicationServer.h --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationServer_h_ +#define liblldb_GDBRemoteCommunicationServer_h_ + +#include <functional> +#include <map> + +#include "GDBRemoteCommunication.h" +#include "lldb/lldb-private-forward.h" + +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +class StringExtractorGDBRemote; + +namespace lldb_private { +namespace process_gdb_remote { + +class ProcessGDBRemote; + +class GDBRemoteCommunicationServer : public GDBRemoteCommunication { +public: +  using PortMap = std::map<uint16_t, lldb::pid_t>; +  using PacketHandler = +      std::function<PacketResult(StringExtractorGDBRemote &packet, +                                 Status &error, bool &interrupt, bool &quit)>; + +  GDBRemoteCommunicationServer(const char *comm_name, +                               const char *listener_name); + +  ~GDBRemoteCommunicationServer() override; + +  void +  RegisterPacketHandler(StringExtractorGDBRemote::ServerPacketType packet_type, +                        PacketHandler handler); + +  PacketResult GetPacketAndSendResponse(Timeout<std::micro> timeout, +                                        Status &error, bool &interrupt, +                                        bool &quit); + +  // After connecting, do a little handshake with the client to make sure +  // we are at least communicating +  bool HandshakeWithClient(); + +protected: +  std::map<StringExtractorGDBRemote::ServerPacketType, PacketHandler> +      m_packet_handlers; +  bool m_exit_now; // use in asynchronous handling to indicate process should +                   // exit. + +  bool m_send_error_strings = false; // If the client enables this then +                                     // we will send error strings as well. + +  PacketResult Handle_QErrorStringEnable(StringExtractorGDBRemote &packet); + +  PacketResult SendErrorResponse(const Status &error); + +  PacketResult SendErrorResponse(llvm::Error error); + +  PacketResult SendUnimplementedResponse(const char *packet); + +  PacketResult SendErrorResponse(uint8_t error); + +  PacketResult SendIllFormedResponse(const StringExtractorGDBRemote &packet, +                                     const char *error_message); + +  PacketResult SendOKResponse(); + +private: +  DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationServer); +}; + +class PacketUnimplementedError +    : public llvm::ErrorInfo<PacketUnimplementedError, llvm::StringError> { +public: +  static char ID; +  using llvm::ErrorInfo<PacketUnimplementedError, +                        llvm::StringError>::ErrorInfo; // inherit constructors +  PacketUnimplementedError(const llvm::Twine &S) +      : ErrorInfo(S, llvm::errc::not_supported) {} + +  PacketUnimplementedError() : ErrorInfo(llvm::errc::not_supported) {} +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_GDBRemoteCommunicationServer_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp new file mode 100644 index 000000000000..d095c7a057ad --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -0,0 +1,1285 @@ +//===-- GDBRemoteCommunicationServerCommon.cpp ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteCommunicationServerCommon.h" + +#include <errno.h> + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +#include <chrono> +#include <cstring> + +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileAction.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/SafeMachO.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Platform.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/JSON.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamGDBRemote.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StructuredData.h" +#include "llvm/ADT/Triple.h" + +#include "ProcessGDBRemoteLog.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +#ifdef __ANDROID__ +#include "lldb/Host/android/HostInfoAndroid.h" +#endif + +#include "llvm/ADT/StringSwitch.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +#ifdef __ANDROID__ +const static uint32_t g_default_packet_timeout_sec = 20; // seconds +#else +const static uint32_t g_default_packet_timeout_sec = 0; // not specified +#endif + +// GDBRemoteCommunicationServerCommon constructor +GDBRemoteCommunicationServerCommon::GDBRemoteCommunicationServerCommon( +    const char *comm_name, const char *listener_name) +    : GDBRemoteCommunicationServer(comm_name, listener_name), +      m_process_launch_info(), m_process_launch_error(), m_proc_infos(), +      m_proc_infos_index(0), m_thread_suffix_supported(false), +      m_list_threads_in_stop_reply(false) { +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_A, +                                &GDBRemoteCommunicationServerCommon::Handle_A); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QEnvironment, +      &GDBRemoteCommunicationServerCommon::Handle_QEnvironment); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QEnvironmentHexEncoded, +      &GDBRemoteCommunicationServerCommon::Handle_QEnvironmentHexEncoded); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qfProcessInfo, +      &GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qGroupName, +      &GDBRemoteCommunicationServerCommon::Handle_qGroupName); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qHostInfo, +      &GDBRemoteCommunicationServerCommon::Handle_qHostInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QLaunchArch, +      &GDBRemoteCommunicationServerCommon::Handle_QLaunchArch); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess, +      &GDBRemoteCommunicationServerCommon::Handle_qLaunchSuccess); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QListThreadsInStopReply, +      &GDBRemoteCommunicationServerCommon::Handle_QListThreadsInStopReply); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qEcho, +      &GDBRemoteCommunicationServerCommon::Handle_qEcho); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qModuleInfo, +      &GDBRemoteCommunicationServerCommon::Handle_qModuleInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_jModulesInfo, +      &GDBRemoteCommunicationServerCommon::Handle_jModulesInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qPlatform_chmod, +      &GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qPlatform_mkdir, +      &GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qPlatform_shell, +      &GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qProcessInfoPID, +      &GDBRemoteCommunicationServerCommon::Handle_qProcessInfoPID); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QSetDetachOnError, +      &GDBRemoteCommunicationServerCommon::Handle_QSetDetachOnError); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QSetSTDERR, +      &GDBRemoteCommunicationServerCommon::Handle_QSetSTDERR); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QSetSTDIN, +      &GDBRemoteCommunicationServerCommon::Handle_QSetSTDIN); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QSetSTDOUT, +      &GDBRemoteCommunicationServerCommon::Handle_QSetSTDOUT); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qSpeedTest, +      &GDBRemoteCommunicationServerCommon::Handle_qSpeedTest); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qsProcessInfo, +      &GDBRemoteCommunicationServerCommon::Handle_qsProcessInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode, +      &GDBRemoteCommunicationServerCommon::Handle_QStartNoAckMode); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qSupported, +      &GDBRemoteCommunicationServerCommon::Handle_qSupported); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QThreadSuffixSupported, +      &GDBRemoteCommunicationServerCommon::Handle_QThreadSuffixSupported); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qUserName, +      &GDBRemoteCommunicationServerCommon::Handle_qUserName); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vFile_close, +      &GDBRemoteCommunicationServerCommon::Handle_vFile_Close); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vFile_exists, +      &GDBRemoteCommunicationServerCommon::Handle_vFile_Exists); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vFile_md5, +      &GDBRemoteCommunicationServerCommon::Handle_vFile_MD5); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vFile_mode, +      &GDBRemoteCommunicationServerCommon::Handle_vFile_Mode); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vFile_open, +      &GDBRemoteCommunicationServerCommon::Handle_vFile_Open); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vFile_pread, +      &GDBRemoteCommunicationServerCommon::Handle_vFile_pRead); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vFile_pwrite, +      &GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vFile_size, +      &GDBRemoteCommunicationServerCommon::Handle_vFile_Size); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vFile_stat, +      &GDBRemoteCommunicationServerCommon::Handle_vFile_Stat); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vFile_symlink, +      &GDBRemoteCommunicationServerCommon::Handle_vFile_symlink); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vFile_unlink, +      &GDBRemoteCommunicationServerCommon::Handle_vFile_unlink); +} + +// Destructor +GDBRemoteCommunicationServerCommon::~GDBRemoteCommunicationServerCommon() {} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qHostInfo( +    StringExtractorGDBRemote &packet) { +  StreamString response; + +  // $cputype:16777223;cpusubtype:3;ostype:Darwin;vendor:apple;endian:little;ptrsize:8;#00 + +  ArchSpec host_arch(HostInfo::GetArchitecture()); +  const llvm::Triple &host_triple = host_arch.GetTriple(); +  response.PutCString("triple:"); +  response.PutStringAsRawHex8(host_triple.getTriple()); +  response.Printf(";ptrsize:%u;", host_arch.GetAddressByteSize()); + +  const char *distribution_id = host_arch.GetDistributionId().AsCString(); +  if (distribution_id) { +    response.PutCString("distribution_id:"); +    response.PutStringAsRawHex8(distribution_id); +    response.PutCString(";"); +  } + +#if defined(__APPLE__) +  // For parity with debugserver, we'll include the vendor key. +  response.PutCString("vendor:apple;"); + +  // Send out MachO info. +  uint32_t cpu = host_arch.GetMachOCPUType(); +  uint32_t sub = host_arch.GetMachOCPUSubType(); +  if (cpu != LLDB_INVALID_CPUTYPE) +    response.Printf("cputype:%u;", cpu); +  if (sub != LLDB_INVALID_CPUTYPE) +    response.Printf("cpusubtype:%u;", sub); + +  if (cpu == llvm::MachO::CPU_TYPE_ARM || cpu == llvm::MachO::CPU_TYPE_ARM64) { +// Indicate the OS type. +#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 +    response.PutCString("ostype:tvos;"); +#elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 +    response.PutCString("ostype:watchos;"); +#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 +    response.PutCString("ostype:bridgeos;"); +#else +    response.PutCString("ostype:ios;"); +#endif + +    // On arm, we use "synchronous" watchpoints which means the exception is +    // delivered before the instruction executes. +    response.PutCString("watchpoint_exceptions_received:before;"); +  } else { +    response.PutCString("ostype:macosx;"); +    response.Printf("watchpoint_exceptions_received:after;"); +  } + +#else +  if (host_arch.GetMachine() == llvm::Triple::aarch64 || +      host_arch.GetMachine() == llvm::Triple::aarch64_be || +      host_arch.GetMachine() == llvm::Triple::arm || +      host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS()) +    response.Printf("watchpoint_exceptions_received:before;"); +  else +    response.Printf("watchpoint_exceptions_received:after;"); +#endif + +  switch (endian::InlHostByteOrder()) { +  case eByteOrderBig: +    response.PutCString("endian:big;"); +    break; +  case eByteOrderLittle: +    response.PutCString("endian:little;"); +    break; +  case eByteOrderPDP: +    response.PutCString("endian:pdp;"); +    break; +  default: +    response.PutCString("endian:unknown;"); +    break; +  } + +  llvm::VersionTuple version = HostInfo::GetOSVersion(); +  if (!version.empty()) { +    response.Format("os_version:{0}", version.getAsString()); +    response.PutChar(';'); +  } + +  std::string s; +  if (HostInfo::GetOSBuildString(s)) { +    response.PutCString("os_build:"); +    response.PutStringAsRawHex8(s); +    response.PutChar(';'); +  } +  if (HostInfo::GetOSKernelDescription(s)) { +    response.PutCString("os_kernel:"); +    response.PutStringAsRawHex8(s); +    response.PutChar(';'); +  } + +#if defined(__APPLE__) + +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) +  // For iOS devices, we are connected through a USB Mux so we never pretend to +  // actually have a hostname as far as the remote lldb that is connecting to +  // this lldb-platform is concerned +  response.PutCString("hostname:"); +  response.PutStringAsRawHex8("127.0.0.1"); +  response.PutChar(';'); +#else  // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) +  if (HostInfo::GetHostname(s)) { +    response.PutCString("hostname:"); +    response.PutStringAsRawHex8(s); +    response.PutChar(';'); +  } +#endif // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + +#else  // #if defined(__APPLE__) +  if (HostInfo::GetHostname(s)) { +    response.PutCString("hostname:"); +    response.PutStringAsRawHex8(s); +    response.PutChar(';'); +  } +#endif // #if defined(__APPLE__) + +  if (g_default_packet_timeout_sec > 0) +    response.Printf("default_packet_timeout:%u;", g_default_packet_timeout_sec); + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qProcessInfoPID( +    StringExtractorGDBRemote &packet) { +  // Packet format: "qProcessInfoPID:%i" where %i is the pid +  packet.SetFilePos(::strlen("qProcessInfoPID:")); +  lldb::pid_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID); +  if (pid != LLDB_INVALID_PROCESS_ID) { +    ProcessInstanceInfo proc_info; +    if (Host::GetProcessInfo(pid, proc_info)) { +      StreamString response; +      CreateProcessInfoResponse(proc_info, response); +      return SendPacketNoLock(response.GetString()); +    } +  } +  return SendErrorResponse(1); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo( +    StringExtractorGDBRemote &packet) { +  m_proc_infos_index = 0; +  m_proc_infos.Clear(); + +  ProcessInstanceInfoMatch match_info; +  packet.SetFilePos(::strlen("qfProcessInfo")); +  if (packet.GetChar() == ':') { +    llvm::StringRef key; +    llvm::StringRef value; +    while (packet.GetNameColonValue(key, value)) { +      bool success = true; +      if (key.equals("name")) { +        StringExtractor extractor(value); +        std::string file; +        extractor.GetHexByteString(file); +        match_info.GetProcessInfo().GetExecutableFile().SetFile( +            file, FileSpec::Style::native); +      } else if (key.equals("name_match")) { +        NameMatch name_match = llvm::StringSwitch<NameMatch>(value) +                                   .Case("equals", NameMatch::Equals) +                                   .Case("starts_with", NameMatch::StartsWith) +                                   .Case("ends_with", NameMatch::EndsWith) +                                   .Case("contains", NameMatch::Contains) +                                   .Case("regex", NameMatch::RegularExpression) +                                   .Default(NameMatch::Ignore); +        match_info.SetNameMatchType(name_match); +        if (name_match == NameMatch::Ignore) +          return SendErrorResponse(2); +      } else if (key.equals("pid")) { +        lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; +        if (value.getAsInteger(0, pid)) +          return SendErrorResponse(2); +        match_info.GetProcessInfo().SetProcessID(pid); +      } else if (key.equals("parent_pid")) { +        lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; +        if (value.getAsInteger(0, pid)) +          return SendErrorResponse(2); +        match_info.GetProcessInfo().SetParentProcessID(pid); +      } else if (key.equals("uid")) { +        uint32_t uid = UINT32_MAX; +        if (value.getAsInteger(0, uid)) +          return SendErrorResponse(2); +        match_info.GetProcessInfo().SetUserID(uid); +      } else if (key.equals("gid")) { +        uint32_t gid = UINT32_MAX; +        if (value.getAsInteger(0, gid)) +          return SendErrorResponse(2); +        match_info.GetProcessInfo().SetGroupID(gid); +      } else if (key.equals("euid")) { +        uint32_t uid = UINT32_MAX; +        if (value.getAsInteger(0, uid)) +          return SendErrorResponse(2); +        match_info.GetProcessInfo().SetEffectiveUserID(uid); +      } else if (key.equals("egid")) { +        uint32_t gid = UINT32_MAX; +        if (value.getAsInteger(0, gid)) +          return SendErrorResponse(2); +        match_info.GetProcessInfo().SetEffectiveGroupID(gid); +      } else if (key.equals("all_users")) { +        match_info.SetMatchAllUsers( +            OptionArgParser::ToBoolean(value, false, &success)); +      } else if (key.equals("triple")) { +        match_info.GetProcessInfo().GetArchitecture() = +            HostInfo::GetAugmentedArchSpec(value); +      } else { +        success = false; +      } + +      if (!success) +        return SendErrorResponse(2); +    } +  } + +  if (Host::FindProcesses(match_info, m_proc_infos)) { +    // We found something, return the first item by calling the get subsequent +    // process info packet handler... +    return Handle_qsProcessInfo(packet); +  } +  return SendErrorResponse(3); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qsProcessInfo( +    StringExtractorGDBRemote &packet) { +  if (m_proc_infos_index < m_proc_infos.GetSize()) { +    StreamString response; +    CreateProcessInfoResponse( +        m_proc_infos.GetProcessInfoAtIndex(m_proc_infos_index), response); +    ++m_proc_infos_index; +    return SendPacketNoLock(response.GetString()); +  } +  return SendErrorResponse(4); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qUserName( +    StringExtractorGDBRemote &packet) { +#if !defined(LLDB_DISABLE_POSIX) +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); +  if (log) +    log->Printf("GDBRemoteCommunicationServerCommon::%s begin", __FUNCTION__); + +  // Packet format: "qUserName:%i" where %i is the uid +  packet.SetFilePos(::strlen("qUserName:")); +  uint32_t uid = packet.GetU32(UINT32_MAX); +  if (uid != UINT32_MAX) { +    if (llvm::Optional<llvm::StringRef> name = +            HostInfo::GetUserIDResolver().GetUserName(uid)) { +      StreamString response; +      response.PutStringAsRawHex8(*name); +      return SendPacketNoLock(response.GetString()); +    } +  } +  if (log) +    log->Printf("GDBRemoteCommunicationServerCommon::%s end", __FUNCTION__); +#endif +  return SendErrorResponse(5); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qGroupName( +    StringExtractorGDBRemote &packet) { +#if !defined(LLDB_DISABLE_POSIX) +  // Packet format: "qGroupName:%i" where %i is the gid +  packet.SetFilePos(::strlen("qGroupName:")); +  uint32_t gid = packet.GetU32(UINT32_MAX); +  if (gid != UINT32_MAX) { +    if (llvm::Optional<llvm::StringRef> name = +            HostInfo::GetUserIDResolver().GetGroupName(gid)) { +      StreamString response; +      response.PutStringAsRawHex8(*name); +      return SendPacketNoLock(response.GetString()); +    } +  } +#endif +  return SendErrorResponse(6); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qSpeedTest( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("qSpeedTest:")); + +  llvm::StringRef key; +  llvm::StringRef value; +  bool success = packet.GetNameColonValue(key, value); +  if (success && key.equals("response_size")) { +    uint32_t response_size = 0; +    if (!value.getAsInteger(0, response_size)) { +      if (response_size == 0) +        return SendOKResponse(); +      StreamString response; +      uint32_t bytes_left = response_size; +      response.PutCString("data:"); +      while (bytes_left > 0) { +        if (bytes_left >= 26) { +          response.PutCString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); +          bytes_left -= 26; +        } else { +          response.Printf("%*.*s;", bytes_left, bytes_left, +                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); +          bytes_left = 0; +        } +      } +      return SendPacketNoLock(response.GetString()); +    } +  } +  return SendErrorResponse(7); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Open( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("vFile:open:")); +  std::string path; +  packet.GetHexByteStringTerminatedBy(path, ','); +  if (!path.empty()) { +    if (packet.GetChar() == ',') { +      uint32_t flags = packet.GetHexMaxU32(false, 0); +      if (packet.GetChar() == ',') { +        mode_t mode = packet.GetHexMaxU32(false, 0600); +        FileSpec path_spec(path); +        FileSystem::Instance().Resolve(path_spec); +        File file; +        // Do not close fd. +        Status error = +            FileSystem::Instance().Open(file, path_spec, flags, mode, false); +        const int save_errno = error.GetError(); +        StreamString response; +        response.PutChar('F'); +        response.Printf("%i", file.GetDescriptor()); +        if (save_errno) +          response.Printf(",%i", save_errno); +        return SendPacketNoLock(response.GetString()); +      } +    } +  } +  return SendErrorResponse(18); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Close( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("vFile:close:")); +  int fd = packet.GetS32(-1); +  int err = -1; +  int save_errno = 0; +  if (fd >= 0) { +    File file(fd, true); +    Status error = file.Close(); +    err = 0; +    save_errno = error.GetError(); +  } else { +    save_errno = EINVAL; +  } +  StreamString response; +  response.PutChar('F'); +  response.Printf("%i", err); +  if (save_errno) +    response.Printf(",%i", save_errno); +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_pRead( +    StringExtractorGDBRemote &packet) { +  StreamGDBRemote response; +  packet.SetFilePos(::strlen("vFile:pread:")); +  int fd = packet.GetS32(-1); +  if (packet.GetChar() == ',') { +    size_t count = packet.GetU64(UINT64_MAX); +    if (packet.GetChar() == ',') { +      off_t offset = packet.GetU64(UINT32_MAX); +      if (count == UINT64_MAX) { +        response.Printf("F-1:%i", EINVAL); +        return SendPacketNoLock(response.GetString()); +      } + +      std::string buffer(count, 0); +      File file(fd, false); +      Status error = file.Read(static_cast<void *>(&buffer[0]), count, offset); +      const ssize_t bytes_read = error.Success() ? count : -1; +      const int save_errno = error.GetError(); +      response.PutChar('F'); +      response.Printf("%zi", bytes_read); +      if (save_errno) +        response.Printf(",%i", save_errno); +      else { +        response.PutChar(';'); +        response.PutEscapedBytes(&buffer[0], bytes_read); +      } +      return SendPacketNoLock(response.GetString()); +    } +  } +  return SendErrorResponse(21); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("vFile:pwrite:")); + +  StreamGDBRemote response; +  response.PutChar('F'); + +  int fd = packet.GetU32(UINT32_MAX); +  if (packet.GetChar() == ',') { +    off_t offset = packet.GetU64(UINT32_MAX); +    if (packet.GetChar() == ',') { +      std::string buffer; +      if (packet.GetEscapedBinaryData(buffer)) { +        File file(fd, false); +        size_t count = buffer.size(); +        Status error = +            file.Write(static_cast<const void *>(&buffer[0]), count, offset); +        const ssize_t bytes_written = error.Success() ? count : -1; +        const int save_errno = error.GetError(); +        response.Printf("%zi", bytes_written); +        if (save_errno) +          response.Printf(",%i", save_errno); +      } else { +        response.Printf("-1,%i", EINVAL); +      } +      return SendPacketNoLock(response.GetString()); +    } +  } +  return SendErrorResponse(27); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Size( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("vFile:size:")); +  std::string path; +  packet.GetHexByteString(path); +  if (!path.empty()) { +    uint64_t Size; +    if (llvm::sys::fs::file_size(path, Size)) +      return SendErrorResponse(5); +    StreamString response; +    response.PutChar('F'); +    response.PutHex64(Size); +    if (Size == UINT64_MAX) { +      response.PutChar(','); +      response.PutHex64(Size); // TODO: replace with Host::GetSyswideErrorCode() +    } +    return SendPacketNoLock(response.GetString()); +  } +  return SendErrorResponse(22); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Mode( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("vFile:mode:")); +  std::string path; +  packet.GetHexByteString(path); +  if (!path.empty()) { +    FileSpec file_spec(path); +    FileSystem::Instance().Resolve(file_spec); +    std::error_code ec; +    const uint32_t mode = FileSystem::Instance().GetPermissions(file_spec, ec); +    StreamString response; +    response.Printf("F%u", mode); +    if (mode == 0 || ec) +      response.Printf(",%i", (int)Status(ec).GetError()); +    return SendPacketNoLock(response.GetString()); +  } +  return SendErrorResponse(23); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Exists( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("vFile:exists:")); +  std::string path; +  packet.GetHexByteString(path); +  if (!path.empty()) { +    bool retcode = llvm::sys::fs::exists(path); +    StreamString response; +    response.PutChar('F'); +    response.PutChar(','); +    if (retcode) +      response.PutChar('1'); +    else +      response.PutChar('0'); +    return SendPacketNoLock(response.GetString()); +  } +  return SendErrorResponse(24); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_symlink( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("vFile:symlink:")); +  std::string dst, src; +  packet.GetHexByteStringTerminatedBy(dst, ','); +  packet.GetChar(); // Skip ',' char +  packet.GetHexByteString(src); + +  FileSpec src_spec(src); +  FileSystem::Instance().Resolve(src_spec); +  Status error = FileSystem::Instance().Symlink(src_spec, FileSpec(dst)); + +  StreamString response; +  response.Printf("F%u,%u", error.GetError(), error.GetError()); +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_unlink( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("vFile:unlink:")); +  std::string path; +  packet.GetHexByteString(path); +  Status error(llvm::sys::fs::remove(path)); +  StreamString response; +  response.Printf("F%u,%u", error.GetError(), error.GetError()); +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("qPlatform_shell:")); +  std::string path; +  std::string working_dir; +  packet.GetHexByteStringTerminatedBy(path, ','); +  if (!path.empty()) { +    if (packet.GetChar() == ',') { +      // FIXME: add timeout to qPlatform_shell packet +      // uint32_t timeout = packet.GetHexMaxU32(false, 32); +      if (packet.GetChar() == ',') +        packet.GetHexByteString(working_dir); +      int status, signo; +      std::string output; +      FileSpec working_spec(working_dir); +      FileSystem::Instance().Resolve(working_spec); +      Status err = +          Host::RunShellCommand(path.c_str(), working_spec, &status, &signo, +                                &output, std::chrono::seconds(10)); +      StreamGDBRemote response; +      if (err.Fail()) { +        response.PutCString("F,"); +        response.PutHex32(UINT32_MAX); +      } else { +        response.PutCString("F,"); +        response.PutHex32(status); +        response.PutChar(','); +        response.PutHex32(signo); +        response.PutChar(','); +        response.PutEscapedBytes(output.c_str(), output.size()); +      } +      return SendPacketNoLock(response.GetString()); +    } +  } +  return SendErrorResponse(24); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Stat( +    StringExtractorGDBRemote &packet) { +  return SendUnimplementedResponse( +      "GDBRemoteCommunicationServerCommon::Handle_vFile_Stat() unimplemented"); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_MD5( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("vFile:MD5:")); +  std::string path; +  packet.GetHexByteString(path); +  if (!path.empty()) { +    StreamGDBRemote response; +    auto Result = llvm::sys::fs::md5_contents(path); +    if (!Result) { +      response.PutCString("F,"); +      response.PutCString("x"); +    } else { +      response.PutCString("F,"); +      response.PutHex64(Result->low()); +      response.PutHex64(Result->high()); +    } +    return SendPacketNoLock(response.GetString()); +  } +  return SendErrorResponse(25); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("qPlatform_mkdir:")); +  mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); +  if (packet.GetChar() == ',') { +    std::string path; +    packet.GetHexByteString(path); +    Status error(llvm::sys::fs::create_directory(path, mode)); + +    StreamGDBRemote response; +    response.Printf("F%u", error.GetError()); + +    return SendPacketNoLock(response.GetString()); +  } +  return SendErrorResponse(20); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("qPlatform_chmod:")); + +  auto perms = +      static_cast<llvm::sys::fs::perms>(packet.GetHexMaxU32(false, UINT32_MAX)); +  if (packet.GetChar() == ',') { +    std::string path; +    packet.GetHexByteString(path); +    Status error(llvm::sys::fs::setPermissions(path, perms)); + +    StreamGDBRemote response; +    response.Printf("F%u", error.GetError()); + +    return SendPacketNoLock(response.GetString()); +  } +  return SendErrorResponse(19); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qSupported( +    StringExtractorGDBRemote &packet) { +  StreamGDBRemote response; + +  // Features common to lldb-platform and llgs. +  uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet +                                         // size--debugger can always use less +  response.Printf("PacketSize=%x", max_packet_size); + +  response.PutCString(";QStartNoAckMode+"); +  response.PutCString(";QThreadSuffixSupported+"); +  response.PutCString(";QListThreadsInStopReply+"); +  response.PutCString(";qEcho+"); +#if defined(__linux__) || defined(__NetBSD__) +  response.PutCString(";QPassSignals+"); +  response.PutCString(";qXfer:auxv:read+"); +#endif + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QThreadSuffixSupported( +    StringExtractorGDBRemote &packet) { +  m_thread_suffix_supported = true; +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QListThreadsInStopReply( +    StringExtractorGDBRemote &packet) { +  m_list_threads_in_stop_reply = true; +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetDetachOnError( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("QSetDetachOnError:")); +  if (packet.GetU32(0)) +    m_process_launch_info.GetFlags().Set(eLaunchFlagDetachOnError); +  else +    m_process_launch_info.GetFlags().Clear(eLaunchFlagDetachOnError); +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QStartNoAckMode( +    StringExtractorGDBRemote &packet) { +  // Send response first before changing m_send_acks to we ack this packet +  PacketResult packet_result = SendOKResponse(); +  m_send_acks = false; +  return packet_result; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetSTDIN( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("QSetSTDIN:")); +  FileAction file_action; +  std::string path; +  packet.GetHexByteString(path); +  const bool read = true; +  const bool write = false; +  if (file_action.Open(STDIN_FILENO, FileSpec(path), read, write)) { +    m_process_launch_info.AppendFileAction(file_action); +    return SendOKResponse(); +  } +  return SendErrorResponse(15); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetSTDOUT( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("QSetSTDOUT:")); +  FileAction file_action; +  std::string path; +  packet.GetHexByteString(path); +  const bool read = false; +  const bool write = true; +  if (file_action.Open(STDOUT_FILENO, FileSpec(path), read, write)) { +    m_process_launch_info.AppendFileAction(file_action); +    return SendOKResponse(); +  } +  return SendErrorResponse(16); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetSTDERR( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("QSetSTDERR:")); +  FileAction file_action; +  std::string path; +  packet.GetHexByteString(path); +  const bool read = false; +  const bool write = true; +  if (file_action.Open(STDERR_FILENO, FileSpec(path), read, write)) { +    m_process_launch_info.AppendFileAction(file_action); +    return SendOKResponse(); +  } +  return SendErrorResponse(17); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qLaunchSuccess( +    StringExtractorGDBRemote &packet) { +  if (m_process_launch_error.Success()) +    return SendOKResponse(); +  StreamString response; +  response.PutChar('E'); +  response.PutCString(m_process_launch_error.AsCString("<unknown error>")); +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QEnvironment( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("QEnvironment:")); +  const uint32_t bytes_left = packet.GetBytesLeft(); +  if (bytes_left > 0) { +    m_process_launch_info.GetEnvironment().insert(packet.Peek()); +    return SendOKResponse(); +  } +  return SendErrorResponse(12); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QEnvironmentHexEncoded( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("QEnvironmentHexEncoded:")); +  const uint32_t bytes_left = packet.GetBytesLeft(); +  if (bytes_left > 0) { +    std::string str; +    packet.GetHexByteString(str); +    m_process_launch_info.GetEnvironment().insert(str); +    return SendOKResponse(); +  } +  return SendErrorResponse(12); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QLaunchArch( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("QLaunchArch:")); +  const uint32_t bytes_left = packet.GetBytesLeft(); +  if (bytes_left > 0) { +    const char *arch_triple = packet.Peek(); +    m_process_launch_info.SetArchitecture( +        HostInfo::GetAugmentedArchSpec(arch_triple)); +    return SendOKResponse(); +  } +  return SendErrorResponse(13); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_A(StringExtractorGDBRemote &packet) { +  // The 'A' packet is the most over designed packet ever here with redundant +  // argument indexes, redundant argument lengths and needed hex encoded +  // argument string values. Really all that is needed is a comma separated hex +  // encoded argument value list, but we will stay true to the documented +  // version of the 'A' packet here... + +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); +  int actual_arg_index = 0; + +  packet.SetFilePos(1); // Skip the 'A' +  bool success = true; +  while (success && packet.GetBytesLeft() > 0) { +    // Decode the decimal argument string length. This length is the number of +    // hex nibbles in the argument string value. +    const uint32_t arg_len = packet.GetU32(UINT32_MAX); +    if (arg_len == UINT32_MAX) +      success = false; +    else { +      // Make sure the argument hex string length is followed by a comma +      if (packet.GetChar() != ',') +        success = false; +      else { +        // Decode the argument index. We ignore this really because who would +        // really send down the arguments in a random order??? +        const uint32_t arg_idx = packet.GetU32(UINT32_MAX); +        if (arg_idx == UINT32_MAX) +          success = false; +        else { +          // Make sure the argument index is followed by a comma +          if (packet.GetChar() != ',') +            success = false; +          else { +            // Decode the argument string value from hex bytes back into a UTF8 +            // string and make sure the length matches the one supplied in the +            // packet +            std::string arg; +            if (packet.GetHexByteStringFixedLength(arg, arg_len) != +                (arg_len / 2)) +              success = false; +            else { +              // If there are any bytes left +              if (packet.GetBytesLeft()) { +                if (packet.GetChar() != ',') +                  success = false; +              } + +              if (success) { +                if (arg_idx == 0) +                  m_process_launch_info.GetExecutableFile().SetFile( +                      arg, FileSpec::Style::native); +                m_process_launch_info.GetArguments().AppendArgument(arg); +                if (log) +                  log->Printf("LLGSPacketHandler::%s added arg %d: \"%s\"", +                              __FUNCTION__, actual_arg_index, arg.c_str()); +                ++actual_arg_index; +              } +            } +          } +        } +      } +    } +  } + +  if (success) { +    m_process_launch_error = LaunchProcess(); +    if (m_process_launch_error.Success()) +      return SendOKResponse(); +    LLDB_LOG(log, "failed to launch exe: {0}", m_process_launch_error); +  } +  return SendErrorResponse(8); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qEcho( +    StringExtractorGDBRemote &packet) { +  // Just echo back the exact same packet for qEcho... +  return SendPacketNoLock(packet.GetStringRef()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qModuleInfo( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("qModuleInfo:")); + +  std::string module_path; +  packet.GetHexByteStringTerminatedBy(module_path, ';'); +  if (module_path.empty()) +    return SendErrorResponse(1); + +  if (packet.GetChar() != ';') +    return SendErrorResponse(2); + +  std::string triple; +  packet.GetHexByteString(triple); + +  ModuleSpec matched_module_spec = GetModuleInfo(module_path, triple); +  if (!matched_module_spec.GetFileSpec()) +    return SendErrorResponse(3); + +  const auto file_offset = matched_module_spec.GetObjectOffset(); +  const auto file_size = matched_module_spec.GetObjectSize(); +  const auto uuid_str = matched_module_spec.GetUUID().GetAsString(""); + +  StreamGDBRemote response; + +  if (uuid_str.empty()) { +    auto Result = llvm::sys::fs::md5_contents( +        matched_module_spec.GetFileSpec().GetPath()); +    if (!Result) +      return SendErrorResponse(5); +    response.PutCString("md5:"); +    response.PutStringAsRawHex8(Result->digest()); +  } else { +    response.PutCString("uuid:"); +    response.PutStringAsRawHex8(uuid_str); +  } +  response.PutChar(';'); + +  const auto &module_arch = matched_module_spec.GetArchitecture(); +  response.PutCString("triple:"); +  response.PutStringAsRawHex8(module_arch.GetTriple().getTriple()); +  response.PutChar(';'); + +  response.PutCString("file_path:"); +  response.PutStringAsRawHex8(matched_module_spec.GetFileSpec().GetCString()); +  response.PutChar(';'); +  response.PutCString("file_offset:"); +  response.PutHex64(file_offset); +  response.PutChar(';'); +  response.PutCString("file_size:"); +  response.PutHex64(file_size); +  response.PutChar(';'); + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_jModulesInfo( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("jModulesInfo:")); + +  StructuredData::ObjectSP object_sp = StructuredData::ParseJSON(packet.Peek()); +  if (!object_sp) +    return SendErrorResponse(1); + +  StructuredData::Array *packet_array = object_sp->GetAsArray(); +  if (!packet_array) +    return SendErrorResponse(2); + +  JSONArray::SP response_array_sp = std::make_shared<JSONArray>(); +  for (size_t i = 0; i < packet_array->GetSize(); ++i) { +    StructuredData::Dictionary *query = +        packet_array->GetItemAtIndex(i)->GetAsDictionary(); +    if (!query) +      continue; +    llvm::StringRef file, triple; +    if (!query->GetValueForKeyAsString("file", file) || +        !query->GetValueForKeyAsString("triple", triple)) +      continue; + +    ModuleSpec matched_module_spec = GetModuleInfo(file, triple); +    if (!matched_module_spec.GetFileSpec()) +      continue; + +    const auto file_offset = matched_module_spec.GetObjectOffset(); +    const auto file_size = matched_module_spec.GetObjectSize(); +    const auto uuid_str = matched_module_spec.GetUUID().GetAsString(""); + +    if (uuid_str.empty()) +      continue; + +    JSONObject::SP response = std::make_shared<JSONObject>(); +    response_array_sp->AppendObject(response); +    response->SetObject("uuid", std::make_shared<JSONString>(uuid_str)); +    response->SetObject( +        "triple", +        std::make_shared<JSONString>( +            matched_module_spec.GetArchitecture().GetTriple().getTriple())); +    response->SetObject("file_path", +                        std::make_shared<JSONString>( +                            matched_module_spec.GetFileSpec().GetPath())); +    response->SetObject("file_offset", +                        std::make_shared<JSONNumber>(file_offset)); +    response->SetObject("file_size", std::make_shared<JSONNumber>(file_size)); +  } + +  StreamString response; +  response_array_sp->Write(response); +  StreamGDBRemote escaped_response; +  escaped_response.PutEscapedBytes(response.GetString().data(), +                                   response.GetSize()); +  return SendPacketNoLock(escaped_response.GetString()); +} + +void GDBRemoteCommunicationServerCommon::CreateProcessInfoResponse( +    const ProcessInstanceInfo &proc_info, StreamString &response) { +  response.Printf( +      "pid:%" PRIu64 ";ppid:%" PRIu64 ";uid:%i;gid:%i;euid:%i;egid:%i;", +      proc_info.GetProcessID(), proc_info.GetParentProcessID(), +      proc_info.GetUserID(), proc_info.GetGroupID(), +      proc_info.GetEffectiveUserID(), proc_info.GetEffectiveGroupID()); +  response.PutCString("name:"); +  response.PutStringAsRawHex8(proc_info.GetExecutableFile().GetCString()); +  response.PutChar(';'); +  const ArchSpec &proc_arch = proc_info.GetArchitecture(); +  if (proc_arch.IsValid()) { +    const llvm::Triple &proc_triple = proc_arch.GetTriple(); +    response.PutCString("triple:"); +    response.PutStringAsRawHex8(proc_triple.getTriple()); +    response.PutChar(';'); +  } +} + +void GDBRemoteCommunicationServerCommon:: +    CreateProcessInfoResponse_DebugServerStyle( +        const ProcessInstanceInfo &proc_info, StreamString &response) { +  response.Printf("pid:%" PRIx64 ";parent-pid:%" PRIx64 +                  ";real-uid:%x;real-gid:%x;effective-uid:%x;effective-gid:%x;", +                  proc_info.GetProcessID(), proc_info.GetParentProcessID(), +                  proc_info.GetUserID(), proc_info.GetGroupID(), +                  proc_info.GetEffectiveUserID(), +                  proc_info.GetEffectiveGroupID()); + +  const ArchSpec &proc_arch = proc_info.GetArchitecture(); +  if (proc_arch.IsValid()) { +    const llvm::Triple &proc_triple = proc_arch.GetTriple(); +#if defined(__APPLE__) +    // We'll send cputype/cpusubtype. +    const uint32_t cpu_type = proc_arch.GetMachOCPUType(); +    if (cpu_type != 0) +      response.Printf("cputype:%" PRIx32 ";", cpu_type); + +    const uint32_t cpu_subtype = proc_arch.GetMachOCPUSubType(); +    if (cpu_subtype != 0) +      response.Printf("cpusubtype:%" PRIx32 ";", cpu_subtype); + +    const std::string vendor = proc_triple.getVendorName(); +    if (!vendor.empty()) +      response.Printf("vendor:%s;", vendor.c_str()); +#else +    // We'll send the triple. +    response.PutCString("triple:"); +    response.PutStringAsRawHex8(proc_triple.getTriple()); +    response.PutChar(';'); +#endif +    std::string ostype = proc_triple.getOSName(); +    // Adjust so ostype reports ios for Apple/ARM and Apple/ARM64. +    if (proc_triple.getVendor() == llvm::Triple::Apple) { +      switch (proc_triple.getArch()) { +      case llvm::Triple::arm: +      case llvm::Triple::thumb: +      case llvm::Triple::aarch64: +        ostype = "ios"; +        break; +      default: +        // No change. +        break; +      } +    } +    response.Printf("ostype:%s;", ostype.c_str()); + +    switch (proc_arch.GetByteOrder()) { +    case lldb::eByteOrderLittle: +      response.PutCString("endian:little;"); +      break; +    case lldb::eByteOrderBig: +      response.PutCString("endian:big;"); +      break; +    case lldb::eByteOrderPDP: +      response.PutCString("endian:pdp;"); +      break; +    default: +      // Nothing. +      break; +    } +    // In case of MIPS64, pointer size is depend on ELF ABI For N32 the pointer +    // size is 4 and for N64 it is 8 +    std::string abi = proc_arch.GetTargetABI(); +    if (!abi.empty()) +      response.Printf("elf_abi:%s;", abi.c_str()); +    response.Printf("ptrsize:%d;", proc_arch.GetAddressByteSize()); +  } +} + +FileSpec GDBRemoteCommunicationServerCommon::FindModuleFile( +    const std::string &module_path, const ArchSpec &arch) { +#ifdef __ANDROID__ +  return HostInfoAndroid::ResolveLibraryPath(module_path, arch); +#else +  FileSpec file_spec(module_path); +  FileSystem::Instance().Resolve(file_spec); +  return file_spec; +#endif +} + +ModuleSpec +GDBRemoteCommunicationServerCommon::GetModuleInfo(llvm::StringRef module_path, +                                                  llvm::StringRef triple) { +  ArchSpec arch(triple); + +  FileSpec req_module_path_spec(module_path); +  FileSystem::Instance().Resolve(req_module_path_spec); + +  const FileSpec module_path_spec = +      FindModuleFile(req_module_path_spec.GetPath(), arch); +  const ModuleSpec module_spec(module_path_spec, arch); + +  ModuleSpecList module_specs; +  if (!ObjectFile::GetModuleSpecifications(module_path_spec, 0, 0, +                                           module_specs)) +    return ModuleSpec(); + +  ModuleSpec matched_module_spec; +  if (!module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) +    return ModuleSpec(); + +  return matched_module_spec; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h new file mode 100644 index 000000000000..525546312470 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h @@ -0,0 +1,155 @@ +//===-- GDBRemoteCommunicationServerCommon.h --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationServerCommon_h_ +#define liblldb_GDBRemoteCommunicationServerCommon_h_ + +#include <string> + +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/lldb-private-forward.h" + +#include "GDBRemoteCommunicationServer.h" +#include "GDBRemoteCommunicationServerCommon.h" + +class StringExtractorGDBRemote; + +namespace lldb_private { +namespace process_gdb_remote { + +class ProcessGDBRemote; + +class GDBRemoteCommunicationServerCommon : public GDBRemoteCommunicationServer { +public: +  GDBRemoteCommunicationServerCommon(const char *comm_name, +                                     const char *listener_name); + +  ~GDBRemoteCommunicationServerCommon() override; + +protected: +  ProcessLaunchInfo m_process_launch_info; +  Status m_process_launch_error; +  ProcessInstanceInfoList m_proc_infos; +  uint32_t m_proc_infos_index; +  bool m_thread_suffix_supported; +  bool m_list_threads_in_stop_reply; + +  PacketResult Handle_A(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qHostInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qProcessInfoPID(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qfProcessInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qsProcessInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qUserName(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qGroupName(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qSpeedTest(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vFile_Open(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vFile_Close(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vFile_pRead(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vFile_pWrite(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vFile_Size(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vFile_Mode(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vFile_Exists(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vFile_symlink(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vFile_unlink(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vFile_Stat(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vFile_MD5(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qEcho(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qModuleInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_jModulesInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qPlatform_shell(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qPlatform_mkdir(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qPlatform_chmod(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qSupported(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QThreadSuffixSupported(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QListThreadsInStopReply(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QSetDetachOnError(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QStartNoAckMode(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QSetSTDIN(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QSetSTDOUT(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QSetSTDERR(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qLaunchSuccess(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QEnvironment(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QEnvironmentHexEncoded(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QLaunchArch(StringExtractorGDBRemote &packet); + +  static void CreateProcessInfoResponse(const ProcessInstanceInfo &proc_info, +                                        StreamString &response); + +  static void CreateProcessInfoResponse_DebugServerStyle( +      const ProcessInstanceInfo &proc_info, StreamString &response); + +  template <typename T> +  void RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::ServerPacketType packet_type, +      PacketResult (T::*handler)(StringExtractorGDBRemote &packet)) { +    RegisterPacketHandler(packet_type, +                          [this, handler](StringExtractorGDBRemote packet, +                                          Status &error, bool &interrupt, +                                          bool &quit) { +                            return (static_cast<T *>(this)->*handler)(packet); +                          }); +  } + +  /// Launch a process with the current launch settings. +  /// +  /// This method supports running an lldb-gdbserver or similar +  /// server in a situation where the startup code has been provided +  /// with all the information for a child process to be launched. +  /// +  /// \return +  ///     An Status object indicating the success or failure of the +  ///     launch. +  virtual Status LaunchProcess() = 0; + +  virtual FileSpec FindModuleFile(const std::string &module_path, +                                  const ArchSpec &arch); + +private: +  ModuleSpec GetModuleInfo(llvm::StringRef module_path, llvm::StringRef triple); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_GDBRemoteCommunicationServerCommon_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp new file mode 100644 index 000000000000..196607665bba --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -0,0 +1,3285 @@ +//===-- GDBRemoteCommunicationServerLLGS.cpp --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <errno.h> + +#include "lldb/Host/Config.h" + +#include "GDBRemoteCommunicationServerLLGS.h" +#include "lldb/Utility/StreamGDBRemote.h" + +#include <chrono> +#include <cstring> +#include <thread> + +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Debug.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileAction.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/PosixApi.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/JSON.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/UriParser.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/ScopedPrinter.h" + +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace llvm; + +// GDBRemote Errors + +namespace { +enum GDBRemoteServerError { +  // Set to the first unused error number in literal form below +  eErrorFirst = 29, +  eErrorNoProcess = eErrorFirst, +  eErrorResume, +  eErrorExitStatus +}; +} + +// GDBRemoteCommunicationServerLLGS constructor +GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS( +    MainLoop &mainloop, const NativeProcessProtocol::Factory &process_factory) +    : GDBRemoteCommunicationServerCommon("gdb-remote.server", +                                         "gdb-remote.server.rx_packet"), +      m_mainloop(mainloop), m_process_factory(process_factory), +      m_stdio_communication("process.stdio") { +  RegisterPacketHandlers(); +} + +void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() { +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_C, +                                &GDBRemoteCommunicationServerLLGS::Handle_C); +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_c, +                                &GDBRemoteCommunicationServerLLGS::Handle_c); +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_D, +                                &GDBRemoteCommunicationServerLLGS::Handle_D); +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_H, +                                &GDBRemoteCommunicationServerLLGS::Handle_H); +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_I, +                                &GDBRemoteCommunicationServerLLGS::Handle_I); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_interrupt, +      &GDBRemoteCommunicationServerLLGS::Handle_interrupt); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_m, +      &GDBRemoteCommunicationServerLLGS::Handle_memory_read); +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_M, +                                &GDBRemoteCommunicationServerLLGS::Handle_M); +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_p, +                                &GDBRemoteCommunicationServerLLGS::Handle_p); +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_P, +                                &GDBRemoteCommunicationServerLLGS::Handle_P); +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC, +                                &GDBRemoteCommunicationServerLLGS::Handle_qC); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qfThreadInfo, +      &GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qFileLoadAddress, +      &GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, +      &GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfo, +      &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfoSupported, +      &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qProcessInfo, +      &GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qRegisterInfo, +      &GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QRestoreRegisterState, +      &GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QSaveRegisterState, +      &GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR, +      &GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, +      &GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qsThreadInfo, +      &GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo, +      &GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_jThreadsInfo, +      &GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo, +      &GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qXfer, +      &GDBRemoteCommunicationServerLLGS::Handle_qXfer); +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_s, +                                &GDBRemoteCommunicationServerLLGS::Handle_s); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_stop_reason, +      &GDBRemoteCommunicationServerLLGS::Handle_stop_reason); // ? +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vAttach, +      &GDBRemoteCommunicationServerLLGS::Handle_vAttach); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vCont, +      &GDBRemoteCommunicationServerLLGS::Handle_vCont); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_vCont_actions, +      &GDBRemoteCommunicationServerLLGS::Handle_vCont_actions); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_x, +      &GDBRemoteCommunicationServerLLGS::Handle_memory_read); +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_Z, +                                &GDBRemoteCommunicationServerLLGS::Handle_Z); +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z, +                                &GDBRemoteCommunicationServerLLGS::Handle_z); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QPassSignals, +      &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals); + +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_jTraceStart, +      &GDBRemoteCommunicationServerLLGS::Handle_jTraceStart); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_jTraceBufferRead, +      &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_jTraceMetaRead, +      &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_jTraceStop, +      &GDBRemoteCommunicationServerLLGS::Handle_jTraceStop); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_jTraceConfigRead, +      &GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead); + +  RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g, +                                &GDBRemoteCommunicationServerLLGS::Handle_g); + +  RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k, +                        [this](StringExtractorGDBRemote packet, Status &error, +                               bool &interrupt, bool &quit) { +                          quit = true; +                          return this->Handle_k(packet); +                        }); +} + +void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(const ProcessLaunchInfo &info) { +  m_process_launch_info = info; +} + +Status GDBRemoteCommunicationServerLLGS::LaunchProcess() { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  if (!m_process_launch_info.GetArguments().GetArgumentCount()) +    return Status("%s: no process command line specified to launch", +                  __FUNCTION__); + +  const bool should_forward_stdio = +      m_process_launch_info.GetFileActionForFD(STDIN_FILENO) == nullptr || +      m_process_launch_info.GetFileActionForFD(STDOUT_FILENO) == nullptr || +      m_process_launch_info.GetFileActionForFD(STDERR_FILENO) == nullptr; +  m_process_launch_info.SetLaunchInSeparateProcessGroup(true); +  m_process_launch_info.GetFlags().Set(eLaunchFlagDebug); + +  if (should_forward_stdio) { +    if (llvm::Error Err = m_process_launch_info.SetUpPtyRedirection()) +      return Status(std::move(Err)); +  } + +  { +    std::lock_guard<std::recursive_mutex> guard(m_debugged_process_mutex); +    assert(!m_debugged_process_up && "lldb-server creating debugged " +                                     "process but one already exists"); +    auto process_or = +        m_process_factory.Launch(m_process_launch_info, *this, m_mainloop); +    if (!process_or) +      return Status(process_or.takeError()); +    m_debugged_process_up = std::move(*process_or); +  } + +  // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol as +  // needed. llgs local-process debugging may specify PTY paths, which will +  // make these file actions non-null process launch -i/e/o will also make +  // these file actions non-null nullptr means that the traffic is expected to +  // flow over gdb-remote protocol +  if (should_forward_stdio) { +    // nullptr means it's not redirected to file or pty (in case of LLGS local) +    // at least one of stdio will be transferred pty<->gdb-remote we need to +    // give the pty master handle to this object to read and/or write +    LLDB_LOG(log, +             "pid = {0}: setting up stdout/stderr redirection via $O " +             "gdb-remote commands", +             m_debugged_process_up->GetID()); + +    // Setup stdout/stderr mapping from inferior to $O +    auto terminal_fd = m_debugged_process_up->GetTerminalFileDescriptor(); +    if (terminal_fd >= 0) { +      if (log) +        log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s setting " +                    "inferior STDIO fd to %d", +                    __FUNCTION__, terminal_fd); +      Status status = SetSTDIOFileDescriptor(terminal_fd); +      if (status.Fail()) +        return status; +    } else { +      if (log) +        log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring " +                    "inferior STDIO since terminal fd reported as %d", +                    __FUNCTION__, terminal_fd); +    } +  } else { +    LLDB_LOG(log, +             "pid = {0} skipping stdout/stderr redirection via $O: inferior " +             "will communicate over client-provided file descriptors", +             m_debugged_process_up->GetID()); +  } + +  printf("Launched '%s' as process %" PRIu64 "...\n", +         m_process_launch_info.GetArguments().GetArgumentAtIndex(0), +         m_debugged_process_up->GetID()); + +  return Status(); +} + +Status GDBRemoteCommunicationServerLLGS::AttachToProcess(lldb::pid_t pid) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); +  if (log) +    log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64, +                __FUNCTION__, pid); + +  // Before we try to attach, make sure we aren't already monitoring something +  // else. +  if (m_debugged_process_up && +      m_debugged_process_up->GetID() != LLDB_INVALID_PROCESS_ID) +    return Status("cannot attach to process %" PRIu64 +                  " when another process with pid %" PRIu64 +                  " is being debugged.", +                  pid, m_debugged_process_up->GetID()); + +  // Try to attach. +  auto process_or = m_process_factory.Attach(pid, *this, m_mainloop); +  if (!process_or) { +    Status status(process_or.takeError()); +    llvm::errs() << llvm::formatv("failed to attach to process {0}: {1}", pid, +                                  status); +    return status; +  } +  m_debugged_process_up = std::move(*process_or); + +  // Setup stdout/stderr mapping from inferior. +  auto terminal_fd = m_debugged_process_up->GetTerminalFileDescriptor(); +  if (terminal_fd >= 0) { +    if (log) +      log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s setting " +                  "inferior STDIO fd to %d", +                  __FUNCTION__, terminal_fd); +    Status status = SetSTDIOFileDescriptor(terminal_fd); +    if (status.Fail()) +      return status; +  } else { +    if (log) +      log->Printf("ProcessGDBRemoteCommunicationServerLLGS::%s ignoring " +                  "inferior STDIO since terminal fd reported as %d", +                  __FUNCTION__, terminal_fd); +  } + +  printf("Attached to process %" PRIu64 "...\n", pid); +  return Status(); +} + +void GDBRemoteCommunicationServerLLGS::InitializeDelegate( +    NativeProcessProtocol *process) { +  assert(process && "process cannot be NULL"); +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); +  if (log) { +    log->Printf("GDBRemoteCommunicationServerLLGS::%s called with " +                "NativeProcessProtocol pid %" PRIu64 ", current state: %s", +                __FUNCTION__, process->GetID(), +                StateAsCString(process->GetState())); +  } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendWResponse( +    NativeProcessProtocol *process) { +  assert(process && "process cannot be NULL"); +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  // send W notification +  auto wait_status = process->GetExitStatus(); +  if (!wait_status) { +    LLDB_LOG(log, "pid = {0}, failed to retrieve process exit status", +             process->GetID()); + +    StreamGDBRemote response; +    response.PutChar('E'); +    response.PutHex8(GDBRemoteServerError::eErrorExitStatus); +    return SendPacketNoLock(response.GetString()); +  } + +  LLDB_LOG(log, "pid = {0}, returning exit type {1}", process->GetID(), +           *wait_status); + +  StreamGDBRemote response; +  response.Format("{0:g}", *wait_status); +  return SendPacketNoLock(response.GetString()); +} + +static void AppendHexValue(StreamString &response, const uint8_t *buf, +                           uint32_t buf_size, bool swap) { +  int64_t i; +  if (swap) { +    for (i = buf_size - 1; i >= 0; i--) +      response.PutHex8(buf[i]); +  } else { +    for (i = 0; i < buf_size; i++) +      response.PutHex8(buf[i]); +  } +} + +static void WriteRegisterValueInHexFixedWidth( +    StreamString &response, NativeRegisterContext ®_ctx, +    const RegisterInfo ®_info, const RegisterValue *reg_value_p, +    lldb::ByteOrder byte_order) { +  RegisterValue reg_value; +  if (!reg_value_p) { +    Status error = reg_ctx.ReadRegister(®_info, reg_value); +    if (error.Success()) +      reg_value_p = ®_value; +    // else log. +  } + +  if (reg_value_p) { +    AppendHexValue(response, (const uint8_t *)reg_value_p->GetBytes(), +                   reg_value_p->GetByteSize(), +                   byte_order == lldb::eByteOrderLittle); +  } else { +    // Zero-out any unreadable values. +    if (reg_info.byte_size > 0) { +      std::basic_string<uint8_t> zeros(reg_info.byte_size, '\0'); +      AppendHexValue(response, zeros.data(), zeros.size(), false); +    } +  } +} + +static JSONObject::SP GetRegistersAsJSON(NativeThreadProtocol &thread) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + +  NativeRegisterContext& reg_ctx = thread.GetRegisterContext(); + +  JSONObject::SP register_object_sp = std::make_shared<JSONObject>(); + +#ifdef LLDB_JTHREADSINFO_FULL_REGISTER_SET +  // Expedite all registers in the first register set (i.e. should be GPRs) +  // that are not contained in other registers. +  const RegisterSet *reg_set_p = reg_ctx_sp->GetRegisterSet(0); +  if (!reg_set_p) +    return nullptr; +  for (const uint32_t *reg_num_p = reg_set_p->registers; +       *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) { +    uint32_t reg_num = *reg_num_p; +#else +  // Expedite only a couple of registers until we figure out why sending +  // registers is expensive. +  static const uint32_t k_expedited_registers[] = { +      LLDB_REGNUM_GENERIC_PC, LLDB_REGNUM_GENERIC_SP, LLDB_REGNUM_GENERIC_FP, +      LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM}; + +  for (const uint32_t *generic_reg_p = k_expedited_registers; +       *generic_reg_p != LLDB_INVALID_REGNUM; ++generic_reg_p) { +    uint32_t reg_num = reg_ctx.ConvertRegisterKindToRegisterNumber( +        eRegisterKindGeneric, *generic_reg_p); +    if (reg_num == LLDB_INVALID_REGNUM) +      continue; // Target does not support the given register. +#endif + +    const RegisterInfo *const reg_info_p = +        reg_ctx.GetRegisterInfoAtIndex(reg_num); +    if (reg_info_p == nullptr) { +      if (log) +        log->Printf( +            "%s failed to get register info for register index %" PRIu32, +            __FUNCTION__, reg_num); +      continue; +    } + +    if (reg_info_p->value_regs != nullptr) +      continue; // Only expedite registers that are not contained in other +                // registers. + +    RegisterValue reg_value; +    Status error = reg_ctx.ReadRegister(reg_info_p, reg_value); +    if (error.Fail()) { +      if (log) +        log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s", +                    __FUNCTION__, +                    reg_info_p->name ? reg_info_p->name : "<unnamed-register>", +                    reg_num, error.AsCString()); +      continue; +    } + +    StreamString stream; +    WriteRegisterValueInHexFixedWidth(stream, reg_ctx, *reg_info_p, +                                      ®_value, lldb::eByteOrderBig); + +    register_object_sp->SetObject( +        llvm::to_string(reg_num), +        std::make_shared<JSONString>(stream.GetString())); +  } + +  return register_object_sp; +} + +static const char *GetStopReasonString(StopReason stop_reason) { +  switch (stop_reason) { +  case eStopReasonTrace: +    return "trace"; +  case eStopReasonBreakpoint: +    return "breakpoint"; +  case eStopReasonWatchpoint: +    return "watchpoint"; +  case eStopReasonSignal: +    return "signal"; +  case eStopReasonException: +    return "exception"; +  case eStopReasonExec: +    return "exec"; +  case eStopReasonInstrumentation: +  case eStopReasonInvalid: +  case eStopReasonPlanComplete: +  case eStopReasonThreadExiting: +  case eStopReasonNone: +    break; // ignored +  } +  return nullptr; +} + +static JSONArray::SP GetJSONThreadsInfo(NativeProcessProtocol &process, +                                        bool abridged) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); + +  JSONArray::SP threads_array_sp = std::make_shared<JSONArray>(); + +  // Ensure we can get info on the given thread. +  uint32_t thread_idx = 0; +  for (NativeThreadProtocol *thread; +       (thread = process.GetThreadAtIndex(thread_idx)) != nullptr; +       ++thread_idx) { + +    lldb::tid_t tid = thread->GetID(); + +    // Grab the reason this thread stopped. +    struct ThreadStopInfo tid_stop_info; +    std::string description; +    if (!thread->GetStopReason(tid_stop_info, description)) +      return nullptr; + +    const int signum = tid_stop_info.details.signal.signo; +    if (log) { +      log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 +                  " tid %" PRIu64 +                  " got signal signo = %d, reason = %d, exc_type = %" PRIu64, +                  __FUNCTION__, process.GetID(), tid, signum, +                  tid_stop_info.reason, tid_stop_info.details.exception.type); +    } + +    JSONObject::SP thread_obj_sp = std::make_shared<JSONObject>(); +    threads_array_sp->AppendObject(thread_obj_sp); + +    if (!abridged) { +      if (JSONObject::SP registers_sp = GetRegistersAsJSON(*thread)) +        thread_obj_sp->SetObject("registers", registers_sp); +    } + +    thread_obj_sp->SetObject("tid", std::make_shared<JSONNumber>(tid)); +    if (signum != 0) +      thread_obj_sp->SetObject("signal", std::make_shared<JSONNumber>(signum)); + +    const std::string thread_name = thread->GetName(); +    if (!thread_name.empty()) +      thread_obj_sp->SetObject("name", +                               std::make_shared<JSONString>(thread_name)); + +    if (const char *stop_reason_str = GetStopReasonString(tid_stop_info.reason)) +      thread_obj_sp->SetObject("reason", +                               std::make_shared<JSONString>(stop_reason_str)); + +    if (!description.empty()) +      thread_obj_sp->SetObject("description", +                               std::make_shared<JSONString>(description)); + +    if ((tid_stop_info.reason == eStopReasonException) && +        tid_stop_info.details.exception.type) { +      thread_obj_sp->SetObject( +          "metype", +          std::make_shared<JSONNumber>(tid_stop_info.details.exception.type)); + +      JSONArray::SP medata_array_sp = std::make_shared<JSONArray>(); +      for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; +           ++i) { +        medata_array_sp->AppendObject(std::make_shared<JSONNumber>( +            tid_stop_info.details.exception.data[i])); +      } +      thread_obj_sp->SetObject("medata", medata_array_sp); +    } + +    // TODO: Expedite interesting regions of inferior memory +  } + +  return threads_array_sp; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread( +    lldb::tid_t tid) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); + +  // Ensure we have a debugged process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) +    return SendErrorResponse(50); + +  LLDB_LOG(log, "preparing packet for pid {0} tid {1}", +           m_debugged_process_up->GetID(), tid); + +  // Ensure we can get info on the given thread. +  NativeThreadProtocol *thread = m_debugged_process_up->GetThreadByID(tid); +  if (!thread) +    return SendErrorResponse(51); + +  // Grab the reason this thread stopped. +  struct ThreadStopInfo tid_stop_info; +  std::string description; +  if (!thread->GetStopReason(tid_stop_info, description)) +    return SendErrorResponse(52); + +  // FIXME implement register handling for exec'd inferiors. +  // if (tid_stop_info.reason == eStopReasonExec) { +  //     const bool force = true; +  //     InitializeRegisters(force); +  // } + +  StreamString response; +  // Output the T packet with the thread +  response.PutChar('T'); +  int signum = tid_stop_info.details.signal.signo; +  LLDB_LOG( +      log, +      "pid {0}, tid {1}, got signal signo = {2}, reason = {3}, exc_type = {4}", +      m_debugged_process_up->GetID(), tid, signum, int(tid_stop_info.reason), +      tid_stop_info.details.exception.type); + +  // Print the signal number. +  response.PutHex8(signum & 0xff); + +  // Include the tid. +  response.Printf("thread:%" PRIx64 ";", tid); + +  // Include the thread name if there is one. +  const std::string thread_name = thread->GetName(); +  if (!thread_name.empty()) { +    size_t thread_name_len = thread_name.length(); + +    if (::strcspn(thread_name.c_str(), "$#+-;:") == thread_name_len) { +      response.PutCString("name:"); +      response.PutCString(thread_name); +    } else { +      // The thread name contains special chars, send as hex bytes. +      response.PutCString("hexname:"); +      response.PutStringAsRawHex8(thread_name); +    } +    response.PutChar(';'); +  } + +  // If a 'QListThreadsInStopReply' was sent to enable this feature, we will +  // send all thread IDs back in the "threads" key whose value is a list of hex +  // thread IDs separated by commas: +  //  "threads:10a,10b,10c;" +  // This will save the debugger from having to send a pair of qfThreadInfo and +  // qsThreadInfo packets, but it also might take a lot of room in the stop +  // reply packet, so it must be enabled only on systems where there are no +  // limits on packet lengths. +  if (m_list_threads_in_stop_reply) { +    response.PutCString("threads:"); + +    uint32_t thread_index = 0; +    NativeThreadProtocol *listed_thread; +    for (listed_thread = m_debugged_process_up->GetThreadAtIndex(thread_index); +         listed_thread; ++thread_index, +        listed_thread = m_debugged_process_up->GetThreadAtIndex(thread_index)) { +      if (thread_index > 0) +        response.PutChar(','); +      response.Printf("%" PRIx64, listed_thread->GetID()); +    } +    response.PutChar(';'); + +    // Include JSON info that describes the stop reason for any threads that +    // actually have stop reasons. We use the new "jstopinfo" key whose values +    // is hex ascii JSON that contains the thread IDs thread stop info only for +    // threads that have stop reasons. Only send this if we have more than one +    // thread otherwise this packet has all the info it needs. +    if (thread_index > 0) { +      const bool threads_with_valid_stop_info_only = true; +      JSONArray::SP threads_info_sp = GetJSONThreadsInfo( +          *m_debugged_process_up, threads_with_valid_stop_info_only); +      if (threads_info_sp) { +        response.PutCString("jstopinfo:"); +        StreamString unescaped_response; +        threads_info_sp->Write(unescaped_response); +        response.PutStringAsRawHex8(unescaped_response.GetData()); +        response.PutChar(';'); +      } else +        LLDB_LOG(log, "failed to prepare a jstopinfo field for pid {0}", +                 m_debugged_process_up->GetID()); +    } + +    uint32_t i = 0; +    response.PutCString("thread-pcs"); +    char delimiter = ':'; +    for (NativeThreadProtocol *thread; +         (thread = m_debugged_process_up->GetThreadAtIndex(i)) != nullptr; +         ++i) { +      NativeRegisterContext& reg_ctx = thread->GetRegisterContext(); + +      uint32_t reg_to_read = reg_ctx.ConvertRegisterKindToRegisterNumber( +          eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); +      const RegisterInfo *const reg_info_p = +          reg_ctx.GetRegisterInfoAtIndex(reg_to_read); + +      RegisterValue reg_value; +      Status error = reg_ctx.ReadRegister(reg_info_p, reg_value); +      if (error.Fail()) { +        if (log) +          log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s", +                      __FUNCTION__, +                      reg_info_p->name ? reg_info_p->name +                                       : "<unnamed-register>", +                      reg_to_read, error.AsCString()); +        continue; +      } + +      response.PutChar(delimiter); +      delimiter = ','; +      WriteRegisterValueInHexFixedWidth(response, reg_ctx, *reg_info_p, +                                        ®_value, endian::InlHostByteOrder()); +    } + +    response.PutChar(';'); +  } + +  // +  // Expedite registers. +  // + +  // Grab the register context. +  NativeRegisterContext& reg_ctx = thread->GetRegisterContext(); +  // Expedite all registers in the first register set (i.e. should be GPRs) +  // that are not contained in other registers. +  const RegisterSet *reg_set_p; +  if (reg_ctx.GetRegisterSetCount() > 0 && +      ((reg_set_p = reg_ctx.GetRegisterSet(0)) != nullptr)) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s expediting registers " +                  "from set '%s' (registers set count: %zu)", +                  __FUNCTION__, +                  reg_set_p->name ? reg_set_p->name : "<unnamed-set>", +                  reg_set_p->num_registers); + +    for (const uint32_t *reg_num_p = reg_set_p->registers; +         *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) { +      const RegisterInfo *const reg_info_p = +          reg_ctx.GetRegisterInfoAtIndex(*reg_num_p); +      if (reg_info_p == nullptr) { +        if (log) +          log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to get " +                      "register info for register set '%s', register index " +                      "%" PRIu32, +                      __FUNCTION__, +                      reg_set_p->name ? reg_set_p->name : "<unnamed-set>", +                      *reg_num_p); +      } else if (reg_info_p->value_regs == nullptr) { +        // Only expediate registers that are not contained in other registers. +        RegisterValue reg_value; +        Status error = reg_ctx.ReadRegister(reg_info_p, reg_value); +        if (error.Success()) { +          response.Printf("%.02x:", *reg_num_p); +          WriteRegisterValueInHexFixedWidth(response, reg_ctx, *reg_info_p, +                                            ®_value, lldb::eByteOrderBig); +          response.PutChar(';'); +        } else { +          if (log) +            log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to read " +                        "register '%s' index %" PRIu32 ": %s", +                        __FUNCTION__, +                        reg_info_p->name ? reg_info_p->name +                                         : "<unnamed-register>", +                        *reg_num_p, error.AsCString()); +        } +      } +    } +  } + +  const char *reason_str = GetStopReasonString(tid_stop_info.reason); +  if (reason_str != nullptr) { +    response.Printf("reason:%s;", reason_str); +  } + +  if (!description.empty()) { +    // Description may contains special chars, send as hex bytes. +    response.PutCString("description:"); +    response.PutStringAsRawHex8(description); +    response.PutChar(';'); +  } else if ((tid_stop_info.reason == eStopReasonException) && +             tid_stop_info.details.exception.type) { +    response.PutCString("metype:"); +    response.PutHex64(tid_stop_info.details.exception.type); +    response.PutCString(";mecount:"); +    response.PutHex32(tid_stop_info.details.exception.data_count); +    response.PutChar(';'); + +    for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) { +      response.PutCString("medata:"); +      response.PutHex64(tid_stop_info.details.exception.data[i]); +      response.PutChar(';'); +    } +  } + +  return SendPacketNoLock(response.GetString()); +} + +void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited( +    NativeProcessProtocol *process) { +  assert(process && "process cannot be NULL"); + +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); +  if (log) +    log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + +  PacketResult result = SendStopReasonForState(StateType::eStateExited); +  if (result != PacketResult::Success) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send stop " +                  "notification for PID %" PRIu64 ", state: eStateExited", +                  __FUNCTION__, process->GetID()); +  } + +  // Close the pipe to the inferior terminal i/o if we launched it and set one +  // up. +  MaybeCloseInferiorTerminalConnection(); + +  // We are ready to exit the debug monitor. +  m_exit_now = true; +  m_mainloop.RequestTermination(); +} + +void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped( +    NativeProcessProtocol *process) { +  assert(process && "process cannot be NULL"); + +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); +  if (log) +    log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + +  // Send the stop reason unless this is the stop after the launch or attach. +  switch (m_inferior_prev_state) { +  case eStateLaunching: +  case eStateAttaching: +    // Don't send anything per debugserver behavior. +    break; +  default: +    // In all other cases, send the stop reason. +    PacketResult result = SendStopReasonForState(StateType::eStateStopped); +    if (result != PacketResult::Success) { +      if (log) +        log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to send stop " +                    "notification for PID %" PRIu64 ", state: eStateExited", +                    __FUNCTION__, process->GetID()); +    } +    break; +  } +} + +void GDBRemoteCommunicationServerLLGS::ProcessStateChanged( +    NativeProcessProtocol *process, lldb::StateType state) { +  assert(process && "process cannot be NULL"); +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); +  if (log) { +    log->Printf("GDBRemoteCommunicationServerLLGS::%s called with " +                "NativeProcessProtocol pid %" PRIu64 ", state: %s", +                __FUNCTION__, process->GetID(), StateAsCString(state)); +  } + +  switch (state) { +  case StateType::eStateRunning: +    StartSTDIOForwarding(); +    break; + +  case StateType::eStateStopped: +    // Make sure we get all of the pending stdout/stderr from the inferior and +    // send it to the lldb host before we send the state change notification +    SendProcessOutput(); +    // Then stop the forwarding, so that any late output (see llvm.org/pr25652) +    // does not interfere with our protocol. +    StopSTDIOForwarding(); +    HandleInferiorState_Stopped(process); +    break; + +  case StateType::eStateExited: +    // Same as above +    SendProcessOutput(); +    StopSTDIOForwarding(); +    HandleInferiorState_Exited(process); +    break; + +  default: +    if (log) { +      log->Printf("GDBRemoteCommunicationServerLLGS::%s didn't handle state " +                  "change for pid %" PRIu64 ", new state: %s", +                  __FUNCTION__, process->GetID(), StateAsCString(state)); +    } +    break; +  } + +  // Remember the previous state reported to us. +  m_inferior_prev_state = state; +} + +void GDBRemoteCommunicationServerLLGS::DidExec(NativeProcessProtocol *process) { +  ClearProcessSpecificData(); +} + +void GDBRemoteCommunicationServerLLGS::DataAvailableCallback() { +  Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_COMM)); + +  if (!m_handshake_completed) { +    if (!HandshakeWithClient()) { +      if (log) +        log->Printf("GDBRemoteCommunicationServerLLGS::%s handshake with " +                    "client failed, exiting", +                    __FUNCTION__); +      m_mainloop.RequestTermination(); +      return; +    } +    m_handshake_completed = true; +  } + +  bool interrupt = false; +  bool done = false; +  Status error; +  while (true) { +    const PacketResult result = GetPacketAndSendResponse( +        std::chrono::microseconds(0), error, interrupt, done); +    if (result == PacketResult::ErrorReplyTimeout) +      break; // No more packets in the queue + +    if ((result != PacketResult::Success)) { +      if (log) +        log->Printf("GDBRemoteCommunicationServerLLGS::%s processing a packet " +                    "failed: %s", +                    __FUNCTION__, error.AsCString()); +      m_mainloop.RequestTermination(); +      break; +    } +  } +} + +Status GDBRemoteCommunicationServerLLGS::InitializeConnection( +    std::unique_ptr<Connection> &&connection) { +  IOObjectSP read_object_sp = connection->GetReadObject(); +  GDBRemoteCommunicationServer::SetConnection(connection.release()); + +  Status error; +  m_network_handle_up = m_mainloop.RegisterReadObject( +      read_object_sp, [this](MainLoopBase &) { DataAvailableCallback(); }, +      error); +  return error; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendONotification(const char *buffer, +                                                    uint32_t len) { +  if ((buffer == nullptr) || (len == 0)) { +    // Nothing to send. +    return PacketResult::Success; +  } + +  StreamString response; +  response.PutChar('O'); +  response.PutBytesAsRawHex8(buffer, len); + +  return SendPacketNoLock(response.GetString()); +} + +Status GDBRemoteCommunicationServerLLGS::SetSTDIOFileDescriptor(int fd) { +  Status error; + +  // Set up the reading/handling of process I/O +  std::unique_ptr<ConnectionFileDescriptor> conn_up( +      new ConnectionFileDescriptor(fd, true)); +  if (!conn_up) { +    error.SetErrorString("failed to create ConnectionFileDescriptor"); +    return error; +  } + +  m_stdio_communication.SetCloseOnEOF(false); +  m_stdio_communication.SetConnection(conn_up.release()); +  if (!m_stdio_communication.IsConnected()) { +    error.SetErrorString( +        "failed to set connection for inferior I/O communication"); +    return error; +  } + +  return Status(); +} + +void GDBRemoteCommunicationServerLLGS::StartSTDIOForwarding() { +  // Don't forward if not connected (e.g. when attaching). +  if (!m_stdio_communication.IsConnected()) +    return; + +  Status error; +  lldbassert(!m_stdio_handle_up); +  m_stdio_handle_up = m_mainloop.RegisterReadObject( +      m_stdio_communication.GetConnection()->GetReadObject(), +      [this](MainLoopBase &) { SendProcessOutput(); }, error); + +  if (!m_stdio_handle_up) { +    // Not much we can do about the failure. Log it and continue without +    // forwarding. +    if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s Failed to set up stdio " +                  "forwarding: %s", +                  __FUNCTION__, error.AsCString()); +  } +} + +void GDBRemoteCommunicationServerLLGS::StopSTDIOForwarding() { +  m_stdio_handle_up.reset(); +} + +void GDBRemoteCommunicationServerLLGS::SendProcessOutput() { +  char buffer[1024]; +  ConnectionStatus status; +  Status error; +  while (true) { +    size_t bytes_read = m_stdio_communication.Read( +        buffer, sizeof buffer, std::chrono::microseconds(0), status, &error); +    switch (status) { +    case eConnectionStatusSuccess: +      SendONotification(buffer, bytes_read); +      break; +    case eConnectionStatusLostConnection: +    case eConnectionStatusEndOfFile: +    case eConnectionStatusError: +    case eConnectionStatusNoConnection: +      if (Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)) +        log->Printf("GDBRemoteCommunicationServerLLGS::%s Stopping stdio " +                    "forwarding as communication returned status %d (error: " +                    "%s)", +                    __FUNCTION__, status, error.AsCString()); +      m_stdio_handle_up.reset(); +      return; + +    case eConnectionStatusInterrupted: +    case eConnectionStatusTimedOut: +      return; +    } +  } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jTraceStart( +    StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) +    return SendErrorResponse(68); + +  if (!packet.ConsumeFront("jTraceStart:")) +    return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet "); + +  TraceOptions options; +  uint64_t type = std::numeric_limits<uint64_t>::max(); +  uint64_t buffersize = std::numeric_limits<uint64_t>::max(); +  lldb::tid_t tid = LLDB_INVALID_THREAD_ID; +  uint64_t metabuffersize = std::numeric_limits<uint64_t>::max(); + +  auto json_object = StructuredData::ParseJSON(packet.Peek()); + +  if (!json_object || +      json_object->GetType() != lldb::eStructuredDataTypeDictionary) +    return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet "); + +  auto json_dict = json_object->GetAsDictionary(); + +  json_dict->GetValueForKeyAsInteger("metabuffersize", metabuffersize); +  options.setMetaDataBufferSize(metabuffersize); + +  json_dict->GetValueForKeyAsInteger("buffersize", buffersize); +  options.setTraceBufferSize(buffersize); + +  json_dict->GetValueForKeyAsInteger("type", type); +  options.setType(static_cast<lldb::TraceType>(type)); + +  json_dict->GetValueForKeyAsInteger("threadid", tid); +  options.setThreadID(tid); + +  StructuredData::ObjectSP custom_params_sp = +      json_dict->GetValueForKey("params"); +  if (custom_params_sp && +      custom_params_sp->GetType() != lldb::eStructuredDataTypeDictionary) +    return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet "); + +  options.setTraceParams( +      static_pointer_cast<StructuredData::Dictionary>(custom_params_sp)); + +  if (buffersize == std::numeric_limits<uint64_t>::max() || +      type != lldb::TraceType::eTraceTypeProcessorTrace) { +    LLDB_LOG(log, "Ill formed packet buffersize = {0} type = {1}", buffersize, +             type); +    return SendIllFormedResponse(packet, "JTrace:start: Ill formed packet "); +  } + +  Status error; +  lldb::user_id_t uid = LLDB_INVALID_UID; +  uid = m_debugged_process_up->StartTrace(options, error); +  LLDB_LOG(log, "uid is {0} , error is {1}", uid, error.GetError()); +  if (error.Fail()) +    return SendErrorResponse(error); + +  StreamGDBRemote response; +  response.Printf("%" PRIx64, uid); +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jTraceStop( +    StringExtractorGDBRemote &packet) { +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) +    return SendErrorResponse(68); + +  if (!packet.ConsumeFront("jTraceStop:")) +    return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet "); + +  lldb::user_id_t uid = LLDB_INVALID_UID; +  lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + +  auto json_object = StructuredData::ParseJSON(packet.Peek()); + +  if (!json_object || +      json_object->GetType() != lldb::eStructuredDataTypeDictionary) +    return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet "); + +  auto json_dict = json_object->GetAsDictionary(); + +  if (!json_dict->GetValueForKeyAsInteger("traceid", uid)) +    return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet "); + +  json_dict->GetValueForKeyAsInteger("threadid", tid); + +  Status error = m_debugged_process_up->StopTrace(uid, tid); + +  if (error.Fail()) +    return SendErrorResponse(error); + +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead( +    StringExtractorGDBRemote &packet) { + +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) +    return SendErrorResponse(68); + +  if (!packet.ConsumeFront("jTraceConfigRead:")) +    return SendIllFormedResponse(packet, +                                 "jTraceConfigRead: Ill formed packet "); + +  lldb::user_id_t uid = LLDB_INVALID_UID; +  lldb::tid_t threadid = LLDB_INVALID_THREAD_ID; + +  auto json_object = StructuredData::ParseJSON(packet.Peek()); + +  if (!json_object || +      json_object->GetType() != lldb::eStructuredDataTypeDictionary) +    return SendIllFormedResponse(packet, +                                 "jTraceConfigRead: Ill formed packet "); + +  auto json_dict = json_object->GetAsDictionary(); + +  if (!json_dict->GetValueForKeyAsInteger("traceid", uid)) +    return SendIllFormedResponse(packet, +                                 "jTraceConfigRead: Ill formed packet "); + +  json_dict->GetValueForKeyAsInteger("threadid", threadid); + +  TraceOptions options; +  StreamGDBRemote response; + +  options.setThreadID(threadid); +  Status error = m_debugged_process_up->GetTraceConfig(uid, options); + +  if (error.Fail()) +    return SendErrorResponse(error); + +  StreamGDBRemote escaped_response; +  StructuredData::Dictionary json_packet; + +  json_packet.AddIntegerItem("type", options.getType()); +  json_packet.AddIntegerItem("buffersize", options.getTraceBufferSize()); +  json_packet.AddIntegerItem("metabuffersize", options.getMetaDataBufferSize()); + +  StructuredData::DictionarySP custom_params = options.getTraceParams(); +  if (custom_params) +    json_packet.AddItem("params", custom_params); + +  StreamString json_string; +  json_packet.Dump(json_string, false); +  escaped_response.PutEscapedBytes(json_string.GetData(), +                                   json_string.GetSize()); +  return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jTraceRead( +    StringExtractorGDBRemote &packet) { + +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) +    return SendErrorResponse(68); + +  enum PacketType { MetaData, BufferData }; +  PacketType tracetype = MetaData; + +  if (packet.ConsumeFront("jTraceBufferRead:")) +    tracetype = BufferData; +  else if (packet.ConsumeFront("jTraceMetaRead:")) +    tracetype = MetaData; +  else { +    return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); +  } + +  lldb::user_id_t uid = LLDB_INVALID_UID; + +  uint64_t byte_count = std::numeric_limits<uint64_t>::max(); +  lldb::tid_t tid = LLDB_INVALID_THREAD_ID; +  uint64_t offset = std::numeric_limits<uint64_t>::max(); + +  auto json_object = StructuredData::ParseJSON(packet.Peek()); + +  if (!json_object || +      json_object->GetType() != lldb::eStructuredDataTypeDictionary) +    return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); + +  auto json_dict = json_object->GetAsDictionary(); + +  if (!json_dict->GetValueForKeyAsInteger("traceid", uid) || +      !json_dict->GetValueForKeyAsInteger("offset", offset) || +      !json_dict->GetValueForKeyAsInteger("buffersize", byte_count)) +    return SendIllFormedResponse(packet, "jTrace: Ill formed packet "); + +  json_dict->GetValueForKeyAsInteger("threadid", tid); + +  // Allocate the response buffer. +  std::unique_ptr<uint8_t[]> buffer (new (std::nothrow) uint8_t[byte_count]); +  if (!buffer) +    return SendErrorResponse(0x78); + +  StreamGDBRemote response; +  Status error; +  llvm::MutableArrayRef<uint8_t> buf(buffer.get(), byte_count); + +  if (tracetype == BufferData) +    error = m_debugged_process_up->GetData(uid, tid, buf, offset); +  else if (tracetype == MetaData) +    error = m_debugged_process_up->GetMetaData(uid, tid, buf, offset); + +  if (error.Fail()) +    return SendErrorResponse(error); + +  for (auto i : buf) +    response.PutHex8(i); + +  StreamGDBRemote escaped_response; +  escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); +  return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo( +    StringExtractorGDBRemote &packet) { +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) +    return SendErrorResponse(68); + +  lldb::pid_t pid = m_debugged_process_up->GetID(); + +  if (pid == LLDB_INVALID_PROCESS_ID) +    return SendErrorResponse(1); + +  ProcessInstanceInfo proc_info; +  if (!Host::GetProcessInfo(pid, proc_info)) +    return SendErrorResponse(1); + +  StreamString response; +  CreateProcessInfoResponse_DebugServerStyle(proc_info, response); +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qC(StringExtractorGDBRemote &packet) { +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) +    return SendErrorResponse(68); + +  // Make sure we set the current thread so g and p packets return the data the +  // gdb will expect. +  lldb::tid_t tid = m_debugged_process_up->GetCurrentThreadID(); +  SetCurrentThreadID(tid); + +  NativeThreadProtocol *thread = m_debugged_process_up->GetCurrentThread(); +  if (!thread) +    return SendErrorResponse(69); + +  StreamString response; +  response.Printf("QC%" PRIx64, thread->GetID()); + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_k(StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  StopSTDIOForwarding(); + +  if (!m_debugged_process_up) { +    LLDB_LOG(log, "No debugged process found."); +    return PacketResult::Success; +  } + +  Status error = m_debugged_process_up->Kill(); +  if (error.Fail()) +    LLDB_LOG(log, "Failed to kill debugged process {0}: {1}", +             m_debugged_process_up->GetID(), error); + +  // No OK response for kill packet. +  // return SendOKResponse (); +  return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("QSetDisableASLR:")); +  if (packet.GetU32(0)) +    m_process_launch_info.GetFlags().Set(eLaunchFlagDisableASLR); +  else +    m_process_launch_info.GetFlags().Clear(eLaunchFlagDisableASLR); +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("QSetWorkingDir:")); +  std::string path; +  packet.GetHexByteString(path); +  m_process_launch_info.SetWorkingDirectory(FileSpec(path)); +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir( +    StringExtractorGDBRemote &packet) { +  FileSpec working_dir{m_process_launch_info.GetWorkingDirectory()}; +  if (working_dir) { +    StreamString response; +    response.PutStringAsRawHex8(working_dir.GetCString()); +    return SendPacketNoLock(response.GetString()); +  } + +  return SendErrorResponse(14); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_C(StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); +  if (log) +    log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + +  // Ensure we have a native process. +  if (!m_debugged_process_up) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process " +                  "shared pointer", +                  __FUNCTION__); +    return SendErrorResponse(0x36); +  } + +  // Pull out the signal number. +  packet.SetFilePos(::strlen("C")); +  if (packet.GetBytesLeft() < 1) { +    // Shouldn't be using a C without a signal. +    return SendIllFormedResponse(packet, "C packet specified without signal."); +  } +  const uint32_t signo = +      packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max()); +  if (signo == std::numeric_limits<uint32_t>::max()) +    return SendIllFormedResponse(packet, "failed to parse signal number"); + +  // Handle optional continue address. +  if (packet.GetBytesLeft() > 0) { +    // FIXME add continue at address support for $C{signo}[;{continue-address}]. +    if (*packet.Peek() == ';') +      return SendUnimplementedResponse(packet.GetStringRef().c_str()); +    else +      return SendIllFormedResponse( +          packet, "unexpected content after $C{signal-number}"); +  } + +  ResumeActionList resume_actions(StateType::eStateRunning, 0); +  Status error; + +  // We have two branches: what to do if a continue thread is specified (in +  // which case we target sending the signal to that thread), or when we don't +  // have a continue thread set (in which case we send a signal to the +  // process). + +  // TODO discuss with Greg Clayton, make sure this makes sense. + +  lldb::tid_t signal_tid = GetContinueThreadID(); +  if (signal_tid != LLDB_INVALID_THREAD_ID) { +    // The resume action for the continue thread (or all threads if a continue +    // thread is not set). +    ResumeAction action = {GetContinueThreadID(), StateType::eStateRunning, +                           static_cast<int>(signo)}; + +    // Add the action for the continue thread (or all threads when the continue +    // thread isn't present). +    resume_actions.Append(action); +  } else { +    // Send the signal to the process since we weren't targeting a specific +    // continue thread with the signal. +    error = m_debugged_process_up->Signal(signo); +    if (error.Fail()) { +      LLDB_LOG(log, "failed to send signal for process {0}: {1}", +               m_debugged_process_up->GetID(), error); + +      return SendErrorResponse(0x52); +    } +  } + +  // Resume the threads. +  error = m_debugged_process_up->Resume(resume_actions); +  if (error.Fail()) { +    LLDB_LOG(log, "failed to resume threads for process {0}: {1}", +             m_debugged_process_up->GetID(), error); + +    return SendErrorResponse(0x38); +  } + +  // Don't send an "OK" packet; response is the stopped/exited message. +  return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_c(StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); +  if (log) +    log->Printf("GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + +  packet.SetFilePos(packet.GetFilePos() + ::strlen("c")); + +  // For now just support all continue. +  const bool has_continue_address = (packet.GetBytesLeft() > 0); +  if (has_continue_address) { +    LLDB_LOG(log, "not implemented for c[address] variant [{0} remains]", +             packet.Peek()); +    return SendUnimplementedResponse(packet.GetStringRef().c_str()); +  } + +  // Ensure we have a native process. +  if (!m_debugged_process_up) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s no debugged process " +                  "shared pointer", +                  __FUNCTION__); +    return SendErrorResponse(0x36); +  } + +  // Build the ResumeActionList +  ResumeActionList actions(StateType::eStateRunning, 0); + +  Status error = m_debugged_process_up->Resume(actions); +  if (error.Fail()) { +    LLDB_LOG(log, "c failed for process {0}: {1}", +             m_debugged_process_up->GetID(), error); +    return SendErrorResponse(GDBRemoteServerError::eErrorResume); +  } + +  LLDB_LOG(log, "continued process {0}", m_debugged_process_up->GetID()); +  // No response required from continue. +  return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vCont_actions( +    StringExtractorGDBRemote &packet) { +  StreamString response; +  response.Printf("vCont;c;C;s;S"); + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vCont( +    StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); +  if (log) +    log->Printf("GDBRemoteCommunicationServerLLGS::%s handling vCont packet", +                __FUNCTION__); + +  packet.SetFilePos(::strlen("vCont")); + +  if (packet.GetBytesLeft() == 0) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s missing action from " +                  "vCont package", +                  __FUNCTION__); +    return SendIllFormedResponse(packet, "Missing action from vCont package"); +  } + +  // Check if this is all continue (no options or ";c"). +  if (::strcmp(packet.Peek(), ";c") == 0) { +    // Move past the ';', then do a simple 'c'. +    packet.SetFilePos(packet.GetFilePos() + 1); +    return Handle_c(packet); +  } else if (::strcmp(packet.Peek(), ";s") == 0) { +    // Move past the ';', then do a simple 's'. +    packet.SetFilePos(packet.GetFilePos() + 1); +    return Handle_s(packet); +  } + +  // Ensure we have a native process. +  if (!m_debugged_process_up) { +    LLDB_LOG(log, "no debugged process"); +    return SendErrorResponse(0x36); +  } + +  ResumeActionList thread_actions; + +  while (packet.GetBytesLeft() && *packet.Peek() == ';') { +    // Skip the semi-colon. +    packet.GetChar(); + +    // Build up the thread action. +    ResumeAction thread_action; +    thread_action.tid = LLDB_INVALID_THREAD_ID; +    thread_action.state = eStateInvalid; +    thread_action.signal = 0; + +    const char action = packet.GetChar(); +    switch (action) { +    case 'C': +      thread_action.signal = packet.GetHexMaxU32(false, 0); +      if (thread_action.signal == 0) +        return SendIllFormedResponse( +            packet, "Could not parse signal in vCont packet C action"); +      LLVM_FALLTHROUGH; + +    case 'c': +      // Continue +      thread_action.state = eStateRunning; +      break; + +    case 'S': +      thread_action.signal = packet.GetHexMaxU32(false, 0); +      if (thread_action.signal == 0) +        return SendIllFormedResponse( +            packet, "Could not parse signal in vCont packet S action"); +      LLVM_FALLTHROUGH; + +    case 's': +      // Step +      thread_action.state = eStateStepping; +      break; + +    default: +      return SendIllFormedResponse(packet, "Unsupported vCont action"); +      break; +    } + +    // Parse out optional :{thread-id} value. +    if (packet.GetBytesLeft() && (*packet.Peek() == ':')) { +      // Consume the separator. +      packet.GetChar(); + +      thread_action.tid = packet.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID); +      if (thread_action.tid == LLDB_INVALID_THREAD_ID) +        return SendIllFormedResponse( +            packet, "Could not parse thread number in vCont packet"); +    } + +    thread_actions.Append(thread_action); +  } + +  Status error = m_debugged_process_up->Resume(thread_actions); +  if (error.Fail()) { +    LLDB_LOG(log, "vCont failed for process {0}: {1}", +             m_debugged_process_up->GetID(), error); +    return SendErrorResponse(GDBRemoteServerError::eErrorResume); +  } + +  LLDB_LOG(log, "continued process {0}", m_debugged_process_up->GetID()); +  // No response required from vCont. +  return PacketResult::Success; +} + +void GDBRemoteCommunicationServerLLGS::SetCurrentThreadID(lldb::tid_t tid) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); +  LLDB_LOG(log, "setting current thread id to {0}", tid); + +  m_current_tid = tid; +  if (m_debugged_process_up) +    m_debugged_process_up->SetCurrentThreadID(m_current_tid); +} + +void GDBRemoteCommunicationServerLLGS::SetContinueThreadID(lldb::tid_t tid) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); +  LLDB_LOG(log, "setting continue thread id to {0}", tid); + +  m_continue_tid = tid; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_stop_reason( +    StringExtractorGDBRemote &packet) { +  // Handle the $? gdbremote command. + +  // If no process, indicate error +  if (!m_debugged_process_up) +    return SendErrorResponse(02); + +  return SendStopReasonForState(m_debugged_process_up->GetState()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendStopReasonForState( +    lldb::StateType process_state) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  switch (process_state) { +  case eStateAttaching: +  case eStateLaunching: +  case eStateRunning: +  case eStateStepping: +  case eStateDetached: +    // NOTE: gdb protocol doc looks like it should return $OK +    // when everything is running (i.e. no stopped result). +    return PacketResult::Success; // Ignore + +  case eStateSuspended: +  case eStateStopped: +  case eStateCrashed: { +    lldb::tid_t tid = m_debugged_process_up->GetCurrentThreadID(); +    // Make sure we set the current thread so g and p packets return the data +    // the gdb will expect. +    SetCurrentThreadID(tid); +    return SendStopReplyPacketForThread(tid); +  } + +  case eStateInvalid: +  case eStateUnloaded: +  case eStateExited: +    return SendWResponse(m_debugged_process_up.get()); + +  default: +    LLDB_LOG(log, "pid {0}, current state reporting not handled: {1}", +             m_debugged_process_up->GetID(), process_state); +    break; +  } + +  return SendErrorResponse(0); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo( +    StringExtractorGDBRemote &packet) { +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) +    return SendErrorResponse(68); + +  // Ensure we have a thread. +  NativeThreadProtocol *thread = m_debugged_process_up->GetThreadAtIndex(0); +  if (!thread) +    return SendErrorResponse(69); + +  // Get the register context for the first thread. +  NativeRegisterContext ®_context = thread->GetRegisterContext(); + +  // Parse out the register number from the request. +  packet.SetFilePos(strlen("qRegisterInfo")); +  const uint32_t reg_index = +      packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max()); +  if (reg_index == std::numeric_limits<uint32_t>::max()) +    return SendErrorResponse(69); + +  // Return the end of registers response if we've iterated one past the end of +  // the register set. +  if (reg_index >= reg_context.GetUserRegisterCount()) +    return SendErrorResponse(69); + +  const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index); +  if (!reg_info) +    return SendErrorResponse(69); + +  // Build the reginfos response. +  StreamGDBRemote response; + +  response.PutCString("name:"); +  response.PutCString(reg_info->name); +  response.PutChar(';'); + +  if (reg_info->alt_name && reg_info->alt_name[0]) { +    response.PutCString("alt-name:"); +    response.PutCString(reg_info->alt_name); +    response.PutChar(';'); +  } + +  response.Printf("bitsize:%" PRIu32 ";offset:%" PRIu32 ";", +                  reg_info->byte_size * 8, reg_info->byte_offset); + +  switch (reg_info->encoding) { +  case eEncodingUint: +    response.PutCString("encoding:uint;"); +    break; +  case eEncodingSint: +    response.PutCString("encoding:sint;"); +    break; +  case eEncodingIEEE754: +    response.PutCString("encoding:ieee754;"); +    break; +  case eEncodingVector: +    response.PutCString("encoding:vector;"); +    break; +  default: +    break; +  } + +  switch (reg_info->format) { +  case eFormatBinary: +    response.PutCString("format:binary;"); +    break; +  case eFormatDecimal: +    response.PutCString("format:decimal;"); +    break; +  case eFormatHex: +    response.PutCString("format:hex;"); +    break; +  case eFormatFloat: +    response.PutCString("format:float;"); +    break; +  case eFormatVectorOfSInt8: +    response.PutCString("format:vector-sint8;"); +    break; +  case eFormatVectorOfUInt8: +    response.PutCString("format:vector-uint8;"); +    break; +  case eFormatVectorOfSInt16: +    response.PutCString("format:vector-sint16;"); +    break; +  case eFormatVectorOfUInt16: +    response.PutCString("format:vector-uint16;"); +    break; +  case eFormatVectorOfSInt32: +    response.PutCString("format:vector-sint32;"); +    break; +  case eFormatVectorOfUInt32: +    response.PutCString("format:vector-uint32;"); +    break; +  case eFormatVectorOfFloat32: +    response.PutCString("format:vector-float32;"); +    break; +  case eFormatVectorOfUInt64: +    response.PutCString("format:vector-uint64;"); +    break; +  case eFormatVectorOfUInt128: +    response.PutCString("format:vector-uint128;"); +    break; +  default: +    break; +  }; + +  const char *const register_set_name = +      reg_context.GetRegisterSetNameForRegisterAtIndex(reg_index); +  if (register_set_name) { +    response.PutCString("set:"); +    response.PutCString(register_set_name); +    response.PutChar(';'); +  } + +  if (reg_info->kinds[RegisterKind::eRegisterKindEHFrame] != +      LLDB_INVALID_REGNUM) +    response.Printf("ehframe:%" PRIu32 ";", +                    reg_info->kinds[RegisterKind::eRegisterKindEHFrame]); + +  if (reg_info->kinds[RegisterKind::eRegisterKindDWARF] != LLDB_INVALID_REGNUM) +    response.Printf("dwarf:%" PRIu32 ";", +                    reg_info->kinds[RegisterKind::eRegisterKindDWARF]); + +  switch (reg_info->kinds[RegisterKind::eRegisterKindGeneric]) { +  case LLDB_REGNUM_GENERIC_PC: +    response.PutCString("generic:pc;"); +    break; +  case LLDB_REGNUM_GENERIC_SP: +    response.PutCString("generic:sp;"); +    break; +  case LLDB_REGNUM_GENERIC_FP: +    response.PutCString("generic:fp;"); +    break; +  case LLDB_REGNUM_GENERIC_RA: +    response.PutCString("generic:ra;"); +    break; +  case LLDB_REGNUM_GENERIC_FLAGS: +    response.PutCString("generic:flags;"); +    break; +  case LLDB_REGNUM_GENERIC_ARG1: +    response.PutCString("generic:arg1;"); +    break; +  case LLDB_REGNUM_GENERIC_ARG2: +    response.PutCString("generic:arg2;"); +    break; +  case LLDB_REGNUM_GENERIC_ARG3: +    response.PutCString("generic:arg3;"); +    break; +  case LLDB_REGNUM_GENERIC_ARG4: +    response.PutCString("generic:arg4;"); +    break; +  case LLDB_REGNUM_GENERIC_ARG5: +    response.PutCString("generic:arg5;"); +    break; +  case LLDB_REGNUM_GENERIC_ARG6: +    response.PutCString("generic:arg6;"); +    break; +  case LLDB_REGNUM_GENERIC_ARG7: +    response.PutCString("generic:arg7;"); +    break; +  case LLDB_REGNUM_GENERIC_ARG8: +    response.PutCString("generic:arg8;"); +    break; +  default: +    break; +  } + +  if (reg_info->value_regs && reg_info->value_regs[0] != LLDB_INVALID_REGNUM) { +    response.PutCString("container-regs:"); +    int i = 0; +    for (const uint32_t *reg_num = reg_info->value_regs; +         *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) { +      if (i > 0) +        response.PutChar(','); +      response.Printf("%" PRIx32, *reg_num); +    } +    response.PutChar(';'); +  } + +  if (reg_info->invalidate_regs && reg_info->invalidate_regs[0]) { +    response.PutCString("invalidate-regs:"); +    int i = 0; +    for (const uint32_t *reg_num = reg_info->invalidate_regs; +         *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) { +      if (i > 0) +        response.PutChar(','); +      response.Printf("%" PRIx32, *reg_num); +    } +    response.PutChar(';'); +  } + +  if (reg_info->dynamic_size_dwarf_expr_bytes) { +    const size_t dwarf_opcode_len = reg_info->dynamic_size_dwarf_len; +    response.PutCString("dynamic_size_dwarf_expr_bytes:"); +    for (uint32_t i = 0; i < dwarf_opcode_len; ++i) +      response.PutHex8(reg_info->dynamic_size_dwarf_expr_bytes[i]); +    response.PutChar(';'); +  } +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo( +    StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    LLDB_LOG(log, "no process ({0}), returning OK", +             m_debugged_process_up ? "invalid process id" +                                   : "null m_debugged_process_up"); +    return SendOKResponse(); +  } + +  StreamGDBRemote response; +  response.PutChar('m'); + +  LLDB_LOG(log, "starting thread iteration"); +  NativeThreadProtocol *thread; +  uint32_t thread_index; +  for (thread_index = 0, +      thread = m_debugged_process_up->GetThreadAtIndex(thread_index); +       thread; ++thread_index, +      thread = m_debugged_process_up->GetThreadAtIndex(thread_index)) { +    LLDB_LOG(log, "iterated thread {0}(tid={2})", thread_index, +             thread->GetID()); +    if (thread_index > 0) +      response.PutChar(','); +    response.Printf("%" PRIx64, thread->GetID()); +  } + +  LLDB_LOG(log, "finished thread iteration"); +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo( +    StringExtractorGDBRemote &packet) { +  // FIXME for now we return the full thread list in the initial packet and +  // always do nothing here. +  return SendPacketNoLock("l"); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_g(StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + +  // Move past packet name. +  packet.SetFilePos(strlen("g")); + +  // Get the thread to use. +  NativeThreadProtocol *thread = GetThreadFromSuffix(packet); +  if (!thread) { +    LLDB_LOG(log, "failed, no thread available"); +    return SendErrorResponse(0x15); +  } + +  // Get the thread's register context. +  NativeRegisterContext ®_ctx = thread->GetRegisterContext(); + +  std::vector<uint8_t> regs_buffer; +  for (uint32_t reg_num = 0; reg_num < reg_ctx.GetUserRegisterCount(); +       ++reg_num) { +    const RegisterInfo *reg_info = reg_ctx.GetRegisterInfoAtIndex(reg_num); + +    if (reg_info == nullptr) { +      LLDB_LOG(log, "failed to get register info for register index {0}", +               reg_num); +      return SendErrorResponse(0x15); +    } + +    if (reg_info->value_regs != nullptr) +      continue; // skip registers that are contained in other registers + +    RegisterValue reg_value; +    Status error = reg_ctx.ReadRegister(reg_info, reg_value); +    if (error.Fail()) { +      LLDB_LOG(log, "failed to read register at index {0}", reg_num); +      return SendErrorResponse(0x15); +    } + +    if (reg_info->byte_offset + reg_info->byte_size >= regs_buffer.size()) +      // Resize the buffer to guarantee it can store the register offsetted +      // data. +      regs_buffer.resize(reg_info->byte_offset + reg_info->byte_size); + +    // Copy the register offsetted data to the buffer. +    memcpy(regs_buffer.data() + reg_info->byte_offset, reg_value.GetBytes(), +           reg_info->byte_size); +  } + +  // Write the response. +  StreamGDBRemote response; +  response.PutBytesAsRawHex8(regs_buffer.data(), regs_buffer.size()); + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_p(StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + +  // Parse out the register number from the request. +  packet.SetFilePos(strlen("p")); +  const uint32_t reg_index = +      packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max()); +  if (reg_index == std::numeric_limits<uint32_t>::max()) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not " +                  "parse register number from request \"%s\"", +                  __FUNCTION__, packet.GetStringRef().c_str()); +    return SendErrorResponse(0x15); +  } + +  // Get the thread to use. +  NativeThreadProtocol *thread = GetThreadFromSuffix(packet); +  if (!thread) { +    LLDB_LOG(log, "failed, no thread available"); +    return SendErrorResponse(0x15); +  } + +  // Get the thread's register context. +  NativeRegisterContext ®_context = thread->GetRegisterContext(); + +  // Return the end of registers response if we've iterated one past the end of +  // the register set. +  if (reg_index >= reg_context.GetUserRegisterCount()) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " +                  "register %" PRIu32 " beyond register count %" PRIu32, +                  __FUNCTION__, reg_index, +                  reg_context.GetUserRegisterCount()); +    return SendErrorResponse(0x15); +  } + +  const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index); +  if (!reg_info) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " +                  "register %" PRIu32 " returned NULL", +                  __FUNCTION__, reg_index); +    return SendErrorResponse(0x15); +  } + +  // Build the reginfos response. +  StreamGDBRemote response; + +  // Retrieve the value +  RegisterValue reg_value; +  Status error = reg_context.ReadRegister(reg_info, reg_value); +  if (error.Fail()) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, read of " +                  "requested register %" PRIu32 " (%s) failed: %s", +                  __FUNCTION__, reg_index, reg_info->name, error.AsCString()); +    return SendErrorResponse(0x15); +  } + +  const uint8_t *const data = +      reinterpret_cast<const uint8_t *>(reg_value.GetBytes()); +  if (!data) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to get data " +                  "bytes from requested register %" PRIu32, +                  __FUNCTION__, reg_index); +    return SendErrorResponse(0x15); +  } + +  // FIXME flip as needed to get data in big/little endian format for this host. +  for (uint32_t i = 0; i < reg_value.GetByteSize(); ++i) +    response.PutHex8(data[i]); + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_P(StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + +  // Ensure there is more content. +  if (packet.GetBytesLeft() < 1) +    return SendIllFormedResponse(packet, "Empty P packet"); + +  // Parse out the register number from the request. +  packet.SetFilePos(strlen("P")); +  const uint32_t reg_index = +      packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max()); +  if (reg_index == std::numeric_limits<uint32_t>::max()) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not " +                  "parse register number from request \"%s\"", +                  __FUNCTION__, packet.GetStringRef().c_str()); +    return SendErrorResponse(0x29); +  } + +  // Note debugserver would send an E30 here. +  if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != '=')) +    return SendIllFormedResponse( +        packet, "P packet missing '=' char after register number"); + +  // Parse out the value. +  uint8_t reg_bytes[32]; // big enough to support up to 256 bit ymmN register +  size_t reg_size = packet.GetHexBytesAvail(reg_bytes); + +  // Get the thread to use. +  NativeThreadProtocol *thread = GetThreadFromSuffix(packet); +  if (!thread) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, no thread " +                  "available (thread index 0)", +                  __FUNCTION__); +    return SendErrorResponse(0x28); +  } + +  // Get the thread's register context. +  NativeRegisterContext ®_context = thread->GetRegisterContext(); +  const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index); +  if (!reg_info) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " +                  "register %" PRIu32 " returned NULL", +                  __FUNCTION__, reg_index); +    return SendErrorResponse(0x48); +  } + +  // Return the end of registers response if we've iterated one past the end of +  // the register set. +  if (reg_index >= reg_context.GetUserRegisterCount()) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, requested " +                  "register %" PRIu32 " beyond register count %" PRIu32, +                  __FUNCTION__, reg_index, reg_context.GetUserRegisterCount()); +    return SendErrorResponse(0x47); +  } + +  // The dwarf expression are evaluate on host site which may cause register +  // size to change Hence the reg_size may not be same as reg_info->bytes_size +  if ((reg_size != reg_info->byte_size) && +      !(reg_info->dynamic_size_dwarf_expr_bytes)) { +    return SendIllFormedResponse(packet, "P packet register size is incorrect"); +  } + +  // Build the reginfos response. +  StreamGDBRemote response; + +  RegisterValue reg_value( +      reg_bytes, reg_size, +      m_debugged_process_up->GetArchitecture().GetByteOrder()); +  Status error = reg_context.WriteRegister(reg_info, reg_value); +  if (error.Fail()) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, write of " +                  "requested register %" PRIu32 " (%s) failed: %s", +                  __FUNCTION__, reg_index, reg_info->name, error.AsCString()); +    return SendErrorResponse(0x32); +  } + +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_H(StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    if (log) +      log->Printf( +          "GDBRemoteCommunicationServerLLGS::%s failed, no process available", +          __FUNCTION__); +    return SendErrorResponse(0x15); +  } + +  // Parse out which variant of $H is requested. +  packet.SetFilePos(strlen("H")); +  if (packet.GetBytesLeft() < 1) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, H command " +                  "missing {g,c} variant", +                  __FUNCTION__); +    return SendIllFormedResponse(packet, "H command missing {g,c} variant"); +  } + +  const char h_variant = packet.GetChar(); +  switch (h_variant) { +  case 'g': +    break; + +  case 'c': +    break; + +  default: +    if (log) +      log->Printf( +          "GDBRemoteCommunicationServerLLGS::%s failed, invalid $H variant %c", +          __FUNCTION__, h_variant); +    return SendIllFormedResponse(packet, +                                 "H variant unsupported, should be c or g"); +  } + +  // Parse out the thread number. +  // FIXME return a parse success/fail value.  All values are valid here. +  const lldb::tid_t tid = +      packet.GetHexMaxU64(false, std::numeric_limits<lldb::tid_t>::max()); + +  // Ensure we have the given thread when not specifying -1 (all threads) or 0 +  // (any thread). +  if (tid != LLDB_INVALID_THREAD_ID && tid != 0) { +    NativeThreadProtocol *thread = m_debugged_process_up->GetThreadByID(tid); +    if (!thread) { +      if (log) +        log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, tid %" PRIu64 +                    " not found", +                    __FUNCTION__, tid); +      return SendErrorResponse(0x15); +    } +  } + +  // Now switch the given thread type. +  switch (h_variant) { +  case 'g': +    SetCurrentThreadID(tid); +    break; + +  case 'c': +    SetContinueThreadID(tid); +    break; + +  default: +    assert(false && "unsupported $H variant - shouldn't get here"); +    return SendIllFormedResponse(packet, +                                 "H variant unsupported, should be c or g"); +  } + +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_I(StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    if (log) +      log->Printf( +          "GDBRemoteCommunicationServerLLGS::%s failed, no process available", +          __FUNCTION__); +    return SendErrorResponse(0x15); +  } + +  packet.SetFilePos(::strlen("I")); +  uint8_t tmp[4096]; +  for (;;) { +    size_t read = packet.GetHexBytesAvail(tmp); +    if (read == 0) { +      break; +    } +    // write directly to stdin *this might block if stdin buffer is full* +    // TODO: enqueue this block in circular buffer and send window size to +    // remote host +    ConnectionStatus status; +    Status error; +    m_stdio_communication.Write(tmp, read, status, &error); +    if (error.Fail()) { +      return SendErrorResponse(0x15); +    } +  } + +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_interrupt( +    StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); + +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    LLDB_LOG(log, "failed, no process available"); +    return SendErrorResponse(0x15); +  } + +  // Interrupt the process. +  Status error = m_debugged_process_up->Interrupt(); +  if (error.Fail()) { +    LLDB_LOG(log, "failed for process {0}: {1}", m_debugged_process_up->GetID(), +             error); +    return SendErrorResponse(GDBRemoteServerError::eErrorResume); +  } + +  LLDB_LOG(log, "stopped process {0}", m_debugged_process_up->GetID()); + +  // No response required from stop all. +  return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_memory_read( +    StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    if (log) +      log->Printf( +          "GDBRemoteCommunicationServerLLGS::%s failed, no process available", +          __FUNCTION__); +    return SendErrorResponse(0x15); +  } + +  // Parse out the memory address. +  packet.SetFilePos(strlen("m")); +  if (packet.GetBytesLeft() < 1) +    return SendIllFormedResponse(packet, "Too short m packet"); + +  // Read the address.  Punting on validation. +  // FIXME replace with Hex U64 read with no default value that fails on failed +  // read. +  const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); + +  // Validate comma. +  if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) +    return SendIllFormedResponse(packet, "Comma sep missing in m packet"); + +  // Get # bytes to read. +  if (packet.GetBytesLeft() < 1) +    return SendIllFormedResponse(packet, "Length missing in m packet"); + +  const uint64_t byte_count = packet.GetHexMaxU64(false, 0); +  if (byte_count == 0) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s nothing to read: " +                  "zero-length packet", +                  __FUNCTION__); +    return SendOKResponse(); +  } + +  // Allocate the response buffer. +  std::string buf(byte_count, '\0'); +  if (buf.empty()) +    return SendErrorResponse(0x78); + +  // Retrieve the process memory. +  size_t bytes_read = 0; +  Status error = m_debugged_process_up->ReadMemoryWithoutTrap( +      read_addr, &buf[0], byte_count, bytes_read); +  if (error.Fail()) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 +                  " mem 0x%" PRIx64 ": failed to read. Error: %s", +                  __FUNCTION__, m_debugged_process_up->GetID(), read_addr, +                  error.AsCString()); +    return SendErrorResponse(0x08); +  } + +  if (bytes_read == 0) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 +                  " mem 0x%" PRIx64 ": read 0 of %" PRIu64 " requested bytes", +                  __FUNCTION__, m_debugged_process_up->GetID(), read_addr, +                  byte_count); +    return SendErrorResponse(0x08); +  } + +  StreamGDBRemote response; +  packet.SetFilePos(0); +  char kind = packet.GetChar('?'); +  if (kind == 'x') +    response.PutEscapedBytes(buf.data(), byte_count); +  else { +    assert(kind == 'm'); +    for (size_t i = 0; i < bytes_read; ++i) +      response.PutHex8(buf[i]); +  } + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_M(StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    if (log) +      log->Printf( +          "GDBRemoteCommunicationServerLLGS::%s failed, no process available", +          __FUNCTION__); +    return SendErrorResponse(0x15); +  } + +  // Parse out the memory address. +  packet.SetFilePos(strlen("M")); +  if (packet.GetBytesLeft() < 1) +    return SendIllFormedResponse(packet, "Too short M packet"); + +  // Read the address.  Punting on validation. +  // FIXME replace with Hex U64 read with no default value that fails on failed +  // read. +  const lldb::addr_t write_addr = packet.GetHexMaxU64(false, 0); + +  // Validate comma. +  if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) +    return SendIllFormedResponse(packet, "Comma sep missing in M packet"); + +  // Get # bytes to read. +  if (packet.GetBytesLeft() < 1) +    return SendIllFormedResponse(packet, "Length missing in M packet"); + +  const uint64_t byte_count = packet.GetHexMaxU64(false, 0); +  if (byte_count == 0) { +    LLDB_LOG(log, "nothing to write: zero-length packet"); +    return PacketResult::Success; +  } + +  // Validate colon. +  if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ':')) +    return SendIllFormedResponse( +        packet, "Comma sep missing in M packet after byte length"); + +  // Allocate the conversion buffer. +  std::vector<uint8_t> buf(byte_count, 0); +  if (buf.empty()) +    return SendErrorResponse(0x78); + +  // Convert the hex memory write contents to bytes. +  StreamGDBRemote response; +  const uint64_t convert_count = packet.GetHexBytes(buf, 0); +  if (convert_count != byte_count) { +    LLDB_LOG(log, +             "pid {0} mem {1:x}: asked to write {2} bytes, but only found {3} " +             "to convert.", +             m_debugged_process_up->GetID(), write_addr, byte_count, +             convert_count); +    return SendIllFormedResponse(packet, "M content byte length specified did " +                                         "not match hex-encoded content " +                                         "length"); +  } + +  // Write the process memory. +  size_t bytes_written = 0; +  Status error = m_debugged_process_up->WriteMemory(write_addr, &buf[0], +                                                    byte_count, bytes_written); +  if (error.Fail()) { +    LLDB_LOG(log, "pid {0} mem {1:x}: failed to write. Error: {2}", +             m_debugged_process_up->GetID(), write_addr, error); +    return SendErrorResponse(0x09); +  } + +  if (bytes_written == 0) { +    LLDB_LOG(log, "pid {0} mem {1:x}: wrote 0 of {2} requested bytes", +             m_debugged_process_up->GetID(), write_addr, byte_count); +    return SendErrorResponse(0x09); +  } + +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported( +    StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  // Currently only the NativeProcessProtocol knows if it can handle a +  // qMemoryRegionInfoSupported request, but we're not guaranteed to be +  // attached to a process.  For now we'll assume the client only asks this +  // when a process is being debugged. + +  // Ensure we have a process running; otherwise, we can't figure this out +  // since we won't have a NativeProcessProtocol. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    if (log) +      log->Printf( +          "GDBRemoteCommunicationServerLLGS::%s failed, no process available", +          __FUNCTION__); +    return SendErrorResponse(0x15); +  } + +  // Test if we can get any region back when asking for the region around NULL. +  MemoryRegionInfo region_info; +  const Status error = +      m_debugged_process_up->GetMemoryRegionInfo(0, region_info); +  if (error.Fail()) { +    // We don't support memory region info collection for this +    // NativeProcessProtocol. +    return SendUnimplementedResponse(""); +  } + +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo( +    StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  // Ensure we have a process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    if (log) +      log->Printf( +          "GDBRemoteCommunicationServerLLGS::%s failed, no process available", +          __FUNCTION__); +    return SendErrorResponse(0x15); +  } + +  // Parse out the memory address. +  packet.SetFilePos(strlen("qMemoryRegionInfo:")); +  if (packet.GetBytesLeft() < 1) +    return SendIllFormedResponse(packet, "Too short qMemoryRegionInfo: packet"); + +  // Read the address.  Punting on validation. +  const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); + +  StreamGDBRemote response; + +  // Get the memory region info for the target address. +  MemoryRegionInfo region_info; +  const Status error = +      m_debugged_process_up->GetMemoryRegionInfo(read_addr, region_info); +  if (error.Fail()) { +    // Return the error message. + +    response.PutCString("error:"); +    response.PutStringAsRawHex8(error.AsCString()); +    response.PutChar(';'); +  } else { +    // Range start and size. +    response.Printf("start:%" PRIx64 ";size:%" PRIx64 ";", +                    region_info.GetRange().GetRangeBase(), +                    region_info.GetRange().GetByteSize()); + +    // Permissions. +    if (region_info.GetReadable() || region_info.GetWritable() || +        region_info.GetExecutable()) { +      // Write permissions info. +      response.PutCString("permissions:"); + +      if (region_info.GetReadable()) +        response.PutChar('r'); +      if (region_info.GetWritable()) +        response.PutChar('w'); +      if (region_info.GetExecutable()) +        response.PutChar('x'); + +      response.PutChar(';'); +    } + +    // Name +    ConstString name = region_info.GetName(); +    if (name) { +      response.PutCString("name:"); +      response.PutStringAsRawHex8(name.AsCString()); +      response.PutChar(';'); +    } +  } + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_Z(StringExtractorGDBRemote &packet) { +  // Ensure we have a process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); +    LLDB_LOG(log, "failed, no process available"); +    return SendErrorResponse(0x15); +  } + +  // Parse out software or hardware breakpoint or watchpoint requested. +  packet.SetFilePos(strlen("Z")); +  if (packet.GetBytesLeft() < 1) +    return SendIllFormedResponse( +        packet, "Too short Z packet, missing software/hardware specifier"); + +  bool want_breakpoint = true; +  bool want_hardware = false; +  uint32_t watch_flags = 0; + +  const GDBStoppointType stoppoint_type = +      GDBStoppointType(packet.GetS32(eStoppointInvalid)); +  switch (stoppoint_type) { +  case eBreakpointSoftware: +    want_hardware = false; +    want_breakpoint = true; +    break; +  case eBreakpointHardware: +    want_hardware = true; +    want_breakpoint = true; +    break; +  case eWatchpointWrite: +    watch_flags = 1; +    want_hardware = true; +    want_breakpoint = false; +    break; +  case eWatchpointRead: +    watch_flags = 2; +    want_hardware = true; +    want_breakpoint = false; +    break; +  case eWatchpointReadWrite: +    watch_flags = 3; +    want_hardware = true; +    want_breakpoint = false; +    break; +  case eStoppointInvalid: +    return SendIllFormedResponse( +        packet, "Z packet had invalid software/hardware specifier"); +  } + +  if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') +    return SendIllFormedResponse( +        packet, "Malformed Z packet, expecting comma after stoppoint type"); + +  // Parse out the stoppoint address. +  if (packet.GetBytesLeft() < 1) +    return SendIllFormedResponse(packet, "Too short Z packet, missing address"); +  const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); + +  if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') +    return SendIllFormedResponse( +        packet, "Malformed Z packet, expecting comma after address"); + +  // Parse out the stoppoint size (i.e. size hint for opcode size). +  const uint32_t size = +      packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max()); +  if (size == std::numeric_limits<uint32_t>::max()) +    return SendIllFormedResponse( +        packet, "Malformed Z packet, failed to parse size argument"); + +  if (want_breakpoint) { +    // Try to set the breakpoint. +    const Status error = +        m_debugged_process_up->SetBreakpoint(addr, size, want_hardware); +    if (error.Success()) +      return SendOKResponse(); +    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); +    LLDB_LOG(log, "pid {0} failed to set breakpoint: {1}", +             m_debugged_process_up->GetID(), error); +    return SendErrorResponse(0x09); +  } else { +    // Try to set the watchpoint. +    const Status error = m_debugged_process_up->SetWatchpoint( +        addr, size, watch_flags, want_hardware); +    if (error.Success()) +      return SendOKResponse(); +    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); +    LLDB_LOG(log, "pid {0} failed to set watchpoint: {1}", +             m_debugged_process_up->GetID(), error); +    return SendErrorResponse(0x09); +  } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_z(StringExtractorGDBRemote &packet) { +  // Ensure we have a process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); +    LLDB_LOG(log, "failed, no process available"); +    return SendErrorResponse(0x15); +  } + +  // Parse out software or hardware breakpoint or watchpoint requested. +  packet.SetFilePos(strlen("z")); +  if (packet.GetBytesLeft() < 1) +    return SendIllFormedResponse( +        packet, "Too short z packet, missing software/hardware specifier"); + +  bool want_breakpoint = true; +  bool want_hardware = false; + +  const GDBStoppointType stoppoint_type = +      GDBStoppointType(packet.GetS32(eStoppointInvalid)); +  switch (stoppoint_type) { +  case eBreakpointHardware: +    want_breakpoint = true; +    want_hardware = true; +    break; +  case eBreakpointSoftware: +    want_breakpoint = true; +    break; +  case eWatchpointWrite: +    want_breakpoint = false; +    break; +  case eWatchpointRead: +    want_breakpoint = false; +    break; +  case eWatchpointReadWrite: +    want_breakpoint = false; +    break; +  default: +    return SendIllFormedResponse( +        packet, "z packet had invalid software/hardware specifier"); +  } + +  if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') +    return SendIllFormedResponse( +        packet, "Malformed z packet, expecting comma after stoppoint type"); + +  // Parse out the stoppoint address. +  if (packet.GetBytesLeft() < 1) +    return SendIllFormedResponse(packet, "Too short z packet, missing address"); +  const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); + +  if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') +    return SendIllFormedResponse( +        packet, "Malformed z packet, expecting comma after address"); + +  /* +  // Parse out the stoppoint size (i.e. size hint for opcode size). +  const uint32_t size = packet.GetHexMaxU32 (false, +  std::numeric_limits<uint32_t>::max ()); +  if (size == std::numeric_limits<uint32_t>::max ()) +      return SendIllFormedResponse(packet, "Malformed z packet, failed to parse +  size argument"); +  */ + +  if (want_breakpoint) { +    // Try to clear the breakpoint. +    const Status error = +        m_debugged_process_up->RemoveBreakpoint(addr, want_hardware); +    if (error.Success()) +      return SendOKResponse(); +    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); +    LLDB_LOG(log, "pid {0} failed to remove breakpoint: {1}", +             m_debugged_process_up->GetID(), error); +    return SendErrorResponse(0x09); +  } else { +    // Try to clear the watchpoint. +    const Status error = m_debugged_process_up->RemoveWatchpoint(addr); +    if (error.Success()) +      return SendOKResponse(); +    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS)); +    LLDB_LOG(log, "pid {0} failed to remove watchpoint: {1}", +             m_debugged_process_up->GetID(), error); +    return SendErrorResponse(0x09); +  } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_s(StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); + +  // Ensure we have a process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    if (log) +      log->Printf( +          "GDBRemoteCommunicationServerLLGS::%s failed, no process available", +          __FUNCTION__); +    return SendErrorResponse(0x32); +  } + +  // We first try to use a continue thread id.  If any one or any all set, use +  // the current thread. Bail out if we don't have a thread id. +  lldb::tid_t tid = GetContinueThreadID(); +  if (tid == 0 || tid == LLDB_INVALID_THREAD_ID) +    tid = GetCurrentThreadID(); +  if (tid == LLDB_INVALID_THREAD_ID) +    return SendErrorResponse(0x33); + +  // Double check that we have such a thread. +  // TODO investigate: on MacOSX we might need to do an UpdateThreads () here. +  NativeThreadProtocol *thread = m_debugged_process_up->GetThreadByID(tid); +  if (!thread) +    return SendErrorResponse(0x33); + +  // Create the step action for the given thread. +  ResumeAction action = {tid, eStateStepping, 0}; + +  // Setup the actions list. +  ResumeActionList actions; +  actions.Append(action); + +  // All other threads stop while we're single stepping a thread. +  actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0); +  Status error = m_debugged_process_up->Resume(actions); +  if (error.Fail()) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 +                  " tid %" PRIu64 " Resume() failed with error: %s", +                  __FUNCTION__, m_debugged_process_up->GetID(), tid, +                  error.AsCString()); +    return SendErrorResponse(0x49); +  } + +  // No response here - the stop or exit will come from the resulting action. +  return PacketResult::Success; +} + +llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> +GDBRemoteCommunicationServerLLGS::ReadXferObject(llvm::StringRef object, +                                                 llvm::StringRef annex) { +  if (object == "auxv") { +    // Make sure we have a valid process. +    if (!m_debugged_process_up || +        (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +      return llvm::createStringError(llvm::inconvertibleErrorCode(), +                                     "No process available"); +    } + +    // Grab the auxv data. +    auto buffer_or_error = m_debugged_process_up->GetAuxvData(); +    if (!buffer_or_error) +      return llvm::errorCodeToError(buffer_or_error.getError()); +    return std::move(*buffer_or_error); +  } + +  return llvm::make_error<PacketUnimplementedError>( +      "Xfer object not supported"); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qXfer( +    StringExtractorGDBRemote &packet) { +  SmallVector<StringRef, 5> fields; +  // The packet format is "qXfer:<object>:<action>:<annex>:offset,length" +  StringRef(packet.GetStringRef()).split(fields, ':', 4); +  if (fields.size() != 5) +    return SendIllFormedResponse(packet, "malformed qXfer packet"); +  StringRef &xfer_object = fields[1]; +  StringRef &xfer_action = fields[2]; +  StringRef &xfer_annex = fields[3]; +  StringExtractor offset_data(fields[4]); +  if (xfer_action != "read") +    return SendUnimplementedResponse("qXfer action not supported"); +  // Parse offset. +  const uint64_t xfer_offset = +      offset_data.GetHexMaxU64(false, std::numeric_limits<uint64_t>::max()); +  if (xfer_offset == std::numeric_limits<uint64_t>::max()) +    return SendIllFormedResponse(packet, "qXfer packet missing offset"); +  // Parse out comma. +  if (offset_data.GetChar() != ',') +    return SendIllFormedResponse(packet, +                                 "qXfer packet missing comma after offset"); +  // Parse out the length. +  const uint64_t xfer_length = +      offset_data.GetHexMaxU64(false, std::numeric_limits<uint64_t>::max()); +  if (xfer_length == std::numeric_limits<uint64_t>::max()) +    return SendIllFormedResponse(packet, "qXfer packet missing length"); + +  // Get a previously constructed buffer if it exists or create it now. +  std::string buffer_key = (xfer_object + xfer_action + xfer_annex).str(); +  auto buffer_it = m_xfer_buffer_map.find(buffer_key); +  if (buffer_it == m_xfer_buffer_map.end()) { +    auto buffer_up = ReadXferObject(xfer_object, xfer_annex); +    if (!buffer_up) +      return SendErrorResponse(buffer_up.takeError()); +    buffer_it = m_xfer_buffer_map +                    .insert(std::make_pair(buffer_key, std::move(*buffer_up))) +                    .first; +  } + +  // Send back the response +  StreamGDBRemote response; +  bool done_with_buffer = false; +  llvm::StringRef buffer = buffer_it->second->getBuffer(); +  if (xfer_offset >= buffer.size()) { +    // We have nothing left to send.  Mark the buffer as complete. +    response.PutChar('l'); +    done_with_buffer = true; +  } else { +    // Figure out how many bytes are available starting at the given offset. +    buffer = buffer.drop_front(xfer_offset); +    // Mark the response type according to whether we're reading the remainder +    // of the data. +    if (xfer_length >= buffer.size()) { +      // There will be nothing left to read after this +      response.PutChar('l'); +      done_with_buffer = true; +    } else { +      // There will still be bytes to read after this request. +      response.PutChar('m'); +      buffer = buffer.take_front(xfer_length); +    } +    // Now write the data in encoded binary form. +    response.PutEscapedBytes(buffer.data(), buffer.size()); +  } + +  if (done_with_buffer) +    m_xfer_buffer_map.erase(buffer_it); + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState( +    StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + +  // Move past packet name. +  packet.SetFilePos(strlen("QSaveRegisterState")); + +  // Get the thread to use. +  NativeThreadProtocol *thread = GetThreadFromSuffix(packet); +  if (!thread) { +    if (m_thread_suffix_supported) +      return SendIllFormedResponse( +          packet, "No thread specified in QSaveRegisterState packet"); +    else +      return SendIllFormedResponse(packet, +                                   "No thread was is set with the Hg packet"); +  } + +  // Grab the register context for the thread. +  NativeRegisterContext& reg_context = thread->GetRegisterContext(); + +  // Save registers to a buffer. +  DataBufferSP register_data_sp; +  Status error = reg_context.ReadAllRegisterValues(register_data_sp); +  if (error.Fail()) { +    LLDB_LOG(log, "pid {0} failed to save all register values: {1}", +             m_debugged_process_up->GetID(), error); +    return SendErrorResponse(0x75); +  } + +  // Allocate a new save id. +  const uint32_t save_id = GetNextSavedRegistersID(); +  assert((m_saved_registers_map.find(save_id) == m_saved_registers_map.end()) && +         "GetNextRegisterSaveID() returned an existing register save id"); + +  // Save the register data buffer under the save id. +  { +    std::lock_guard<std::mutex> guard(m_saved_registers_mutex); +    m_saved_registers_map[save_id] = register_data_sp; +  } + +  // Write the response. +  StreamGDBRemote response; +  response.Printf("%" PRIu32, save_id); +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState( +    StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + +  // Parse out save id. +  packet.SetFilePos(strlen("QRestoreRegisterState:")); +  if (packet.GetBytesLeft() < 1) +    return SendIllFormedResponse( +        packet, "QRestoreRegisterState packet missing register save id"); + +  const uint32_t save_id = packet.GetU32(0); +  if (save_id == 0) { +    LLDB_LOG(log, "QRestoreRegisterState packet has malformed save id, " +                  "expecting decimal uint32_t"); +    return SendErrorResponse(0x76); +  } + +  // Get the thread to use. +  NativeThreadProtocol *thread = GetThreadFromSuffix(packet); +  if (!thread) { +    if (m_thread_suffix_supported) +      return SendIllFormedResponse( +          packet, "No thread specified in QRestoreRegisterState packet"); +    else +      return SendIllFormedResponse(packet, +                                   "No thread was is set with the Hg packet"); +  } + +  // Grab the register context for the thread. +  NativeRegisterContext ®_context = thread->GetRegisterContext(); + +  // Retrieve register state buffer, then remove from the list. +  DataBufferSP register_data_sp; +  { +    std::lock_guard<std::mutex> guard(m_saved_registers_mutex); + +    // Find the register set buffer for the given save id. +    auto it = m_saved_registers_map.find(save_id); +    if (it == m_saved_registers_map.end()) { +      LLDB_LOG(log, +               "pid {0} does not have a register set save buffer for id {1}", +               m_debugged_process_up->GetID(), save_id); +      return SendErrorResponse(0x77); +    } +    register_data_sp = it->second; + +    // Remove it from the map. +    m_saved_registers_map.erase(it); +  } + +  Status error = reg_context.WriteAllRegisterValues(register_data_sp); +  if (error.Fail()) { +    LLDB_LOG(log, "pid {0} failed to restore all register values: {1}", +             m_debugged_process_up->GetID(), error); +    return SendErrorResponse(0x77); +  } + +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vAttach( +    StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  // Consume the ';' after vAttach. +  packet.SetFilePos(strlen("vAttach")); +  if (!packet.GetBytesLeft() || packet.GetChar() != ';') +    return SendIllFormedResponse(packet, "vAttach missing expected ';'"); + +  // Grab the PID to which we will attach (assume hex encoding). +  lldb::pid_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16); +  if (pid == LLDB_INVALID_PROCESS_ID) +    return SendIllFormedResponse(packet, +                                 "vAttach failed to parse the process id"); + +  // Attempt to attach. +  if (log) +    log->Printf("GDBRemoteCommunicationServerLLGS::%s attempting to attach to " +                "pid %" PRIu64, +                __FUNCTION__, pid); + +  Status error = AttachToProcess(pid); + +  if (error.Fail()) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to attach to " +                  "pid %" PRIu64 ": %s\n", +                  __FUNCTION__, pid, error.AsCString()); +    return SendErrorResponse(error); +  } + +  // Notify we attached by sending a stop packet. +  return SendStopReasonForState(m_debugged_process_up->GetState()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_D(StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  StopSTDIOForwarding(); + +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) { +    if (log) +      log->Printf( +          "GDBRemoteCommunicationServerLLGS::%s failed, no process available", +          __FUNCTION__); +    return SendErrorResponse(0x15); +  } + +  lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + +  // Consume the ';' after D. +  packet.SetFilePos(1); +  if (packet.GetBytesLeft()) { +    if (packet.GetChar() != ';') +      return SendIllFormedResponse(packet, "D missing expected ';'"); + +    // Grab the PID from which we will detach (assume hex encoding). +    pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16); +    if (pid == LLDB_INVALID_PROCESS_ID) +      return SendIllFormedResponse(packet, "D failed to parse the process id"); +  } + +  if (pid != LLDB_INVALID_PROCESS_ID && m_debugged_process_up->GetID() != pid) { +    return SendIllFormedResponse(packet, "Invalid pid"); +  } + +  const Status error = m_debugged_process_up->Detach(); +  if (error.Fail()) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed to detach from " +                  "pid %" PRIu64 ": %s\n", +                  __FUNCTION__, m_debugged_process_up->GetID(), +                  error.AsCString()); +    return SendErrorResponse(0x01); +  } + +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo( +    StringExtractorGDBRemote &packet) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + +  packet.SetFilePos(strlen("qThreadStopInfo")); +  const lldb::tid_t tid = packet.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID); +  if (tid == LLDB_INVALID_THREAD_ID) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s failed, could not " +                  "parse thread id from request \"%s\"", +                  __FUNCTION__, packet.GetStringRef().c_str()); +    return SendErrorResponse(0x15); +  } +  return SendStopReplyPacketForThread(tid); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo( +    StringExtractorGDBRemote &) { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); + +  // Ensure we have a debugged process. +  if (!m_debugged_process_up || +      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) +    return SendErrorResponse(50); +  LLDB_LOG(log, "preparing packet for pid {0}", m_debugged_process_up->GetID()); + +  StreamString response; +  const bool threads_with_valid_stop_info_only = false; +  JSONArray::SP threads_array_sp = GetJSONThreadsInfo( +      *m_debugged_process_up, threads_with_valid_stop_info_only); +  if (!threads_array_sp) { +    LLDB_LOG(log, "failed to prepare a packet for pid {0}", +             m_debugged_process_up->GetID()); +    return SendErrorResponse(52); +  } + +  threads_array_sp->Write(response); +  StreamGDBRemote escaped_response; +  escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); +  return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo( +    StringExtractorGDBRemote &packet) { +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID) +    return SendErrorResponse(68); + +  packet.SetFilePos(strlen("qWatchpointSupportInfo")); +  if (packet.GetBytesLeft() == 0) +    return SendOKResponse(); +  if (packet.GetChar() != ':') +    return SendErrorResponse(67); + +  auto hw_debug_cap = m_debugged_process_up->GetHardwareDebugSupportInfo(); + +  StreamGDBRemote response; +  if (hw_debug_cap == llvm::None) +    response.Printf("num:0;"); +  else +    response.Printf("num:%d;", hw_debug_cap->second); + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress( +    StringExtractorGDBRemote &packet) { +  // Fail if we don't have a current process. +  if (!m_debugged_process_up || +      m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID) +    return SendErrorResponse(67); + +  packet.SetFilePos(strlen("qFileLoadAddress:")); +  if (packet.GetBytesLeft() == 0) +    return SendErrorResponse(68); + +  std::string file_name; +  packet.GetHexByteString(file_name); + +  lldb::addr_t file_load_address = LLDB_INVALID_ADDRESS; +  Status error = +      m_debugged_process_up->GetFileLoadAddress(file_name, file_load_address); +  if (error.Fail()) +    return SendErrorResponse(69); + +  if (file_load_address == LLDB_INVALID_ADDRESS) +    return SendErrorResponse(1); // File not loaded + +  StreamGDBRemote response; +  response.PutHex64(file_load_address); +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QPassSignals( +    StringExtractorGDBRemote &packet) { +  std::vector<int> signals; +  packet.SetFilePos(strlen("QPassSignals:")); + +  // Read sequence of hex signal numbers divided by a semicolon and optionally +  // spaces. +  while (packet.GetBytesLeft() > 0) { +    int signal = packet.GetS32(-1, 16); +    if (signal < 0) +      return SendIllFormedResponse(packet, "Failed to parse signal number."); +    signals.push_back(signal); + +    packet.SkipSpaces(); +    char separator = packet.GetChar(); +    if (separator == '\0') +      break; // End of string +    if (separator != ';') +      return SendIllFormedResponse(packet, "Invalid separator," +                                            " expected semicolon."); +  } + +  // Fail if we don't have a current process. +  if (!m_debugged_process_up) +    return SendErrorResponse(68); + +  Status error = m_debugged_process_up->IgnoreSignals(signals); +  if (error.Fail()) +    return SendErrorResponse(69); + +  return SendOKResponse(); +} + +void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  // Tell the stdio connection to shut down. +  if (m_stdio_communication.IsConnected()) { +    auto connection = m_stdio_communication.GetConnection(); +    if (connection) { +      Status error; +      connection->Disconnect(&error); + +      if (error.Success()) { +        if (log) +          log->Printf("GDBRemoteCommunicationServerLLGS::%s disconnect process " +                      "terminal stdio - SUCCESS", +                      __FUNCTION__); +      } else { +        if (log) +          log->Printf("GDBRemoteCommunicationServerLLGS::%s disconnect process " +                      "terminal stdio - FAIL: %s", +                      __FUNCTION__, error.AsCString()); +      } +    } +  } +} + +NativeThreadProtocol *GDBRemoteCommunicationServerLLGS::GetThreadFromSuffix( +    StringExtractorGDBRemote &packet) { +  // We have no thread if we don't have a process. +  if (!m_debugged_process_up || +      m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID) +    return nullptr; + +  // If the client hasn't asked for thread suffix support, there will not be a +  // thread suffix. Use the current thread in that case. +  if (!m_thread_suffix_supported) { +    const lldb::tid_t current_tid = GetCurrentThreadID(); +    if (current_tid == LLDB_INVALID_THREAD_ID) +      return nullptr; +    else if (current_tid == 0) { +      // Pick a thread. +      return m_debugged_process_up->GetThreadAtIndex(0); +    } else +      return m_debugged_process_up->GetThreadByID(current_tid); +  } + +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + +  // Parse out the ';'. +  if (packet.GetBytesLeft() < 1 || packet.GetChar() != ';') { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse " +                  "error: expected ';' prior to start of thread suffix: packet " +                  "contents = '%s'", +                  __FUNCTION__, packet.GetStringRef().c_str()); +    return nullptr; +  } + +  if (!packet.GetBytesLeft()) +    return nullptr; + +  // Parse out thread: portion. +  if (strncmp(packet.Peek(), "thread:", strlen("thread:")) != 0) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerLLGS::%s gdb-remote parse " +                  "error: expected 'thread:' but not found, packet contents = " +                  "'%s'", +                  __FUNCTION__, packet.GetStringRef().c_str()); +    return nullptr; +  } +  packet.SetFilePos(packet.GetFilePos() + strlen("thread:")); +  const lldb::tid_t tid = packet.GetHexMaxU64(false, 0); +  if (tid != 0) +    return m_debugged_process_up->GetThreadByID(tid); + +  return nullptr; +} + +lldb::tid_t GDBRemoteCommunicationServerLLGS::GetCurrentThreadID() const { +  if (m_current_tid == 0 || m_current_tid == LLDB_INVALID_THREAD_ID) { +    // Use whatever the debug process says is the current thread id since the +    // protocol either didn't specify or specified we want any/all threads +    // marked as the current thread. +    if (!m_debugged_process_up) +      return LLDB_INVALID_THREAD_ID; +    return m_debugged_process_up->GetCurrentThreadID(); +  } +  // Use the specific current thread id set by the gdb remote protocol. +  return m_current_tid; +} + +uint32_t GDBRemoteCommunicationServerLLGS::GetNextSavedRegistersID() { +  std::lock_guard<std::mutex> guard(m_saved_registers_mutex); +  return m_next_saved_registers_id++; +} + +void GDBRemoteCommunicationServerLLGS::ClearProcessSpecificData() { +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  LLDB_LOG(log, "clearing {0} xfer buffers", m_xfer_buffer_map.size()); +  m_xfer_buffer_map.clear(); +} + +FileSpec +GDBRemoteCommunicationServerLLGS::FindModuleFile(const std::string &module_path, +                                                 const ArchSpec &arch) { +  if (m_debugged_process_up) { +    FileSpec file_spec; +    if (m_debugged_process_up +            ->GetLoadedModuleFileSpec(module_path.c_str(), file_spec) +            .Success()) { +      if (FileSystem::Instance().Exists(file_spec)) +        return file_spec; +    } +  } + +  return GDBRemoteCommunicationServerCommon::FindModuleFile(module_path, arch); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h new file mode 100644 index 000000000000..068ea52caaaf --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -0,0 +1,229 @@ +//===-- GDBRemoteCommunicationServerLLGS.h ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationServerLLGS_h_ +#define liblldb_GDBRemoteCommunicationServerLLGS_h_ + +#include <mutex> +#include <unordered_map> + +#include "lldb/Core/Communication.h" +#include "lldb/Host/MainLoop.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/lldb-private-forward.h" + +#include "GDBRemoteCommunicationServerCommon.h" + +class StringExtractorGDBRemote; + +namespace lldb_private { + +namespace process_gdb_remote { + +class ProcessGDBRemote; + +class GDBRemoteCommunicationServerLLGS +    : public GDBRemoteCommunicationServerCommon, +      public NativeProcessProtocol::NativeDelegate { +public: +  // Constructors and Destructors +  GDBRemoteCommunicationServerLLGS( +      MainLoop &mainloop, +      const NativeProcessProtocol::Factory &process_factory); + +  void SetLaunchInfo(const ProcessLaunchInfo &info); + +  /// Launch a process with the current launch settings. +  /// +  /// This method supports running an lldb-gdbserver or similar +  /// server in a situation where the startup code has been provided +  /// with all the information for a child process to be launched. +  /// +  /// \return +  ///     An Status object indicating the success or failure of the +  ///     launch. +  Status LaunchProcess() override; + +  /// Attach to a process. +  /// +  /// This method supports attaching llgs to a process accessible via the +  /// configured Platform. +  /// +  /// \return +  ///     An Status object indicating the success or failure of the +  ///     attach operation. +  Status AttachToProcess(lldb::pid_t pid); + +  // NativeProcessProtocol::NativeDelegate overrides +  void InitializeDelegate(NativeProcessProtocol *process) override; + +  void ProcessStateChanged(NativeProcessProtocol *process, +                           lldb::StateType state) override; + +  void DidExec(NativeProcessProtocol *process) override; + +  Status InitializeConnection(std::unique_ptr<Connection> &&connection); + +protected: +  MainLoop &m_mainloop; +  MainLoop::ReadHandleUP m_network_handle_up; +  const NativeProcessProtocol::Factory &m_process_factory; +  lldb::tid_t m_current_tid = LLDB_INVALID_THREAD_ID; +  lldb::tid_t m_continue_tid = LLDB_INVALID_THREAD_ID; +  std::recursive_mutex m_debugged_process_mutex; +  std::unique_ptr<NativeProcessProtocol> m_debugged_process_up; + +  Communication m_stdio_communication; +  MainLoop::ReadHandleUP m_stdio_handle_up; + +  lldb::StateType m_inferior_prev_state = lldb::StateType::eStateInvalid; +  llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> m_xfer_buffer_map; +  std::mutex m_saved_registers_mutex; +  std::unordered_map<uint32_t, lldb::DataBufferSP> m_saved_registers_map; +  uint32_t m_next_saved_registers_id = 1; +  bool m_handshake_completed = false; + +  PacketResult SendONotification(const char *buffer, uint32_t len); + +  PacketResult SendWResponse(NativeProcessProtocol *process); + +  PacketResult SendStopReplyPacketForThread(lldb::tid_t tid); + +  PacketResult SendStopReasonForState(lldb::StateType process_state); + +  PacketResult Handle_k(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qC(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QSetDisableASLR(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QSetWorkingDir(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet); + +  PacketResult Handle_C(StringExtractorGDBRemote &packet); + +  PacketResult Handle_c(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vCont(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vCont_actions(StringExtractorGDBRemote &packet); + +  PacketResult Handle_stop_reason(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qRegisterInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qfThreadInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qsThreadInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_p(StringExtractorGDBRemote &packet); + +  PacketResult Handle_P(StringExtractorGDBRemote &packet); + +  PacketResult Handle_H(StringExtractorGDBRemote &packet); + +  PacketResult Handle_I(StringExtractorGDBRemote &packet); + +  PacketResult Handle_interrupt(StringExtractorGDBRemote &packet); + +  // Handles $m and $x packets. +  PacketResult Handle_memory_read(StringExtractorGDBRemote &packet); + +  PacketResult Handle_M(StringExtractorGDBRemote &packet); + +  PacketResult +  Handle_qMemoryRegionInfoSupported(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qMemoryRegionInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_Z(StringExtractorGDBRemote &packet); + +  PacketResult Handle_z(StringExtractorGDBRemote &packet); + +  PacketResult Handle_s(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qXfer(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QSaveRegisterState(StringExtractorGDBRemote &packet); + +  PacketResult Handle_jTraceStart(StringExtractorGDBRemote &packet); + +  PacketResult Handle_jTraceRead(StringExtractorGDBRemote &packet); + +  PacketResult Handle_jTraceStop(StringExtractorGDBRemote &packet); + +  PacketResult Handle_jTraceConfigRead(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QRestoreRegisterState(StringExtractorGDBRemote &packet); + +  PacketResult Handle_vAttach(StringExtractorGDBRemote &packet); + +  PacketResult Handle_D(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qThreadStopInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_jThreadsInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qWatchpointSupportInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet); + +  PacketResult Handle_g(StringExtractorGDBRemote &packet); + +  void SetCurrentThreadID(lldb::tid_t tid); + +  lldb::tid_t GetCurrentThreadID() const; + +  void SetContinueThreadID(lldb::tid_t tid); + +  lldb::tid_t GetContinueThreadID() const { return m_continue_tid; } + +  Status SetSTDIOFileDescriptor(int fd); + +  FileSpec FindModuleFile(const std::string &module_path, +                          const ArchSpec &arch) override; + +  llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> +  ReadXferObject(llvm::StringRef object, llvm::StringRef annex); + +private: +  void HandleInferiorState_Exited(NativeProcessProtocol *process); + +  void HandleInferiorState_Stopped(NativeProcessProtocol *process); + +  NativeThreadProtocol *GetThreadFromSuffix(StringExtractorGDBRemote &packet); + +  uint32_t GetNextSavedRegistersID(); + +  void MaybeCloseInferiorTerminalConnection(); + +  void ClearProcessSpecificData(); + +  void RegisterPacketHandlers(); + +  void DataAvailableCallback(); + +  void SendProcessOutput(); + +  void StartSTDIOForwarding(); + +  void StopSTDIOForwarding(); + +  // For GDBRemoteCommunicationServerLLGS only +  DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationServerLLGS); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_GDBRemoteCommunicationServerLLGS_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp new file mode 100644 index 000000000000..6deb75f2f021 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp @@ -0,0 +1,550 @@ +//===-- GDBRemoteCommunicationServerPlatform.cpp ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteCommunicationServerPlatform.h" + +#include <errno.h> + +#include <chrono> +#include <csignal> +#include <cstring> +#include <mutex> +#include <sstream> + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Threading.h" + +#include "lldb/Host/Config.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/FileAction.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/JSON.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamGDBRemote.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/Utility/UriParser.h" + +#include "lldb/Utility/StringExtractorGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +// GDBRemoteCommunicationServerPlatform constructor +GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform( +    const Socket::SocketProtocol socket_protocol, const char *socket_scheme) +    : GDBRemoteCommunicationServerCommon("gdb-remote.server", +                                         "gdb-remote.server.rx_packet"), +      m_socket_protocol(socket_protocol), m_socket_scheme(socket_scheme), +      m_spawned_pids_mutex(), m_port_map(), m_port_offset(0) { +  m_pending_gdb_server.pid = LLDB_INVALID_PROCESS_ID; +  m_pending_gdb_server.port = 0; + +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qC, +      &GDBRemoteCommunicationServerPlatform::Handle_qC); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, +      &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer, +      &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer, +      &GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess, +      &GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_qProcessInfo, +      &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, +      &GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir); +  RegisterMemberFunctionHandler( +      StringExtractorGDBRemote::eServerPacketType_jSignalsInfo, +      &GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo); + +  RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_interrupt, +                        [](StringExtractorGDBRemote packet, Status &error, +                           bool &interrupt, bool &quit) { +                          error.SetErrorString("interrupt received"); +                          interrupt = true; +                          return PacketResult::Success; +                        }); +} + +// Destructor +GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() {} + +Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer( +    const lldb_private::Args &args, std::string hostname, lldb::pid_t &pid, +    uint16_t &port, std::string &socket_name) { +  if (port == UINT16_MAX) +    port = GetNextAvailablePort(); + +  // Spawn a new thread to accept the port that gets bound after binding to +  // port 0 (zero). + +  // ignore the hostname send from the remote end, just use the ip address that +  // we're currently communicating with as the hostname + +  // Spawn a debugserver and try to get the port it listens to. +  ProcessLaunchInfo debugserver_launch_info; +  if (hostname.empty()) +    hostname = "127.0.0.1"; + +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); +  if (log) +    log->Printf("Launching debugserver with: %s:%u...", hostname.c_str(), port); + +  // Do not run in a new session so that it can not linger after the platform +  // closes. +  debugserver_launch_info.SetLaunchInSeparateProcessGroup(false); +  debugserver_launch_info.SetMonitorProcessCallback( +      std::bind(&GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, +                this, std::placeholders::_1), +      false); + +  std::ostringstream url; +// debugserver does not accept the URL scheme prefix. +#if !defined(__APPLE__) +  url << m_socket_scheme << "://"; +#endif +  uint16_t *port_ptr = &port; +  if (m_socket_protocol == Socket::ProtocolTcp) { +    llvm::StringRef platform_scheme; +    llvm::StringRef platform_ip; +    int platform_port; +    llvm::StringRef platform_path; +    std::string platform_uri = GetConnection()->GetURI(); +    bool ok = UriParser::Parse(platform_uri, platform_scheme, platform_ip, +                               platform_port, platform_path); +    UNUSED_IF_ASSERT_DISABLED(ok); +    assert(ok); +    url << platform_ip.str() << ":" << port; +  } else { +    socket_name = GetDomainSocketPath("gdbserver").GetPath(); +    url << socket_name; +    port_ptr = nullptr; +  } + +  Status error = StartDebugserverProcess( +      url.str().c_str(), nullptr, debugserver_launch_info, port_ptr, &args, -1); + +  pid = debugserver_launch_info.GetProcessID(); +  if (pid != LLDB_INVALID_PROCESS_ID) { +    std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); +    m_spawned_pids.insert(pid); +    if (port > 0) +      AssociatePortWithProcess(port, pid); +  } else { +    if (port > 0) +      FreePort(port); +  } +  return error; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer( +    StringExtractorGDBRemote &packet) { +  // Spawn a local debugserver as a platform so we can then attach or launch a +  // process... + +  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); +  if (log) +    log->Printf("GDBRemoteCommunicationServerPlatform::%s() called", +                __FUNCTION__); + +  ConnectionFileDescriptor file_conn; +  std::string hostname; +  packet.SetFilePos(::strlen("qLaunchGDBServer;")); +  llvm::StringRef name; +  llvm::StringRef value; +  uint16_t port = UINT16_MAX; +  while (packet.GetNameColonValue(name, value)) { +    if (name.equals("host")) +      hostname = value; +    else if (name.equals("port")) +      value.getAsInteger(0, port); +  } + +  lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; +  std::string socket_name; +  Status error = +      LaunchGDBServer(Args(), hostname, debugserver_pid, port, socket_name); +  if (error.Fail()) { +    if (log) +      log->Printf("GDBRemoteCommunicationServerPlatform::%s() debugserver " +                  "launch failed: %s", +                  __FUNCTION__, error.AsCString()); +    return SendErrorResponse(9); +  } + +  if (log) +    log->Printf("GDBRemoteCommunicationServerPlatform::%s() debugserver " +                "launched successfully as pid %" PRIu64, +                __FUNCTION__, debugserver_pid); + +  StreamGDBRemote response; +  response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid, +                  port + m_port_offset); +  if (!socket_name.empty()) { +    response.PutCString("socket_name:"); +    response.PutStringAsRawHex8(socket_name); +    response.PutChar(';'); +  } + +  PacketResult packet_result = SendPacketNoLock(response.GetString()); +  if (packet_result != PacketResult::Success) { +    if (debugserver_pid != LLDB_INVALID_PROCESS_ID) +      Host::Kill(debugserver_pid, SIGINT); +  } +  return packet_result; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer( +    StringExtractorGDBRemote &packet) { +  if (m_pending_gdb_server.pid == LLDB_INVALID_PROCESS_ID) +    return SendErrorResponse(4); + +  JSONObject::SP server_sp = std::make_shared<JSONObject>(); +  server_sp->SetObject("port", +                       std::make_shared<JSONNumber>(m_pending_gdb_server.port)); +  if (!m_pending_gdb_server.socket_name.empty()) +    server_sp->SetObject( +        "socket_name", +        std::make_shared<JSONString>(m_pending_gdb_server.socket_name.c_str())); + +  JSONArray server_list; +  server_list.AppendObject(server_sp); + +  StreamGDBRemote response; +  server_list.Write(response); + +  StreamGDBRemote escaped_response; +  escaped_response.PutEscapedBytes(response.GetString().data(), +                                   response.GetSize()); +  return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("qKillSpawnedProcess:")); + +  lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); + +  // verify that we know anything about this pid. Scope for locker +  { +    std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); +    if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { +      // not a pid we know about +      return SendErrorResponse(10); +    } +  } + +  // go ahead and attempt to kill the spawned process +  if (KillSpawnedProcess(pid)) +    return SendOKResponse(); +  else +    return SendErrorResponse(11); +} + +bool GDBRemoteCommunicationServerPlatform::KillSpawnedProcess(lldb::pid_t pid) { +  // make sure we know about this process +  { +    std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); +    if (m_spawned_pids.find(pid) == m_spawned_pids.end()) +      return false; +  } + +  // first try a SIGTERM (standard kill) +  Host::Kill(pid, SIGTERM); + +  // check if that worked +  for (size_t i = 0; i < 10; ++i) { +    { +      std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); +      if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { +        // it is now killed +        return true; +      } +    } +    usleep(10000); +  } + +  // check one more time after the final usleep +  { +    std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); +    if (m_spawned_pids.find(pid) == m_spawned_pids.end()) +      return true; +  } + +  // the launched process still lives.  Now try killing it again, this time +  // with an unblockable signal. +  Host::Kill(pid, SIGKILL); + +  for (size_t i = 0; i < 10; ++i) { +    { +      std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); +      if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { +        // it is now killed +        return true; +      } +    } +    usleep(10000); +  } + +  // check one more time after the final usleep Scope for locker +  { +    std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); +    if (m_spawned_pids.find(pid) == m_spawned_pids.end()) +      return true; +  } + +  // no luck - the process still lives +  return false; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo( +    StringExtractorGDBRemote &packet) { +  lldb::pid_t pid = m_process_launch_info.GetProcessID(); +  m_process_launch_info.Clear(); + +  if (pid == LLDB_INVALID_PROCESS_ID) +    return SendErrorResponse(1); + +  ProcessInstanceInfo proc_info; +  if (!Host::GetProcessInfo(pid, proc_info)) +    return SendErrorResponse(1); + +  StreamString response; +  CreateProcessInfoResponse_DebugServerStyle(proc_info, response); +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir( +    StringExtractorGDBRemote &packet) { + +  llvm::SmallString<64> cwd; +  if (std::error_code ec = llvm::sys::fs::current_path(cwd)) +    return SendErrorResponse(ec.value()); + +  StreamString response; +  response.PutBytesAsRawHex8(cwd.data(), cwd.size()); +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir( +    StringExtractorGDBRemote &packet) { +  packet.SetFilePos(::strlen("QSetWorkingDir:")); +  std::string path; +  packet.GetHexByteString(path); + +  if (std::error_code ec = llvm::sys::fs::set_current_path(path)) +    return SendErrorResponse(ec.value()); +  return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qC( +    StringExtractorGDBRemote &packet) { +  // NOTE: lldb should now be using qProcessInfo for process IDs.  This path +  // here +  // should not be used.  It is reporting process id instead of thread id.  The +  // correct answer doesn't seem to make much sense for lldb-platform. +  // CONSIDER: flip to "unsupported". +  lldb::pid_t pid = m_process_launch_info.GetProcessID(); + +  StreamString response; +  response.Printf("QC%" PRIx64, pid); + +  // If we launch a process and this GDB server is acting as a platform, then +  // we need to clear the process launch state so we can start launching +  // another process. In order to launch a process a bunch or packets need to +  // be sent: environment packets, working directory, disable ASLR, and many +  // more settings. When we launch a process we then need to know when to clear +  // this information. Currently we are selecting the 'qC' packet as that +  // packet which seems to make the most sense. +  if (pid != LLDB_INVALID_PROCESS_ID) { +    m_process_launch_info.Clear(); +  } + +  return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo( +    StringExtractorGDBRemote &packet) { +  StructuredData::Array signal_array; + +  lldb::UnixSignalsSP signals = UnixSignals::CreateForHost(); +  for (auto signo = signals->GetFirstSignalNumber(); +       signo != LLDB_INVALID_SIGNAL_NUMBER; +       signo = signals->GetNextSignalNumber(signo)) { +    auto dictionary = std::make_shared<StructuredData::Dictionary>(); + +    dictionary->AddIntegerItem("signo", signo); +    dictionary->AddStringItem("name", signals->GetSignalAsCString(signo)); + +    bool suppress, stop, notify; +    signals->GetSignalInfo(signo, suppress, stop, notify); +    dictionary->AddBooleanItem("suppress", suppress); +    dictionary->AddBooleanItem("stop", stop); +    dictionary->AddBooleanItem("notify", notify); + +    signal_array.Push(dictionary); +  } + +  StreamString response; +  signal_array.Dump(response); +  return SendPacketNoLock(response.GetString()); +} + +bool GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped( +    lldb::pid_t pid) { +  std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); +  FreePortForProcess(pid); +  m_spawned_pids.erase(pid); +  return true; +} + +Status GDBRemoteCommunicationServerPlatform::LaunchProcess() { +  if (!m_process_launch_info.GetArguments().GetArgumentCount()) +    return Status("%s: no process command line specified to launch", +                  __FUNCTION__); + +  // specify the process monitor if not already set.  This should generally be +  // what happens since we need to reap started processes. +  if (!m_process_launch_info.GetMonitorProcessCallback()) +    m_process_launch_info.SetMonitorProcessCallback( +        std::bind( +            &GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, +            this, std::placeholders::_1), +        false); + +  Status error = Host::LaunchProcess(m_process_launch_info); +  if (!error.Success()) { +    fprintf(stderr, "%s: failed to launch executable %s", __FUNCTION__, +            m_process_launch_info.GetArguments().GetArgumentAtIndex(0)); +    return error; +  } + +  printf("Launched '%s' as process %" PRIu64 "...\n", +         m_process_launch_info.GetArguments().GetArgumentAtIndex(0), +         m_process_launch_info.GetProcessID()); + +  // add to list of spawned processes.  On an lldb-gdbserver, we would expect +  // there to be only one. +  const auto pid = m_process_launch_info.GetProcessID(); +  if (pid != LLDB_INVALID_PROCESS_ID) { +    // add to spawned pids +    std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); +    m_spawned_pids.insert(pid); +  } + +  return error; +} + +void GDBRemoteCommunicationServerPlatform::SetPortMap(PortMap &&port_map) { +  m_port_map = port_map; +} + +uint16_t GDBRemoteCommunicationServerPlatform::GetNextAvailablePort() { +  if (m_port_map.empty()) +    return 0; // Bind to port zero and get a port, we didn't have any +              // limitations + +  for (auto &pair : m_port_map) { +    if (pair.second == LLDB_INVALID_PROCESS_ID) { +      pair.second = ~(lldb::pid_t)LLDB_INVALID_PROCESS_ID; +      return pair.first; +    } +  } +  return UINT16_MAX; +} + +bool GDBRemoteCommunicationServerPlatform::AssociatePortWithProcess( +    uint16_t port, lldb::pid_t pid) { +  PortMap::iterator pos = m_port_map.find(port); +  if (pos != m_port_map.end()) { +    pos->second = pid; +    return true; +  } +  return false; +} + +bool GDBRemoteCommunicationServerPlatform::FreePort(uint16_t port) { +  PortMap::iterator pos = m_port_map.find(port); +  if (pos != m_port_map.end()) { +    pos->second = LLDB_INVALID_PROCESS_ID; +    return true; +  } +  return false; +} + +bool GDBRemoteCommunicationServerPlatform::FreePortForProcess(lldb::pid_t pid) { +  if (!m_port_map.empty()) { +    for (auto &pair : m_port_map) { +      if (pair.second == pid) { +        pair.second = LLDB_INVALID_PROCESS_ID; +        return true; +      } +    } +  } +  return false; +} + +const FileSpec &GDBRemoteCommunicationServerPlatform::GetDomainSocketDir() { +  static FileSpec g_domainsocket_dir; +  static llvm::once_flag g_once_flag; + +  llvm::call_once(g_once_flag, []() { +    const char *domainsocket_dir_env = +        ::getenv("LLDB_DEBUGSERVER_DOMAINSOCKET_DIR"); +    if (domainsocket_dir_env != nullptr) +      g_domainsocket_dir = FileSpec(domainsocket_dir_env); +    else +      g_domainsocket_dir = HostInfo::GetProcessTempDir(); +  }); + +  return g_domainsocket_dir; +} + +FileSpec +GDBRemoteCommunicationServerPlatform::GetDomainSocketPath(const char *prefix) { +  llvm::SmallString<128> socket_path; +  llvm::SmallString<128> socket_name( +      (llvm::StringRef(prefix) + ".%%%%%%").str()); + +  FileSpec socket_path_spec(GetDomainSocketDir()); +  socket_path_spec.AppendPathComponent(socket_name.c_str()); + +  llvm::sys::fs::createUniqueFile(socket_path_spec.GetCString(), socket_path); +  return FileSpec(socket_path.c_str()); +} + +void GDBRemoteCommunicationServerPlatform::SetPortOffset(uint16_t port_offset) { +  m_port_offset = port_offset; +} + +void GDBRemoteCommunicationServerPlatform::SetPendingGdbServer( +    lldb::pid_t pid, uint16_t port, const std::string &socket_name) { +  m_pending_gdb_server.pid = pid; +  m_pending_gdb_server.port = port; +  m_pending_gdb_server.socket_name = socket_name; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h new file mode 100644 index 000000000000..eacc99a012db --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h @@ -0,0 +1,109 @@ +//===-- GDBRemoteCommunicationServerPlatform.h ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationServerPlatform_h_ +#define liblldb_GDBRemoteCommunicationServerPlatform_h_ + +#include <map> +#include <mutex> +#include <set> + +#include "GDBRemoteCommunicationServerCommon.h" +#include "lldb/Host/Socket.h" + +namespace lldb_private { +namespace process_gdb_remote { + +class GDBRemoteCommunicationServerPlatform +    : public GDBRemoteCommunicationServerCommon { +public: +  typedef std::map<uint16_t, lldb::pid_t> PortMap; + +  GDBRemoteCommunicationServerPlatform( +      const Socket::SocketProtocol socket_protocol, const char *socket_scheme); + +  ~GDBRemoteCommunicationServerPlatform() override; + +  Status LaunchProcess() override; + +  // Set both ports to zero to let the platform automatically bind to +  // a port chosen by the OS. +  void SetPortMap(PortMap &&port_map); + +  // If we are using a port map where we can only use certain ports, +  // get the next available port. +  // +  // If we are using a port map and we are out of ports, return UINT16_MAX +  // +  // If we aren't using a port map, return 0 to indicate we should bind to +  // port 0 and then figure out which port we used. +  uint16_t GetNextAvailablePort(); + +  bool AssociatePortWithProcess(uint16_t port, lldb::pid_t pid); + +  bool FreePort(uint16_t port); + +  bool FreePortForProcess(lldb::pid_t pid); + +  void SetPortOffset(uint16_t port_offset); + +  void SetInferiorArguments(const lldb_private::Args &args); + +  Status LaunchGDBServer(const lldb_private::Args &args, std::string hostname, +                         lldb::pid_t &pid, uint16_t &port, +                         std::string &socket_name); + +  void SetPendingGdbServer(lldb::pid_t pid, uint16_t port, +                           const std::string &socket_name); + +protected: +  const Socket::SocketProtocol m_socket_protocol; +  const std::string m_socket_scheme; +  std::recursive_mutex m_spawned_pids_mutex; +  std::set<lldb::pid_t> m_spawned_pids; + +  PortMap m_port_map; +  uint16_t m_port_offset; +  struct { +    lldb::pid_t pid; +    uint16_t port; +    std::string socket_name; +  } m_pending_gdb_server; + +  PacketResult Handle_qLaunchGDBServer(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qQueryGDBServer(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qKillSpawnedProcess(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet); + +  PacketResult Handle_QSetWorkingDir(StringExtractorGDBRemote &packet); + +  PacketResult Handle_qC(StringExtractorGDBRemote &packet); + +  PacketResult Handle_jSignalsInfo(StringExtractorGDBRemote &packet); + +private: +  bool KillSpawnedProcess(lldb::pid_t pid); + +  bool DebugserverProcessReaped(lldb::pid_t pid); + +  static const FileSpec &GetDomainSocketDir(); + +  static FileSpec GetDomainSocketPath(const char *prefix); + +  DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationServerPlatform); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_GDBRemoteCommunicationServerPlatform_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp new file mode 100644 index 000000000000..a77e659a55fa --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -0,0 +1,958 @@ +//===-- GDBRemoteRegisterContext.cpp ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteRegisterContext.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Utility/StreamString.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "ThreadGDBRemote.h" +#include "Utility/ARM_DWARF_Registers.h" +#include "Utility/ARM_ehframe_Registers.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +// GDBRemoteRegisterContext constructor +GDBRemoteRegisterContext::GDBRemoteRegisterContext( +    ThreadGDBRemote &thread, uint32_t concrete_frame_idx, +    GDBRemoteDynamicRegisterInfo ®_info, bool read_all_at_once) +    : RegisterContext(thread, concrete_frame_idx), m_reg_info(reg_info), +      m_reg_valid(), m_reg_data(), m_read_all_at_once(read_all_at_once) { +  // Resize our vector of bools to contain one bool for every register. We will +  // use these boolean values to know when a register value is valid in +  // m_reg_data. +  m_reg_valid.resize(reg_info.GetNumRegisters()); + +  // Make a heap based buffer that is big enough to store all registers +  DataBufferSP reg_data_sp( +      new DataBufferHeap(reg_info.GetRegisterDataByteSize(), 0)); +  m_reg_data.SetData(reg_data_sp); +  m_reg_data.SetByteOrder(thread.GetProcess()->GetByteOrder()); +} + +// Destructor +GDBRemoteRegisterContext::~GDBRemoteRegisterContext() {} + +void GDBRemoteRegisterContext::InvalidateAllRegisters() { +  SetAllRegisterValid(false); +} + +void GDBRemoteRegisterContext::SetAllRegisterValid(bool b) { +  std::vector<bool>::iterator pos, end = m_reg_valid.end(); +  for (pos = m_reg_valid.begin(); pos != end; ++pos) +    *pos = b; +} + +size_t GDBRemoteRegisterContext::GetRegisterCount() { +  return m_reg_info.GetNumRegisters(); +} + +const RegisterInfo * +GDBRemoteRegisterContext::GetRegisterInfoAtIndex(size_t reg) { +  RegisterInfo *reg_info = m_reg_info.GetRegisterInfoAtIndex(reg); + +  if (reg_info && reg_info->dynamic_size_dwarf_expr_bytes) { +    const ArchSpec &arch = m_thread.GetProcess()->GetTarget().GetArchitecture(); +    uint8_t reg_size = UpdateDynamicRegisterSize(arch, reg_info); +    reg_info->byte_size = reg_size; +  } +  return reg_info; +} + +size_t GDBRemoteRegisterContext::GetRegisterSetCount() { +  return m_reg_info.GetNumRegisterSets(); +} + +const RegisterSet *GDBRemoteRegisterContext::GetRegisterSet(size_t reg_set) { +  return m_reg_info.GetRegisterSet(reg_set); +} + +bool GDBRemoteRegisterContext::ReadRegister(const RegisterInfo *reg_info, +                                            RegisterValue &value) { +  // Read the register +  if (ReadRegisterBytes(reg_info, m_reg_data)) { +    const bool partial_data_ok = false; +    Status error(value.SetValueFromData( +        reg_info, m_reg_data, reg_info->byte_offset, partial_data_ok)); +    return error.Success(); +  } +  return false; +} + +bool GDBRemoteRegisterContext::PrivateSetRegisterValue( +    uint32_t reg, llvm::ArrayRef<uint8_t> data) { +  const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); +  if (reg_info == nullptr) +    return false; + +  // Invalidate if needed +  InvalidateIfNeeded(false); + +  const size_t reg_byte_size = reg_info->byte_size; +  memcpy(const_cast<uint8_t *>( +             m_reg_data.PeekData(reg_info->byte_offset, reg_byte_size)), +         data.data(), std::min(data.size(), reg_byte_size)); +  bool success = data.size() >= reg_byte_size; +  if (success) { +    SetRegisterIsValid(reg, true); +  } else if (data.size() > 0) { +    // Only set register is valid to false if we copied some bytes, else leave +    // it as it was. +    SetRegisterIsValid(reg, false); +  } +  return success; +} + +bool GDBRemoteRegisterContext::PrivateSetRegisterValue(uint32_t reg, +                                                       uint64_t new_reg_val) { +  const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); +  if (reg_info == nullptr) +    return false; + +  // Early in process startup, we can get a thread that has an invalid byte +  // order because the process hasn't been completely set up yet (see the ctor +  // where the byte order is setfrom the process).  If that's the case, we +  // can't set the value here. +  if (m_reg_data.GetByteOrder() == eByteOrderInvalid) { +    return false; +  } + +  // Invalidate if needed +  InvalidateIfNeeded(false); + +  DataBufferSP buffer_sp(new DataBufferHeap(&new_reg_val, sizeof(new_reg_val))); +  DataExtractor data(buffer_sp, endian::InlHostByteOrder(), sizeof(void *)); + +  // If our register context and our register info disagree, which should never +  // happen, don't overwrite past the end of the buffer. +  if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size) +    return false; + +  // Grab a pointer to where we are going to put this register +  uint8_t *dst = const_cast<uint8_t *>( +      m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size)); + +  if (dst == nullptr) +    return false; + +  if (data.CopyByteOrderedData(0,                          // src offset +                               reg_info->byte_size,        // src length +                               dst,                        // dst +                               reg_info->byte_size,        // dst length +                               m_reg_data.GetByteOrder())) // dst byte order +  { +    SetRegisterIsValid(reg, true); +    return true; +  } +  return false; +} + +// Helper function for GDBRemoteRegisterContext::ReadRegisterBytes(). +bool GDBRemoteRegisterContext::GetPrimordialRegister( +    const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) { +  const uint32_t lldb_reg = reg_info->kinds[eRegisterKindLLDB]; +  const uint32_t remote_reg = reg_info->kinds[eRegisterKindProcessPlugin]; + +  if (DataBufferSP buffer_sp = +          gdb_comm.ReadRegister(m_thread.GetProtocolID(), remote_reg)) +    return PrivateSetRegisterValue( +        lldb_reg, llvm::ArrayRef<uint8_t>(buffer_sp->GetBytes(), +                                          buffer_sp->GetByteSize())); +  return false; +} + +bool GDBRemoteRegisterContext::ReadRegisterBytes(const RegisterInfo *reg_info, +                                                 DataExtractor &data) { +  ExecutionContext exe_ctx(CalculateThread()); + +  Process *process = exe_ctx.GetProcessPtr(); +  Thread *thread = exe_ctx.GetThreadPtr(); +  if (process == nullptr || thread == nullptr) +    return false; + +  GDBRemoteCommunicationClient &gdb_comm( +      ((ProcessGDBRemote *)process)->GetGDBRemote()); + +  InvalidateIfNeeded(false); + +  const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + +  if (!GetRegisterIsValid(reg)) { +    if (m_read_all_at_once) { +      if (DataBufferSP buffer_sp = +              gdb_comm.ReadAllRegisters(m_thread.GetProtocolID())) { +        memcpy(const_cast<uint8_t *>(m_reg_data.GetDataStart()), +               buffer_sp->GetBytes(), +               std::min(buffer_sp->GetByteSize(), m_reg_data.GetByteSize())); +        if (buffer_sp->GetByteSize() >= m_reg_data.GetByteSize()) { +          SetAllRegisterValid(true); +          return true; +        } else { +          Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD | +                                                                GDBR_LOG_PACKETS)); +          if (log) +            log->Printf ("error: GDBRemoteRegisterContext::ReadRegisterBytes tried to read the " +                        "entire register context at once, expected at least %" PRId64 " bytes " +                        "but only got %" PRId64 " bytes.", m_reg_data.GetByteSize(), +                        buffer_sp->GetByteSize()); +        } +      } +      return false; +    } +    if (reg_info->value_regs) { +      // Process this composite register request by delegating to the +      // constituent primordial registers. + +      // Index of the primordial register. +      bool success = true; +      for (uint32_t idx = 0; success; ++idx) { +        const uint32_t prim_reg = reg_info->value_regs[idx]; +        if (prim_reg == LLDB_INVALID_REGNUM) +          break; +        // We have a valid primordial register as our constituent. Grab the +        // corresponding register info. +        const RegisterInfo *prim_reg_info = GetRegisterInfoAtIndex(prim_reg); +        if (prim_reg_info == nullptr) +          success = false; +        else { +          // Read the containing register if it hasn't already been read +          if (!GetRegisterIsValid(prim_reg)) +            success = GetPrimordialRegister(prim_reg_info, gdb_comm); +        } +      } + +      if (success) { +        // If we reach this point, all primordial register requests have +        // succeeded. Validate this composite register. +        SetRegisterIsValid(reg_info, true); +      } +    } else { +      // Get each register individually +      GetPrimordialRegister(reg_info, gdb_comm); +    } + +    // Make sure we got a valid register value after reading it +    if (!GetRegisterIsValid(reg)) +      return false; +  } + +  if (&data != &m_reg_data) { +    assert(m_reg_data.GetByteSize() >= +           reg_info->byte_offset + reg_info->byte_size); +    // If our register context and our register info disagree, which should +    // never happen, don't read past the end of the buffer. +    if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size) +      return false; + +    // If we aren't extracting into our own buffer (which only happens when +    // this function is called from ReadRegisterValue(uint32_t, Scalar&)) then +    // we transfer bytes from our buffer into the data buffer that was passed +    // in + +    data.SetByteOrder(m_reg_data.GetByteOrder()); +    data.SetData(m_reg_data, reg_info->byte_offset, reg_info->byte_size); +  } +  return true; +} + +bool GDBRemoteRegisterContext::WriteRegister(const RegisterInfo *reg_info, +                                             const RegisterValue &value) { +  DataExtractor data; +  if (value.GetData(data)) +    return WriteRegisterBytes(reg_info, data, 0); +  return false; +} + +// Helper function for GDBRemoteRegisterContext::WriteRegisterBytes(). +bool GDBRemoteRegisterContext::SetPrimordialRegister( +    const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) { +  StreamString packet; +  StringExtractorGDBRemote response; +  const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; +  // Invalidate just this register +  SetRegisterIsValid(reg, false); + +  return gdb_comm.WriteRegister( +      m_thread.GetProtocolID(), reg_info->kinds[eRegisterKindProcessPlugin], +      {m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size), +       reg_info->byte_size}); +} + +bool GDBRemoteRegisterContext::WriteRegisterBytes(const RegisterInfo *reg_info, +                                                  DataExtractor &data, +                                                  uint32_t data_offset) { +  ExecutionContext exe_ctx(CalculateThread()); + +  Process *process = exe_ctx.GetProcessPtr(); +  Thread *thread = exe_ctx.GetThreadPtr(); +  if (process == nullptr || thread == nullptr) +    return false; + +  GDBRemoteCommunicationClient &gdb_comm( +      ((ProcessGDBRemote *)process)->GetGDBRemote()); + +  assert(m_reg_data.GetByteSize() >= +         reg_info->byte_offset + reg_info->byte_size); + +  // If our register context and our register info disagree, which should never +  // happen, don't overwrite past the end of the buffer. +  if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size) +    return false; + +  // Grab a pointer to where we are going to put this register +  uint8_t *dst = const_cast<uint8_t *>( +      m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size)); + +  if (dst == nullptr) +    return false; + +  if (data.CopyByteOrderedData(data_offset,                // src offset +                               reg_info->byte_size,        // src length +                               dst,                        // dst +                               reg_info->byte_size,        // dst length +                               m_reg_data.GetByteOrder())) // dst byte order +  { +    GDBRemoteClientBase::Lock lock(gdb_comm, false); +    if (lock) { +      if (m_read_all_at_once) { +        // Invalidate all register values +        InvalidateIfNeeded(true); + +        // Set all registers in one packet +        if (gdb_comm.WriteAllRegisters( +                m_thread.GetProtocolID(), +                {m_reg_data.GetDataStart(), size_t(m_reg_data.GetByteSize())})) + +        { +          SetAllRegisterValid(false); +          return true; +        } +      } else { +        bool success = true; + +        if (reg_info->value_regs) { +          // This register is part of another register. In this case we read +          // the actual register data for any "value_regs", and once all that +          // data is read, we will have enough data in our register context +          // bytes for the value of this register + +          // Invalidate this composite register first. + +          for (uint32_t idx = 0; success; ++idx) { +            const uint32_t reg = reg_info->value_regs[idx]; +            if (reg == LLDB_INVALID_REGNUM) +              break; +            // We have a valid primordial register as our constituent. Grab the +            // corresponding register info. +            const RegisterInfo *value_reg_info = GetRegisterInfoAtIndex(reg); +            if (value_reg_info == nullptr) +              success = false; +            else +              success = SetPrimordialRegister(value_reg_info, gdb_comm); +          } +        } else { +          // This is an actual register, write it +          success = SetPrimordialRegister(reg_info, gdb_comm); +        } + +        // Check if writing this register will invalidate any other register +        // values? If so, invalidate them +        if (reg_info->invalidate_regs) { +          for (uint32_t idx = 0, reg = reg_info->invalidate_regs[0]; +               reg != LLDB_INVALID_REGNUM; +               reg = reg_info->invalidate_regs[++idx]) { +            SetRegisterIsValid(reg, false); +          } +        } + +        return success; +      } +    } else { +      Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD | +                                                             GDBR_LOG_PACKETS)); +      if (log) { +        if (log->GetVerbose()) { +          StreamString strm; +          gdb_comm.DumpHistory(strm); +          log->Printf("error: failed to get packet sequence mutex, not sending " +                      "write register for \"%s\":\n%s", +                      reg_info->name, strm.GetData()); +        } else +          log->Printf("error: failed to get packet sequence mutex, not sending " +                      "write register for \"%s\"", +                      reg_info->name); +      } +    } +  } +  return false; +} + +bool GDBRemoteRegisterContext::ReadAllRegisterValues( +    RegisterCheckpoint ®_checkpoint) { +  ExecutionContext exe_ctx(CalculateThread()); + +  Process *process = exe_ctx.GetProcessPtr(); +  Thread *thread = exe_ctx.GetThreadPtr(); +  if (process == nullptr || thread == nullptr) +    return false; + +  GDBRemoteCommunicationClient &gdb_comm( +      ((ProcessGDBRemote *)process)->GetGDBRemote()); + +  uint32_t save_id = 0; +  if (gdb_comm.SaveRegisterState(thread->GetProtocolID(), save_id)) { +    reg_checkpoint.SetID(save_id); +    reg_checkpoint.GetData().reset(); +    return true; +  } else { +    reg_checkpoint.SetID(0); // Invalid save ID is zero +    return ReadAllRegisterValues(reg_checkpoint.GetData()); +  } +} + +bool GDBRemoteRegisterContext::WriteAllRegisterValues( +    const RegisterCheckpoint ®_checkpoint) { +  uint32_t save_id = reg_checkpoint.GetID(); +  if (save_id != 0) { +    ExecutionContext exe_ctx(CalculateThread()); + +    Process *process = exe_ctx.GetProcessPtr(); +    Thread *thread = exe_ctx.GetThreadPtr(); +    if (process == nullptr || thread == nullptr) +      return false; + +    GDBRemoteCommunicationClient &gdb_comm( +        ((ProcessGDBRemote *)process)->GetGDBRemote()); + +    return gdb_comm.RestoreRegisterState(m_thread.GetProtocolID(), save_id); +  } else { +    return WriteAllRegisterValues(reg_checkpoint.GetData()); +  } +} + +bool GDBRemoteRegisterContext::ReadAllRegisterValues( +    lldb::DataBufferSP &data_sp) { +  ExecutionContext exe_ctx(CalculateThread()); + +  Process *process = exe_ctx.GetProcessPtr(); +  Thread *thread = exe_ctx.GetThreadPtr(); +  if (process == nullptr || thread == nullptr) +    return false; + +  GDBRemoteCommunicationClient &gdb_comm( +      ((ProcessGDBRemote *)process)->GetGDBRemote()); + +  const bool use_g_packet = +      !gdb_comm.AvoidGPackets((ProcessGDBRemote *)process); + +  GDBRemoteClientBase::Lock lock(gdb_comm, false); +  if (lock) { +    if (gdb_comm.SyncThreadState(m_thread.GetProtocolID())) +      InvalidateAllRegisters(); + +    if (use_g_packet && +        (data_sp = gdb_comm.ReadAllRegisters(m_thread.GetProtocolID()))) +      return true; + +    // We're going to read each register +    // individually and store them as binary data in a buffer. +    const RegisterInfo *reg_info; + +    for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex(i)) != nullptr; +         i++) { +      if (reg_info +              ->value_regs) // skip registers that are slices of real registers +        continue; +      ReadRegisterBytes(reg_info, m_reg_data); +      // ReadRegisterBytes saves the contents of the register in to the +      // m_reg_data buffer +    } +    data_sp = std::make_shared<DataBufferHeap>( +        m_reg_data.GetDataStart(), m_reg_info.GetRegisterDataByteSize()); +    return true; +  } else { + +    Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD | +                                                           GDBR_LOG_PACKETS)); +    if (log) { +      if (log->GetVerbose()) { +        StreamString strm; +        gdb_comm.DumpHistory(strm); +        log->Printf("error: failed to get packet sequence mutex, not sending " +                    "read all registers:\n%s", +                    strm.GetData()); +      } else +        log->Printf("error: failed to get packet sequence mutex, not sending " +                    "read all registers"); +    } +  } + +  data_sp.reset(); +  return false; +} + +bool GDBRemoteRegisterContext::WriteAllRegisterValues( +    const lldb::DataBufferSP &data_sp) { +  if (!data_sp || data_sp->GetBytes() == nullptr || data_sp->GetByteSize() == 0) +    return false; + +  ExecutionContext exe_ctx(CalculateThread()); + +  Process *process = exe_ctx.GetProcessPtr(); +  Thread *thread = exe_ctx.GetThreadPtr(); +  if (process == nullptr || thread == nullptr) +    return false; + +  GDBRemoteCommunicationClient &gdb_comm( +      ((ProcessGDBRemote *)process)->GetGDBRemote()); + +  const bool use_g_packet = +      !gdb_comm.AvoidGPackets((ProcessGDBRemote *)process); + +  GDBRemoteClientBase::Lock lock(gdb_comm, false); +  if (lock) { +    // The data_sp contains the G response packet. +    if (use_g_packet) { +      if (gdb_comm.WriteAllRegisters( +              m_thread.GetProtocolID(), +              {data_sp->GetBytes(), size_t(data_sp->GetByteSize())})) +        return true; + +      uint32_t num_restored = 0; +      // We need to manually go through all of the registers and restore them +      // manually +      DataExtractor restore_data(data_sp, m_reg_data.GetByteOrder(), +                                 m_reg_data.GetAddressByteSize()); + +      const RegisterInfo *reg_info; + +      // The g packet contents may either include the slice registers +      // (registers defined in terms of other registers, e.g. eax is a subset +      // of rax) or not.  The slice registers should NOT be in the g packet, +      // but some implementations may incorrectly include them. +      // +      // If the slice registers are included in the packet, we must step over +      // the slice registers when parsing the packet -- relying on the +      // RegisterInfo byte_offset field would be incorrect. If the slice +      // registers are not included, then using the byte_offset values into the +      // data buffer is the best way to find individual register values. + +      uint64_t size_including_slice_registers = 0; +      uint64_t size_not_including_slice_registers = 0; +      uint64_t size_by_highest_offset = 0; + +      for (uint32_t reg_idx = 0; +           (reg_info = GetRegisterInfoAtIndex(reg_idx)) != nullptr; ++reg_idx) { +        size_including_slice_registers += reg_info->byte_size; +        if (reg_info->value_regs == nullptr) +          size_not_including_slice_registers += reg_info->byte_size; +        if (reg_info->byte_offset >= size_by_highest_offset) +          size_by_highest_offset = reg_info->byte_offset + reg_info->byte_size; +      } + +      bool use_byte_offset_into_buffer; +      if (size_by_highest_offset == restore_data.GetByteSize()) { +        // The size of the packet agrees with the highest offset: + size in the +        // register file +        use_byte_offset_into_buffer = true; +      } else if (size_not_including_slice_registers == +                 restore_data.GetByteSize()) { +        // The size of the packet is the same as concatenating all of the +        // registers sequentially, skipping the slice registers +        use_byte_offset_into_buffer = true; +      } else if (size_including_slice_registers == restore_data.GetByteSize()) { +        // The slice registers are present in the packet (when they shouldn't +        // be). Don't try to use the RegisterInfo byte_offset into the +        // restore_data, it will point to the wrong place. +        use_byte_offset_into_buffer = false; +      } else { +        // None of our expected sizes match the actual g packet data we're +        // looking at. The most conservative approach here is to use the +        // running total byte offset. +        use_byte_offset_into_buffer = false; +      } + +      // In case our register definitions don't include the correct offsets, +      // keep track of the size of each reg & compute offset based on that. +      uint32_t running_byte_offset = 0; +      for (uint32_t reg_idx = 0; +           (reg_info = GetRegisterInfoAtIndex(reg_idx)) != nullptr; +           ++reg_idx, running_byte_offset += reg_info->byte_size) { +        // Skip composite aka slice registers (e.g. eax is a slice of rax). +        if (reg_info->value_regs) +          continue; + +        const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + +        uint32_t register_offset; +        if (use_byte_offset_into_buffer) { +          register_offset = reg_info->byte_offset; +        } else { +          register_offset = running_byte_offset; +        } + +        const uint32_t reg_byte_size = reg_info->byte_size; + +        const uint8_t *restore_src = +            restore_data.PeekData(register_offset, reg_byte_size); +        if (restore_src) { +          SetRegisterIsValid(reg, false); +          if (gdb_comm.WriteRegister( +                  m_thread.GetProtocolID(), +                  reg_info->kinds[eRegisterKindProcessPlugin], +                  {restore_src, reg_byte_size})) +            ++num_restored; +        } +      } +      return num_restored > 0; +    } else { +      // For the use_g_packet == false case, we're going to write each register +      // individually.  The data buffer is binary data in this case, instead of +      // ascii characters. + +      bool arm64_debugserver = false; +      if (m_thread.GetProcess().get()) { +        const ArchSpec &arch = +            m_thread.GetProcess()->GetTarget().GetArchitecture(); +        if (arch.IsValid() && arch.GetMachine() == llvm::Triple::aarch64 && +            arch.GetTriple().getVendor() == llvm::Triple::Apple && +            arch.GetTriple().getOS() == llvm::Triple::IOS) { +          arm64_debugserver = true; +        } +      } +      uint32_t num_restored = 0; +      const RegisterInfo *reg_info; +      for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex(i)) != nullptr; +           i++) { +        if (reg_info->value_regs) // skip registers that are slices of real +                                  // registers +          continue; +        // Skip the fpsr and fpcr floating point status/control register +        // writing to work around a bug in an older version of debugserver that +        // would lead to register context corruption when writing fpsr/fpcr. +        if (arm64_debugserver && (strcmp(reg_info->name, "fpsr") == 0 || +                                  strcmp(reg_info->name, "fpcr") == 0)) { +          continue; +        } + +        SetRegisterIsValid(reg_info, false); +        if (gdb_comm.WriteRegister(m_thread.GetProtocolID(), +                                   reg_info->kinds[eRegisterKindProcessPlugin], +                                   {data_sp->GetBytes() + reg_info->byte_offset, +                                    reg_info->byte_size})) +          ++num_restored; +      } +      return num_restored > 0; +    } +  } else { +    Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_THREAD | +                                                           GDBR_LOG_PACKETS)); +    if (log) { +      if (log->GetVerbose()) { +        StreamString strm; +        gdb_comm.DumpHistory(strm); +        log->Printf("error: failed to get packet sequence mutex, not sending " +                    "write all registers:\n%s", +                    strm.GetData()); +      } else +        log->Printf("error: failed to get packet sequence mutex, not sending " +                    "write all registers"); +    } +  } +  return false; +} + +uint32_t GDBRemoteRegisterContext::ConvertRegisterKindToRegisterNumber( +    lldb::RegisterKind kind, uint32_t num) { +  return m_reg_info.ConvertRegisterKindToRegisterNumber(kind, num); +} + +void GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters(bool from_scratch) { +  // For Advanced SIMD and VFP register mapping. +  static uint32_t g_d0_regs[] = {26, 27, LLDB_INVALID_REGNUM};  // (s0, s1) +  static uint32_t g_d1_regs[] = {28, 29, LLDB_INVALID_REGNUM};  // (s2, s3) +  static uint32_t g_d2_regs[] = {30, 31, LLDB_INVALID_REGNUM};  // (s4, s5) +  static uint32_t g_d3_regs[] = {32, 33, LLDB_INVALID_REGNUM};  // (s6, s7) +  static uint32_t g_d4_regs[] = {34, 35, LLDB_INVALID_REGNUM};  // (s8, s9) +  static uint32_t g_d5_regs[] = {36, 37, LLDB_INVALID_REGNUM};  // (s10, s11) +  static uint32_t g_d6_regs[] = {38, 39, LLDB_INVALID_REGNUM};  // (s12, s13) +  static uint32_t g_d7_regs[] = {40, 41, LLDB_INVALID_REGNUM};  // (s14, s15) +  static uint32_t g_d8_regs[] = {42, 43, LLDB_INVALID_REGNUM};  // (s16, s17) +  static uint32_t g_d9_regs[] = {44, 45, LLDB_INVALID_REGNUM};  // (s18, s19) +  static uint32_t g_d10_regs[] = {46, 47, LLDB_INVALID_REGNUM}; // (s20, s21) +  static uint32_t g_d11_regs[] = {48, 49, LLDB_INVALID_REGNUM}; // (s22, s23) +  static uint32_t g_d12_regs[] = {50, 51, LLDB_INVALID_REGNUM}; // (s24, s25) +  static uint32_t g_d13_regs[] = {52, 53, LLDB_INVALID_REGNUM}; // (s26, s27) +  static uint32_t g_d14_regs[] = {54, 55, LLDB_INVALID_REGNUM}; // (s28, s29) +  static uint32_t g_d15_regs[] = {56, 57, LLDB_INVALID_REGNUM}; // (s30, s31) +  static uint32_t g_q0_regs[] = { +      26, 27, 28, 29, LLDB_INVALID_REGNUM}; // (d0, d1) -> (s0, s1, s2, s3) +  static uint32_t g_q1_regs[] = { +      30, 31, 32, 33, LLDB_INVALID_REGNUM}; // (d2, d3) -> (s4, s5, s6, s7) +  static uint32_t g_q2_regs[] = { +      34, 35, 36, 37, LLDB_INVALID_REGNUM}; // (d4, d5) -> (s8, s9, s10, s11) +  static uint32_t g_q3_regs[] = { +      38, 39, 40, 41, LLDB_INVALID_REGNUM}; // (d6, d7) -> (s12, s13, s14, s15) +  static uint32_t g_q4_regs[] = { +      42, 43, 44, 45, LLDB_INVALID_REGNUM}; // (d8, d9) -> (s16, s17, s18, s19) +  static uint32_t g_q5_regs[] = { +      46, 47, 48, 49, +      LLDB_INVALID_REGNUM}; // (d10, d11) -> (s20, s21, s22, s23) +  static uint32_t g_q6_regs[] = { +      50, 51, 52, 53, +      LLDB_INVALID_REGNUM}; // (d12, d13) -> (s24, s25, s26, s27) +  static uint32_t g_q7_regs[] = { +      54, 55, 56, 57, +      LLDB_INVALID_REGNUM}; // (d14, d15) -> (s28, s29, s30, s31) +  static uint32_t g_q8_regs[] = {59, 60, LLDB_INVALID_REGNUM};  // (d16, d17) +  static uint32_t g_q9_regs[] = {61, 62, LLDB_INVALID_REGNUM};  // (d18, d19) +  static uint32_t g_q10_regs[] = {63, 64, LLDB_INVALID_REGNUM}; // (d20, d21) +  static uint32_t g_q11_regs[] = {65, 66, LLDB_INVALID_REGNUM}; // (d22, d23) +  static uint32_t g_q12_regs[] = {67, 68, LLDB_INVALID_REGNUM}; // (d24, d25) +  static uint32_t g_q13_regs[] = {69, 70, LLDB_INVALID_REGNUM}; // (d26, d27) +  static uint32_t g_q14_regs[] = {71, 72, LLDB_INVALID_REGNUM}; // (d28, d29) +  static uint32_t g_q15_regs[] = {73, 74, LLDB_INVALID_REGNUM}; // (d30, d31) + +  // This is our array of composite registers, with each element coming from +  // the above register mappings. +  static uint32_t *g_composites[] = { +      g_d0_regs,  g_d1_regs,  g_d2_regs,  g_d3_regs,  g_d4_regs,  g_d5_regs, +      g_d6_regs,  g_d7_regs,  g_d8_regs,  g_d9_regs,  g_d10_regs, g_d11_regs, +      g_d12_regs, g_d13_regs, g_d14_regs, g_d15_regs, g_q0_regs,  g_q1_regs, +      g_q2_regs,  g_q3_regs,  g_q4_regs,  g_q5_regs,  g_q6_regs,  g_q7_regs, +      g_q8_regs,  g_q9_regs,  g_q10_regs, g_q11_regs, g_q12_regs, g_q13_regs, +      g_q14_regs, g_q15_regs}; + +  // clang-format off +    static RegisterInfo g_register_infos[] = { +//   NAME     ALT     SZ   OFF  ENCODING          FORMAT          EH_FRAME             DWARF                GENERIC                 PROCESS PLUGIN  LLDB    VALUE REGS    INVALIDATE REGS SIZE EXPR SIZE LEN +//   ======   ======  ===  ===  =============     ==========      ===================  ===================  ======================  =============   ====    ==========    =============== ========= ======== +    { "r0",   "arg1",   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r0,          dwarf_r0,            LLDB_REGNUM_GENERIC_ARG1,0,               0 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r1",   "arg2",   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r1,          dwarf_r1,            LLDB_REGNUM_GENERIC_ARG2,1,               1 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r2",   "arg3",   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r2,          dwarf_r2,            LLDB_REGNUM_GENERIC_ARG3,2,               2 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r3",   "arg4",   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r3,          dwarf_r3,            LLDB_REGNUM_GENERIC_ARG4,3,               3 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r4",  nullptr,   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r4,          dwarf_r4,            LLDB_INVALID_REGNUM,     4,               4 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r5",  nullptr,   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r5,          dwarf_r5,            LLDB_INVALID_REGNUM,     5,               5 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r6",  nullptr,   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r6,          dwarf_r6,            LLDB_INVALID_REGNUM,     6,               6 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r7",     "fp",   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r7,          dwarf_r7,            LLDB_REGNUM_GENERIC_FP,  7,               7 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r8",  nullptr,   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r8,          dwarf_r8,            LLDB_INVALID_REGNUM,     8,               8 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r9",  nullptr,   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r9,          dwarf_r9,            LLDB_INVALID_REGNUM,     9,               9 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r10", nullptr,   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r10,         dwarf_r10,           LLDB_INVALID_REGNUM,    10,              10 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r11", nullptr,   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r11,         dwarf_r11,           LLDB_INVALID_REGNUM,    11,              11 },     nullptr,           nullptr,  nullptr,       0 }, +    { "r12", nullptr,   4,   0, eEncodingUint,    eFormatHex,   { ehframe_r12,         dwarf_r12,           LLDB_INVALID_REGNUM,    12,              12 },     nullptr,           nullptr,  nullptr,       0 }, +    { "sp",     "r13",  4,   0, eEncodingUint,    eFormatHex,   { ehframe_sp,          dwarf_sp,            LLDB_REGNUM_GENERIC_SP, 13,              13 },     nullptr,           nullptr,  nullptr,       0 }, +    { "lr",     "r14",  4,   0, eEncodingUint,    eFormatHex,   { ehframe_lr,          dwarf_lr,            LLDB_REGNUM_GENERIC_RA, 14,              14 },     nullptr,           nullptr,  nullptr,       0 }, +    { "pc",     "r15",  4,   0, eEncodingUint,    eFormatHex,   { ehframe_pc,          dwarf_pc,            LLDB_REGNUM_GENERIC_PC, 15,              15 },     nullptr,           nullptr,  nullptr,       0 }, +    { "f0",  nullptr,  12,   0, eEncodingUint,    eFormatHex,   { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,    16,              16 },     nullptr,           nullptr,  nullptr,       0 }, +    { "f1",  nullptr,  12,   0, eEncodingUint,    eFormatHex,   { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,    17,              17 },     nullptr,           nullptr,  nullptr,       0 }, +    { "f2",  nullptr,  12,   0, eEncodingUint,    eFormatHex,   { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,    18,              18 },     nullptr,           nullptr,  nullptr,       0 }, +    { "f3",  nullptr,  12,   0, eEncodingUint,    eFormatHex,   { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,    19,              19 },     nullptr,           nullptr,  nullptr,       0 }, +    { "f4",  nullptr,  12,   0, eEncodingUint,    eFormatHex,   { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,    20,              20 },     nullptr,           nullptr,  nullptr,       0 }, +    { "f5",  nullptr,  12,   0, eEncodingUint,    eFormatHex,   { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,    21,              21 },     nullptr,           nullptr,  nullptr,       0 }, +    { "f6",  nullptr,  12,   0, eEncodingUint,    eFormatHex,   { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,    22,              22 },     nullptr,           nullptr,  nullptr,       0 }, +    { "f7",  nullptr,  12,   0, eEncodingUint,    eFormatHex,   { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,    23,              23 },     nullptr,           nullptr,  nullptr,       0 }, +    { "fps", nullptr,   4,   0, eEncodingUint,    eFormatHex,   { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,    24,              24 },     nullptr,           nullptr,  nullptr,       0 }, +    { "cpsr","flags",   4,   0, eEncodingUint,    eFormatHex,   { ehframe_cpsr,        dwarf_cpsr,          LLDB_INVALID_REGNUM,    25,              25 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s0",  nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s0,            LLDB_INVALID_REGNUM,    26,              26 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s1",  nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s1,            LLDB_INVALID_REGNUM,    27,              27 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s2",  nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s2,            LLDB_INVALID_REGNUM,    28,              28 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s3",  nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s3,            LLDB_INVALID_REGNUM,    29,              29 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s4",  nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s4,            LLDB_INVALID_REGNUM,    30,              30 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s5",  nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s5,            LLDB_INVALID_REGNUM,    31,              31 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s6",  nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s6,            LLDB_INVALID_REGNUM,    32,              32 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s7",  nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s7,            LLDB_INVALID_REGNUM,    33,              33 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s8",  nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s8,            LLDB_INVALID_REGNUM,    34,              34 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s9",  nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s9,            LLDB_INVALID_REGNUM,    35,              35 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s10", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s10,           LLDB_INVALID_REGNUM,    36,              36 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s11", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s11,           LLDB_INVALID_REGNUM,    37,              37 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s12", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s12,           LLDB_INVALID_REGNUM,    38,              38 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s13", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s13,           LLDB_INVALID_REGNUM,    39,              39 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s14", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s14,           LLDB_INVALID_REGNUM,    40,              40 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s15", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s15,           LLDB_INVALID_REGNUM,    41,              41 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s16", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s16,           LLDB_INVALID_REGNUM,    42,              42 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s17", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s17,           LLDB_INVALID_REGNUM,    43,              43 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s18", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s18,           LLDB_INVALID_REGNUM,    44,              44 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s19", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s19,           LLDB_INVALID_REGNUM,    45,              45 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s20", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s20,           LLDB_INVALID_REGNUM,    46,              46 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s21", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s21,           LLDB_INVALID_REGNUM,    47,              47 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s22", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s22,           LLDB_INVALID_REGNUM,    48,              48 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s23", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s23,           LLDB_INVALID_REGNUM,    49,              49 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s24", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s24,           LLDB_INVALID_REGNUM,    50,              50 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s25", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s25,           LLDB_INVALID_REGNUM,    51,              51 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s26", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s26,           LLDB_INVALID_REGNUM,    52,              52 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s27", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s27,           LLDB_INVALID_REGNUM,    53,              53 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s28", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s28,           LLDB_INVALID_REGNUM,    54,              54 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s29", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s29,           LLDB_INVALID_REGNUM,    55,              55 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s30", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s30,           LLDB_INVALID_REGNUM,    56,              56 },     nullptr,           nullptr,  nullptr,       0 }, +    { "s31", nullptr,   4,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s31,           LLDB_INVALID_REGNUM,    57,              57 },     nullptr,           nullptr,  nullptr,       0 }, +    { "fpscr",nullptr,  4,   0, eEncodingUint,    eFormatHex,   { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,    58,              58 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d16", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d16,           LLDB_INVALID_REGNUM,    59,              59 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d17", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d17,           LLDB_INVALID_REGNUM,    60,              60 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d18", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d18,           LLDB_INVALID_REGNUM,    61,              61 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d19", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d19,           LLDB_INVALID_REGNUM,    62,              62 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d20", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d20,           LLDB_INVALID_REGNUM,    63,              63 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d21", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d21,           LLDB_INVALID_REGNUM,    64,              64 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d22", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d22,           LLDB_INVALID_REGNUM,    65,              65 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d23", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d23,           LLDB_INVALID_REGNUM,    66,              66 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d24", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d24,           LLDB_INVALID_REGNUM,    67,              67 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d25", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d25,           LLDB_INVALID_REGNUM,    68,              68 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d26", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d26,           LLDB_INVALID_REGNUM,    69,              69 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d27", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d27,           LLDB_INVALID_REGNUM,    70,              70 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d28", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d28,           LLDB_INVALID_REGNUM,    71,              71 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d29", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d29,           LLDB_INVALID_REGNUM,    72,              72 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d30", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d30,           LLDB_INVALID_REGNUM,    73,              73 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d31", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d31,           LLDB_INVALID_REGNUM,    74,              74 },     nullptr,           nullptr,  nullptr,       0 }, +    { "d0",  nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d0,            LLDB_INVALID_REGNUM,    75,              75 },   g_d0_regs,           nullptr,  nullptr,       0 }, +    { "d1",  nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d1,            LLDB_INVALID_REGNUM,    76,              76 },   g_d1_regs,           nullptr,  nullptr,       0 }, +    { "d2",  nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d2,            LLDB_INVALID_REGNUM,    77,              77 },   g_d2_regs,           nullptr,  nullptr,       0 }, +    { "d3",  nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d3,            LLDB_INVALID_REGNUM,    78,              78 },   g_d3_regs,           nullptr,  nullptr,       0 }, +    { "d4",  nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d4,            LLDB_INVALID_REGNUM,    79,              79 },   g_d4_regs,           nullptr,  nullptr,       0 }, +    { "d5",  nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d5,            LLDB_INVALID_REGNUM,    80,              80 },   g_d5_regs,           nullptr,  nullptr,       0 }, +    { "d6",  nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d6,            LLDB_INVALID_REGNUM,    81,              81 },   g_d6_regs,           nullptr,  nullptr,       0 }, +    { "d7",  nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d7,            LLDB_INVALID_REGNUM,    82,              82 },   g_d7_regs,           nullptr,  nullptr,       0 }, +    { "d8",  nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d8,            LLDB_INVALID_REGNUM,    83,              83 },   g_d8_regs,           nullptr,  nullptr,       0 }, +    { "d9",  nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d9,            LLDB_INVALID_REGNUM,    84,              84 },   g_d9_regs,           nullptr,  nullptr,       0 }, +    { "d10", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d10,           LLDB_INVALID_REGNUM,    85,              85 },  g_d10_regs,           nullptr,  nullptr,       0 }, +    { "d11", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d11,           LLDB_INVALID_REGNUM,    86,              86 },  g_d11_regs,           nullptr,  nullptr,       0 }, +    { "d12", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d12,           LLDB_INVALID_REGNUM,    87,              87 },  g_d12_regs,           nullptr,  nullptr,       0 }, +    { "d13", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d13,           LLDB_INVALID_REGNUM,    88,              88 },  g_d13_regs,           nullptr,  nullptr,       0 }, +    { "d14", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d14,           LLDB_INVALID_REGNUM,    89,              89 },  g_d14_regs,           nullptr,  nullptr,       0 }, +    { "d15", nullptr,   8,   0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d15,           LLDB_INVALID_REGNUM,    90,              90 },  g_d15_regs,           nullptr,  nullptr,       0 }, +    { "q0",  nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q0,    LLDB_INVALID_REGNUM,    91,              91 },   g_q0_regs,           nullptr,  nullptr,       0 }, +    { "q1",  nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q1,    LLDB_INVALID_REGNUM,    92,              92 },   g_q1_regs,           nullptr,  nullptr,       0 }, +    { "q2",  nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q2,    LLDB_INVALID_REGNUM,    93,              93 },   g_q2_regs,           nullptr,  nullptr,       0 }, +    { "q3",  nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q3,    LLDB_INVALID_REGNUM,    94,              94 },   g_q3_regs,           nullptr,  nullptr,       0 }, +    { "q4",  nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q4,    LLDB_INVALID_REGNUM,    95,              95 },   g_q4_regs,           nullptr,  nullptr,       0 }, +    { "q5",  nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q5,    LLDB_INVALID_REGNUM,    96,              96 },   g_q5_regs,           nullptr,  nullptr,       0 }, +    { "q6",  nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q6,    LLDB_INVALID_REGNUM,    97,              97 },   g_q6_regs,           nullptr,  nullptr,       0 }, +    { "q7",  nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q7,    LLDB_INVALID_REGNUM,    98,              98 },   g_q7_regs,           nullptr,  nullptr,       0 }, +    { "q8",  nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q8,    LLDB_INVALID_REGNUM,    99,              99 },   g_q8_regs,           nullptr,  nullptr,       0 }, +    { "q9",  nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q9,    LLDB_INVALID_REGNUM,   100,             100 },   g_q9_regs,           nullptr,  nullptr,       0 }, +    { "q10", nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q10,   LLDB_INVALID_REGNUM,   101,             101 },  g_q10_regs,           nullptr,  nullptr,       0 }, +    { "q11", nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q11,   LLDB_INVALID_REGNUM,   102,             102 },  g_q11_regs,           nullptr,  nullptr,       0 }, +    { "q12", nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q12,   LLDB_INVALID_REGNUM,   103,             103 },  g_q12_regs,           nullptr,  nullptr,       0 }, +    { "q13", nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q13,   LLDB_INVALID_REGNUM,   104,             104 },  g_q13_regs,           nullptr,  nullptr,       0 }, +    { "q14", nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q14,   LLDB_INVALID_REGNUM,   105,             105 },  g_q14_regs,           nullptr,  nullptr,       0 }, +    { "q15", nullptr,   16,  0, eEncodingVector,  eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q15,   LLDB_INVALID_REGNUM,   106,             106 },  g_q15_regs,           nullptr,  nullptr,       0 } +    }; +  // clang-format on + +  static const uint32_t num_registers = llvm::array_lengthof(g_register_infos); +  static ConstString gpr_reg_set("General Purpose Registers"); +  static ConstString sfp_reg_set("Software Floating Point Registers"); +  static ConstString vfp_reg_set("Floating Point Registers"); +  size_t i; +  if (from_scratch) { +    // Calculate the offsets of the registers +    // Note that the layout of the "composite" registers (d0-d15 and q0-q15) +    // which comes after the "primordial" registers is important.  This enables +    // us to calculate the offset of the composite register by using the offset +    // of its first primordial register.  For example, to calculate the offset +    // of q0, use s0's offset. +    if (g_register_infos[2].byte_offset == 0) { +      uint32_t byte_offset = 0; +      for (i = 0; i < num_registers; ++i) { +        // For primordial registers, increment the byte_offset by the byte_size +        // to arrive at the byte_offset for the next register.  Otherwise, we +        // have a composite register whose offset can be calculated by +        // consulting the offset of its first primordial register. +        if (!g_register_infos[i].value_regs) { +          g_register_infos[i].byte_offset = byte_offset; +          byte_offset += g_register_infos[i].byte_size; +        } else { +          const uint32_t first_primordial_reg = +              g_register_infos[i].value_regs[0]; +          g_register_infos[i].byte_offset = +              g_register_infos[first_primordial_reg].byte_offset; +        } +      } +    } +    for (i = 0; i < num_registers; ++i) { +      ConstString name; +      ConstString alt_name; +      if (g_register_infos[i].name && g_register_infos[i].name[0]) +        name.SetCString(g_register_infos[i].name); +      if (g_register_infos[i].alt_name && g_register_infos[i].alt_name[0]) +        alt_name.SetCString(g_register_infos[i].alt_name); + +      if (i <= 15 || i == 25) +        AddRegister(g_register_infos[i], name, alt_name, gpr_reg_set); +      else if (i <= 24) +        AddRegister(g_register_infos[i], name, alt_name, sfp_reg_set); +      else +        AddRegister(g_register_infos[i], name, alt_name, vfp_reg_set); +    } +  } else { +    // Add composite registers to our primordial registers, then. +    const size_t num_composites = llvm::array_lengthof(g_composites); +    const size_t num_dynamic_regs = GetNumRegisters(); +    const size_t num_common_regs = num_registers - num_composites; +    RegisterInfo *g_comp_register_infos = g_register_infos + num_common_regs; + +    // First we need to validate that all registers that we already have match +    // the non composite regs. If so, then we can add the registers, else we +    // need to bail +    bool match = true; +    if (num_dynamic_regs == num_common_regs) { +      for (i = 0; match && i < num_dynamic_regs; ++i) { +        // Make sure all register names match +        if (m_regs[i].name && g_register_infos[i].name) { +          if (strcmp(m_regs[i].name, g_register_infos[i].name)) { +            match = false; +            break; +          } +        } + +        // Make sure all register byte sizes match +        if (m_regs[i].byte_size != g_register_infos[i].byte_size) { +          match = false; +          break; +        } +      } +    } else { +      // Wrong number of registers. +      match = false; +    } +    // If "match" is true, then we can add extra registers. +    if (match) { +      for (i = 0; i < num_composites; ++i) { +        ConstString name; +        ConstString alt_name; +        const uint32_t first_primordial_reg = +            g_comp_register_infos[i].value_regs[0]; +        const char *reg_name = g_register_infos[first_primordial_reg].name; +        if (reg_name && reg_name[0]) { +          for (uint32_t j = 0; j < num_dynamic_regs; ++j) { +            const RegisterInfo *reg_info = GetRegisterInfoAtIndex(j); +            // Find a matching primordial register info entry. +            if (reg_info && reg_info->name && +                ::strcasecmp(reg_info->name, reg_name) == 0) { +              // The name matches the existing primordial entry. Find and +              // assign the offset, and then add this composite register entry. +              g_comp_register_infos[i].byte_offset = reg_info->byte_offset; +              name.SetCString(g_comp_register_infos[i].name); +              AddRegister(g_comp_register_infos[i], name, alt_name, +                          vfp_reg_set); +            } +          } +        } +      } +    } +  } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h new file mode 100644 index 000000000000..25e9b716f8cb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -0,0 +1,132 @@ +//===-- GDBRemoteRegisterContext.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_GDBRemoteRegisterContext_h_ +#define lldb_GDBRemoteRegisterContext_h_ + +#include <vector> + +#include "Plugins/Process/Utility/DynamicRegisterInfo.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-private.h" + +#include "GDBRemoteCommunicationClient.h" + +class StringExtractor; + +namespace lldb_private { +namespace process_gdb_remote { + +class ThreadGDBRemote; +class ProcessGDBRemote; + +class GDBRemoteDynamicRegisterInfo : public DynamicRegisterInfo { +public: +  GDBRemoteDynamicRegisterInfo() : DynamicRegisterInfo() {} + +  ~GDBRemoteDynamicRegisterInfo() override = default; + +  void HardcodeARMRegisters(bool from_scratch); +}; + +class GDBRemoteRegisterContext : public RegisterContext { +public: +  GDBRemoteRegisterContext(ThreadGDBRemote &thread, uint32_t concrete_frame_idx, +                           GDBRemoteDynamicRegisterInfo ®_info, +                           bool read_all_at_once); + +  ~GDBRemoteRegisterContext() override; + +  void InvalidateAllRegisters() override; + +  size_t GetRegisterCount() override; + +  const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + +  size_t GetRegisterSetCount() override; + +  const RegisterSet *GetRegisterSet(size_t reg_set) override; + +  bool ReadRegister(const RegisterInfo *reg_info, +                    RegisterValue &value) override; + +  bool WriteRegister(const RegisterInfo *reg_info, +                     const RegisterValue &value) override; + +  bool ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + +  bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + +  bool ReadAllRegisterValues(RegisterCheckpoint ®_checkpoint) override; + +  bool +  WriteAllRegisterValues(const RegisterCheckpoint ®_checkpoint) override; + +  uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, +                                               uint32_t num) override; + +protected: +  friend class ThreadGDBRemote; + +  bool ReadRegisterBytes(const RegisterInfo *reg_info, DataExtractor &data); + +  bool WriteRegisterBytes(const RegisterInfo *reg_info, DataExtractor &data, +                          uint32_t data_offset); + +  bool PrivateSetRegisterValue(uint32_t reg, llvm::ArrayRef<uint8_t> data); + +  bool PrivateSetRegisterValue(uint32_t reg, uint64_t val); + +  void SetAllRegisterValid(bool b); + +  bool GetRegisterIsValid(uint32_t reg) const { +#if defined(LLDB_CONFIGURATION_DEBUG) +    assert(reg < m_reg_valid.size()); +#endif +    if (reg < m_reg_valid.size()) +      return m_reg_valid[reg]; +    return false; +  } + +  void SetRegisterIsValid(const RegisterInfo *reg_info, bool valid) { +    if (reg_info) +      return SetRegisterIsValid(reg_info->kinds[lldb::eRegisterKindLLDB], +                                valid); +  } + +  void SetRegisterIsValid(uint32_t reg, bool valid) { +#if defined(LLDB_CONFIGURATION_DEBUG) +    assert(reg < m_reg_valid.size()); +#endif +    if (reg < m_reg_valid.size()) +      m_reg_valid[reg] = valid; +  } + +  GDBRemoteDynamicRegisterInfo &m_reg_info; +  std::vector<bool> m_reg_valid; +  DataExtractor m_reg_data; +  bool m_read_all_at_once; + +private: +  // Helper function for ReadRegisterBytes(). +  bool GetPrimordialRegister(const RegisterInfo *reg_info, +                             GDBRemoteCommunicationClient &gdb_comm); +  // Helper function for WriteRegisterBytes(). +  bool SetPrimordialRegister(const RegisterInfo *reg_info, +                             GDBRemoteCommunicationClient &gdb_comm); + +  DISALLOW_COPY_AND_ASSIGN(GDBRemoteRegisterContext); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // lldb_GDBRemoteRegisterContext_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp new file mode 100644 index 000000000000..915e301ecc30 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -0,0 +1,5486 @@ +//===-- ProcessGDBRemote.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Config.h" + +#include <errno.h> +#include <stdlib.h> +#ifndef LLDB_DISABLE_POSIX +#include <netinet/in.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <unistd.h> +#endif +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> + +#include <algorithm> +#include <csignal> +#include <map> +#include <memory> +#include <mutex> +#include <sstream> + +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Value.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/PosixApi.h" +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/XML.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupBoolean.h" +#include "lldb/Interpreter/OptionGroupUInt64.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/Property.h" +#include "lldb/Symbol/LocateSymbolFile.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/CleanUp.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Reproducer.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/Timer.h" + +#include "GDBRemoteRegisterContext.h" +#ifdef LLDB_ENABLE_ALL +#include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h" +#endif // LLDB_ENABLE_ALL +#include "Plugins/Process/Utility/GDBRemoteSignals.h" +#include "Plugins/Process/Utility/InferiorCallPOSIX.h" +#include "Plugins/Process/Utility/StopInfoMachException.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "ThreadGDBRemote.h" +#include "lldb/Host/Host.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/raw_ostream.h" + +#define DEBUGSERVER_BASENAME "debugserver" +using namespace llvm; +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +namespace lldb { +// Provide a function that can easily dump the packet history if we know a +// ProcessGDBRemote * value (which we can get from logs or from debugging). We +// need the function in the lldb namespace so it makes it into the final +// executable since the LLDB shared library only exports stuff in the lldb +// namespace. This allows you to attach with a debugger and call this function +// and get the packet history dumped to a file. +void DumpProcessGDBRemotePacketHistory(void *p, const char *path) { +  StreamFile strm; +  Status error = FileSystem::Instance().Open(strm.GetFile(), FileSpec(path), +                                             File::eOpenOptionWrite | +                                                 File::eOpenOptionCanCreate); +  if (error.Success()) +    ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory(strm); +} +} // namespace lldb + +namespace { + +static constexpr PropertyDefinition g_properties[] = { +    {"packet-timeout", +     OptionValue::eTypeUInt64, +     true, +     5 +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +         * 2 +#endif +#endif +     , +     nullptr, +     {}, +     "Specify the default packet timeout in seconds."}, +    {"target-definition-file", +     OptionValue::eTypeFileSpec, +     true, +     0, +     nullptr, +     {}, +     "The file that provides the description for remote target registers."}, +    {"use-libraries-svr4", +     OptionValue::eTypeBoolean, +     true, +     false, +     nullptr, +     {}, +     "If true, the libraries-svr4 feature will be used to get a hold of the " +     "process's loaded modules."}}; + +enum { +  ePropertyPacketTimeout, +  ePropertyTargetDefinitionFile, +  ePropertyUseSVR4 +}; + +class PluginProperties : public Properties { +public: +  static ConstString GetSettingName() { +    return ProcessGDBRemote::GetPluginNameStatic(); +  } + +  PluginProperties() : Properties() { +    m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName()); +    m_collection_sp->Initialize(g_properties); +  } + +  ~PluginProperties() override {} + +  uint64_t GetPacketTimeout() { +    const uint32_t idx = ePropertyPacketTimeout; +    return m_collection_sp->GetPropertyAtIndexAsUInt64( +        nullptr, idx, g_properties[idx].default_uint_value); +  } + +  bool SetPacketTimeout(uint64_t timeout) { +    const uint32_t idx = ePropertyPacketTimeout; +    return m_collection_sp->SetPropertyAtIndexAsUInt64(nullptr, idx, timeout); +  } + +  FileSpec GetTargetDefinitionFile() const { +    const uint32_t idx = ePropertyTargetDefinitionFile; +    return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); +  } + +  bool GetUseSVR4() const { +    const uint32_t idx = ePropertyUseSVR4; +    return m_collection_sp->GetPropertyAtIndexAsBoolean( +        nullptr, idx, g_properties[idx].default_uint_value != 0); +  } +}; + +typedef std::shared_ptr<PluginProperties> ProcessKDPPropertiesSP; + +static const ProcessKDPPropertiesSP &GetGlobalPluginProperties() { +  static ProcessKDPPropertiesSP g_settings_sp; +  if (!g_settings_sp) +    g_settings_sp = std::make_shared<PluginProperties>(); +  return g_settings_sp; +} + +class ProcessGDBRemoteProvider +    : public repro::Provider<ProcessGDBRemoteProvider> { +public: +  struct Info { +    static const char *name; +    static const char *file; +  }; + +  ProcessGDBRemoteProvider(const FileSpec &directory) : Provider(directory) { +  } + +  raw_ostream *GetHistoryStream() { +    FileSpec history_file = GetRoot().CopyByAppendingPathComponent(Info::file); + +    std::error_code EC; +    m_stream_up = llvm::make_unique<raw_fd_ostream>(history_file.GetPath(), EC, +                                                    sys::fs::OpenFlags::F_Text); +    return m_stream_up.get(); +  } + +  void SetCallback(std::function<void()> callback) { +    m_callback = std::move(callback); +  } + +  void Keep() override { m_callback(); } + +  void Discard() override { m_callback(); } + +  static char ID; + +private: +  std::function<void()> m_callback; +  std::unique_ptr<raw_fd_ostream> m_stream_up; +}; + +char ProcessGDBRemoteProvider::ID = 0; +const char *ProcessGDBRemoteProvider::Info::name = "gdb-remote"; +const char *ProcessGDBRemoteProvider::Info::file = "gdb-remote.yaml"; + +} // namespace + +// TODO Randomly assigning a port is unsafe.  We should get an unused +// ephemeral port from the kernel and make sure we reserve it before passing it +// to debugserver. + +#if defined(__APPLE__) +#define LOW_PORT (IPPORT_RESERVED) +#define HIGH_PORT (IPPORT_HIFIRSTAUTO) +#else +#define LOW_PORT (1024u) +#define HIGH_PORT (49151u) +#endif + +#if defined(__APPLE__) &&                                                      \ +    (defined(__arm__) || defined(__arm64__) || defined(__aarch64__)) +static bool rand_initialized = false; + +static inline uint16_t get_random_port() { +  if (!rand_initialized) { +    time_t seed = time(NULL); + +    rand_initialized = true; +    srand(seed); +  } +  return (rand() % (HIGH_PORT - LOW_PORT)) + LOW_PORT; +} +#endif + +ConstString ProcessGDBRemote::GetPluginNameStatic() { +  static ConstString g_name("gdb-remote"); +  return g_name; +} + +const char *ProcessGDBRemote::GetPluginDescriptionStatic() { +  return "GDB Remote protocol based debugging plug-in."; +} + +void ProcessGDBRemote::Terminate() { +  PluginManager::UnregisterPlugin(ProcessGDBRemote::CreateInstance); +} + +lldb::ProcessSP +ProcessGDBRemote::CreateInstance(lldb::TargetSP target_sp, +                                 ListenerSP listener_sp, +                                 const FileSpec *crash_file_path) { +  lldb::ProcessSP process_sp; +  if (crash_file_path == nullptr) +    process_sp = std::make_shared<ProcessGDBRemote>(target_sp, listener_sp); +  return process_sp; +} + +bool ProcessGDBRemote::CanDebug(lldb::TargetSP target_sp, +                                bool plugin_specified_by_name) { +  if (plugin_specified_by_name) +    return true; + +  // For now we are just making sure the file exists for a given module +  Module *exe_module = target_sp->GetExecutableModulePointer(); +  if (exe_module) { +    ObjectFile *exe_objfile = exe_module->GetObjectFile(); +    // We can't debug core files... +    switch (exe_objfile->GetType()) { +    case ObjectFile::eTypeInvalid: +    case ObjectFile::eTypeCoreFile: +    case ObjectFile::eTypeDebugInfo: +    case ObjectFile::eTypeObjectFile: +    case ObjectFile::eTypeSharedLibrary: +    case ObjectFile::eTypeStubLibrary: +    case ObjectFile::eTypeJIT: +      return false; +    case ObjectFile::eTypeExecutable: +    case ObjectFile::eTypeDynamicLinker: +    case ObjectFile::eTypeUnknown: +      break; +    } +    return FileSystem::Instance().Exists(exe_module->GetFileSpec()); +  } +  // However, if there is no executable module, we return true since we might +  // be preparing to attach. +  return true; +} + +// ProcessGDBRemote constructor +ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, +                                   ListenerSP listener_sp) +    : Process(target_sp, listener_sp), +      m_debugserver_pid(LLDB_INVALID_PROCESS_ID), m_last_stop_packet_mutex(), +      m_register_info(), +      m_async_broadcaster(nullptr, "lldb.process.gdb-remote.async-broadcaster"), +      m_async_listener_sp( +          Listener::MakeListener("lldb.process.gdb-remote.async-listener")), +      m_async_thread_state_mutex(), m_thread_ids(), m_thread_pcs(), +      m_jstopinfo_sp(), m_jthreadsinfo_sp(), m_continue_c_tids(), +      m_continue_C_tids(), m_continue_s_tids(), m_continue_S_tids(), +      m_max_memory_size(0), m_remote_stub_max_memory_size(0), +      m_addr_to_mmap_size(), m_thread_create_bp_sp(), +      m_waiting_for_attach(false), m_destroy_tried_resuming(false), +      m_command_sp(), m_breakpoint_pc_offset(0), +      m_initial_tid(LLDB_INVALID_THREAD_ID), m_replay_mode(false), +      m_allow_flash_writes(false), m_erased_flash_ranges() { +  m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, +                                   "async thread should exit"); +  m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, +                                   "async thread continue"); +  m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadDidExit, +                                   "async thread did exit"); + +  if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) { +    ProcessGDBRemoteProvider &provider = +        g->GetOrCreate<ProcessGDBRemoteProvider>(); +    // Set the history stream to the stream owned by the provider. +    m_gdb_comm.SetHistoryStream(provider.GetHistoryStream()); +    // Make sure to clear the stream again when we're finished. +    provider.SetCallback([&]() { m_gdb_comm.SetHistoryStream(nullptr); }); +  } + +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_ASYNC)); + +  const uint32_t async_event_mask = +      eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; + +  if (m_async_listener_sp->StartListeningForEvents( +          &m_async_broadcaster, async_event_mask) != async_event_mask) { +    if (log) +      log->Printf("ProcessGDBRemote::%s failed to listen for " +                  "m_async_broadcaster events", +                  __FUNCTION__); +  } + +  const uint32_t gdb_event_mask = +      Communication::eBroadcastBitReadThreadDidExit | +      GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify; +  if (m_async_listener_sp->StartListeningForEvents( +          &m_gdb_comm, gdb_event_mask) != gdb_event_mask) { +    if (log) +      log->Printf("ProcessGDBRemote::%s failed to listen for m_gdb_comm events", +                  __FUNCTION__); +  } + +  const uint64_t timeout_seconds = +      GetGlobalPluginProperties()->GetPacketTimeout(); +  if (timeout_seconds > 0) +    m_gdb_comm.SetPacketTimeout(std::chrono::seconds(timeout_seconds)); +} + +// Destructor +ProcessGDBRemote::~ProcessGDBRemote() { +  //  m_mach_process.UnregisterNotificationCallbacks (this); +  Clear(); +  // We need to call finalize on the process before destroying ourselves to +  // make sure all of the broadcaster cleanup goes as planned. If we destruct +  // this class, then Process::~Process() might have problems trying to fully +  // destroy the broadcaster. +  Finalize(); + +  // The general Finalize is going to try to destroy the process and that +  // SHOULD shut down the async thread.  However, if we don't kill it it will +  // get stranded and its connection will go away so when it wakes up it will +  // crash.  So kill it for sure here. +  StopAsyncThread(); +  KillDebugserverProcess(); +} + +// PluginInterface +ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); } + +uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; } + +bool ProcessGDBRemote::ParsePythonTargetDefinition( +    const FileSpec &target_definition_fspec) { +  ScriptInterpreter *interpreter = +      GetTarget().GetDebugger().GetScriptInterpreter(); +  Status error; +  StructuredData::ObjectSP module_object_sp( +      interpreter->LoadPluginModule(target_definition_fspec, error)); +  if (module_object_sp) { +    StructuredData::DictionarySP target_definition_sp( +        interpreter->GetDynamicSettings(module_object_sp, &GetTarget(), +                                        "gdb-server-target-definition", error)); + +    if (target_definition_sp) { +      StructuredData::ObjectSP target_object( +          target_definition_sp->GetValueForKey("host-info")); +      if (target_object) { +        if (auto host_info_dict = target_object->GetAsDictionary()) { +          StructuredData::ObjectSP triple_value = +              host_info_dict->GetValueForKey("triple"); +          if (auto triple_string_value = triple_value->GetAsString()) { +            std::string triple_string = triple_string_value->GetValue(); +            ArchSpec host_arch(triple_string.c_str()); +            if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) { +              GetTarget().SetArchitecture(host_arch); +            } +          } +        } +      } +      m_breakpoint_pc_offset = 0; +      StructuredData::ObjectSP breakpoint_pc_offset_value = +          target_definition_sp->GetValueForKey("breakpoint-pc-offset"); +      if (breakpoint_pc_offset_value) { +        if (auto breakpoint_pc_int_value = +                breakpoint_pc_offset_value->GetAsInteger()) +          m_breakpoint_pc_offset = breakpoint_pc_int_value->GetValue(); +      } + +      if (m_register_info.SetRegisterInfo(*target_definition_sp, +                                          GetTarget().GetArchitecture()) > 0) { +        return true; +      } +    } +  } +  return false; +} + +// If the remote stub didn't give us eh_frame or DWARF register numbers for a +// register, see if the ABI can provide them. +// DWARF and eh_frame register numbers are defined as a part of the ABI. +static void AugmentRegisterInfoViaABI(RegisterInfo ®_info, +                                      ConstString reg_name, ABISP abi_sp) { +  if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM || +      reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM) { +    if (abi_sp) { +      RegisterInfo abi_reg_info; +      if (abi_sp->GetRegisterInfoByName(reg_name, abi_reg_info)) { +        if (reg_info.kinds[eRegisterKindEHFrame] == LLDB_INVALID_REGNUM && +            abi_reg_info.kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM) { +          reg_info.kinds[eRegisterKindEHFrame] = +              abi_reg_info.kinds[eRegisterKindEHFrame]; +        } +        if (reg_info.kinds[eRegisterKindDWARF] == LLDB_INVALID_REGNUM && +            abi_reg_info.kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM) { +          reg_info.kinds[eRegisterKindDWARF] = +              abi_reg_info.kinds[eRegisterKindDWARF]; +        } +        if (reg_info.kinds[eRegisterKindGeneric] == LLDB_INVALID_REGNUM && +            abi_reg_info.kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) { +          reg_info.kinds[eRegisterKindGeneric] = +              abi_reg_info.kinds[eRegisterKindGeneric]; +        } +      } +    } +  } +} + +static size_t SplitCommaSeparatedRegisterNumberString( +    const llvm::StringRef &comma_separated_regiter_numbers, +    std::vector<uint32_t> ®nums, int base) { +  regnums.clear(); +  std::pair<llvm::StringRef, llvm::StringRef> value_pair; +  value_pair.second = comma_separated_regiter_numbers; +  do { +    value_pair = value_pair.second.split(','); +    if (!value_pair.first.empty()) { +      uint32_t reg = StringConvert::ToUInt32(value_pair.first.str().c_str(), +                                             LLDB_INVALID_REGNUM, base); +      if (reg != LLDB_INVALID_REGNUM) +        regnums.push_back(reg); +    } +  } while (!value_pair.second.empty()); +  return regnums.size(); +} + +void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { +  if (!force && m_register_info.GetNumRegisters() > 0) +    return; + +  m_register_info.Clear(); + +  // Check if qHostInfo specified a specific packet timeout for this +  // connection. If so then lets update our setting so the user knows what the +  // timeout is and can see it. +  const auto host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); +  if (host_packet_timeout > std::chrono::seconds(0)) { +    GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout.count()); +  } + +  // Register info search order: +  //     1 - Use the target definition python file if one is specified. +  //     2 - If the target definition doesn't have any of the info from the +  //     target.xml (registers) then proceed to read the target.xml. +  //     3 - Fall back on the qRegisterInfo packets. + +  FileSpec target_definition_fspec = +      GetGlobalPluginProperties()->GetTargetDefinitionFile(); +  if (!FileSystem::Instance().Exists(target_definition_fspec)) { +    // If the filename doesn't exist, it may be a ~ not having been expanded - +    // try to resolve it. +    FileSystem::Instance().Resolve(target_definition_fspec); +  } +  if (target_definition_fspec) { +    // See if we can get register definitions from a python file +    if (ParsePythonTargetDefinition(target_definition_fspec)) { +      return; +    } else { +      StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream(); +      stream_sp->Printf("ERROR: target description file %s failed to parse.\n", +                        target_definition_fspec.GetPath().c_str()); +    } +  } + +  const ArchSpec &target_arch = GetTarget().GetArchitecture(); +  const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); +  const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); + +  // Use the process' architecture instead of the host arch, if available +  ArchSpec arch_to_use; +  if (remote_process_arch.IsValid()) +    arch_to_use = remote_process_arch; +  else +    arch_to_use = remote_host_arch; + +  if (!arch_to_use.IsValid()) +    arch_to_use = target_arch; + +  if (GetGDBServerRegisterInfo(arch_to_use)) +    return; + +  char packet[128]; +  uint32_t reg_offset = 0; +  uint32_t reg_num = 0; +  for (StringExtractorGDBRemote::ResponseType response_type = +           StringExtractorGDBRemote::eResponse; +       response_type == StringExtractorGDBRemote::eResponse; ++reg_num) { +    const int packet_len = +        ::snprintf(packet, sizeof(packet), "qRegisterInfo%x", reg_num); +    assert(packet_len < (int)sizeof(packet)); +    UNUSED_IF_ASSERT_DISABLED(packet_len); +    StringExtractorGDBRemote response; +    if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, false) == +        GDBRemoteCommunication::PacketResult::Success) { +      response_type = response.GetResponseType(); +      if (response_type == StringExtractorGDBRemote::eResponse) { +        llvm::StringRef name; +        llvm::StringRef value; +        ConstString reg_name; +        ConstString alt_name; +        ConstString set_name; +        std::vector<uint32_t> value_regs; +        std::vector<uint32_t> invalidate_regs; +        std::vector<uint8_t> dwarf_opcode_bytes; +        RegisterInfo reg_info = { +            nullptr,       // Name +            nullptr,       // Alt name +            0,             // byte size +            reg_offset,    // offset +            eEncodingUint, // encoding +            eFormatHex,    // format +            { +                LLDB_INVALID_REGNUM, // eh_frame reg num +                LLDB_INVALID_REGNUM, // DWARF reg num +                LLDB_INVALID_REGNUM, // generic reg num +                reg_num,             // process plugin reg num +                reg_num              // native register number +            }, +            nullptr, +            nullptr, +            nullptr, // Dwarf expression opcode bytes pointer +            0        // Dwarf expression opcode bytes length +        }; + +        while (response.GetNameColonValue(name, value)) { +          if (name.equals("name")) { +            reg_name.SetString(value); +          } else if (name.equals("alt-name")) { +            alt_name.SetString(value); +          } else if (name.equals("bitsize")) { +            value.getAsInteger(0, reg_info.byte_size); +            reg_info.byte_size /= CHAR_BIT; +          } else if (name.equals("offset")) { +            if (value.getAsInteger(0, reg_offset)) +              reg_offset = UINT32_MAX; +          } else if (name.equals("encoding")) { +            const Encoding encoding = Args::StringToEncoding(value); +            if (encoding != eEncodingInvalid) +              reg_info.encoding = encoding; +          } else if (name.equals("format")) { +            Format format = eFormatInvalid; +            if (OptionArgParser::ToFormat(value.str().c_str(), format, nullptr) +                    .Success()) +              reg_info.format = format; +            else { +              reg_info.format = +                  llvm::StringSwitch<Format>(value) +                      .Case("binary", eFormatBinary) +                      .Case("decimal", eFormatDecimal) +                      .Case("hex", eFormatHex) +                      .Case("float", eFormatFloat) +                      .Case("vector-sint8", eFormatVectorOfSInt8) +                      .Case("vector-uint8", eFormatVectorOfUInt8) +                      .Case("vector-sint16", eFormatVectorOfSInt16) +                      .Case("vector-uint16", eFormatVectorOfUInt16) +                      .Case("vector-sint32", eFormatVectorOfSInt32) +                      .Case("vector-uint32", eFormatVectorOfUInt32) +                      .Case("vector-float32", eFormatVectorOfFloat32) +                      .Case("vector-uint64", eFormatVectorOfUInt64) +                      .Case("vector-uint128", eFormatVectorOfUInt128) +                      .Default(eFormatInvalid); +            } +          } else if (name.equals("set")) { +            set_name.SetString(value); +          } else if (name.equals("gcc") || name.equals("ehframe")) { +            if (value.getAsInteger(0, reg_info.kinds[eRegisterKindEHFrame])) +              reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM; +          } else if (name.equals("dwarf")) { +            if (value.getAsInteger(0, reg_info.kinds[eRegisterKindDWARF])) +              reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM; +          } else if (name.equals("generic")) { +            reg_info.kinds[eRegisterKindGeneric] = +                Args::StringToGenericRegister(value); +          } else if (name.equals("container-regs")) { +            SplitCommaSeparatedRegisterNumberString(value, value_regs, 16); +          } else if (name.equals("invalidate-regs")) { +            SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 16); +          } else if (name.equals("dynamic_size_dwarf_expr_bytes")) { +            size_t dwarf_opcode_len = value.size() / 2; +            assert(dwarf_opcode_len > 0); + +            dwarf_opcode_bytes.resize(dwarf_opcode_len); +            reg_info.dynamic_size_dwarf_len = dwarf_opcode_len; + +            StringExtractor opcode_extractor(value); +            uint32_t ret_val = +                opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes); +            assert(dwarf_opcode_len == ret_val); +            UNUSED_IF_ASSERT_DISABLED(ret_val); +            reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data(); +          } +        } + +        reg_info.byte_offset = reg_offset; +        assert(reg_info.byte_size != 0); +        reg_offset += reg_info.byte_size; +        if (!value_regs.empty()) { +          value_regs.push_back(LLDB_INVALID_REGNUM); +          reg_info.value_regs = value_regs.data(); +        } +        if (!invalidate_regs.empty()) { +          invalidate_regs.push_back(LLDB_INVALID_REGNUM); +          reg_info.invalidate_regs = invalidate_regs.data(); +        } + +        // We have to make a temporary ABI here, and not use the GetABI because +        // this code gets called in DidAttach, when the target architecture +        // (and consequently the ABI we'll get from the process) may be wrong. +        ABISP abi_to_use = ABI::FindPlugin(shared_from_this(), arch_to_use); + +        AugmentRegisterInfoViaABI(reg_info, reg_name, abi_to_use); + +        m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name); +      } else { +        break; // ensure exit before reg_num is incremented +      } +    } else { +      break; +    } +  } + +  if (m_register_info.GetNumRegisters() > 0) { +    m_register_info.Finalize(GetTarget().GetArchitecture()); +    return; +  } + +  // We didn't get anything if the accumulated reg_num is zero.  See if we are +  // debugging ARM and fill with a hard coded register set until we can get an +  // updated debugserver down on the devices. On the other hand, if the +  // accumulated reg_num is positive, see if we can add composite registers to +  // the existing primordial ones. +  bool from_scratch = (m_register_info.GetNumRegisters() == 0); + +  if (!target_arch.IsValid()) { +    if (arch_to_use.IsValid() && +        (arch_to_use.GetMachine() == llvm::Triple::arm || +         arch_to_use.GetMachine() == llvm::Triple::thumb) && +        arch_to_use.GetTriple().getVendor() == llvm::Triple::Apple) +      m_register_info.HardcodeARMRegisters(from_scratch); +  } else if (target_arch.GetMachine() == llvm::Triple::arm || +             target_arch.GetMachine() == llvm::Triple::thumb) { +    m_register_info.HardcodeARMRegisters(from_scratch); +  } + +  // At this point, we can finalize our register info. +  m_register_info.Finalize(GetTarget().GetArchitecture()); +} + +Status ProcessGDBRemote::WillLaunch(lldb_private::Module *module) { +  return WillLaunchOrAttach(); +} + +Status ProcessGDBRemote::WillAttachToProcessWithID(lldb::pid_t pid) { +  return WillLaunchOrAttach(); +} + +Status ProcessGDBRemote::WillAttachToProcessWithName(const char *process_name, +                                                     bool wait_for_launch) { +  return WillLaunchOrAttach(); +} + +Status ProcessGDBRemote::DoConnectRemote(Stream *strm, +                                         llvm::StringRef remote_url) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  Status error(WillLaunchOrAttach()); + +  if (error.Fail()) +    return error; + +  error = ConnectToDebugserver(remote_url); + +  if (error.Fail()) +    return error; +  StartAsyncThread(); + +  lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); +  if (pid == LLDB_INVALID_PROCESS_ID) { +    // We don't have a valid process ID, so note that we are connected and +    // could now request to launch or attach, or get remote process listings... +    SetPrivateState(eStateConnected); +  } else { +    // We have a valid process +    SetID(pid); +    GetThreadList(); +    StringExtractorGDBRemote response; +    if (m_gdb_comm.GetStopReply(response)) { +      SetLastStopPacket(response); + +      // '?' Packets must be handled differently in non-stop mode +      if (GetTarget().GetNonStopModeEnabled()) +        HandleStopReplySequence(); + +      Target &target = GetTarget(); +      if (!target.GetArchitecture().IsValid()) { +        if (m_gdb_comm.GetProcessArchitecture().IsValid()) { +          target.SetArchitecture(m_gdb_comm.GetProcessArchitecture()); +        } else { +          if (m_gdb_comm.GetHostArchitecture().IsValid()) { +            target.SetArchitecture(m_gdb_comm.GetHostArchitecture()); +          } +        } +      } + +      const StateType state = SetThreadStopInfo(response); +      if (state != eStateInvalid) { +        SetPrivateState(state); +      } else +        error.SetErrorStringWithFormat( +            "Process %" PRIu64 " was reported after connecting to " +            "'%s', but state was not stopped: %s", +            pid, remote_url.str().c_str(), StateAsCString(state)); +    } else +      error.SetErrorStringWithFormat("Process %" PRIu64 +                                     " was reported after connecting to '%s', " +                                     "but no stop reply packet was received", +                                     pid, remote_url.str().c_str()); +  } + +  if (log) +    log->Printf("ProcessGDBRemote::%s pid %" PRIu64 +                ": normalizing target architecture initial triple: %s " +                "(GetTarget().GetArchitecture().IsValid() %s, " +                "m_gdb_comm.GetHostArchitecture().IsValid(): %s)", +                __FUNCTION__, GetID(), +                GetTarget().GetArchitecture().GetTriple().getTriple().c_str(), +                GetTarget().GetArchitecture().IsValid() ? "true" : "false", +                m_gdb_comm.GetHostArchitecture().IsValid() ? "true" : "false"); + +  if (error.Success() && !GetTarget().GetArchitecture().IsValid() && +      m_gdb_comm.GetHostArchitecture().IsValid()) { +    // Prefer the *process'* architecture over that of the *host*, if +    // available. +    if (m_gdb_comm.GetProcessArchitecture().IsValid()) +      GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture()); +    else +      GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture()); +  } + +  if (log) +    log->Printf("ProcessGDBRemote::%s pid %" PRIu64 +                ": normalized target architecture triple: %s", +                __FUNCTION__, GetID(), +                GetTarget().GetArchitecture().GetTriple().getTriple().c_str()); + +  if (error.Success()) { +    PlatformSP platform_sp = GetTarget().GetPlatform(); +    if (platform_sp && platform_sp->IsConnected()) +      SetUnixSignals(platform_sp->GetUnixSignals()); +    else +      SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture())); +  } + +  return error; +} + +Status ProcessGDBRemote::WillLaunchOrAttach() { +  Status error; +  m_stdio_communication.Clear(); +  return error; +} + +// Process Control +Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module, +                                  ProcessLaunchInfo &launch_info) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  Status error; + +  if (log) +    log->Printf("ProcessGDBRemote::%s() entered", __FUNCTION__); + +  uint32_t launch_flags = launch_info.GetFlags().Get(); +  FileSpec stdin_file_spec{}; +  FileSpec stdout_file_spec{}; +  FileSpec stderr_file_spec{}; +  FileSpec working_dir = launch_info.GetWorkingDirectory(); + +  const FileAction *file_action; +  file_action = launch_info.GetFileActionForFD(STDIN_FILENO); +  if (file_action) { +    if (file_action->GetAction() == FileAction::eFileActionOpen) +      stdin_file_spec = file_action->GetFileSpec(); +  } +  file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); +  if (file_action) { +    if (file_action->GetAction() == FileAction::eFileActionOpen) +      stdout_file_spec = file_action->GetFileSpec(); +  } +  file_action = launch_info.GetFileActionForFD(STDERR_FILENO); +  if (file_action) { +    if (file_action->GetAction() == FileAction::eFileActionOpen) +      stderr_file_spec = file_action->GetFileSpec(); +  } + +  if (log) { +    if (stdin_file_spec || stdout_file_spec || stderr_file_spec) +      log->Printf("ProcessGDBRemote::%s provided with STDIO paths via " +                  "launch_info: stdin=%s, stdout=%s, stderr=%s", +                  __FUNCTION__, +                  stdin_file_spec ? stdin_file_spec.GetCString() : "<null>", +                  stdout_file_spec ? stdout_file_spec.GetCString() : "<null>", +                  stderr_file_spec ? stderr_file_spec.GetCString() : "<null>"); +    else +      log->Printf("ProcessGDBRemote::%s no STDIO paths given via launch_info", +                  __FUNCTION__); +  } + +  const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; +  if (stdin_file_spec || disable_stdio) { +    // the inferior will be reading stdin from the specified file or stdio is +    // completely disabled +    m_stdin_forward = false; +  } else { +    m_stdin_forward = true; +  } + +  //  ::LogSetBitMask (GDBR_LOG_DEFAULT); +  //  ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | +  //  LLDB_LOG_OPTION_PREPEND_TIMESTAMP | +  //  LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); +  //  ::LogSetLogFile ("/dev/stdout"); + +  ObjectFile *object_file = exe_module->GetObjectFile(); +  if (object_file) { +    error = EstablishConnectionIfNeeded(launch_info); +    if (error.Success()) { +      PseudoTerminal pty; +      const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; + +      PlatformSP platform_sp(GetTarget().GetPlatform()); +      if (disable_stdio) { +        // set to /dev/null unless redirected to a file above +        if (!stdin_file_spec) +          stdin_file_spec.SetFile(FileSystem::DEV_NULL, +                                  FileSpec::Style::native); +        if (!stdout_file_spec) +          stdout_file_spec.SetFile(FileSystem::DEV_NULL, +                                   FileSpec::Style::native); +        if (!stderr_file_spec) +          stderr_file_spec.SetFile(FileSystem::DEV_NULL, +                                   FileSpec::Style::native); +      } else if (platform_sp && platform_sp->IsHost()) { +        // If the debugserver is local and we aren't disabling STDIO, lets use +        // a pseudo terminal to instead of relying on the 'O' packets for stdio +        // since 'O' packets can really slow down debugging if the inferior +        // does a lot of output. +        if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) && +            pty.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY, nullptr, 0)) { +          FileSpec slave_name{pty.GetSlaveName(nullptr, 0)}; + +          if (!stdin_file_spec) +            stdin_file_spec = slave_name; + +          if (!stdout_file_spec) +            stdout_file_spec = slave_name; + +          if (!stderr_file_spec) +            stderr_file_spec = slave_name; +        } +        if (log) +          log->Printf( +              "ProcessGDBRemote::%s adjusted STDIO paths for local platform " +              "(IsHost() is true) using slave: stdin=%s, stdout=%s, stderr=%s", +              __FUNCTION__, +              stdin_file_spec ? stdin_file_spec.GetCString() : "<null>", +              stdout_file_spec ? stdout_file_spec.GetCString() : "<null>", +              stderr_file_spec ? stderr_file_spec.GetCString() : "<null>"); +      } + +      if (log) +        log->Printf("ProcessGDBRemote::%s final STDIO paths after all " +                    "adjustments: stdin=%s, stdout=%s, stderr=%s", +                    __FUNCTION__, +                    stdin_file_spec ? stdin_file_spec.GetCString() : "<null>", +                    stdout_file_spec ? stdout_file_spec.GetCString() : "<null>", +                    stderr_file_spec ? stderr_file_spec.GetCString() +                                     : "<null>"); + +      if (stdin_file_spec) +        m_gdb_comm.SetSTDIN(stdin_file_spec); +      if (stdout_file_spec) +        m_gdb_comm.SetSTDOUT(stdout_file_spec); +      if (stderr_file_spec) +        m_gdb_comm.SetSTDERR(stderr_file_spec); + +      m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR); +      m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError); + +      m_gdb_comm.SendLaunchArchPacket( +          GetTarget().GetArchitecture().GetArchitectureName()); + +      const char *launch_event_data = launch_info.GetLaunchEventData(); +      if (launch_event_data != nullptr && *launch_event_data != '\0') +        m_gdb_comm.SendLaunchEventDataPacket(launch_event_data); + +      if (working_dir) { +        m_gdb_comm.SetWorkingDir(working_dir); +      } + +      // Send the environment and the program + arguments after we connect +      m_gdb_comm.SendEnvironment(launch_info.GetEnvironment()); + +      { +        // Scope for the scoped timeout object +        GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, +                                                      std::chrono::seconds(10)); + +        int arg_packet_err = m_gdb_comm.SendArgumentsPacket(launch_info); +        if (arg_packet_err == 0) { +          std::string error_str; +          if (m_gdb_comm.GetLaunchSuccess(error_str)) { +            SetID(m_gdb_comm.GetCurrentProcessID()); +          } else { +            error.SetErrorString(error_str.c_str()); +          } +        } else { +          error.SetErrorStringWithFormat("'A' packet returned an error: %i", +                                         arg_packet_err); +        } +      } + +      if (GetID() == LLDB_INVALID_PROCESS_ID) { +        if (log) +          log->Printf("failed to connect to debugserver: %s", +                      error.AsCString()); +        KillDebugserverProcess(); +        return error; +      } + +      StringExtractorGDBRemote response; +      if (m_gdb_comm.GetStopReply(response)) { +        SetLastStopPacket(response); +        // '?' Packets must be handled differently in non-stop mode +        if (GetTarget().GetNonStopModeEnabled()) +          HandleStopReplySequence(); + +        const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture(); + +        if (process_arch.IsValid()) { +          GetTarget().MergeArchitecture(process_arch); +        } else { +          const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture(); +          if (host_arch.IsValid()) +            GetTarget().MergeArchitecture(host_arch); +        } + +        SetPrivateState(SetThreadStopInfo(response)); + +        if (!disable_stdio) { +          if (pty.GetMasterFileDescriptor() != PseudoTerminal::invalid_fd) +            SetSTDIOFileDescriptor(pty.ReleaseMasterFileDescriptor()); +        } +      } +    } else { +      if (log) +        log->Printf("failed to connect to debugserver: %s", error.AsCString()); +    } +  } else { +    // Set our user ID to an invalid process ID. +    SetID(LLDB_INVALID_PROCESS_ID); +    error.SetErrorStringWithFormat( +        "failed to get object file from '%s' for arch %s", +        exe_module->GetFileSpec().GetFilename().AsCString(), +        exe_module->GetArchitecture().GetArchitectureName()); +  } +  return error; +} + +Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) { +  Status error; +  // Only connect if we have a valid connect URL +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + +  if (!connect_url.empty()) { +    if (log) +      log->Printf("ProcessGDBRemote::%s Connecting to %s", __FUNCTION__, +                  connect_url.str().c_str()); +    std::unique_ptr<ConnectionFileDescriptor> conn_up( +        new ConnectionFileDescriptor()); +    if (conn_up) { +      const uint32_t max_retry_count = 50; +      uint32_t retry_count = 0; +      while (!m_gdb_comm.IsConnected()) { +        if (conn_up->Connect(connect_url, &error) == eConnectionStatusSuccess) { +          m_gdb_comm.SetConnection(conn_up.release()); +          break; +        } else if (error.WasInterrupted()) { +          // If we were interrupted, don't keep retrying. +          break; +        } + +        retry_count++; + +        if (retry_count >= max_retry_count) +          break; + +        usleep(100000); +      } +    } +  } + +  if (!m_gdb_comm.IsConnected()) { +    if (error.Success()) +      error.SetErrorString("not connected to remote gdb server"); +    return error; +  } + +  // Start the communications read thread so all incoming data can be parsed +  // into packets and queued as they arrive. +  if (GetTarget().GetNonStopModeEnabled()) +    m_gdb_comm.StartReadThread(); + +  // We always seem to be able to open a connection to a local port so we need +  // to make sure we can then send data to it. If we can't then we aren't +  // actually connected to anything, so try and do the handshake with the +  // remote GDB server and make sure that goes alright. +  if (!m_gdb_comm.HandshakeWithServer(&error)) { +    m_gdb_comm.Disconnect(); +    if (error.Success()) +      error.SetErrorString("not connected to remote gdb server"); +    return error; +  } + +  // Send $QNonStop:1 packet on startup if required +  if (GetTarget().GetNonStopModeEnabled()) +    GetTarget().SetNonStopModeEnabled(m_gdb_comm.SetNonStopMode(true)); + +  m_gdb_comm.GetEchoSupported(); +  m_gdb_comm.GetThreadSuffixSupported(); +  m_gdb_comm.GetListThreadsInStopReplySupported(); +  m_gdb_comm.GetHostInfo(); +  m_gdb_comm.GetVContSupported('c'); +  m_gdb_comm.GetVAttachOrWaitSupported(); +  m_gdb_comm.EnableErrorStringInPacket(); + +  // Ask the remote server for the default thread id +  if (GetTarget().GetNonStopModeEnabled()) +    m_gdb_comm.GetDefaultThreadId(m_initial_tid); + +  size_t num_cmds = GetExtraStartupCommands().GetArgumentCount(); +  for (size_t idx = 0; idx < num_cmds; idx++) { +    StringExtractorGDBRemote response; +    m_gdb_comm.SendPacketAndWaitForResponse( +        GetExtraStartupCommands().GetArgumentAtIndex(idx), response, false); +  } +  return error; +} + +void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  if (log) +    log->Printf("ProcessGDBRemote::%s()", __FUNCTION__); +  if (GetID() != LLDB_INVALID_PROCESS_ID) { +    BuildDynamicRegisterInfo(false); + +    // See if the GDB server supports the qHostInfo information + +    // See if the GDB server supports the qProcessInfo packet, if so prefer +    // that over the Host information as it will be more specific to our +    // process. + +    const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); +    if (remote_process_arch.IsValid()) { +      process_arch = remote_process_arch; +      if (log) +        log->Printf("ProcessGDBRemote::%s gdb-remote had process architecture, " +                    "using %s %s", +                    __FUNCTION__, +                    process_arch.GetArchitectureName() +                        ? process_arch.GetArchitectureName() +                        : "<null>", +                    process_arch.GetTriple().getTriple().c_str() +                        ? process_arch.GetTriple().getTriple().c_str() +                        : "<null>"); +    } else { +      process_arch = m_gdb_comm.GetHostArchitecture(); +      if (log) +        log->Printf("ProcessGDBRemote::%s gdb-remote did not have process " +                    "architecture, using gdb-remote host architecture %s %s", +                    __FUNCTION__, +                    process_arch.GetArchitectureName() +                        ? process_arch.GetArchitectureName() +                        : "<null>", +                    process_arch.GetTriple().getTriple().c_str() +                        ? process_arch.GetTriple().getTriple().c_str() +                        : "<null>"); +    } + +    if (process_arch.IsValid()) { +      const ArchSpec &target_arch = GetTarget().GetArchitecture(); +      if (target_arch.IsValid()) { +        if (log) +          log->Printf( +              "ProcessGDBRemote::%s analyzing target arch, currently %s %s", +              __FUNCTION__, +              target_arch.GetArchitectureName() +                  ? target_arch.GetArchitectureName() +                  : "<null>", +              target_arch.GetTriple().getTriple().c_str() +                  ? target_arch.GetTriple().getTriple().c_str() +                  : "<null>"); + +        // If the remote host is ARM and we have apple as the vendor, then +        // ARM executables and shared libraries can have mixed ARM +        // architectures. +        // You can have an armv6 executable, and if the host is armv7, then the +        // system will load the best possible architecture for all shared +        // libraries it has, so we really need to take the remote host +        // architecture as our defacto architecture in this case. + +        if ((process_arch.GetMachine() == llvm::Triple::arm || +             process_arch.GetMachine() == llvm::Triple::thumb) && +            process_arch.GetTriple().getVendor() == llvm::Triple::Apple) { +          GetTarget().SetArchitecture(process_arch); +          if (log) +            log->Printf("ProcessGDBRemote::%s remote process is ARM/Apple, " +                        "setting target arch to %s %s", +                        __FUNCTION__, +                        process_arch.GetArchitectureName() +                            ? process_arch.GetArchitectureName() +                            : "<null>", +                        process_arch.GetTriple().getTriple().c_str() +                            ? process_arch.GetTriple().getTriple().c_str() +                            : "<null>"); +        } else { +          // Fill in what is missing in the triple +          const llvm::Triple &remote_triple = process_arch.GetTriple(); +          llvm::Triple new_target_triple = target_arch.GetTriple(); +          if (new_target_triple.getVendorName().size() == 0) { +            new_target_triple.setVendor(remote_triple.getVendor()); + +            if (new_target_triple.getOSName().size() == 0) { +              new_target_triple.setOS(remote_triple.getOS()); + +              if (new_target_triple.getEnvironmentName().size() == 0) +                new_target_triple.setEnvironment( +                    remote_triple.getEnvironment()); +            } + +            ArchSpec new_target_arch = target_arch; +            new_target_arch.SetTriple(new_target_triple); +            GetTarget().SetArchitecture(new_target_arch); +          } +        } + +        if (log) +          log->Printf("ProcessGDBRemote::%s final target arch after " +                      "adjustments for remote architecture: %s %s", +                      __FUNCTION__, +                      target_arch.GetArchitectureName() +                          ? target_arch.GetArchitectureName() +                          : "<null>", +                      target_arch.GetTriple().getTriple().c_str() +                          ? target_arch.GetTriple().getTriple().c_str() +                          : "<null>"); +      } else { +        // The target doesn't have a valid architecture yet, set it from the +        // architecture we got from the remote GDB server +        GetTarget().SetArchitecture(process_arch); +      } +    } + +    // Find out which StructuredDataPlugins are supported by the debug monitor. +    // These plugins transmit data over async $J packets. +    auto supported_packets_array = +        m_gdb_comm.GetSupportedStructuredDataPlugins(); +    if (supported_packets_array) +      MapSupportedStructuredDataPlugins(*supported_packets_array); +  } +} + +void ProcessGDBRemote::DidLaunch() { +  ArchSpec process_arch; +  DidLaunchOrAttach(process_arch); +} + +Status ProcessGDBRemote::DoAttachToProcessWithID( +    lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  Status error; + +  if (log) +    log->Printf("ProcessGDBRemote::%s()", __FUNCTION__); + +  // Clear out and clean up from any current state +  Clear(); +  if (attach_pid != LLDB_INVALID_PROCESS_ID) { +    error = EstablishConnectionIfNeeded(attach_info); +    if (error.Success()) { +      m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); + +      char packet[64]; +      const int packet_len = +          ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); +      SetID(attach_pid); +      m_async_broadcaster.BroadcastEvent( +          eBroadcastBitAsyncContinue, new EventDataBytes(packet, packet_len)); +    } else +      SetExitStatus(-1, error.AsCString()); +  } + +  return error; +} + +Status ProcessGDBRemote::DoAttachToProcessWithName( +    const char *process_name, const ProcessAttachInfo &attach_info) { +  Status error; +  // Clear out and clean up from any current state +  Clear(); + +  if (process_name && process_name[0]) { +    error = EstablishConnectionIfNeeded(attach_info); +    if (error.Success()) { +      StreamString packet; + +      m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); + +      if (attach_info.GetWaitForLaunch()) { +        if (!m_gdb_comm.GetVAttachOrWaitSupported()) { +          packet.PutCString("vAttachWait"); +        } else { +          if (attach_info.GetIgnoreExisting()) +            packet.PutCString("vAttachWait"); +          else +            packet.PutCString("vAttachOrWait"); +        } +      } else +        packet.PutCString("vAttachName"); +      packet.PutChar(';'); +      packet.PutBytesAsRawHex8(process_name, strlen(process_name), +                               endian::InlHostByteOrder(), +                               endian::InlHostByteOrder()); + +      m_async_broadcaster.BroadcastEvent( +          eBroadcastBitAsyncContinue, +          new EventDataBytes(packet.GetString().data(), packet.GetSize())); + +    } else +      SetExitStatus(-1, error.AsCString()); +  } +  return error; +} + +lldb::user_id_t ProcessGDBRemote::StartTrace(const TraceOptions &options, +                                             Status &error) { +  return m_gdb_comm.SendStartTracePacket(options, error); +} + +Status ProcessGDBRemote::StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) { +  return m_gdb_comm.SendStopTracePacket(uid, thread_id); +} + +Status ProcessGDBRemote::GetData(lldb::user_id_t uid, lldb::tid_t thread_id, +                                 llvm::MutableArrayRef<uint8_t> &buffer, +                                 size_t offset) { +  return m_gdb_comm.SendGetDataPacket(uid, thread_id, buffer, offset); +} + +Status ProcessGDBRemote::GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, +                                     llvm::MutableArrayRef<uint8_t> &buffer, +                                     size_t offset) { +  return m_gdb_comm.SendGetMetaDataPacket(uid, thread_id, buffer, offset); +} + +Status ProcessGDBRemote::GetTraceConfig(lldb::user_id_t uid, +                                        TraceOptions &options) { +  return m_gdb_comm.SendGetTraceConfigPacket(uid, options); +} + +void ProcessGDBRemote::DidExit() { +  // When we exit, disconnect from the GDB server communications +  m_gdb_comm.Disconnect(); +} + +void ProcessGDBRemote::DidAttach(ArchSpec &process_arch) { +  // If you can figure out what the architecture is, fill it in here. +  process_arch.Clear(); +  DidLaunchOrAttach(process_arch); +} + +Status ProcessGDBRemote::WillResume() { +  m_continue_c_tids.clear(); +  m_continue_C_tids.clear(); +  m_continue_s_tids.clear(); +  m_continue_S_tids.clear(); +  m_jstopinfo_sp.reset(); +  m_jthreadsinfo_sp.reset(); +  return Status(); +} + +Status ProcessGDBRemote::DoResume() { +  Status error; +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  if (log) +    log->Printf("ProcessGDBRemote::Resume()"); + +  ListenerSP listener_sp( +      Listener::MakeListener("gdb-remote.resume-packet-sent")); +  if (listener_sp->StartListeningForEvents( +          &m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) { +    listener_sp->StartListeningForEvents( +        &m_async_broadcaster, +        ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); + +    const size_t num_threads = GetThreadList().GetSize(); + +    StreamString continue_packet; +    bool continue_packet_error = false; +    if (m_gdb_comm.HasAnyVContSupport()) { +      if (!GetTarget().GetNonStopModeEnabled() && +          (m_continue_c_tids.size() == num_threads || +           (m_continue_c_tids.empty() && m_continue_C_tids.empty() && +            m_continue_s_tids.empty() && m_continue_S_tids.empty()))) { +        // All threads are continuing, just send a "c" packet +        continue_packet.PutCString("c"); +      } else { +        continue_packet.PutCString("vCont"); + +        if (!m_continue_c_tids.empty()) { +          if (m_gdb_comm.GetVContSupported('c')) { +            for (tid_collection::const_iterator +                     t_pos = m_continue_c_tids.begin(), +                     t_end = m_continue_c_tids.end(); +                 t_pos != t_end; ++t_pos) +              continue_packet.Printf(";c:%4.4" PRIx64, *t_pos); +          } else +            continue_packet_error = true; +        } + +        if (!continue_packet_error && !m_continue_C_tids.empty()) { +          if (m_gdb_comm.GetVContSupported('C')) { +            for (tid_sig_collection::const_iterator +                     s_pos = m_continue_C_tids.begin(), +                     s_end = m_continue_C_tids.end(); +                 s_pos != s_end; ++s_pos) +              continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second, +                                     s_pos->first); +          } else +            continue_packet_error = true; +        } + +        if (!continue_packet_error && !m_continue_s_tids.empty()) { +          if (m_gdb_comm.GetVContSupported('s')) { +            for (tid_collection::const_iterator +                     t_pos = m_continue_s_tids.begin(), +                     t_end = m_continue_s_tids.end(); +                 t_pos != t_end; ++t_pos) +              continue_packet.Printf(";s:%4.4" PRIx64, *t_pos); +          } else +            continue_packet_error = true; +        } + +        if (!continue_packet_error && !m_continue_S_tids.empty()) { +          if (m_gdb_comm.GetVContSupported('S')) { +            for (tid_sig_collection::const_iterator +                     s_pos = m_continue_S_tids.begin(), +                     s_end = m_continue_S_tids.end(); +                 s_pos != s_end; ++s_pos) +              continue_packet.Printf(";S%2.2x:%4.4" PRIx64, s_pos->second, +                                     s_pos->first); +          } else +            continue_packet_error = true; +        } + +        if (continue_packet_error) +          continue_packet.Clear(); +      } +    } else +      continue_packet_error = true; + +    if (continue_packet_error) { +      // Either no vCont support, or we tried to use part of the vCont packet +      // that wasn't supported by the remote GDB server. We need to try and +      // make a simple packet that can do our continue +      const size_t num_continue_c_tids = m_continue_c_tids.size(); +      const size_t num_continue_C_tids = m_continue_C_tids.size(); +      const size_t num_continue_s_tids = m_continue_s_tids.size(); +      const size_t num_continue_S_tids = m_continue_S_tids.size(); +      if (num_continue_c_tids > 0) { +        if (num_continue_c_tids == num_threads) { +          // All threads are resuming... +          m_gdb_comm.SetCurrentThreadForRun(-1); +          continue_packet.PutChar('c'); +          continue_packet_error = false; +        } else if (num_continue_c_tids == 1 && num_continue_C_tids == 0 && +                   num_continue_s_tids == 0 && num_continue_S_tids == 0) { +          // Only one thread is continuing +          m_gdb_comm.SetCurrentThreadForRun(m_continue_c_tids.front()); +          continue_packet.PutChar('c'); +          continue_packet_error = false; +        } +      } + +      if (continue_packet_error && num_continue_C_tids > 0) { +        if ((num_continue_C_tids + num_continue_c_tids) == num_threads && +            num_continue_C_tids > 0 && num_continue_s_tids == 0 && +            num_continue_S_tids == 0) { +          const int continue_signo = m_continue_C_tids.front().second; +          // Only one thread is continuing +          if (num_continue_C_tids > 1) { +            // More that one thread with a signal, yet we don't have vCont +            // support and we are being asked to resume each thread with a +            // signal, we need to make sure they are all the same signal, or we +            // can't issue the continue accurately with the current support... +            if (num_continue_C_tids > 1) { +              continue_packet_error = false; +              for (size_t i = 1; i < m_continue_C_tids.size(); ++i) { +                if (m_continue_C_tids[i].second != continue_signo) +                  continue_packet_error = true; +              } +            } +            if (!continue_packet_error) +              m_gdb_comm.SetCurrentThreadForRun(-1); +          } else { +            // Set the continue thread ID +            continue_packet_error = false; +            m_gdb_comm.SetCurrentThreadForRun(m_continue_C_tids.front().first); +          } +          if (!continue_packet_error) { +            // Add threads continuing with the same signo... +            continue_packet.Printf("C%2.2x", continue_signo); +          } +        } +      } + +      if (continue_packet_error && num_continue_s_tids > 0) { +        if (num_continue_s_tids == num_threads) { +          // All threads are resuming... +          m_gdb_comm.SetCurrentThreadForRun(-1); + +          // If in Non-Stop-Mode use vCont when stepping +          if (GetTarget().GetNonStopModeEnabled()) { +            if (m_gdb_comm.GetVContSupported('s')) +              continue_packet.PutCString("vCont;s"); +            else +              continue_packet.PutChar('s'); +          } else +            continue_packet.PutChar('s'); + +          continue_packet_error = false; +        } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && +                   num_continue_s_tids == 1 && num_continue_S_tids == 0) { +          // Only one thread is stepping +          m_gdb_comm.SetCurrentThreadForRun(m_continue_s_tids.front()); +          continue_packet.PutChar('s'); +          continue_packet_error = false; +        } +      } + +      if (!continue_packet_error && num_continue_S_tids > 0) { +        if (num_continue_S_tids == num_threads) { +          const int step_signo = m_continue_S_tids.front().second; +          // Are all threads trying to step with the same signal? +          continue_packet_error = false; +          if (num_continue_S_tids > 1) { +            for (size_t i = 1; i < num_threads; ++i) { +              if (m_continue_S_tids[i].second != step_signo) +                continue_packet_error = true; +            } +          } +          if (!continue_packet_error) { +            // Add threads stepping with the same signo... +            m_gdb_comm.SetCurrentThreadForRun(-1); +            continue_packet.Printf("S%2.2x", step_signo); +          } +        } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && +                   num_continue_s_tids == 0 && num_continue_S_tids == 1) { +          // Only one thread is stepping with signal +          m_gdb_comm.SetCurrentThreadForRun(m_continue_S_tids.front().first); +          continue_packet.Printf("S%2.2x", m_continue_S_tids.front().second); +          continue_packet_error = false; +        } +      } +    } + +    if (continue_packet_error) { +      error.SetErrorString("can't make continue packet for this resume"); +    } else { +      EventSP event_sp; +      if (!m_async_thread.IsJoinable()) { +        error.SetErrorString("Trying to resume but the async thread is dead."); +        if (log) +          log->Printf("ProcessGDBRemote::DoResume: Trying to resume but the " +                      "async thread is dead."); +        return error; +      } + +      m_async_broadcaster.BroadcastEvent( +          eBroadcastBitAsyncContinue, +          new EventDataBytes(continue_packet.GetString().data(), +                             continue_packet.GetSize())); + +      if (!listener_sp->GetEvent(event_sp, std::chrono::seconds(5))) { +        error.SetErrorString("Resume timed out."); +        if (log) +          log->Printf("ProcessGDBRemote::DoResume: Resume timed out."); +      } else if (event_sp->BroadcasterIs(&m_async_broadcaster)) { +        error.SetErrorString("Broadcast continue, but the async thread was " +                             "killed before we got an ack back."); +        if (log) +          log->Printf("ProcessGDBRemote::DoResume: Broadcast continue, but the " +                      "async thread was killed before we got an ack back."); +        return error; +      } +    } +  } + +  return error; +} + +void ProcessGDBRemote::HandleStopReplySequence() { +  while (true) { +    // Send vStopped +    StringExtractorGDBRemote response; +    m_gdb_comm.SendPacketAndWaitForResponse("vStopped", response, false); + +    // OK represents end of signal list +    if (response.IsOKResponse()) +      break; + +    // If not OK or a normal packet we have a problem +    if (!response.IsNormalResponse()) +      break; + +    SetLastStopPacket(response); +  } +} + +void ProcessGDBRemote::ClearThreadIDList() { +  std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); +  m_thread_ids.clear(); +  m_thread_pcs.clear(); +} + +size_t +ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue(std::string &value) { +  m_thread_ids.clear(); +  size_t comma_pos; +  lldb::tid_t tid; +  while ((comma_pos = value.find(',')) != std::string::npos) { +    value[comma_pos] = '\0'; +    // thread in big endian hex +    tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16); +    if (tid != LLDB_INVALID_THREAD_ID) +      m_thread_ids.push_back(tid); +    value.erase(0, comma_pos + 1); +  } +  tid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_THREAD_ID, 16); +  if (tid != LLDB_INVALID_THREAD_ID) +    m_thread_ids.push_back(tid); +  return m_thread_ids.size(); +} + +size_t +ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue(std::string &value) { +  m_thread_pcs.clear(); +  size_t comma_pos; +  lldb::addr_t pc; +  while ((comma_pos = value.find(',')) != std::string::npos) { +    value[comma_pos] = '\0'; +    pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16); +    if (pc != LLDB_INVALID_ADDRESS) +      m_thread_pcs.push_back(pc); +    value.erase(0, comma_pos + 1); +  } +  pc = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16); +  if (pc != LLDB_INVALID_THREAD_ID) +    m_thread_pcs.push_back(pc); +  return m_thread_pcs.size(); +} + +bool ProcessGDBRemote::UpdateThreadIDList() { +  std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); + +  if (m_jthreadsinfo_sp) { +    // If we have the JSON threads info, we can get the thread list from that +    StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); +    if (thread_infos && thread_infos->GetSize() > 0) { +      m_thread_ids.clear(); +      m_thread_pcs.clear(); +      thread_infos->ForEach([this](StructuredData::Object *object) -> bool { +        StructuredData::Dictionary *thread_dict = object->GetAsDictionary(); +        if (thread_dict) { +          // Set the thread stop info from the JSON dictionary +          SetThreadStopInfo(thread_dict); +          lldb::tid_t tid = LLDB_INVALID_THREAD_ID; +          if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>("tid", tid)) +            m_thread_ids.push_back(tid); +        } +        return true; // Keep iterating through all thread_info objects +      }); +    } +    if (!m_thread_ids.empty()) +      return true; +  } else { +    // See if we can get the thread IDs from the current stop reply packets +    // that might contain a "threads" key/value pair + +    // Lock the thread stack while we access it +    // Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); +    std::unique_lock<std::recursive_mutex> stop_stack_lock( +        m_last_stop_packet_mutex, std::defer_lock); +    if (stop_stack_lock.try_lock()) { +      // Get the number of stop packets on the stack +      int nItems = m_stop_packet_stack.size(); +      // Iterate over them +      for (int i = 0; i < nItems; i++) { +        // Get the thread stop info +        StringExtractorGDBRemote &stop_info = m_stop_packet_stack[i]; +        const std::string &stop_info_str = stop_info.GetStringRef(); + +        m_thread_pcs.clear(); +        const size_t thread_pcs_pos = stop_info_str.find(";thread-pcs:"); +        if (thread_pcs_pos != std::string::npos) { +          const size_t start = thread_pcs_pos + strlen(";thread-pcs:"); +          const size_t end = stop_info_str.find(';', start); +          if (end != std::string::npos) { +            std::string value = stop_info_str.substr(start, end - start); +            UpdateThreadPCsFromStopReplyThreadsValue(value); +          } +        } + +        const size_t threads_pos = stop_info_str.find(";threads:"); +        if (threads_pos != std::string::npos) { +          const size_t start = threads_pos + strlen(";threads:"); +          const size_t end = stop_info_str.find(';', start); +          if (end != std::string::npos) { +            std::string value = stop_info_str.substr(start, end - start); +            if (UpdateThreadIDsFromStopReplyThreadsValue(value)) +              return true; +          } +        } +      } +    } +  } + +  bool sequence_mutex_unavailable = false; +  m_gdb_comm.GetCurrentThreadIDs(m_thread_ids, sequence_mutex_unavailable); +  if (sequence_mutex_unavailable) { +    return false; // We just didn't get the list +  } +  return true; +} + +bool ProcessGDBRemote::UpdateThreadList(ThreadList &old_thread_list, +                                        ThreadList &new_thread_list) { +  // locker will keep a mutex locked until it goes out of scope +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_THREAD)); +  LLDB_LOGV(log, "pid = {0}", GetID()); + +  size_t num_thread_ids = m_thread_ids.size(); +  // The "m_thread_ids" thread ID list should always be updated after each stop +  // reply packet, but in case it isn't, update it here. +  if (num_thread_ids == 0) { +    if (!UpdateThreadIDList()) +      return false; +    num_thread_ids = m_thread_ids.size(); +  } + +  ThreadList old_thread_list_copy(old_thread_list); +  if (num_thread_ids > 0) { +    for (size_t i = 0; i < num_thread_ids; ++i) { +      tid_t tid = m_thread_ids[i]; +      ThreadSP thread_sp( +          old_thread_list_copy.RemoveThreadByProtocolID(tid, false)); +      if (!thread_sp) { +        thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid); +        LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.", +                  thread_sp.get(), thread_sp->GetID()); +      } else { +        LLDB_LOGV(log, "Found old thread: {0} for thread ID: {1:x}.", +                  thread_sp.get(), thread_sp->GetID()); +      } + +      SetThreadPc(thread_sp, i); +      new_thread_list.AddThreadSortedByIndexID(thread_sp); +    } +  } + +  // Whatever that is left in old_thread_list_copy are not present in +  // new_thread_list. Remove non-existent threads from internal id table. +  size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); +  for (size_t i = 0; i < old_num_thread_ids; i++) { +    ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false)); +    if (old_thread_sp) { +      lldb::tid_t old_thread_id = old_thread_sp->GetProtocolID(); +      m_thread_id_to_index_id_map.erase(old_thread_id); +    } +  } + +  return true; +} + +void ProcessGDBRemote::SetThreadPc(const ThreadSP &thread_sp, uint64_t index) { +  if (m_thread_ids.size() == m_thread_pcs.size() && thread_sp.get() && +      GetByteOrder() != eByteOrderInvalid) { +    ThreadGDBRemote *gdb_thread = +        static_cast<ThreadGDBRemote *>(thread_sp.get()); +    RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); +    if (reg_ctx_sp) { +      uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber( +          eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); +      if (pc_regnum != LLDB_INVALID_REGNUM) { +        gdb_thread->PrivateSetRegisterValue(pc_regnum, m_thread_pcs[index]); +      } +    } +  } +} + +bool ProcessGDBRemote::GetThreadStopInfoFromJSON( +    ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp) { +  // See if we got thread stop infos for all threads via the "jThreadsInfo" +  // packet +  if (thread_infos_sp) { +    StructuredData::Array *thread_infos = thread_infos_sp->GetAsArray(); +    if (thread_infos) { +      lldb::tid_t tid; +      const size_t n = thread_infos->GetSize(); +      for (size_t i = 0; i < n; ++i) { +        StructuredData::Dictionary *thread_dict = +            thread_infos->GetItemAtIndex(i)->GetAsDictionary(); +        if (thread_dict) { +          if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>( +                  "tid", tid, LLDB_INVALID_THREAD_ID)) { +            if (tid == thread->GetID()) +              return (bool)SetThreadStopInfo(thread_dict); +          } +        } +      } +    } +  } +  return false; +} + +bool ProcessGDBRemote::CalculateThreadStopInfo(ThreadGDBRemote *thread) { +  // See if we got thread stop infos for all threads via the "jThreadsInfo" +  // packet +  if (GetThreadStopInfoFromJSON(thread, m_jthreadsinfo_sp)) +    return true; + +  // See if we got thread stop info for any threads valid stop info reasons +  // threads via the "jstopinfo" packet stop reply packet key/value pair? +  if (m_jstopinfo_sp) { +    // If we have "jstopinfo" then we have stop descriptions for all threads +    // that have stop reasons, and if there is no entry for a thread, then it +    // has no stop reason. +    thread->GetRegisterContext()->InvalidateIfNeeded(true); +    if (!GetThreadStopInfoFromJSON(thread, m_jstopinfo_sp)) { +      thread->SetStopInfo(StopInfoSP()); +    } +    return true; +  } + +  // Fall back to using the qThreadStopInfo packet +  StringExtractorGDBRemote stop_packet; +  if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet)) +    return SetThreadStopInfo(stop_packet) == eStateStopped; +  return false; +} + +ThreadSP ProcessGDBRemote::SetThreadStopInfo( +    lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map, +    uint8_t signo, const std::string &thread_name, const std::string &reason, +    const std::string &description, uint32_t exc_type, +    const std::vector<addr_t> &exc_data, addr_t thread_dispatch_qaddr, +    bool queue_vars_valid, // Set to true if queue_name, queue_kind and +                           // queue_serial are valid +    LazyBool associated_with_dispatch_queue, addr_t dispatch_queue_t, +    std::string &queue_name, QueueKind queue_kind, uint64_t queue_serial) { +  ThreadSP thread_sp; +  if (tid != LLDB_INVALID_THREAD_ID) { +    // Scope for "locker" below +    { +      // m_thread_list_real does have its own mutex, but we need to hold onto +      // the mutex between the call to m_thread_list_real.FindThreadByID(...) +      // and the m_thread_list_real.AddThread(...) so it doesn't change on us +      std::lock_guard<std::recursive_mutex> guard( +          m_thread_list_real.GetMutex()); +      thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); + +      if (!thread_sp) { +        // Create the thread if we need to +        thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid); +        m_thread_list_real.AddThread(thread_sp); +      } +    } + +    if (thread_sp) { +      ThreadGDBRemote *gdb_thread = +          static_cast<ThreadGDBRemote *>(thread_sp.get()); +      gdb_thread->GetRegisterContext()->InvalidateIfNeeded(true); + +      auto iter = std::find(m_thread_ids.begin(), m_thread_ids.end(), tid); +      if (iter != m_thread_ids.end()) { +        SetThreadPc(thread_sp, iter - m_thread_ids.begin()); +      } + +      for (const auto &pair : expedited_register_map) { +        StringExtractor reg_value_extractor; +        reg_value_extractor.GetStringRef() = pair.second; +        DataBufferSP buffer_sp(new DataBufferHeap( +            reg_value_extractor.GetStringRef().size() / 2, 0)); +        reg_value_extractor.GetHexBytes(buffer_sp->GetData(), '\xcc'); +        gdb_thread->PrivateSetRegisterValue(pair.first, buffer_sp->GetData()); +      } + +      thread_sp->SetName(thread_name.empty() ? nullptr : thread_name.c_str()); + +      gdb_thread->SetThreadDispatchQAddr(thread_dispatch_qaddr); +      // Check if the GDB server was able to provide the queue name, kind and +      // serial number +      if (queue_vars_valid) +        gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind, +                                 queue_serial, dispatch_queue_t, +                                 associated_with_dispatch_queue); +      else +        gdb_thread->ClearQueueInfo(); + +      gdb_thread->SetAssociatedWithLibdispatchQueue( +          associated_with_dispatch_queue); + +      if (dispatch_queue_t != LLDB_INVALID_ADDRESS) +        gdb_thread->SetQueueLibdispatchQueueAddress(dispatch_queue_t); + +      // Make sure we update our thread stop reason just once +      if (!thread_sp->StopInfoIsUpToDate()) { +        thread_sp->SetStopInfo(StopInfoSP()); +        // If there's a memory thread backed by this thread, we need to use it +        // to calculate StopInfo. +        if (ThreadSP memory_thread_sp = +                m_thread_list.GetBackingThread(thread_sp)) +          thread_sp = memory_thread_sp; + +        if (exc_type != 0) { +          const size_t exc_data_size = exc_data.size(); + +          thread_sp->SetStopInfo( +              StopInfoMachException::CreateStopReasonWithMachException( +                  *thread_sp, exc_type, exc_data_size, +                  exc_data_size >= 1 ? exc_data[0] : 0, +                  exc_data_size >= 2 ? exc_data[1] : 0, +                  exc_data_size >= 3 ? exc_data[2] : 0)); +        } else { +          bool handled = false; +          bool did_exec = false; +          if (!reason.empty()) { +            if (reason == "trace") { +              addr_t pc = thread_sp->GetRegisterContext()->GetPC(); +              lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() +                                                      ->GetBreakpointSiteList() +                                                      .FindByAddress(pc); + +              // If the current pc is a breakpoint site then the StopInfo +              // should be set to Breakpoint Otherwise, it will be set to +              // Trace. +              if (bp_site_sp && +                  bp_site_sp->ValidForThisThread(thread_sp.get())) { +                thread_sp->SetStopInfo( +                    StopInfo::CreateStopReasonWithBreakpointSiteID( +                        *thread_sp, bp_site_sp->GetID())); +              } else +                thread_sp->SetStopInfo( +                    StopInfo::CreateStopReasonToTrace(*thread_sp)); +              handled = true; +            } else if (reason == "breakpoint") { +              addr_t pc = thread_sp->GetRegisterContext()->GetPC(); +              lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() +                                                      ->GetBreakpointSiteList() +                                                      .FindByAddress(pc); +              if (bp_site_sp) { +                // If the breakpoint is for this thread, then we'll report the +                // hit, but if it is for another thread, we can just report no +                // reason.  We don't need to worry about stepping over the +                // breakpoint here, that will be taken care of when the thread +                // resumes and notices that there's a breakpoint under the pc. +                handled = true; +                if (bp_site_sp->ValidForThisThread(thread_sp.get())) { +                  thread_sp->SetStopInfo( +                      StopInfo::CreateStopReasonWithBreakpointSiteID( +                          *thread_sp, bp_site_sp->GetID())); +                } else { +                  StopInfoSP invalid_stop_info_sp; +                  thread_sp->SetStopInfo(invalid_stop_info_sp); +                } +              } +            } else if (reason == "trap") { +              // Let the trap just use the standard signal stop reason below... +            } else if (reason == "watchpoint") { +              StringExtractor desc_extractor(description.c_str()); +              addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); +              uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32); +              addr_t wp_hit_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); +              watch_id_t watch_id = LLDB_INVALID_WATCH_ID; +              if (wp_addr != LLDB_INVALID_ADDRESS) { +                WatchpointSP wp_sp; +                ArchSpec::Core core = GetTarget().GetArchitecture().GetCore(); +                if ((core >= ArchSpec::kCore_mips_first && +                     core <= ArchSpec::kCore_mips_last) || +                    (core >= ArchSpec::eCore_arm_generic && +                     core <= ArchSpec::eCore_arm_aarch64)) +                  wp_sp = GetTarget().GetWatchpointList().FindByAddress( +                      wp_hit_addr); +                if (!wp_sp) +                  wp_sp = +                      GetTarget().GetWatchpointList().FindByAddress(wp_addr); +                if (wp_sp) { +                  wp_sp->SetHardwareIndex(wp_index); +                  watch_id = wp_sp->GetID(); +                } +              } +              if (watch_id == LLDB_INVALID_WATCH_ID) { +                Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet( +                    GDBR_LOG_WATCHPOINTS)); +                if (log) +                  log->Printf("failed to find watchpoint"); +              } +              thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithWatchpointID( +                  *thread_sp, watch_id, wp_hit_addr)); +              handled = true; +            } else if (reason == "exception") { +              thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException( +                  *thread_sp, description.c_str())); +              handled = true; +            } else if (reason == "exec") { +              did_exec = true; +              thread_sp->SetStopInfo( +                  StopInfo::CreateStopReasonWithExec(*thread_sp)); +              handled = true; +            } +          } else if (!signo) { +            addr_t pc = thread_sp->GetRegisterContext()->GetPC(); +            lldb::BreakpointSiteSP bp_site_sp = +                thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress( +                    pc); + +            // If the current pc is a breakpoint site then the StopInfo should +            // be set to Breakpoint even though the remote stub did not set it +            // as such. This can happen when the thread is involuntarily +            // interrupted (e.g. due to stops on other threads) just as it is +            // about to execute the breakpoint instruction. +            if (bp_site_sp && bp_site_sp->ValidForThisThread(thread_sp.get())) { +              thread_sp->SetStopInfo( +                  StopInfo::CreateStopReasonWithBreakpointSiteID( +                      *thread_sp, bp_site_sp->GetID())); +              handled = true; +            } +          } + +          if (!handled && signo && !did_exec) { +            if (signo == SIGTRAP) { +              // Currently we are going to assume SIGTRAP means we are either +              // hitting a breakpoint or hardware single stepping. +              handled = true; +              addr_t pc = thread_sp->GetRegisterContext()->GetPC() + +                          m_breakpoint_pc_offset; +              lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess() +                                                      ->GetBreakpointSiteList() +                                                      .FindByAddress(pc); + +              if (bp_site_sp) { +                // If the breakpoint is for this thread, then we'll report the +                // hit, but if it is for another thread, we can just report no +                // reason.  We don't need to worry about stepping over the +                // breakpoint here, that will be taken care of when the thread +                // resumes and notices that there's a breakpoint under the pc. +                if (bp_site_sp->ValidForThisThread(thread_sp.get())) { +                  if (m_breakpoint_pc_offset != 0) +                    thread_sp->GetRegisterContext()->SetPC(pc); +                  thread_sp->SetStopInfo( +                      StopInfo::CreateStopReasonWithBreakpointSiteID( +                          *thread_sp, bp_site_sp->GetID())); +                } else { +                  StopInfoSP invalid_stop_info_sp; +                  thread_sp->SetStopInfo(invalid_stop_info_sp); +                } +              } else { +                // If we were stepping then assume the stop was the result of +                // the trace.  If we were not stepping then report the SIGTRAP. +                // FIXME: We are still missing the case where we single step +                // over a trap instruction. +                if (thread_sp->GetTemporaryResumeState() == eStateStepping) +                  thread_sp->SetStopInfo( +                      StopInfo::CreateStopReasonToTrace(*thread_sp)); +                else +                  thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal( +                      *thread_sp, signo, description.c_str())); +              } +            } +            if (!handled) +              thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal( +                  *thread_sp, signo, description.c_str())); +          } + +          if (!description.empty()) { +            lldb::StopInfoSP stop_info_sp(thread_sp->GetStopInfo()); +            if (stop_info_sp) { +              const char *stop_info_desc = stop_info_sp->GetDescription(); +              if (!stop_info_desc || !stop_info_desc[0]) +                stop_info_sp->SetDescription(description.c_str()); +            } else { +              thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException( +                  *thread_sp, description.c_str())); +            } +          } +        } +      } +    } +  } +  return thread_sp; +} + +lldb::ThreadSP +ProcessGDBRemote::SetThreadStopInfo(StructuredData::Dictionary *thread_dict) { +  static ConstString g_key_tid("tid"); +  static ConstString g_key_name("name"); +  static ConstString g_key_reason("reason"); +  static ConstString g_key_metype("metype"); +  static ConstString g_key_medata("medata"); +  static ConstString g_key_qaddr("qaddr"); +  static ConstString g_key_dispatch_queue_t("dispatch_queue_t"); +  static ConstString g_key_associated_with_dispatch_queue( +      "associated_with_dispatch_queue"); +  static ConstString g_key_queue_name("qname"); +  static ConstString g_key_queue_kind("qkind"); +  static ConstString g_key_queue_serial_number("qserialnum"); +  static ConstString g_key_registers("registers"); +  static ConstString g_key_memory("memory"); +  static ConstString g_key_address("address"); +  static ConstString g_key_bytes("bytes"); +  static ConstString g_key_description("description"); +  static ConstString g_key_signal("signal"); + +  // Stop with signal and thread info +  lldb::tid_t tid = LLDB_INVALID_THREAD_ID; +  uint8_t signo = 0; +  std::string value; +  std::string thread_name; +  std::string reason; +  std::string description; +  uint32_t exc_type = 0; +  std::vector<addr_t> exc_data; +  addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; +  ExpeditedRegisterMap expedited_register_map; +  bool queue_vars_valid = false; +  addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS; +  LazyBool associated_with_dispatch_queue = eLazyBoolCalculate; +  std::string queue_name; +  QueueKind queue_kind = eQueueKindUnknown; +  uint64_t queue_serial_number = 0; +  // Iterate through all of the thread dictionary key/value pairs from the +  // structured data dictionary + +  thread_dict->ForEach([this, &tid, &expedited_register_map, &thread_name, +                        &signo, &reason, &description, &exc_type, &exc_data, +                        &thread_dispatch_qaddr, &queue_vars_valid, +                        &associated_with_dispatch_queue, &dispatch_queue_t, +                        &queue_name, &queue_kind, &queue_serial_number]( +                           ConstString key, +                           StructuredData::Object *object) -> bool { +    if (key == g_key_tid) { +      // thread in big endian hex +      tid = object->GetIntegerValue(LLDB_INVALID_THREAD_ID); +    } else if (key == g_key_metype) { +      // exception type in big endian hex +      exc_type = object->GetIntegerValue(0); +    } else if (key == g_key_medata) { +      // exception data in big endian hex +      StructuredData::Array *array = object->GetAsArray(); +      if (array) { +        array->ForEach([&exc_data](StructuredData::Object *object) -> bool { +          exc_data.push_back(object->GetIntegerValue()); +          return true; // Keep iterating through all array items +        }); +      } +    } else if (key == g_key_name) { +      thread_name = object->GetStringValue(); +    } else if (key == g_key_qaddr) { +      thread_dispatch_qaddr = object->GetIntegerValue(LLDB_INVALID_ADDRESS); +    } else if (key == g_key_queue_name) { +      queue_vars_valid = true; +      queue_name = object->GetStringValue(); +    } else if (key == g_key_queue_kind) { +      std::string queue_kind_str = object->GetStringValue(); +      if (queue_kind_str == "serial") { +        queue_vars_valid = true; +        queue_kind = eQueueKindSerial; +      } else if (queue_kind_str == "concurrent") { +        queue_vars_valid = true; +        queue_kind = eQueueKindConcurrent; +      } +    } else if (key == g_key_queue_serial_number) { +      queue_serial_number = object->GetIntegerValue(0); +      if (queue_serial_number != 0) +        queue_vars_valid = true; +    } else if (key == g_key_dispatch_queue_t) { +      dispatch_queue_t = object->GetIntegerValue(0); +      if (dispatch_queue_t != 0 && dispatch_queue_t != LLDB_INVALID_ADDRESS) +        queue_vars_valid = true; +    } else if (key == g_key_associated_with_dispatch_queue) { +      queue_vars_valid = true; +      bool associated = object->GetBooleanValue(); +      if (associated) +        associated_with_dispatch_queue = eLazyBoolYes; +      else +        associated_with_dispatch_queue = eLazyBoolNo; +    } else if (key == g_key_reason) { +      reason = object->GetStringValue(); +    } else if (key == g_key_description) { +      description = object->GetStringValue(); +    } else if (key == g_key_registers) { +      StructuredData::Dictionary *registers_dict = object->GetAsDictionary(); + +      if (registers_dict) { +        registers_dict->ForEach( +            [&expedited_register_map](ConstString key, +                                      StructuredData::Object *object) -> bool { +              const uint32_t reg = +                  StringConvert::ToUInt32(key.GetCString(), UINT32_MAX, 10); +              if (reg != UINT32_MAX) +                expedited_register_map[reg] = object->GetStringValue(); +              return true; // Keep iterating through all array items +            }); +      } +    } else if (key == g_key_memory) { +      StructuredData::Array *array = object->GetAsArray(); +      if (array) { +        array->ForEach([this](StructuredData::Object *object) -> bool { +          StructuredData::Dictionary *mem_cache_dict = +              object->GetAsDictionary(); +          if (mem_cache_dict) { +            lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; +            if (mem_cache_dict->GetValueForKeyAsInteger<lldb::addr_t>( +                    "address", mem_cache_addr)) { +              if (mem_cache_addr != LLDB_INVALID_ADDRESS) { +                llvm::StringRef str; +                if (mem_cache_dict->GetValueForKeyAsString("bytes", str)) { +                  StringExtractor bytes(str); +                  bytes.SetFilePos(0); + +                  const size_t byte_size = bytes.GetStringRef().size() / 2; +                  DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); +                  const size_t bytes_copied = +                      bytes.GetHexBytes(data_buffer_sp->GetData(), 0); +                  if (bytes_copied == byte_size) +                    m_memory_cache.AddL1CacheData(mem_cache_addr, +                                                  data_buffer_sp); +                } +              } +            } +          } +          return true; // Keep iterating through all array items +        }); +      } + +    } else if (key == g_key_signal) +      signo = object->GetIntegerValue(LLDB_INVALID_SIGNAL_NUMBER); +    return true; // Keep iterating through all dictionary key/value pairs +  }); + +  return SetThreadStopInfo(tid, expedited_register_map, signo, thread_name, +                           reason, description, exc_type, exc_data, +                           thread_dispatch_qaddr, queue_vars_valid, +                           associated_with_dispatch_queue, dispatch_queue_t, +                           queue_name, queue_kind, queue_serial_number); +} + +StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { +  stop_packet.SetFilePos(0); +  const char stop_type = stop_packet.GetChar(); +  switch (stop_type) { +  case 'T': +  case 'S': { +    // This is a bit of a hack, but is is required. If we did exec, we need to +    // clear our thread lists and also know to rebuild our dynamic register +    // info before we lookup and threads and populate the expedited register +    // values so we need to know this right away so we can cleanup and update +    // our registers. +    const uint32_t stop_id = GetStopID(); +    if (stop_id == 0) { +      // Our first stop, make sure we have a process ID, and also make sure we +      // know about our registers +      if (GetID() == LLDB_INVALID_PROCESS_ID) { +        lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); +        if (pid != LLDB_INVALID_PROCESS_ID) +          SetID(pid); +      } +      BuildDynamicRegisterInfo(true); +    } +    // Stop with signal and thread info +    lldb::tid_t tid = LLDB_INVALID_THREAD_ID; +    const uint8_t signo = stop_packet.GetHexU8(); +    llvm::StringRef key; +    llvm::StringRef value; +    std::string thread_name; +    std::string reason; +    std::string description; +    uint32_t exc_type = 0; +    std::vector<addr_t> exc_data; +    addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; +    bool queue_vars_valid = +        false; // says if locals below that start with "queue_" are valid +    addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS; +    LazyBool associated_with_dispatch_queue = eLazyBoolCalculate; +    std::string queue_name; +    QueueKind queue_kind = eQueueKindUnknown; +    uint64_t queue_serial_number = 0; +    ExpeditedRegisterMap expedited_register_map; +    while (stop_packet.GetNameColonValue(key, value)) { +      if (key.compare("metype") == 0) { +        // exception type in big endian hex +        value.getAsInteger(16, exc_type); +      } else if (key.compare("medata") == 0) { +        // exception data in big endian hex +        uint64_t x; +        value.getAsInteger(16, x); +        exc_data.push_back(x); +      } else if (key.compare("thread") == 0) { +        // thread in big endian hex +        if (value.getAsInteger(16, tid)) +          tid = LLDB_INVALID_THREAD_ID; +      } else if (key.compare("threads") == 0) { +        std::lock_guard<std::recursive_mutex> guard( +            m_thread_list_real.GetMutex()); + +        m_thread_ids.clear(); +        // A comma separated list of all threads in the current +        // process that includes the thread for this stop reply packet +        lldb::tid_t tid; +        while (!value.empty()) { +          llvm::StringRef tid_str; +          std::tie(tid_str, value) = value.split(','); +          if (tid_str.getAsInteger(16, tid)) +            tid = LLDB_INVALID_THREAD_ID; +          m_thread_ids.push_back(tid); +        } +      } else if (key.compare("thread-pcs") == 0) { +        m_thread_pcs.clear(); +        // A comma separated list of all threads in the current +        // process that includes the thread for this stop reply packet +        lldb::addr_t pc; +        while (!value.empty()) { +          llvm::StringRef pc_str; +          std::tie(pc_str, value) = value.split(','); +          if (pc_str.getAsInteger(16, pc)) +            pc = LLDB_INVALID_ADDRESS; +          m_thread_pcs.push_back(pc); +        } +      } else if (key.compare("jstopinfo") == 0) { +        StringExtractor json_extractor(value); +        std::string json; +        // Now convert the HEX bytes into a string value +        json_extractor.GetHexByteString(json); + +        // This JSON contains thread IDs and thread stop info for all threads. +        // It doesn't contain expedited registers, memory or queue info. +        m_jstopinfo_sp = StructuredData::ParseJSON(json); +      } else if (key.compare("hexname") == 0) { +        StringExtractor name_extractor(value); +        std::string name; +        // Now convert the HEX bytes into a string value +        name_extractor.GetHexByteString(thread_name); +      } else if (key.compare("name") == 0) { +        thread_name = value; +      } else if (key.compare("qaddr") == 0) { +        value.getAsInteger(16, thread_dispatch_qaddr); +      } else if (key.compare("dispatch_queue_t") == 0) { +        queue_vars_valid = true; +        value.getAsInteger(16, dispatch_queue_t); +      } else if (key.compare("qname") == 0) { +        queue_vars_valid = true; +        StringExtractor name_extractor(value); +        // Now convert the HEX bytes into a string value +        name_extractor.GetHexByteString(queue_name); +      } else if (key.compare("qkind") == 0) { +        queue_kind = llvm::StringSwitch<QueueKind>(value) +                         .Case("serial", eQueueKindSerial) +                         .Case("concurrent", eQueueKindConcurrent) +                         .Default(eQueueKindUnknown); +        queue_vars_valid = queue_kind != eQueueKindUnknown; +      } else if (key.compare("qserialnum") == 0) { +        if (!value.getAsInteger(0, queue_serial_number)) +          queue_vars_valid = true; +      } else if (key.compare("reason") == 0) { +        reason = value; +      } else if (key.compare("description") == 0) { +        StringExtractor desc_extractor(value); +        // Now convert the HEX bytes into a string value +        desc_extractor.GetHexByteString(description); +      } else if (key.compare("memory") == 0) { +        // Expedited memory. GDB servers can choose to send back expedited +        // memory that can populate the L1 memory cache in the process so that +        // things like the frame pointer backchain can be expedited. This will +        // help stack backtracing be more efficient by not having to send as +        // many memory read requests down the remote GDB server. + +        // Key/value pair format: memory:<addr>=<bytes>; +        // <addr> is a number whose base will be interpreted by the prefix: +        //      "0x[0-9a-fA-F]+" for hex +        //      "0[0-7]+" for octal +        //      "[1-9]+" for decimal +        // <bytes> is native endian ASCII hex bytes just like the register +        // values +        llvm::StringRef addr_str, bytes_str; +        std::tie(addr_str, bytes_str) = value.split('='); +        if (!addr_str.empty() && !bytes_str.empty()) { +          lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; +          if (!addr_str.getAsInteger(0, mem_cache_addr)) { +            StringExtractor bytes(bytes_str); +            const size_t byte_size = bytes.GetBytesLeft() / 2; +            DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); +            const size_t bytes_copied = +                bytes.GetHexBytes(data_buffer_sp->GetData(), 0); +            if (bytes_copied == byte_size) +              m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); +          } +        } +      } else if (key.compare("watch") == 0 || key.compare("rwatch") == 0 || +                 key.compare("awatch") == 0) { +        // Support standard GDB remote stop reply packet 'TAAwatch:addr' +        lldb::addr_t wp_addr = LLDB_INVALID_ADDRESS; +        value.getAsInteger(16, wp_addr); + +        WatchpointSP wp_sp = +            GetTarget().GetWatchpointList().FindByAddress(wp_addr); +        uint32_t wp_index = LLDB_INVALID_INDEX32; + +        if (wp_sp) +          wp_index = wp_sp->GetHardwareIndex(); + +        reason = "watchpoint"; +        StreamString ostr; +        ostr.Printf("%" PRIu64 " %" PRIu32, wp_addr, wp_index); +        description = ostr.GetString(); +      } else if (key.compare("library") == 0) { +        LoadModules(); +      } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) { +        uint32_t reg = UINT32_MAX; +        if (!key.getAsInteger(16, reg)) +          expedited_register_map[reg] = std::move(value); +      } +    } + +    if (tid == LLDB_INVALID_THREAD_ID) { +      // A thread id may be invalid if the response is old style 'S' packet +      // which does not provide the +      // thread information. So update the thread list and choose the first +      // one. +      UpdateThreadIDList(); + +      if (!m_thread_ids.empty()) { +        tid = m_thread_ids.front(); +      } +    } + +    ThreadSP thread_sp = SetThreadStopInfo( +        tid, expedited_register_map, signo, thread_name, reason, description, +        exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, +        associated_with_dispatch_queue, dispatch_queue_t, queue_name, +        queue_kind, queue_serial_number); + +    return eStateStopped; +  } break; + +  case 'W': +  case 'X': +    // process exited +    return eStateExited; + +  default: +    break; +  } +  return eStateInvalid; +} + +void ProcessGDBRemote::RefreshStateAfterStop() { +  std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); + +  m_thread_ids.clear(); +  m_thread_pcs.clear(); +  // Set the thread stop info. It might have a "threads" key whose value is a +  // list of all thread IDs in the current process, so m_thread_ids might get +  // set. + +  // Scope for the lock +  { +    // Check to see if SetThreadStopInfo() filled in m_thread_ids? +    if (m_thread_ids.empty()) { +        // No, we need to fetch the thread list manually +        UpdateThreadIDList(); +    } +    // We might set some stop info's so make sure the thread list is up to +    // date before we do that or we might overwrite what was computed here. +    UpdateThreadListIfNeeded(); + +    // Lock the thread stack while we access it +    std::lock_guard<std::recursive_mutex> guard(m_last_stop_packet_mutex); +    // Get the number of stop packets on the stack +    int nItems = m_stop_packet_stack.size(); +    // Iterate over them +    for (int i = 0; i < nItems; i++) { +      // Get the thread stop info +      StringExtractorGDBRemote stop_info = m_stop_packet_stack[i]; +      // Process thread stop info +      SetThreadStopInfo(stop_info); +    } +    // Clear the thread stop stack +    m_stop_packet_stack.clear(); +  } +   +  // If we have queried for a default thread id +  if (m_initial_tid != LLDB_INVALID_THREAD_ID) { +    m_thread_list.SetSelectedThreadByID(m_initial_tid); +    m_initial_tid = LLDB_INVALID_THREAD_ID; +  } + +  // Let all threads recover from stopping and do any clean up based on the +  // previous thread state (if any). +  m_thread_list_real.RefreshStateAfterStop(); +} + +Status ProcessGDBRemote::DoHalt(bool &caused_stop) { +  Status error; + +  if (m_public_state.GetValue() == eStateAttaching) { +    // We are being asked to halt during an attach. We need to just close our +    // file handle and debugserver will go away, and we can be done... +    m_gdb_comm.Disconnect(); +  } else +    caused_stop = m_gdb_comm.Interrupt(); +  return error; +} + +Status ProcessGDBRemote::DoDetach(bool keep_stopped) { +  Status error; +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  if (log) +    log->Printf("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped); + +  error = m_gdb_comm.Detach(keep_stopped); +  if (log) { +    if (error.Success()) +      log->PutCString( +          "ProcessGDBRemote::DoDetach() detach packet sent successfully"); +    else +      log->Printf("ProcessGDBRemote::DoDetach() detach packet send failed: %s", +                  error.AsCString() ? error.AsCString() : "<unknown error>"); +  } + +  if (!error.Success()) +    return error; + +  // Sleep for one second to let the process get all detached... +  StopAsyncThread(); + +  SetPrivateState(eStateDetached); +  ResumePrivateStateThread(); + +  // KillDebugserverProcess (); +  return error; +} + +Status ProcessGDBRemote::DoDestroy() { +  Status error; +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  if (log) +    log->Printf("ProcessGDBRemote::DoDestroy()"); + +#ifdef LLDB_ENABLE_ALL // XXX Currently no iOS target support on FreeBSD +  // There is a bug in older iOS debugservers where they don't shut down the +  // process they are debugging properly.  If the process is sitting at a +  // breakpoint or an exception, this can cause problems with restarting.  So +  // we check to see if any of our threads are stopped at a breakpoint, and if +  // so we remove all the breakpoints, resume the process, and THEN destroy it +  // again. +  // +  // Note, we don't have a good way to test the version of debugserver, but I +  // happen to know that the set of all the iOS debugservers which don't +  // support GetThreadSuffixSupported() and that of the debugservers with this +  // bug are equal.  There really should be a better way to test this! +  // +  // We also use m_destroy_tried_resuming to make sure we only do this once, if +  // we resume and then halt and get called here to destroy again and we're +  // still at a breakpoint or exception, then we should just do the straight- +  // forward kill. +  // +  // And of course, if we weren't able to stop the process by the time we get +  // here, it isn't necessary (or helpful) to do any of this. + +  if (!m_gdb_comm.GetThreadSuffixSupported() && +      m_public_state.GetValue() != eStateRunning) { +    PlatformSP platform_sp = GetTarget().GetPlatform(); + +    // FIXME: These should be ConstStrings so we aren't doing strcmp'ing. +    if (platform_sp && platform_sp->GetName() && +        platform_sp->GetName() == PlatformRemoteiOS::GetPluginNameStatic()) { +      if (m_destroy_tried_resuming) { +        if (log) +          log->PutCString("ProcessGDBRemote::DoDestroy() - Tried resuming to " +                          "destroy once already, not doing it again."); +      } else { +        // At present, the plans are discarded and the breakpoints disabled +        // Process::Destroy, but we really need it to happen here and it +        // doesn't matter if we do it twice. +        m_thread_list.DiscardThreadPlans(); +        DisableAllBreakpointSites(); + +        bool stop_looks_like_crash = false; +        ThreadList &threads = GetThreadList(); + +        { +          std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); + +          size_t num_threads = threads.GetSize(); +          for (size_t i = 0; i < num_threads; i++) { +            ThreadSP thread_sp = threads.GetThreadAtIndex(i); +            StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); +            StopReason reason = eStopReasonInvalid; +            if (stop_info_sp) +              reason = stop_info_sp->GetStopReason(); +            if (reason == eStopReasonBreakpoint || +                reason == eStopReasonException) { +              if (log) +                log->Printf( +                    "ProcessGDBRemote::DoDestroy() - thread: 0x%4.4" PRIx64 +                    " stopped with reason: %s.", +                    thread_sp->GetProtocolID(), stop_info_sp->GetDescription()); +              stop_looks_like_crash = true; +              break; +            } +          } +        } + +        if (stop_looks_like_crash) { +          if (log) +            log->PutCString("ProcessGDBRemote::DoDestroy() - Stopped at a " +                            "breakpoint, continue and then kill."); +          m_destroy_tried_resuming = true; + +          // If we are going to run again before killing, it would be good to +          // suspend all the threads before resuming so they won't get into +          // more trouble.  Sadly, for the threads stopped with the breakpoint +          // or exception, the exception doesn't get cleared if it is +          // suspended, so we do have to run the risk of letting those threads +          // proceed a bit. + +          { +            std::lock_guard<std::recursive_mutex> guard(threads.GetMutex()); + +            size_t num_threads = threads.GetSize(); +            for (size_t i = 0; i < num_threads; i++) { +              ThreadSP thread_sp = threads.GetThreadAtIndex(i); +              StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); +              StopReason reason = eStopReasonInvalid; +              if (stop_info_sp) +                reason = stop_info_sp->GetStopReason(); +              if (reason != eStopReasonBreakpoint && +                  reason != eStopReasonException) { +                if (log) +                  log->Printf("ProcessGDBRemote::DoDestroy() - Suspending " +                              "thread: 0x%4.4" PRIx64 " before running.", +                              thread_sp->GetProtocolID()); +                thread_sp->SetResumeState(eStateSuspended); +              } +            } +          } +          Resume(); +          return Destroy(false); +        } +      } +    } +  } +#endif // LLDB_ENABLE_ALL + +  // Interrupt if our inferior is running... +  int exit_status = SIGABRT; +  std::string exit_string; + +  if (m_gdb_comm.IsConnected()) { +    if (m_public_state.GetValue() != eStateAttaching) { +      StringExtractorGDBRemote response; +      bool send_async = true; +      GDBRemoteCommunication::ScopedTimeout(m_gdb_comm, +                                            std::chrono::seconds(3)); + +      if (m_gdb_comm.SendPacketAndWaitForResponse("k", response, send_async) == +          GDBRemoteCommunication::PacketResult::Success) { +        char packet_cmd = response.GetChar(0); + +        if (packet_cmd == 'W' || packet_cmd == 'X') { +#if defined(__APPLE__) +          // For Native processes on Mac OS X, we launch through the Host +          // Platform, then hand the process off to debugserver, which becomes +          // the parent process through "PT_ATTACH".  Then when we go to kill +          // the process on Mac OS X we call ptrace(PT_KILL) to kill it, then +          // we call waitpid which returns with no error and the correct +          // status.  But amusingly enough that doesn't seem to actually reap +          // the process, but instead it is left around as a Zombie.  Probably +          // the kernel is in the process of switching ownership back to lldb +          // which was the original parent, and gets confused in the handoff. +          // Anyway, so call waitpid here to finally reap it. +          PlatformSP platform_sp(GetTarget().GetPlatform()); +          if (platform_sp && platform_sp->IsHost()) { +            int status; +            ::pid_t reap_pid; +            reap_pid = waitpid(GetID(), &status, WNOHANG); +            if (log) +              log->Printf("Reaped pid: %d, status: %d.\n", reap_pid, status); +          } +#endif +          SetLastStopPacket(response); +          ClearThreadIDList(); +          exit_status = response.GetHexU8(); +        } else { +          if (log) +            log->Printf("ProcessGDBRemote::DoDestroy - got unexpected response " +                        "to k packet: %s", +                        response.GetStringRef().c_str()); +          exit_string.assign("got unexpected response to k packet: "); +          exit_string.append(response.GetStringRef()); +        } +      } else { +        if (log) +          log->Printf("ProcessGDBRemote::DoDestroy - failed to send k packet"); +        exit_string.assign("failed to send the k packet"); +      } +    } else { +      if (log) +        log->Printf("ProcessGDBRemote::DoDestroy - killed or interrupted while " +                    "attaching"); +      exit_string.assign("killed or interrupted while attaching."); +    } +  } else { +    // If we missed setting the exit status on the way out, do it here. +    // NB set exit status can be called multiple times, the first one sets the +    // status. +    exit_string.assign("destroying when not connected to debugserver"); +  } + +  SetExitStatus(exit_status, exit_string.c_str()); + +  StopAsyncThread(); +  KillDebugserverProcess(); +  return error; +} + +void ProcessGDBRemote::SetLastStopPacket( +    const StringExtractorGDBRemote &response) { +  const bool did_exec = +      response.GetStringRef().find(";reason:exec;") != std::string::npos; +  if (did_exec) { +    Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +    if (log) +      log->Printf("ProcessGDBRemote::SetLastStopPacket () - detected exec"); + +    m_thread_list_real.Clear(); +    m_thread_list.Clear(); +    BuildDynamicRegisterInfo(true); +    m_gdb_comm.ResetDiscoverableSettings(did_exec); +  } + +  // Scope the lock +  { +    // Lock the thread stack while we access it +    std::lock_guard<std::recursive_mutex> guard(m_last_stop_packet_mutex); + +    // We are are not using non-stop mode, there can only be one last stop +    // reply packet, so clear the list. +    if (!GetTarget().GetNonStopModeEnabled()) +      m_stop_packet_stack.clear(); + +    // Add this stop packet to the stop packet stack This stack will get popped +    // and examined when we switch to the Stopped state +    m_stop_packet_stack.push_back(response); +  } +} + +void ProcessGDBRemote::SetUnixSignals(const UnixSignalsSP &signals_sp) { +  Process::SetUnixSignals(std::make_shared<GDBRemoteSignals>(signals_sp)); +} + +// Process Queries + +bool ProcessGDBRemote::IsAlive() { +  return m_gdb_comm.IsConnected() && Process::IsAlive(); +} + +addr_t ProcessGDBRemote::GetImageInfoAddress() { +  // request the link map address via the $qShlibInfoAddr packet +  lldb::addr_t addr = m_gdb_comm.GetShlibInfoAddr(); + +  // the loaded module list can also provides a link map address +  if (addr == LLDB_INVALID_ADDRESS) { +    LoadedModuleInfoList list; +    if (GetLoadedModuleList(list).Success()) +      addr = list.m_link_map; +  } + +  return addr; +} + +void ProcessGDBRemote::WillPublicStop() { +  // See if the GDB remote client supports the JSON threads info. If so, we +  // gather stop info for all threads, expedited registers, expedited memory, +  // runtime queue information (iOS and MacOSX only), and more. Expediting +  // memory will help stack backtracing be much faster. Expediting registers +  // will make sure we don't have to read the thread registers for GPRs. +  m_jthreadsinfo_sp = m_gdb_comm.GetThreadsInfo(); + +  if (m_jthreadsinfo_sp) { +    // Now set the stop info for each thread and also expedite any registers +    // and memory that was in the jThreadsInfo response. +    StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); +    if (thread_infos) { +      const size_t n = thread_infos->GetSize(); +      for (size_t i = 0; i < n; ++i) { +        StructuredData::Dictionary *thread_dict = +            thread_infos->GetItemAtIndex(i)->GetAsDictionary(); +        if (thread_dict) +          SetThreadStopInfo(thread_dict); +      } +    } +  } +} + +// Process Memory +size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size, +                                      Status &error) { +  GetMaxMemorySize(); +  bool binary_memory_read = m_gdb_comm.GetxPacketSupported(); +  // M and m packets take 2 bytes for 1 byte of memory +  size_t max_memory_size = +      binary_memory_read ? m_max_memory_size : m_max_memory_size / 2; +  if (size > max_memory_size) { +    // Keep memory read sizes down to a sane limit. This function will be +    // called multiple times in order to complete the task by +    // lldb_private::Process so it is ok to do this. +    size = max_memory_size; +  } + +  char packet[64]; +  int packet_len; +  packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64, +                          binary_memory_read ? 'x' : 'm', (uint64_t)addr, +                          (uint64_t)size); +  assert(packet_len + 1 < (int)sizeof(packet)); +  UNUSED_IF_ASSERT_DISABLED(packet_len); +  StringExtractorGDBRemote response; +  if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, true) == +      GDBRemoteCommunication::PacketResult::Success) { +    if (response.IsNormalResponse()) { +      error.Clear(); +      if (binary_memory_read) { +        // The lower level GDBRemoteCommunication packet receive layer has +        // already de-quoted any 0x7d character escaping that was present in +        // the packet + +        size_t data_received_size = response.GetBytesLeft(); +        if (data_received_size > size) { +          // Don't write past the end of BUF if the remote debug server gave us +          // too much data for some reason. +          data_received_size = size; +        } +        memcpy(buf, response.GetStringRef().data(), data_received_size); +        return data_received_size; +      } else { +        return response.GetHexBytes( +            llvm::MutableArrayRef<uint8_t>((uint8_t *)buf, size), '\xdd'); +      } +    } else if (response.IsErrorResponse()) +      error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr); +    else if (response.IsUnsupportedResponse()) +      error.SetErrorStringWithFormat( +          "GDB server does not support reading memory"); +    else +      error.SetErrorStringWithFormat( +          "unexpected response to GDB server memory read packet '%s': '%s'", +          packet, response.GetStringRef().c_str()); +  } else { +    error.SetErrorStringWithFormat("failed to send packet: '%s'", packet); +  } +  return 0; +} + +Status ProcessGDBRemote::WriteObjectFile( +    std::vector<ObjectFile::LoadableData> entries) { +  Status error; +  // Sort the entries by address because some writes, like those to flash +  // memory, must happen in order of increasing address. +  std::stable_sort( +      std::begin(entries), std::end(entries), +      [](const ObjectFile::LoadableData a, const ObjectFile::LoadableData b) { +        return a.Dest < b.Dest; +      }); +  m_allow_flash_writes = true; +  error = Process::WriteObjectFile(entries); +  if (error.Success()) +    error = FlashDone(); +  else +    // Even though some of the writing failed, try to send a flash done if some +    // of the writing succeeded so the flash state is reset to normal, but +    // don't stomp on the error status that was set in the write failure since +    // that's the one we want to report back. +    FlashDone(); +  m_allow_flash_writes = false; +  return error; +} + +bool ProcessGDBRemote::HasErased(FlashRange range) { +  auto size = m_erased_flash_ranges.GetSize(); +  for (size_t i = 0; i < size; ++i) +    if (m_erased_flash_ranges.GetEntryAtIndex(i)->Contains(range)) +      return true; +  return false; +} + +Status ProcessGDBRemote::FlashErase(lldb::addr_t addr, size_t size) { +  Status status; + +  MemoryRegionInfo region; +  status = GetMemoryRegionInfo(addr, region); +  if (!status.Success()) +    return status; + +  // The gdb spec doesn't say if erasures are allowed across multiple regions, +  // but we'll disallow it to be safe and to keep the logic simple by worring +  // about only one region's block size.  DoMemoryWrite is this function's +  // primary user, and it can easily keep writes within a single memory region +  if (addr + size > region.GetRange().GetRangeEnd()) { +    status.SetErrorString("Unable to erase flash in multiple regions"); +    return status; +  } + +  uint64_t blocksize = region.GetBlocksize(); +  if (blocksize == 0) { +    status.SetErrorString("Unable to erase flash because blocksize is 0"); +    return status; +  } + +  // Erasures can only be done on block boundary adresses, so round down addr +  // and round up size +  lldb::addr_t block_start_addr = addr - (addr % blocksize); +  size += (addr - block_start_addr); +  if ((size % blocksize) != 0) +    size += (blocksize - size % blocksize); + +  FlashRange range(block_start_addr, size); + +  if (HasErased(range)) +    return status; + +  // We haven't erased the entire range, but we may have erased part of it. +  // (e.g., block A is already erased and range starts in A and ends in B). So, +  // adjust range if necessary to exclude already erased blocks. +  if (!m_erased_flash_ranges.IsEmpty()) { +    // Assuming that writes and erasures are done in increasing addr order, +    // because that is a requirement of the vFlashWrite command.  Therefore, we +    // only need to look at the last range in the list for overlap. +    const auto &last_range = *m_erased_flash_ranges.Back(); +    if (range.GetRangeBase() < last_range.GetRangeEnd()) { +      auto overlap = last_range.GetRangeEnd() - range.GetRangeBase(); +      // overlap will be less than range.GetByteSize() or else HasErased() +      // would have been true +      range.SetByteSize(range.GetByteSize() - overlap); +      range.SetRangeBase(range.GetRangeBase() + overlap); +    } +  } + +  StreamString packet; +  packet.Printf("vFlashErase:%" PRIx64 ",%" PRIx64, range.GetRangeBase(), +                (uint64_t)range.GetByteSize()); + +  StringExtractorGDBRemote response; +  if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, +                                              true) == +      GDBRemoteCommunication::PacketResult::Success) { +    if (response.IsOKResponse()) { +      m_erased_flash_ranges.Insert(range, true); +    } else { +      if (response.IsErrorResponse()) +        status.SetErrorStringWithFormat("flash erase failed for 0x%" PRIx64, +                                        addr); +      else if (response.IsUnsupportedResponse()) +        status.SetErrorStringWithFormat("GDB server does not support flashing"); +      else +        status.SetErrorStringWithFormat( +            "unexpected response to GDB server flash erase packet '%s': '%s'", +            packet.GetData(), response.GetStringRef().c_str()); +    } +  } else { +    status.SetErrorStringWithFormat("failed to send packet: '%s'", +                                    packet.GetData()); +  } +  return status; +} + +Status ProcessGDBRemote::FlashDone() { +  Status status; +  // If we haven't erased any blocks, then we must not have written anything +  // either, so there is no need to actually send a vFlashDone command +  if (m_erased_flash_ranges.IsEmpty()) +    return status; +  StringExtractorGDBRemote response; +  if (m_gdb_comm.SendPacketAndWaitForResponse("vFlashDone", response, true) == +      GDBRemoteCommunication::PacketResult::Success) { +    if (response.IsOKResponse()) { +      m_erased_flash_ranges.Clear(); +    } else { +      if (response.IsErrorResponse()) +        status.SetErrorStringWithFormat("flash done failed"); +      else if (response.IsUnsupportedResponse()) +        status.SetErrorStringWithFormat("GDB server does not support flashing"); +      else +        status.SetErrorStringWithFormat( +            "unexpected response to GDB server flash done packet: '%s'", +            response.GetStringRef().c_str()); +    } +  } else { +    status.SetErrorStringWithFormat("failed to send flash done packet"); +  } +  return status; +} + +size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf, +                                       size_t size, Status &error) { +  GetMaxMemorySize(); +  // M and m packets take 2 bytes for 1 byte of memory +  size_t max_memory_size = m_max_memory_size / 2; +  if (size > max_memory_size) { +    // Keep memory read sizes down to a sane limit. This function will be +    // called multiple times in order to complete the task by +    // lldb_private::Process so it is ok to do this. +    size = max_memory_size; +  } + +  StreamGDBRemote packet; + +  MemoryRegionInfo region; +  Status region_status = GetMemoryRegionInfo(addr, region); + +  bool is_flash = +      region_status.Success() && region.GetFlash() == MemoryRegionInfo::eYes; + +  if (is_flash) { +    if (!m_allow_flash_writes) { +      error.SetErrorString("Writing to flash memory is not allowed"); +      return 0; +    } +    // Keep the write within a flash memory region +    if (addr + size > region.GetRange().GetRangeEnd()) +      size = region.GetRange().GetRangeEnd() - addr; +    // Flash memory must be erased before it can be written +    error = FlashErase(addr, size); +    if (!error.Success()) +      return 0; +    packet.Printf("vFlashWrite:%" PRIx64 ":", addr); +    packet.PutEscapedBytes(buf, size); +  } else { +    packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size); +    packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(), +                             endian::InlHostByteOrder()); +  } +  StringExtractorGDBRemote response; +  if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, +                                              true) == +      GDBRemoteCommunication::PacketResult::Success) { +    if (response.IsOKResponse()) { +      error.Clear(); +      return size; +    } else if (response.IsErrorResponse()) +      error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64, +                                     addr); +    else if (response.IsUnsupportedResponse()) +      error.SetErrorStringWithFormat( +          "GDB server does not support writing memory"); +    else +      error.SetErrorStringWithFormat( +          "unexpected response to GDB server memory write packet '%s': '%s'", +          packet.GetData(), response.GetStringRef().c_str()); +  } else { +    error.SetErrorStringWithFormat("failed to send packet: '%s'", +                                   packet.GetData()); +  } +  return 0; +} + +lldb::addr_t ProcessGDBRemote::DoAllocateMemory(size_t size, +                                                uint32_t permissions, +                                                Status &error) { +  Log *log( +      GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_EXPRESSIONS)); +  addr_t allocated_addr = LLDB_INVALID_ADDRESS; + +  if (m_gdb_comm.SupportsAllocDeallocMemory() != eLazyBoolNo) { +    allocated_addr = m_gdb_comm.AllocateMemory(size, permissions); +    if (allocated_addr != LLDB_INVALID_ADDRESS || +        m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes) +      return allocated_addr; +  } + +  if (m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolNo) { +    // Call mmap() to create memory in the inferior.. +    unsigned prot = 0; +    if (permissions & lldb::ePermissionsReadable) +      prot |= eMmapProtRead; +    if (permissions & lldb::ePermissionsWritable) +      prot |= eMmapProtWrite; +    if (permissions & lldb::ePermissionsExecutable) +      prot |= eMmapProtExec; + +    if (InferiorCallMmap(this, allocated_addr, 0, size, prot, +                         eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) +      m_addr_to_mmap_size[allocated_addr] = size; +    else { +      allocated_addr = LLDB_INVALID_ADDRESS; +      if (log) +        log->Printf("ProcessGDBRemote::%s no direct stub support for memory " +                    "allocation, and InferiorCallMmap also failed - is stub " +                    "missing register context save/restore capability?", +                    __FUNCTION__); +    } +  } + +  if (allocated_addr == LLDB_INVALID_ADDRESS) +    error.SetErrorStringWithFormat( +        "unable to allocate %" PRIu64 " bytes of memory with permissions %s", +        (uint64_t)size, GetPermissionsAsCString(permissions)); +  else +    error.Clear(); +  return allocated_addr; +} + +Status ProcessGDBRemote::GetMemoryRegionInfo(addr_t load_addr, +                                             MemoryRegionInfo ®ion_info) { + +  Status error(m_gdb_comm.GetMemoryRegionInfo(load_addr, region_info)); +  return error; +} + +Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num) { + +  Status error(m_gdb_comm.GetWatchpointSupportInfo(num)); +  return error; +} + +Status ProcessGDBRemote::GetWatchpointSupportInfo(uint32_t &num, bool &after) { +  Status error(m_gdb_comm.GetWatchpointSupportInfo( +      num, after, GetTarget().GetArchitecture())); +  return error; +} + +Status ProcessGDBRemote::DoDeallocateMemory(lldb::addr_t addr) { +  Status error; +  LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); + +  switch (supported) { +  case eLazyBoolCalculate: +    // We should never be deallocating memory without allocating memory first +    // so we should never get eLazyBoolCalculate +    error.SetErrorString( +        "tried to deallocate memory without ever allocating memory"); +    break; + +  case eLazyBoolYes: +    if (!m_gdb_comm.DeallocateMemory(addr)) +      error.SetErrorStringWithFormat( +          "unable to deallocate memory at 0x%" PRIx64, addr); +    break; + +  case eLazyBoolNo: +    // Call munmap() to deallocate memory in the inferior.. +    { +      MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); +      if (pos != m_addr_to_mmap_size.end() && +          InferiorCallMunmap(this, addr, pos->second)) +        m_addr_to_mmap_size.erase(pos); +      else +        error.SetErrorStringWithFormat( +            "unable to deallocate memory at 0x%" PRIx64, addr); +    } +    break; +  } + +  return error; +} + +// Process STDIO +size_t ProcessGDBRemote::PutSTDIN(const char *src, size_t src_len, +                                  Status &error) { +  if (m_stdio_communication.IsConnected()) { +    ConnectionStatus status; +    m_stdio_communication.Write(src, src_len, status, nullptr); +  } else if (m_stdin_forward) { +    m_gdb_comm.SendStdinNotification(src, src_len); +  } +  return 0; +} + +Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) { +  Status error; +  assert(bp_site != nullptr); + +  // Get logging info +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); +  user_id_t site_id = bp_site->GetID(); + +  // Get the breakpoint address +  const addr_t addr = bp_site->GetLoadAddress(); + +  // Log that a breakpoint was requested +  if (log) +    log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 +                ") address = 0x%" PRIx64, +                site_id, (uint64_t)addr); + +  // Breakpoint already exists and is enabled +  if (bp_site->IsEnabled()) { +    if (log) +      log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 +                  ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)", +                  site_id, (uint64_t)addr); +    return error; +  } + +  // Get the software breakpoint trap opcode size +  const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); + +  // SupportsGDBStoppointPacket() simply checks a boolean, indicating if this +  // breakpoint type is supported by the remote stub. These are set to true by +  // default, and later set to false only after we receive an unimplemented +  // response when sending a breakpoint packet. This means initially that +  // unless we were specifically instructed to use a hardware breakpoint, LLDB +  // will attempt to set a software breakpoint. HardwareRequired() also queries +  // a boolean variable which indicates if the user specifically asked for +  // hardware breakpoints.  If true then we will skip over software +  // breakpoints. +  if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) && +      (!bp_site->HardwareRequired())) { +    // Try to send off a software breakpoint packet ($Z0) +    uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( +        eBreakpointSoftware, true, addr, bp_op_size); +    if (error_no == 0) { +      // The breakpoint was placed successfully +      bp_site->SetEnabled(true); +      bp_site->SetType(BreakpointSite::eExternal); +      return error; +    } + +    // SendGDBStoppointTypePacket() will return an error if it was unable to +    // set this breakpoint. We need to differentiate between a error specific +    // to placing this breakpoint or if we have learned that this breakpoint +    // type is unsupported. To do this, we must test the support boolean for +    // this breakpoint type to see if it now indicates that this breakpoint +    // type is unsupported.  If they are still supported then we should return +    // with the error code.  If they are now unsupported, then we would like to +    // fall through and try another form of breakpoint. +    if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) { +      if (error_no != UINT8_MAX) +        error.SetErrorStringWithFormat( +            "error: %d sending the breakpoint request", errno); +      else +        error.SetErrorString("error sending the breakpoint request"); +      return error; +    } + +    // We reach here when software breakpoints have been found to be +    // unsupported. For future calls to set a breakpoint, we will not attempt +    // to set a breakpoint with a type that is known not to be supported. +    if (log) +      log->Printf("Software breakpoints are unsupported"); + +    // So we will fall through and try a hardware breakpoint +  } + +  // The process of setting a hardware breakpoint is much the same as above. +  // We check the supported boolean for this breakpoint type, and if it is +  // thought to be supported then we will try to set this breakpoint with a +  // hardware breakpoint. +  if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { +    // Try to send off a hardware breakpoint packet ($Z1) +    uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( +        eBreakpointHardware, true, addr, bp_op_size); +    if (error_no == 0) { +      // The breakpoint was placed successfully +      bp_site->SetEnabled(true); +      bp_site->SetType(BreakpointSite::eHardware); +      return error; +    } + +    // Check if the error was something other then an unsupported breakpoint +    // type +    if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { +      // Unable to set this hardware breakpoint +      if (error_no != UINT8_MAX) +        error.SetErrorStringWithFormat( +            "error: %d sending the hardware breakpoint request " +            "(hardware breakpoint resources might be exhausted or unavailable)", +            error_no); +      else +        error.SetErrorString("error sending the hardware breakpoint request " +                             "(hardware breakpoint resources " +                             "might be exhausted or unavailable)"); +      return error; +    } + +    // We will reach here when the stub gives an unsupported response to a +    // hardware breakpoint +    if (log) +      log->Printf("Hardware breakpoints are unsupported"); + +    // Finally we will falling through to a #trap style breakpoint +  } + +  // Don't fall through when hardware breakpoints were specifically requested +  if (bp_site->HardwareRequired()) { +    error.SetErrorString("hardware breakpoints are not supported"); +    return error; +  } + +  // As a last resort we want to place a manual breakpoint. An instruction is +  // placed into the process memory using memory write packets. +  return EnableSoftwareBreakpoint(bp_site); +} + +Status ProcessGDBRemote::DisableBreakpointSite(BreakpointSite *bp_site) { +  Status error; +  assert(bp_site != nullptr); +  addr_t addr = bp_site->GetLoadAddress(); +  user_id_t site_id = bp_site->GetID(); +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); +  if (log) +    log->Printf("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 +                ") addr = 0x%8.8" PRIx64, +                site_id, (uint64_t)addr); + +  if (bp_site->IsEnabled()) { +    const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); + +    BreakpointSite::Type bp_type = bp_site->GetType(); +    switch (bp_type) { +    case BreakpointSite::eSoftware: +      error = DisableSoftwareBreakpoint(bp_site); +      break; + +    case BreakpointSite::eHardware: +      if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, false, +                                                addr, bp_op_size)) +        error.SetErrorToGenericError(); +      break; + +    case BreakpointSite::eExternal: { +      GDBStoppointType stoppoint_type; +      if (bp_site->IsHardware()) +        stoppoint_type = eBreakpointHardware; +      else +        stoppoint_type = eBreakpointSoftware; + +      if (m_gdb_comm.SendGDBStoppointTypePacket(stoppoint_type, false, addr, +                                                bp_op_size)) +        error.SetErrorToGenericError(); +    } break; +    } +    if (error.Success()) +      bp_site->SetEnabled(false); +  } else { +    if (log) +      log->Printf("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 +                  ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", +                  site_id, (uint64_t)addr); +    return error; +  } + +  if (error.Success()) +    error.SetErrorToGenericError(); +  return error; +} + +// Pre-requisite: wp != NULL. +static GDBStoppointType GetGDBStoppointType(Watchpoint *wp) { +  assert(wp); +  bool watch_read = wp->WatchpointRead(); +  bool watch_write = wp->WatchpointWrite(); + +  // watch_read and watch_write cannot both be false. +  assert(watch_read || watch_write); +  if (watch_read && watch_write) +    return eWatchpointReadWrite; +  else if (watch_read) +    return eWatchpointRead; +  else // Must be watch_write, then. +    return eWatchpointWrite; +} + +Status ProcessGDBRemote::EnableWatchpoint(Watchpoint *wp, bool notify) { +  Status error; +  if (wp) { +    user_id_t watchID = wp->GetID(); +    addr_t addr = wp->GetLoadAddress(); +    Log *log( +        ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); +    if (log) +      log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")", +                  watchID); +    if (wp->IsEnabled()) { +      if (log) +        log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 +                    ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", +                    watchID, (uint64_t)addr); +      return error; +    } + +    GDBStoppointType type = GetGDBStoppointType(wp); +    // Pass down an appropriate z/Z packet... +    if (m_gdb_comm.SupportsGDBStoppointPacket(type)) { +      if (m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr, +                                                wp->GetByteSize()) == 0) { +        wp->SetEnabled(true, notify); +        return error; +      } else +        error.SetErrorString("sending gdb watchpoint packet failed"); +    } else +      error.SetErrorString("watchpoints not supported"); +  } else { +    error.SetErrorString("Watchpoint argument was NULL."); +  } +  if (error.Success()) +    error.SetErrorToGenericError(); +  return error; +} + +Status ProcessGDBRemote::DisableWatchpoint(Watchpoint *wp, bool notify) { +  Status error; +  if (wp) { +    user_id_t watchID = wp->GetID(); + +    Log *log( +        ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); + +    addr_t addr = wp->GetLoadAddress(); + +    if (log) +      log->Printf("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 +                  ") addr = 0x%8.8" PRIx64, +                  watchID, (uint64_t)addr); + +    if (!wp->IsEnabled()) { +      if (log) +        log->Printf("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 +                    ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", +                    watchID, (uint64_t)addr); +      // See also 'class WatchpointSentry' within StopInfo.cpp. This disabling +      // attempt might come from the user-supplied actions, we'll route it in +      // order for the watchpoint object to intelligently process this action. +      wp->SetEnabled(false, notify); +      return error; +    } + +    if (wp->IsHardware()) { +      GDBStoppointType type = GetGDBStoppointType(wp); +      // Pass down an appropriate z/Z packet... +      if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, +                                                wp->GetByteSize()) == 0) { +        wp->SetEnabled(false, notify); +        return error; +      } else +        error.SetErrorString("sending gdb watchpoint packet failed"); +    } +    // TODO: clear software watchpoints if we implement them +  } else { +    error.SetErrorString("Watchpoint argument was NULL."); +  } +  if (error.Success()) +    error.SetErrorToGenericError(); +  return error; +} + +void ProcessGDBRemote::Clear() { +  m_thread_list_real.Clear(); +  m_thread_list.Clear(); +} + +Status ProcessGDBRemote::DoSignal(int signo) { +  Status error; +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  if (log) +    log->Printf("ProcessGDBRemote::DoSignal (signal = %d)", signo); + +  if (!m_gdb_comm.SendAsyncSignal(signo)) +    error.SetErrorStringWithFormat("failed to send signal %i", signo); +  return error; +} + +Status ProcessGDBRemote::ConnectToReplayServer(repro::Loader *loader) { +  if (!loader) +    return Status("No loader provided."); + +  // Construct replay history path. +  FileSpec history_file = loader->GetFile<ProcessGDBRemoteProvider::Info>(); +  if (!history_file) +    return Status("No provider for gdb-remote."); + +  // Enable replay mode. +  m_replay_mode = true; + +  // Load replay history. +  if (auto error = m_gdb_replay_server.LoadReplayHistory(history_file)) +    return Status("Unable to load replay history"); + +  // Make a local connection. +  if (auto error = GDBRemoteCommunication::ConnectLocally(m_gdb_comm, +                                                          m_gdb_replay_server)) +    return Status("Unable to connect to replay server"); + +  // Start server thread. +  m_gdb_replay_server.StartAsyncThread(); + +  // Start client thread. +  StartAsyncThread(); + +  // Do the usual setup. +  return ConnectToDebugserver(""); +} + +Status +ProcessGDBRemote::EstablishConnectionIfNeeded(const ProcessInfo &process_info) { +  // Make sure we aren't already connected? +  if (m_gdb_comm.IsConnected()) +    return Status(); + +  PlatformSP platform_sp(GetTarget().GetPlatform()); +  if (platform_sp && !platform_sp->IsHost()) +    return Status("Lost debug server connection"); + +  if (repro::Loader *loader = repro::Reproducer::Instance().GetLoader()) +    return ConnectToReplayServer(loader); + +  auto error = LaunchAndConnectToDebugserver(process_info); +  if (error.Fail()) { +    const char *error_string = error.AsCString(); +    if (error_string == nullptr) +      error_string = "unable to launch " DEBUGSERVER_BASENAME; +  } +  return error; +} +#if !defined(_WIN32) +#define USE_SOCKETPAIR_FOR_LOCAL_CONNECTION 1 +#endif + +#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION +static bool SetCloexecFlag(int fd) { +#if defined(FD_CLOEXEC) +  int flags = ::fcntl(fd, F_GETFD); +  if (flags == -1) +    return false; +  return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); +#else +  return false; +#endif +} +#endif + +Status ProcessGDBRemote::LaunchAndConnectToDebugserver( +    const ProcessInfo &process_info) { +  using namespace std::placeholders; // For _1, _2, etc. + +  Status error; +  if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) { +    // If we locate debugserver, keep that located version around +    static FileSpec g_debugserver_file_spec; + +    ProcessLaunchInfo debugserver_launch_info; +    // Make debugserver run in its own session so signals generated by special +    // terminal key sequences (^C) don't affect debugserver. +    debugserver_launch_info.SetLaunchInSeparateProcessGroup(true); + +    const std::weak_ptr<ProcessGDBRemote> this_wp = +        std::static_pointer_cast<ProcessGDBRemote>(shared_from_this()); +    debugserver_launch_info.SetMonitorProcessCallback( +        std::bind(MonitorDebugserverProcess, this_wp, _1, _2, _3, _4), false); +    debugserver_launch_info.SetUserID(process_info.GetUserID()); + +    int communication_fd = -1; +#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION +    // Use a socketpair on non-Windows systems for security and performance +    // reasons. +    int sockets[2]; /* the pair of socket descriptors */ +    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) { +      error.SetErrorToErrno(); +      return error; +    } + +    int our_socket = sockets[0]; +    int gdb_socket = sockets[1]; +    CleanUp cleanup_our(close, our_socket); +    CleanUp cleanup_gdb(close, gdb_socket); + +    // Don't let any child processes inherit our communication socket +    SetCloexecFlag(our_socket); +    communication_fd = gdb_socket; +#endif + +    error = m_gdb_comm.StartDebugserverProcess( +        nullptr, GetTarget().GetPlatform().get(), debugserver_launch_info, +        nullptr, nullptr, communication_fd); + +    if (error.Success()) +      m_debugserver_pid = debugserver_launch_info.GetProcessID(); +    else +      m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + +    if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { +#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION +      // Our process spawned correctly, we can now set our connection to use +      // our end of the socket pair +      cleanup_our.disable(); +      m_gdb_comm.SetConnection(new ConnectionFileDescriptor(our_socket, true)); +#endif +      StartAsyncThread(); +    } + +    if (error.Fail()) { +      Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + +      if (log) +        log->Printf("failed to start debugserver process: %s", +                    error.AsCString()); +      return error; +    } + +    if (m_gdb_comm.IsConnected()) { +      // Finish the connection process by doing the handshake without +      // connecting (send NULL URL) +      error = ConnectToDebugserver(""); +    } else { +      error.SetErrorString("connection failed"); +    } +  } +  return error; +} + +bool ProcessGDBRemote::MonitorDebugserverProcess( +    std::weak_ptr<ProcessGDBRemote> process_wp, lldb::pid_t debugserver_pid, +    bool exited,    // True if the process did exit +    int signo,      // Zero for no signal +    int exit_status // Exit value of process if signal is zero +) { +  // "debugserver_pid" argument passed in is the process ID for debugserver +  // that we are tracking... +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  const bool handled = true; + +  if (log) +    log->Printf("ProcessGDBRemote::%s(process_wp, pid=%" PRIu64 +                ", signo=%i (0x%x), exit_status=%i)", +                __FUNCTION__, debugserver_pid, signo, signo, exit_status); + +  std::shared_ptr<ProcessGDBRemote> process_sp = process_wp.lock(); +  if (log) +    log->Printf("ProcessGDBRemote::%s(process = %p)", __FUNCTION__, +                static_cast<void *>(process_sp.get())); +  if (!process_sp || process_sp->m_debugserver_pid != debugserver_pid) +    return handled; + +  // Sleep for a half a second to make sure our inferior process has time to +  // set its exit status before we set it incorrectly when both the debugserver +  // and the inferior process shut down. +  usleep(500000); +  // If our process hasn't yet exited, debugserver might have died. If the +  // process did exit, then we are reaping it. +  const StateType state = process_sp->GetState(); + +  if (state != eStateInvalid && state != eStateUnloaded && +      state != eStateExited && state != eStateDetached) { +    char error_str[1024]; +    if (signo) { +      const char *signal_cstr = +          process_sp->GetUnixSignals()->GetSignalAsCString(signo); +      if (signal_cstr) +        ::snprintf(error_str, sizeof(error_str), +                   DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); +      else +        ::snprintf(error_str, sizeof(error_str), +                   DEBUGSERVER_BASENAME " died with signal %i", signo); +    } else { +      ::snprintf(error_str, sizeof(error_str), +                 DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", +                 exit_status); +    } + +    process_sp->SetExitStatus(-1, error_str); +  } +  // Debugserver has exited we need to let our ProcessGDBRemote know that it no +  // longer has a debugserver instance +  process_sp->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; +  return handled; +} + +void ProcessGDBRemote::KillDebugserverProcess() { +  m_gdb_comm.Disconnect(); +  if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { +    Host::Kill(m_debugserver_pid, SIGINT); +    m_debugserver_pid = LLDB_INVALID_PROCESS_ID; +  } +} + +void ProcessGDBRemote::Initialize() { +  static llvm::once_flag g_once_flag; + +  llvm::call_once(g_once_flag, []() { +    PluginManager::RegisterPlugin(GetPluginNameStatic(), +                                  GetPluginDescriptionStatic(), CreateInstance, +                                  DebuggerInitialize); +  }); +} + +void ProcessGDBRemote::DebuggerInitialize(Debugger &debugger) { +  if (!PluginManager::GetSettingForProcessPlugin( +          debugger, PluginProperties::GetSettingName())) { +    const bool is_global_setting = true; +    PluginManager::CreateSettingForProcessPlugin( +        debugger, GetGlobalPluginProperties()->GetValueProperties(), +        ConstString("Properties for the gdb-remote process plug-in."), +        is_global_setting); +  } +} + +bool ProcessGDBRemote::StartAsyncThread() { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + +  if (log) +    log->Printf("ProcessGDBRemote::%s ()", __FUNCTION__); + +  std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); +  if (!m_async_thread.IsJoinable()) { +    // Create a thread that watches our internal state and controls which +    // events make it to clients (into the DCProcess event queue). + +    llvm::Expected<HostThread> async_thread = ThreadLauncher::LaunchThread( +        "<lldb.process.gdb-remote.async>", ProcessGDBRemote::AsyncThread, this); +    if (!async_thread) { +      LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), +               "failed to launch host thread: {}", +               llvm::toString(async_thread.takeError())); +      return false; +    } +    m_async_thread = *async_thread; +  } else if (log) +    log->Printf("ProcessGDBRemote::%s () - Called when Async thread was " +                "already running.", +                __FUNCTION__); + +  return m_async_thread.IsJoinable(); +} + +void ProcessGDBRemote::StopAsyncThread() { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + +  if (log) +    log->Printf("ProcessGDBRemote::%s ()", __FUNCTION__); + +  std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); +  if (m_async_thread.IsJoinable()) { +    m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit); + +    //  This will shut down the async thread. +    m_gdb_comm.Disconnect(); // Disconnect from the debug server. + +    // Stop the stdio thread +    m_async_thread.Join(nullptr); +    m_async_thread.Reset(); +  } else if (log) +    log->Printf( +        "ProcessGDBRemote::%s () - Called when Async thread was not running.", +        __FUNCTION__); +} + +bool ProcessGDBRemote::HandleNotifyPacket(StringExtractorGDBRemote &packet) { +  // get the packet at a string +  const std::string &pkt = packet.GetStringRef(); +  // skip %stop: +  StringExtractorGDBRemote stop_info(pkt.c_str() + 5); + +  // pass as a thread stop info packet +  SetLastStopPacket(stop_info); + +  // check for more stop reasons +  HandleStopReplySequence(); + +  // if the process is stopped then we need to fake a resume so that we can +  // stop properly with the new break. This is possible due to +  // SetPrivateState() broadcasting the state change as a side effect. +  if (GetPrivateState() == lldb::StateType::eStateStopped) { +    SetPrivateState(lldb::StateType::eStateRunning); +  } + +  // since we have some stopped packets we can halt the process +  SetPrivateState(lldb::StateType::eStateStopped); + +  return true; +} + +thread_result_t ProcessGDBRemote::AsyncThread(void *arg) { +  ProcessGDBRemote *process = (ProcessGDBRemote *)arg; + +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  if (log) +    log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 +                ") thread starting...", +                __FUNCTION__, arg, process->GetID()); + +  EventSP event_sp; +  bool done = false; +  while (!done) { +    if (log) +      log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 +                  ") listener.WaitForEvent (NULL, event_sp)...", +                  __FUNCTION__, arg, process->GetID()); +    if (process->m_async_listener_sp->GetEvent(event_sp, llvm::None)) { +      const uint32_t event_type = event_sp->GetType(); +      if (event_sp->BroadcasterIs(&process->m_async_broadcaster)) { +        if (log) +          log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 +                      ") Got an event of type: %d...", +                      __FUNCTION__, arg, process->GetID(), event_type); + +        switch (event_type) { +        case eBroadcastBitAsyncContinue: { +          const EventDataBytes *continue_packet = +              EventDataBytes::GetEventDataFromEvent(event_sp.get()); + +          if (continue_packet) { +            const char *continue_cstr = +                (const char *)continue_packet->GetBytes(); +            const size_t continue_cstr_len = continue_packet->GetByteSize(); +            if (log) +              log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 +                          ") got eBroadcastBitAsyncContinue: %s", +                          __FUNCTION__, arg, process->GetID(), continue_cstr); + +            if (::strstr(continue_cstr, "vAttach") == nullptr) +              process->SetPrivateState(eStateRunning); +            StringExtractorGDBRemote response; + +            // If in Non-Stop-Mode +            if (process->GetTarget().GetNonStopModeEnabled()) { +              // send the vCont packet +              if (!process->GetGDBRemote().SendvContPacket( +                      llvm::StringRef(continue_cstr, continue_cstr_len), +                      response)) { +                // Something went wrong +                done = true; +                break; +              } +            } +            // If in All-Stop-Mode +            else { +              StateType stop_state = +                  process->GetGDBRemote().SendContinuePacketAndWaitForResponse( +                      *process, *process->GetUnixSignals(), +                      llvm::StringRef(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 +              // caused the stop. So clear it now before we give the stop reply +              // packet to the process using the +              // process->SetLastStopPacket()... +              process->ClearThreadIDList(); + +              switch (stop_state) { +              case eStateStopped: +              case eStateCrashed: +              case eStateSuspended: +                process->SetLastStopPacket(response); +                process->SetPrivateState(stop_state); +                break; + +              case eStateExited: { +                process->SetLastStopPacket(response); +                process->ClearThreadIDList(); +                response.SetFilePos(1); + +                int exit_status = response.GetHexU8(); +                std::string desc_string; +                if (response.GetBytesLeft() > 0 && +                    response.GetChar('-') == ';') { +                  llvm::StringRef desc_str; +                  llvm::StringRef desc_token; +                  while (response.GetNameColonValue(desc_token, desc_str)) { +                    if (desc_token != "description") +                      continue; +                    StringExtractor extractor(desc_str); +                    extractor.GetHexByteString(desc_string); +                  } +                } +                process->SetExitStatus(exit_status, desc_string.c_str()); +                done = true; +                break; +              } +              case eStateInvalid: { +                // Check to see if we were trying to attach and if we got back +                // the "E87" error code from debugserver -- this indicates that +                // the process is not debuggable.  Return a slightly more +                // helpful error message about why the attach failed. +                if (::strstr(continue_cstr, "vAttach") != nullptr && +                    response.GetError() == 0x87) { +                  process->SetExitStatus(-1, "cannot attach to process due to " +                                             "System Integrity Protection"); +                } else if (::strstr(continue_cstr, "vAttach") != nullptr && +                           response.GetStatus().Fail()) { +                  process->SetExitStatus(-1, response.GetStatus().AsCString()); +                } else { +                  process->SetExitStatus(-1, "lost connection"); +                } +                break; +              } + +              default: +                process->SetPrivateState(stop_state); +                break; +              } // switch(stop_state) +            }   // else // if in All-stop-mode +          }     // if (continue_packet) +        }       // case eBroadcastBitAysncContinue +        break; + +        case eBroadcastBitAsyncThreadShouldExit: +          if (log) +            log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 +                        ") got eBroadcastBitAsyncThreadShouldExit...", +                        __FUNCTION__, arg, process->GetID()); +          done = true; +          break; + +        default: +          if (log) +            log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 +                        ") got unknown event 0x%8.8x", +                        __FUNCTION__, arg, process->GetID(), event_type); +          done = true; +          break; +        } +      } else if (event_sp->BroadcasterIs(&process->m_gdb_comm)) { +        switch (event_type) { +        case Communication::eBroadcastBitReadThreadDidExit: +          process->SetExitStatus(-1, "lost connection"); +          done = true; +          break; + +        case GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify: { +          lldb_private::Event *event = event_sp.get(); +          const EventDataBytes *continue_packet = +              EventDataBytes::GetEventDataFromEvent(event); +          StringExtractorGDBRemote notify( +              (const char *)continue_packet->GetBytes()); +          // Hand this over to the process to handle +          process->HandleNotifyPacket(notify); +          break; +        } + +        default: +          if (log) +            log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 +                        ") got unknown event 0x%8.8x", +                        __FUNCTION__, arg, process->GetID(), event_type); +          done = true; +          break; +        } +      } +    } else { +      if (log) +        log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 +                    ") listener.WaitForEvent (NULL, event_sp) => false", +                    __FUNCTION__, arg, process->GetID()); +      done = true; +    } +  } + +  if (log) +    log->Printf("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 +                ") thread exiting...", +                __FUNCTION__, arg, process->GetID()); + +  return {}; +} + +// uint32_t +// ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList +// &matches, std::vector<lldb::pid_t> &pids) +//{ +//    // If we are planning to launch the debugserver remotely, then we need to +//    fire up a debugserver +//    // process and ask it for the list of processes. But if we are local, we +//    can let the Host do it. +//    if (m_local_debugserver) +//    { +//        return Host::ListProcessesMatchingName (name, matches, pids); +//    } +//    else +//    { +//        // FIXME: Implement talking to the remote debugserver. +//        return 0; +//    } +// +//} +// +bool ProcessGDBRemote::NewThreadNotifyBreakpointHit( +    void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, +    lldb::user_id_t break_loc_id) { +  // I don't think I have to do anything here, just make sure I notice the new +  // thread when it starts to +  // run so I can stop it if that's what I want to do. +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); +  if (log) +    log->Printf("Hit New Thread Notification breakpoint."); +  return false; +} + +Status ProcessGDBRemote::UpdateAutomaticSignalFiltering() { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); +  LLDB_LOG(log, "Check if need to update ignored signals"); + +  // QPassSignals package is not supported by the server, there is no way we +  // can ignore any signals on server side. +  if (!m_gdb_comm.GetQPassSignalsSupported()) +    return Status(); + +  // No signals, nothing to send. +  if (m_unix_signals_sp == nullptr) +    return Status(); + +  // Signals' version hasn't changed, no need to send anything. +  uint64_t new_signals_version = m_unix_signals_sp->GetVersion(); +  if (new_signals_version == m_last_signals_version) { +    LLDB_LOG(log, "Signals' version hasn't changed. version={0}", +             m_last_signals_version); +    return Status(); +  } + +  auto signals_to_ignore = +      m_unix_signals_sp->GetFilteredSignals(false, false, false); +  Status error = m_gdb_comm.SendSignalsToIgnore(signals_to_ignore); + +  LLDB_LOG(log, +           "Signals' version changed. old version={0}, new version={1}, " +           "signals ignored={2}, update result={3}", +           m_last_signals_version, new_signals_version, +           signals_to_ignore.size(), error); + +  if (error.Success()) +    m_last_signals_version = new_signals_version; + +  return error; +} + +bool ProcessGDBRemote::StartNoticingNewThreads() { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); +  if (m_thread_create_bp_sp) { +    if (log && log->GetVerbose()) +      log->Printf("Enabled noticing new thread breakpoint."); +    m_thread_create_bp_sp->SetEnabled(true); +  } else { +    PlatformSP platform_sp(GetTarget().GetPlatform()); +    if (platform_sp) { +      m_thread_create_bp_sp = +          platform_sp->SetThreadCreationBreakpoint(GetTarget()); +      if (m_thread_create_bp_sp) { +        if (log && log->GetVerbose()) +          log->Printf( +              "Successfully created new thread notification breakpoint %i", +              m_thread_create_bp_sp->GetID()); +        m_thread_create_bp_sp->SetCallback( +            ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true); +      } else { +        if (log) +          log->Printf("Failed to create new thread notification breakpoint."); +      } +    } +  } +  return m_thread_create_bp_sp.get() != nullptr; +} + +bool ProcessGDBRemote::StopNoticingNewThreads() { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); +  if (log && log->GetVerbose()) +    log->Printf("Disabling new thread notification breakpoint."); + +  if (m_thread_create_bp_sp) +    m_thread_create_bp_sp->SetEnabled(false); + +  return true; +} + +DynamicLoader *ProcessGDBRemote::GetDynamicLoader() { +  if (m_dyld_up.get() == nullptr) +    m_dyld_up.reset(DynamicLoader::FindPlugin(this, nullptr)); +  return m_dyld_up.get(); +} + +Status ProcessGDBRemote::SendEventData(const char *data) { +  int return_value; +  bool was_supported; + +  Status error; + +  return_value = m_gdb_comm.SendLaunchEventDataPacket(data, &was_supported); +  if (return_value != 0) { +    if (!was_supported) +      error.SetErrorString("Sending events is not supported for this process."); +    else +      error.SetErrorStringWithFormat("Error sending event data: %d.", +                                     return_value); +  } +  return error; +} + +DataExtractor ProcessGDBRemote::GetAuxvData() { +  DataBufferSP buf; +  if (m_gdb_comm.GetQXferAuxvReadSupported()) { +    std::string response_string; +    if (m_gdb_comm.SendPacketsAndConcatenateResponses("qXfer:auxv:read::", +                                                      response_string) == +        GDBRemoteCommunication::PacketResult::Success) +      buf = std::make_shared<DataBufferHeap>(response_string.c_str(), +                                             response_string.length()); +  } +  return DataExtractor(buf, GetByteOrder(), GetAddressByteSize()); +} + +StructuredData::ObjectSP +ProcessGDBRemote::GetExtendedInfoForThread(lldb::tid_t tid) { +  StructuredData::ObjectSP object_sp; + +  if (m_gdb_comm.GetThreadExtendedInfoSupported()) { +    StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); +    SystemRuntime *runtime = GetSystemRuntime(); +    if (runtime) { +      runtime->AddThreadExtendedInfoPacketHints(args_dict); +    } +    args_dict->GetAsDictionary()->AddIntegerItem("thread", tid); + +    StreamString packet; +    packet << "jThreadExtendedInfo:"; +    args_dict->Dump(packet, false); + +    // FIXME the final character of a JSON dictionary, '}', is the escape +    // character in gdb-remote binary mode.  lldb currently doesn't escape +    // these characters in its packet output -- so we add the quoted version of +    // the } character here manually in case we talk to a debugserver which un- +    // escapes the characters at packet read time. +    packet << (char)(0x7d ^ 0x20); + +    StringExtractorGDBRemote response; +    response.SetResponseValidatorToJSON(); +    if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, +                                                false) == +        GDBRemoteCommunication::PacketResult::Success) { +      StringExtractorGDBRemote::ResponseType response_type = +          response.GetResponseType(); +      if (response_type == StringExtractorGDBRemote::eResponse) { +        if (!response.Empty()) { +          object_sp = StructuredData::ParseJSON(response.GetStringRef()); +        } +      } +    } +  } +  return object_sp; +} + +StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos( +    lldb::addr_t image_list_address, lldb::addr_t image_count) { + +  StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); +  args_dict->GetAsDictionary()->AddIntegerItem("image_list_address", +                                               image_list_address); +  args_dict->GetAsDictionary()->AddIntegerItem("image_count", image_count); + +  return GetLoadedDynamicLibrariesInfos_sender(args_dict); +} + +StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos() { +  StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); + +  args_dict->GetAsDictionary()->AddBooleanItem("fetch_all_solibs", true); + +  return GetLoadedDynamicLibrariesInfos_sender(args_dict); +} + +StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos( +    const std::vector<lldb::addr_t> &load_addresses) { +  StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); +  StructuredData::ArraySP addresses(new StructuredData::Array); + +  for (auto addr : load_addresses) { +    StructuredData::ObjectSP addr_sp(new StructuredData::Integer(addr)); +    addresses->AddItem(addr_sp); +  } + +  args_dict->GetAsDictionary()->AddItem("solib_addresses", addresses); + +  return GetLoadedDynamicLibrariesInfos_sender(args_dict); +} + +StructuredData::ObjectSP +ProcessGDBRemote::GetLoadedDynamicLibrariesInfos_sender( +    StructuredData::ObjectSP args_dict) { +  StructuredData::ObjectSP object_sp; + +  if (m_gdb_comm.GetLoadedDynamicLibrariesInfosSupported()) { +    // Scope for the scoped timeout object +    GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, +                                                  std::chrono::seconds(10)); + +    StreamString packet; +    packet << "jGetLoadedDynamicLibrariesInfos:"; +    args_dict->Dump(packet, false); + +    // FIXME the final character of a JSON dictionary, '}', is the escape +    // character in gdb-remote binary mode.  lldb currently doesn't escape +    // these characters in its packet output -- so we add the quoted version of +    // the } character here manually in case we talk to a debugserver which un- +    // escapes the characters at packet read time. +    packet << (char)(0x7d ^ 0x20); + +    StringExtractorGDBRemote response; +    response.SetResponseValidatorToJSON(); +    if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, +                                                false) == +        GDBRemoteCommunication::PacketResult::Success) { +      StringExtractorGDBRemote::ResponseType response_type = +          response.GetResponseType(); +      if (response_type == StringExtractorGDBRemote::eResponse) { +        if (!response.Empty()) { +          object_sp = StructuredData::ParseJSON(response.GetStringRef()); +        } +      } +    } +  } +  return object_sp; +} + +StructuredData::ObjectSP ProcessGDBRemote::GetSharedCacheInfo() { +  StructuredData::ObjectSP object_sp; +  StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); + +  if (m_gdb_comm.GetSharedCacheInfoSupported()) { +    StreamString packet; +    packet << "jGetSharedCacheInfo:"; +    args_dict->Dump(packet, false); + +    // FIXME the final character of a JSON dictionary, '}', is the escape +    // character in gdb-remote binary mode.  lldb currently doesn't escape +    // these characters in its packet output -- so we add the quoted version of +    // the } character here manually in case we talk to a debugserver which un- +    // escapes the characters at packet read time. +    packet << (char)(0x7d ^ 0x20); + +    StringExtractorGDBRemote response; +    response.SetResponseValidatorToJSON(); +    if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, +                                                false) == +        GDBRemoteCommunication::PacketResult::Success) { +      StringExtractorGDBRemote::ResponseType response_type = +          response.GetResponseType(); +      if (response_type == StringExtractorGDBRemote::eResponse) { +        if (!response.Empty()) { +          object_sp = StructuredData::ParseJSON(response.GetStringRef()); +        } +      } +    } +  } +  return object_sp; +} + +Status ProcessGDBRemote::ConfigureStructuredData( +    ConstString type_name, const StructuredData::ObjectSP &config_sp) { +  return m_gdb_comm.ConfigureRemoteStructuredData(type_name, config_sp); +} + +// Establish the largest memory read/write payloads we should use. If the +// remote stub has a max packet size, stay under that size. +// +// If the remote stub's max packet size is crazy large, use a reasonable +// largeish default. +// +// If the remote stub doesn't advertise a max packet size, use a conservative +// default. + +void ProcessGDBRemote::GetMaxMemorySize() { +  const uint64_t reasonable_largeish_default = 128 * 1024; +  const uint64_t conservative_default = 512; + +  if (m_max_memory_size == 0) { +    uint64_t stub_max_size = m_gdb_comm.GetRemoteMaxPacketSize(); +    if (stub_max_size != UINT64_MAX && stub_max_size != 0) { +      // Save the stub's claimed maximum packet size +      m_remote_stub_max_memory_size = stub_max_size; + +      // Even if the stub says it can support ginormous packets, don't exceed +      // our reasonable largeish default packet size. +      if (stub_max_size > reasonable_largeish_default) { +        stub_max_size = reasonable_largeish_default; +      } + +      // Memory packet have other overheads too like Maddr,size:#NN Instead of +      // calculating the bytes taken by size and addr every time, we take a +      // maximum guess here. +      if (stub_max_size > 70) +        stub_max_size -= 32 + 32 + 6; +      else { +        // In unlikely scenario that max packet size is less then 70, we will +        // hope that data being written is small enough to fit. +        Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( +            GDBR_LOG_COMM | GDBR_LOG_MEMORY)); +        if (log) +          log->Warning("Packet size is too small. " +                       "LLDB may face problems while writing memory"); +      } + +      m_max_memory_size = stub_max_size; +    } else { +      m_max_memory_size = conservative_default; +    } +  } +} + +void ProcessGDBRemote::SetUserSpecifiedMaxMemoryTransferSize( +    uint64_t user_specified_max) { +  if (user_specified_max != 0) { +    GetMaxMemorySize(); + +    if (m_remote_stub_max_memory_size != 0) { +      if (m_remote_stub_max_memory_size < user_specified_max) { +        m_max_memory_size = m_remote_stub_max_memory_size; // user specified a +                                                           // packet size too +                                                           // big, go as big +        // as the remote stub says we can go. +      } else { +        m_max_memory_size = user_specified_max; // user's packet size is good +      } +    } else { +      m_max_memory_size = +          user_specified_max; // user's packet size is probably fine +    } +  } +} + +bool ProcessGDBRemote::GetModuleSpec(const FileSpec &module_file_spec, +                                     const ArchSpec &arch, +                                     ModuleSpec &module_spec) { +  Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); + +  const ModuleCacheKey key(module_file_spec.GetPath(), +                           arch.GetTriple().getTriple()); +  auto cached = m_cached_module_specs.find(key); +  if (cached != m_cached_module_specs.end()) { +    module_spec = cached->second; +    return bool(module_spec); +  } + +  if (!m_gdb_comm.GetModuleInfo(module_file_spec, arch, module_spec)) { +    if (log) +      log->Printf("ProcessGDBRemote::%s - failed to get module info for %s:%s", +                  __FUNCTION__, module_file_spec.GetPath().c_str(), +                  arch.GetTriple().getTriple().c_str()); +    return false; +  } + +  if (log) { +    StreamString stream; +    module_spec.Dump(stream); +    log->Printf("ProcessGDBRemote::%s - got module info for (%s:%s) : %s", +                __FUNCTION__, module_file_spec.GetPath().c_str(), +                arch.GetTriple().getTriple().c_str(), stream.GetData()); +  } + +  m_cached_module_specs[key] = module_spec; +  return true; +} + +void ProcessGDBRemote::PrefetchModuleSpecs( +    llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple) { +  auto module_specs = m_gdb_comm.GetModulesInfo(module_file_specs, triple); +  if (module_specs) { +    for (const FileSpec &spec : module_file_specs) +      m_cached_module_specs[ModuleCacheKey(spec.GetPath(), +                                           triple.getTriple())] = ModuleSpec(); +    for (const ModuleSpec &spec : *module_specs) +      m_cached_module_specs[ModuleCacheKey(spec.GetFileSpec().GetPath(), +                                           triple.getTriple())] = spec; +  } +} + +llvm::VersionTuple ProcessGDBRemote::GetHostOSVersion() { +  return m_gdb_comm.GetOSVersion(); +} + +namespace { + +typedef std::vector<std::string> stringVec; + +typedef std::vector<struct GdbServerRegisterInfo> GDBServerRegisterVec; +struct RegisterSetInfo { +  ConstString name; +}; + +typedef std::map<uint32_t, RegisterSetInfo> RegisterSetMap; + +struct GdbServerTargetInfo { +  std::string arch; +  std::string osabi; +  stringVec includes; +  RegisterSetMap reg_set_map; +}; + +bool ParseRegisters(XMLNode feature_node, GdbServerTargetInfo &target_info, +                    GDBRemoteDynamicRegisterInfo &dyn_reg_info, ABISP abi_sp, +                    uint32_t &cur_reg_num, uint32_t ®_offset) { +  if (!feature_node) +    return false; + +  feature_node.ForEachChildElementWithName( +      "reg", +      [&target_info, &dyn_reg_info, &cur_reg_num, ®_offset, +       &abi_sp](const XMLNode ®_node) -> bool { +        std::string gdb_group; +        std::string gdb_type; +        ConstString reg_name; +        ConstString alt_name; +        ConstString set_name; +        std::vector<uint32_t> value_regs; +        std::vector<uint32_t> invalidate_regs; +        std::vector<uint8_t> dwarf_opcode_bytes; +        bool encoding_set = false; +        bool format_set = false; +        RegisterInfo reg_info = { +            nullptr,       // Name +            nullptr,       // Alt name +            0,             // byte size +            reg_offset,    // offset +            eEncodingUint, // encoding +            eFormatHex,    // format +            { +                LLDB_INVALID_REGNUM, // eh_frame reg num +                LLDB_INVALID_REGNUM, // DWARF reg num +                LLDB_INVALID_REGNUM, // generic reg num +                cur_reg_num,         // process plugin reg num +                cur_reg_num          // native register number +            }, +            nullptr, +            nullptr, +            nullptr, // Dwarf Expression opcode bytes pointer +            0        // Dwarf Expression opcode bytes length +        }; + +        reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type, +                                   ®_name, &alt_name, &set_name, &value_regs, +                                   &invalidate_regs, &encoding_set, &format_set, +                                   ®_info, ®_offset, &dwarf_opcode_bytes]( +                                      const llvm::StringRef &name, +                                      const llvm::StringRef &value) -> bool { +          if (name == "name") { +            reg_name.SetString(value); +          } else if (name == "bitsize") { +            reg_info.byte_size = +                StringConvert::ToUInt32(value.data(), 0, 0) / CHAR_BIT; +          } else if (name == "type") { +            gdb_type = value.str(); +          } else if (name == "group") { +            gdb_group = value.str(); +          } else if (name == "regnum") { +            const uint32_t regnum = +                StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); +            if (regnum != LLDB_INVALID_REGNUM) { +              reg_info.kinds[eRegisterKindProcessPlugin] = regnum; +            } +          } else if (name == "offset") { +            reg_offset = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); +          } else if (name == "altname") { +            alt_name.SetString(value); +          } else if (name == "encoding") { +            encoding_set = true; +            reg_info.encoding = Args::StringToEncoding(value, eEncodingUint); +          } else if (name == "format") { +            format_set = true; +            Format format = eFormatInvalid; +            if (OptionArgParser::ToFormat(value.data(), format, nullptr) +                    .Success()) +              reg_info.format = format; +            else if (value == "vector-sint8") +              reg_info.format = eFormatVectorOfSInt8; +            else if (value == "vector-uint8") +              reg_info.format = eFormatVectorOfUInt8; +            else if (value == "vector-sint16") +              reg_info.format = eFormatVectorOfSInt16; +            else if (value == "vector-uint16") +              reg_info.format = eFormatVectorOfUInt16; +            else if (value == "vector-sint32") +              reg_info.format = eFormatVectorOfSInt32; +            else if (value == "vector-uint32") +              reg_info.format = eFormatVectorOfUInt32; +            else if (value == "vector-float32") +              reg_info.format = eFormatVectorOfFloat32; +            else if (value == "vector-uint64") +              reg_info.format = eFormatVectorOfUInt64; +            else if (value == "vector-uint128") +              reg_info.format = eFormatVectorOfUInt128; +          } else if (name == "group_id") { +            const uint32_t set_id = +                StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); +            RegisterSetMap::const_iterator pos = +                target_info.reg_set_map.find(set_id); +            if (pos != target_info.reg_set_map.end()) +              set_name = pos->second.name; +          } else if (name == "gcc_regnum" || name == "ehframe_regnum") { +            reg_info.kinds[eRegisterKindEHFrame] = +                StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); +          } else if (name == "dwarf_regnum") { +            reg_info.kinds[eRegisterKindDWARF] = +                StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); +          } else if (name == "generic") { +            reg_info.kinds[eRegisterKindGeneric] = +                Args::StringToGenericRegister(value); +          } else if (name == "value_regnums") { +            SplitCommaSeparatedRegisterNumberString(value, value_regs, 0); +          } else if (name == "invalidate_regnums") { +            SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 0); +          } else if (name == "dynamic_size_dwarf_expr_bytes") { +            StringExtractor opcode_extractor; +            std::string opcode_string = value.str(); +            size_t dwarf_opcode_len = opcode_string.length() / 2; +            assert(dwarf_opcode_len > 0); + +            dwarf_opcode_bytes.resize(dwarf_opcode_len); +            reg_info.dynamic_size_dwarf_len = dwarf_opcode_len; +            opcode_extractor.GetStringRef().swap(opcode_string); +            uint32_t ret_val = +                opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes); +            assert(dwarf_opcode_len == ret_val); +            UNUSED_IF_ASSERT_DISABLED(ret_val); +            reg_info.dynamic_size_dwarf_expr_bytes = dwarf_opcode_bytes.data(); +          } else { +            printf("unhandled attribute %s = %s\n", name.data(), value.data()); +          } +          return true; // Keep iterating through all attributes +        }); + +        if (!gdb_type.empty() && !(encoding_set || format_set)) { +          if (gdb_type.find("int") == 0) { +            reg_info.format = eFormatHex; +            reg_info.encoding = eEncodingUint; +          } else if (gdb_type == "data_ptr" || gdb_type == "code_ptr") { +            reg_info.format = eFormatAddressInfo; +            reg_info.encoding = eEncodingUint; +          } else if (gdb_type == "i387_ext" || gdb_type == "float") { +            reg_info.format = eFormatFloat; +            reg_info.encoding = eEncodingIEEE754; +          } +        } + +        // Only update the register set name if we didn't get a "reg_set" +        // attribute. "set_name" will be empty if we didn't have a "reg_set" +        // attribute. +        if (!set_name) { +          if (!gdb_group.empty()) { +            set_name.SetCString(gdb_group.c_str()); +          } else { +            // If no register group name provided anywhere, +            // we'll create a 'general' register set +            set_name.SetCString("general"); +          } +        } + +        reg_info.byte_offset = reg_offset; +        assert(reg_info.byte_size != 0); +        reg_offset += reg_info.byte_size; +        if (!value_regs.empty()) { +          value_regs.push_back(LLDB_INVALID_REGNUM); +          reg_info.value_regs = value_regs.data(); +        } +        if (!invalidate_regs.empty()) { +          invalidate_regs.push_back(LLDB_INVALID_REGNUM); +          reg_info.invalidate_regs = invalidate_regs.data(); +        } + +        ++cur_reg_num; +        AugmentRegisterInfoViaABI(reg_info, reg_name, abi_sp); +        dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name); + +        return true; // Keep iterating through all "reg" elements +      }); +  return true; +} + +} // namespace + +// This method fetches a register description feature xml file from +// the remote stub and adds registers/register groupsets/architecture +// information to the current process.  It will call itself recursively +// for nested register definition files.  It returns true if it was able +// to fetch and parse an xml file. +bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess(ArchSpec &arch_to_use,  +                                                             std::string xml_filename, +                                                             uint32_t &cur_reg_num, +                                                             uint32_t ®_offset) { +  // request the target xml file +  std::string raw; +  lldb_private::Status lldberr; +  if (!m_gdb_comm.ReadExtFeature(ConstString("features"),  +                           ConstString(xml_filename.c_str()),  +                           raw, lldberr)) { +    return false; +  } + +  XMLDocument xml_document; + +  if (xml_document.ParseMemory(raw.c_str(), raw.size(), xml_filename.c_str())) { +    GdbServerTargetInfo target_info; +    std::vector<XMLNode> feature_nodes; + +    // The top level feature XML file will start with a <target> tag. +    XMLNode target_node = xml_document.GetRootElement("target"); +    if (target_node) { +      target_node.ForEachChildElement([&target_info, &feature_nodes]( +                                          const XMLNode &node) -> bool { +        llvm::StringRef name = node.GetName(); +        if (name == "architecture") { +          node.GetElementText(target_info.arch); +        } else if (name == "osabi") { +          node.GetElementText(target_info.osabi); +        } else if (name == "xi:include" || name == "include") { +          llvm::StringRef href = node.GetAttributeValue("href"); +          if (!href.empty()) +            target_info.includes.push_back(href.str()); +        } else if (name == "feature") { +          feature_nodes.push_back(node); +        } else if (name == "groups") { +          node.ForEachChildElementWithName( +              "group", [&target_info](const XMLNode &node) -> bool { +                uint32_t set_id = UINT32_MAX; +                RegisterSetInfo set_info; + +                node.ForEachAttribute( +                    [&set_id, &set_info](const llvm::StringRef &name, +                                         const llvm::StringRef &value) -> bool { +                      if (name == "id") +                        set_id = StringConvert::ToUInt32(value.data(), +                                                         UINT32_MAX, 0); +                      if (name == "name") +                        set_info.name = ConstString(value); +                      return true; // Keep iterating through all attributes +                    }); + +                if (set_id != UINT32_MAX) +                  target_info.reg_set_map[set_id] = set_info; +                return true; // Keep iterating through all "group" elements +              }); +        } +        return true; // Keep iterating through all children of the target_node +      }); +    } else { +      // In an included XML feature file, we're already "inside" the <target> +      // tag of the initial XML file; this included file will likely only have +      // a <feature> tag.  Need to check for any more included files in this +      // <feature> element. +      XMLNode feature_node = xml_document.GetRootElement("feature"); +      if (feature_node) { +        feature_nodes.push_back(feature_node); +        feature_node.ForEachChildElement([&target_info]( +                                        const XMLNode &node) -> bool { +          llvm::StringRef name = node.GetName(); +          if (name == "xi:include" || name == "include") { +            llvm::StringRef href = node.GetAttributeValue("href"); +            if (!href.empty()) +              target_info.includes.push_back(href.str()); +            } +            return true; +          }); +      } +    } + +    // If the target.xml includes an architecture entry like +    //   <architecture>i386:x86-64</architecture> (seen from VMWare ESXi) +    //   <architecture>arm</architecture> (seen from Segger JLink on unspecified arm board) +    // use that if we don't have anything better. +    if (!arch_to_use.IsValid() && !target_info.arch.empty()) { +      if (target_info.arch == "i386:x86-64") { +        // We don't have any information about vendor or OS. +        arch_to_use.SetTriple("x86_64--"); +        GetTarget().MergeArchitecture(arch_to_use); +      } + +      // SEGGER J-Link jtag boards send this very-generic arch name, +      // we'll need to use this if we have absolutely nothing better +      // to work with or the register definitions won't be accepted. +      if (target_info.arch == "arm") { +        arch_to_use.SetTriple("arm--"); +        GetTarget().MergeArchitecture(arch_to_use); +      } +    } + +    if (arch_to_use.IsValid()) { +      // Don't use Process::GetABI, this code gets called from DidAttach, and +      // in that context we haven't set the Target's architecture yet, so the +      // ABI is also potentially incorrect. +      ABISP abi_to_use_sp = ABI::FindPlugin(shared_from_this(), arch_to_use); +      for (auto &feature_node : feature_nodes) { +        ParseRegisters(feature_node, target_info, this->m_register_info, +                       abi_to_use_sp, cur_reg_num, reg_offset); +      } + +      for (const auto &include : target_info.includes) { +        GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, include,  +                                              cur_reg_num, reg_offset); +      } +    } +  } else { +    return false; +  } +  return true; +} + +// query the target of gdb-remote for extended target information returns +// true on success (got register definitions), false on failure (did not). +bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { +  // Make sure LLDB has an XML parser it can use first +  if (!XMLDocument::XMLEnabled()) +    return false; + +  // check that we have extended feature read support +  if (!m_gdb_comm.GetQXferFeaturesReadSupported()) +    return false; + +  uint32_t cur_reg_num = 0; +  uint32_t reg_offset = 0; +  if (GetGDBServerRegisterInfoXMLAndProcess (arch_to_use, "target.xml", cur_reg_num, reg_offset)) +    this->m_register_info.Finalize(arch_to_use); + +  return m_register_info.GetNumRegisters() > 0; +} + +Status ProcessGDBRemote::GetLoadedModuleList(LoadedModuleInfoList &list) { +  // Make sure LLDB has an XML parser it can use first +  if (!XMLDocument::XMLEnabled()) +    return Status(0, ErrorType::eErrorTypeGeneric); + +  Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS); +  if (log) +    log->Printf("ProcessGDBRemote::%s", __FUNCTION__); + +  GDBRemoteCommunicationClient &comm = m_gdb_comm; +  bool can_use_svr4 = GetGlobalPluginProperties()->GetUseSVR4(); + +  // check that we have extended feature read support +  if (can_use_svr4 && comm.GetQXferLibrariesSVR4ReadSupported()) { +    list.clear(); + +    // request the loaded library list +    std::string raw; +    lldb_private::Status lldberr; + +    if (!comm.ReadExtFeature(ConstString("libraries-svr4"), ConstString(""), +                             raw, lldberr)) +      return Status(0, ErrorType::eErrorTypeGeneric); + +    // parse the xml file in memory +    if (log) +      log->Printf("parsing: %s", raw.c_str()); +    XMLDocument doc; + +    if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) +      return Status(0, ErrorType::eErrorTypeGeneric); + +    XMLNode root_element = doc.GetRootElement("library-list-svr4"); +    if (!root_element) +      return Status(); + +    // main link map structure +    llvm::StringRef main_lm = root_element.GetAttributeValue("main-lm"); +    if (!main_lm.empty()) { +      list.m_link_map = +          StringConvert::ToUInt64(main_lm.data(), LLDB_INVALID_ADDRESS, 0); +    } + +    root_element.ForEachChildElementWithName( +        "library", [log, &list](const XMLNode &library) -> bool { + +          LoadedModuleInfoList::LoadedModuleInfo module; + +          library.ForEachAttribute( +              [&module](const llvm::StringRef &name, +                        const llvm::StringRef &value) -> bool { + +                if (name == "name") +                  module.set_name(value.str()); +                else if (name == "lm") { +                  // the address of the link_map struct. +                  module.set_link_map(StringConvert::ToUInt64( +                      value.data(), LLDB_INVALID_ADDRESS, 0)); +                } else if (name == "l_addr") { +                  // the displacement as read from the field 'l_addr' of the +                  // link_map struct. +                  module.set_base(StringConvert::ToUInt64( +                      value.data(), LLDB_INVALID_ADDRESS, 0)); +                  // base address is always a displacement, not an absolute +                  // value. +                  module.set_base_is_offset(true); +                } else if (name == "l_ld") { +                  // the memory address of the libraries PT_DYAMIC section. +                  module.set_dynamic(StringConvert::ToUInt64( +                      value.data(), LLDB_INVALID_ADDRESS, 0)); +                } + +                return true; // Keep iterating over all properties of "library" +              }); + +          if (log) { +            std::string name; +            lldb::addr_t lm = 0, base = 0, ld = 0; +            bool base_is_offset; + +            module.get_name(name); +            module.get_link_map(lm); +            module.get_base(base); +            module.get_base_is_offset(base_is_offset); +            module.get_dynamic(ld); + +            log->Printf("found (link_map:0x%08" PRIx64 ", base:0x%08" PRIx64 +                        "[%s], ld:0x%08" PRIx64 ", name:'%s')", +                        lm, base, (base_is_offset ? "offset" : "absolute"), ld, +                        name.c_str()); +          } + +          list.add(module); +          return true; // Keep iterating over all "library" elements in the root +                       // node +        }); + +    if (log) +      log->Printf("found %" PRId32 " modules in total", +                  (int)list.m_list.size()); +  } else if (comm.GetQXferLibrariesReadSupported()) { +    list.clear(); + +    // request the loaded library list +    std::string raw; +    lldb_private::Status lldberr; + +    if (!comm.ReadExtFeature(ConstString("libraries"), ConstString(""), raw, +                             lldberr)) +      return Status(0, ErrorType::eErrorTypeGeneric); + +    if (log) +      log->Printf("parsing: %s", raw.c_str()); +    XMLDocument doc; + +    if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) +      return Status(0, ErrorType::eErrorTypeGeneric); + +    XMLNode root_element = doc.GetRootElement("library-list"); +    if (!root_element) +      return Status(); + +    root_element.ForEachChildElementWithName( +        "library", [log, &list](const XMLNode &library) -> bool { +          LoadedModuleInfoList::LoadedModuleInfo module; + +          llvm::StringRef name = library.GetAttributeValue("name"); +          module.set_name(name.str()); + +          // The base address of a given library will be the address of its +          // first section. Most remotes send only one section for Windows +          // targets for example. +          const XMLNode §ion = +              library.FindFirstChildElementWithName("section"); +          llvm::StringRef address = section.GetAttributeValue("address"); +          module.set_base( +              StringConvert::ToUInt64(address.data(), LLDB_INVALID_ADDRESS, 0)); +          // These addresses are absolute values. +          module.set_base_is_offset(false); + +          if (log) { +            std::string name; +            lldb::addr_t base = 0; +            bool base_is_offset; +            module.get_name(name); +            module.get_base(base); +            module.get_base_is_offset(base_is_offset); + +            log->Printf("found (base:0x%08" PRIx64 "[%s], name:'%s')", base, +                        (base_is_offset ? "offset" : "absolute"), name.c_str()); +          } + +          list.add(module); +          return true; // Keep iterating over all "library" elements in the root +                       // node +        }); + +    if (log) +      log->Printf("found %" PRId32 " modules in total", +                  (int)list.m_list.size()); +  } else { +    return Status(0, ErrorType::eErrorTypeGeneric); +  } + +  return Status(); +} + +lldb::ModuleSP ProcessGDBRemote::LoadModuleAtAddress(const FileSpec &file, +                                                     lldb::addr_t link_map, +                                                     lldb::addr_t base_addr, +                                                     bool value_is_offset) { +  DynamicLoader *loader = GetDynamicLoader(); +  if (!loader) +    return nullptr; + +  return loader->LoadModuleAtAddress(file, link_map, base_addr, +                                     value_is_offset); +} + +size_t ProcessGDBRemote::LoadModules(LoadedModuleInfoList &module_list) { +  using lldb_private::process_gdb_remote::ProcessGDBRemote; + +  // request a list of loaded libraries from GDBServer +  if (GetLoadedModuleList(module_list).Fail()) +    return 0; + +  // get a list of all the modules +  ModuleList new_modules; + +  for (LoadedModuleInfoList::LoadedModuleInfo &modInfo : module_list.m_list) { +    std::string mod_name; +    lldb::addr_t mod_base; +    lldb::addr_t link_map; +    bool mod_base_is_offset; + +    bool valid = true; +    valid &= modInfo.get_name(mod_name); +    valid &= modInfo.get_base(mod_base); +    valid &= modInfo.get_base_is_offset(mod_base_is_offset); +    if (!valid) +      continue; + +    if (!modInfo.get_link_map(link_map)) +      link_map = LLDB_INVALID_ADDRESS; + +    FileSpec file(mod_name); +    FileSystem::Instance().Resolve(file); +    lldb::ModuleSP module_sp = +        LoadModuleAtAddress(file, link_map, mod_base, mod_base_is_offset); + +    if (module_sp.get()) +      new_modules.Append(module_sp); +  } + +  if (new_modules.GetSize() > 0) { +    ModuleList removed_modules; +    Target &target = GetTarget(); +    ModuleList &loaded_modules = m_process->GetTarget().GetImages(); + +    for (size_t i = 0; i < loaded_modules.GetSize(); ++i) { +      const lldb::ModuleSP loaded_module = loaded_modules.GetModuleAtIndex(i); + +      bool found = false; +      for (size_t j = 0; j < new_modules.GetSize(); ++j) { +        if (new_modules.GetModuleAtIndex(j).get() == loaded_module.get()) +          found = true; +      } + +      // The main executable will never be included in libraries-svr4, don't +      // remove it +      if (!found && +          loaded_module.get() != target.GetExecutableModulePointer()) { +        removed_modules.Append(loaded_module); +      } +    } + +    loaded_modules.Remove(removed_modules); +    m_process->GetTarget().ModulesDidUnload(removed_modules, false); + +    new_modules.ForEach([&target](const lldb::ModuleSP module_sp) -> bool { +      lldb_private::ObjectFile *obj = module_sp->GetObjectFile(); +      if (!obj) +        return true; + +      if (obj->GetType() != ObjectFile::Type::eTypeExecutable) +        return true; + +      lldb::ModuleSP module_copy_sp = module_sp; +      target.SetExecutableModule(module_copy_sp, eLoadDependentsNo); +      return false; +    }); + +    loaded_modules.AppendIfNeeded(new_modules); +    m_process->GetTarget().ModulesDidLoad(new_modules); +  } + +  return new_modules.GetSize(); +} + +size_t ProcessGDBRemote::LoadModules() { +  LoadedModuleInfoList module_list; +  return LoadModules(module_list); +} + +Status ProcessGDBRemote::GetFileLoadAddress(const FileSpec &file, +                                            bool &is_loaded, +                                            lldb::addr_t &load_addr) { +  is_loaded = false; +  load_addr = LLDB_INVALID_ADDRESS; + +  std::string file_path = file.GetPath(false); +  if (file_path.empty()) +    return Status("Empty file name specified"); + +  StreamString packet; +  packet.PutCString("qFileLoadAddress:"); +  packet.PutStringAsRawHex8(file_path); + +  StringExtractorGDBRemote response; +  if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, +                                              false) != +      GDBRemoteCommunication::PacketResult::Success) +    return Status("Sending qFileLoadAddress packet failed"); + +  if (response.IsErrorResponse()) { +    if (response.GetError() == 1) { +      // The file is not loaded into the inferior +      is_loaded = false; +      load_addr = LLDB_INVALID_ADDRESS; +      return Status(); +    } + +    return Status( +        "Fetching file load address from remote server returned an error"); +  } + +  if (response.IsNormalResponse()) { +    is_loaded = true; +    load_addr = response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); +    return Status(); +  } + +  return Status( +      "Unknown error happened during sending the load address packet"); +} + +void ProcessGDBRemote::ModulesDidLoad(ModuleList &module_list) { +  // We must call the lldb_private::Process::ModulesDidLoad () first before we +  // do anything +  Process::ModulesDidLoad(module_list); + +  // After loading shared libraries, we can ask our remote GDB server if it +  // needs any symbols. +  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::string output; +  llvm::raw_string_ostream output_stream(output); +  llvm::StringRef 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); +      uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0); + +      bool has_used_usec = false; +      uint32_t curr_used_usec = 0; +      llvm::StringRef usec_name, usec_value; +      uint32_t input_file_pos = profileDataExtractor.GetFilePos(); +      if (profileDataExtractor.GetNameColonValue(usec_name, usec_value)) { +        if (usec_name.equals("thread_used_usec")) { +          has_used_usec = true; +          usec_value.getAsInteger(0, curr_used_usec); +        } 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. + +          output_stream << name << ":"; +          int32_t index_id = AssignIndexIDToThread(thread_id); +          output_stream << index_id << ";"; + +          output_stream << usec_name << ":" << usec_value << ";"; +        } else { +          // Skip past 'thread_used_name'. +          llvm::StringRef 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. +        output_stream << name << ":" << value << ";"; +      } +    } else { +      output_stream << name << ":" << value << ";"; +    } +  } +  output_stream << end_delimiter; +  m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map; + +  return output_stream.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); +} + +static const char *const s_async_json_packet_prefix = "JSON-async:"; + +static StructuredData::ObjectSP +ParseStructuredDataPacket(llvm::StringRef packet) { +  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + +  if (!packet.consume_front(s_async_json_packet_prefix)) { +    if (log) { +      log->Printf( +          "GDBRemoteCommunicationClientBase::%s() received $J packet " +          "but was not a StructuredData packet: packet starts with " +          "%s", +          __FUNCTION__, +          packet.slice(0, strlen(s_async_json_packet_prefix)).str().c_str()); +    } +    return StructuredData::ObjectSP(); +  } + +  // This is an asynchronous JSON packet, destined for a StructuredDataPlugin. +  StructuredData::ObjectSP json_sp = StructuredData::ParseJSON(packet); +  if (log) { +    if (json_sp) { +      StreamString json_str; +      json_sp->Dump(json_str); +      json_str.Flush(); +      log->Printf("ProcessGDBRemote::%s() " +                  "received Async StructuredData packet: %s", +                  __FUNCTION__, json_str.GetData()); +    } else { +      log->Printf("ProcessGDBRemote::%s" +                  "() received StructuredData packet:" +                  " parse failure", +                  __FUNCTION__); +    } +  } +  return json_sp; +} + +void ProcessGDBRemote::HandleAsyncStructuredDataPacket(llvm::StringRef data) { +  auto structured_data_sp = ParseStructuredDataPacket(data); +  if (structured_data_sp) +    RouteAsyncStructuredData(structured_data_sp); +} + +class CommandObjectProcessGDBRemoteSpeedTest : public CommandObjectParsed { +public: +  CommandObjectProcessGDBRemoteSpeedTest(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "process plugin packet speed-test", +                            "Tests packet speeds of various sizes to determine " +                            "the performance characteristics of the GDB remote " +                            "connection. ", +                            nullptr), +        m_option_group(), +        m_num_packets(LLDB_OPT_SET_1, false, "count", 'c', 0, eArgTypeCount, +                      "The number of packets to send of each varying size " +                      "(default is 1000).", +                      1000), +        m_max_send(LLDB_OPT_SET_1, false, "max-send", 's', 0, eArgTypeCount, +                   "The maximum number of bytes to send in a packet. Sizes " +                   "increase in powers of 2 while the size is less than or " +                   "equal to this option value. (default 1024).", +                   1024), +        m_max_recv(LLDB_OPT_SET_1, false, "max-receive", 'r', 0, eArgTypeCount, +                   "The maximum number of bytes to receive in a packet. Sizes " +                   "increase in powers of 2 while the size is less than or " +                   "equal to this option value. (default 1024).", +                   1024), +        m_json(LLDB_OPT_SET_1, false, "json", 'j', +               "Print the output as JSON data for easy parsing.", false, true) { +    m_option_group.Append(&m_num_packets, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); +    m_option_group.Append(&m_max_send, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); +    m_option_group.Append(&m_max_recv, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); +    m_option_group.Append(&m_json, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); +    m_option_group.Finalize(); +  } + +  ~CommandObjectProcessGDBRemoteSpeedTest() override {} + +  Options *GetOptions() override { return &m_option_group; } + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    const size_t argc = command.GetArgumentCount(); +    if (argc == 0) { +      ProcessGDBRemote *process = +          (ProcessGDBRemote *)m_interpreter.GetExecutionContext() +              .GetProcessPtr(); +      if (process) { +        StreamSP output_stream_sp( +            m_interpreter.GetDebugger().GetAsyncOutputStream()); +        result.SetImmediateOutputStream(output_stream_sp); + +        const uint32_t num_packets = +            (uint32_t)m_num_packets.GetOptionValue().GetCurrentValue(); +        const uint64_t max_send = m_max_send.GetOptionValue().GetCurrentValue(); +        const uint64_t max_recv = m_max_recv.GetOptionValue().GetCurrentValue(); +        const bool json = m_json.GetOptionValue().GetCurrentValue(); +        const uint64_t k_recv_amount = +            4 * 1024 * 1024; // Receive amount in bytes +        process->GetGDBRemote().TestPacketSpeed( +            num_packets, max_send, max_recv, k_recv_amount, json, +            output_stream_sp ? *output_stream_sp : result.GetOutputStream()); +        result.SetStatus(eReturnStatusSuccessFinishResult); +        return true; +      } +    } else { +      result.AppendErrorWithFormat("'%s' takes no arguments", +                                   m_cmd_name.c_str()); +    } +    result.SetStatus(eReturnStatusFailed); +    return false; +  } + +protected: +  OptionGroupOptions m_option_group; +  OptionGroupUInt64 m_num_packets; +  OptionGroupUInt64 m_max_send; +  OptionGroupUInt64 m_max_recv; +  OptionGroupBoolean m_json; +}; + +class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { +private: +public: +  CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "process plugin packet history", +                            "Dumps the packet history buffer. ", nullptr) {} + +  ~CommandObjectProcessGDBRemotePacketHistory() override {} + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    const size_t argc = command.GetArgumentCount(); +    if (argc == 0) { +      ProcessGDBRemote *process = +          (ProcessGDBRemote *)m_interpreter.GetExecutionContext() +              .GetProcessPtr(); +      if (process) { +        process->GetGDBRemote().DumpHistory(result.GetOutputStream()); +        result.SetStatus(eReturnStatusSuccessFinishResult); +        return true; +      } +    } else { +      result.AppendErrorWithFormat("'%s' takes no arguments", +                                   m_cmd_name.c_str()); +    } +    result.SetStatus(eReturnStatusFailed); +    return false; +  } +}; + +class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed { +private: +public: +  CommandObjectProcessGDBRemotePacketXferSize(CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "process plugin packet xfer-size", +            "Maximum size that lldb will try to read/write one one chunk.", +            nullptr) {} + +  ~CommandObjectProcessGDBRemotePacketXferSize() override {} + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    const size_t argc = command.GetArgumentCount(); +    if (argc == 0) { +      result.AppendErrorWithFormat("'%s' takes an argument to specify the max " +                                   "amount to be transferred when " +                                   "reading/writing", +                                   m_cmd_name.c_str()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    ProcessGDBRemote *process = +        (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); +    if (process) { +      const char *packet_size = command.GetArgumentAtIndex(0); +      errno = 0; +      uint64_t user_specified_max = strtoul(packet_size, nullptr, 10); +      if (errno == 0 && user_specified_max != 0) { +        process->SetUserSpecifiedMaxMemoryTransferSize(user_specified_max); +        result.SetStatus(eReturnStatusSuccessFinishResult); +        return true; +      } +    } +    result.SetStatus(eReturnStatusFailed); +    return false; +  } +}; + +class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed { +private: +public: +  CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "process plugin packet send", +                            "Send a custom packet through the GDB remote " +                            "protocol and print the answer. " +                            "The packet header and footer will automatically " +                            "be added to the packet prior to sending and " +                            "stripped from the result.", +                            nullptr) {} + +  ~CommandObjectProcessGDBRemotePacketSend() override {} + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    const size_t argc = command.GetArgumentCount(); +    if (argc == 0) { +      result.AppendErrorWithFormat( +          "'%s' takes a one or more packet content arguments", +          m_cmd_name.c_str()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    ProcessGDBRemote *process = +        (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); +    if (process) { +      for (size_t i = 0; i < argc; ++i) { +        const char *packet_cstr = command.GetArgumentAtIndex(0); +        bool send_async = true; +        StringExtractorGDBRemote response; +        process->GetGDBRemote().SendPacketAndWaitForResponse( +            packet_cstr, response, send_async); +        result.SetStatus(eReturnStatusSuccessFinishResult); +        Stream &output_strm = result.GetOutputStream(); +        output_strm.Printf("  packet: %s\n", packet_cstr); +        std::string &response_str = response.GetStringRef(); + +        if (strstr(packet_cstr, "qGetProfileData") != nullptr) { +          response_str = process->HarmonizeThreadIdsForProfileData(response); +        } + +        if (response_str.empty()) +          output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n"); +        else +          output_strm.Printf("response: %s\n", response.GetStringRef().c_str()); +      } +    } +    return true; +  } +}; + +class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw { +private: +public: +  CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) +      : CommandObjectRaw(interpreter, "process plugin packet monitor", +                         "Send a qRcmd packet through the GDB remote protocol " +                         "and print the response." +                         "The argument passed to this command will be hex " +                         "encoded into a valid 'qRcmd' packet, sent and the " +                         "response will be printed.") {} + +  ~CommandObjectProcessGDBRemotePacketMonitor() override {} + +  bool DoExecute(llvm::StringRef command, +                 CommandReturnObject &result) override { +    if (command.empty()) { +      result.AppendErrorWithFormat("'%s' takes a command string argument", +                                   m_cmd_name.c_str()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    ProcessGDBRemote *process = +        (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); +    if (process) { +      StreamString packet; +      packet.PutCString("qRcmd,"); +      packet.PutBytesAsRawHex8(command.data(), command.size()); + +      bool send_async = true; +      StringExtractorGDBRemote response; +      Stream &output_strm = result.GetOutputStream(); +      process->GetGDBRemote().SendPacketAndReceiveResponseWithOutputSupport( +          packet.GetString(), response, send_async, +          [&output_strm](llvm::StringRef output) { output_strm << output; }); +      result.SetStatus(eReturnStatusSuccessFinishResult); +      output_strm.Printf("  packet: %s\n", packet.GetData()); +      const std::string &response_str = response.GetStringRef(); + +      if (response_str.empty()) +        output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n"); +      else +        output_strm.Printf("response: %s\n", response.GetStringRef().c_str()); +    } +    return true; +  } +}; + +class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword { +private: +public: +  CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) +      : CommandObjectMultiword(interpreter, "process plugin packet", +                               "Commands that deal with GDB remote packets.", +                               nullptr) { +    LoadSubCommand( +        "history", +        CommandObjectSP( +            new CommandObjectProcessGDBRemotePacketHistory(interpreter))); +    LoadSubCommand( +        "send", CommandObjectSP( +                    new CommandObjectProcessGDBRemotePacketSend(interpreter))); +    LoadSubCommand( +        "monitor", +        CommandObjectSP( +            new CommandObjectProcessGDBRemotePacketMonitor(interpreter))); +    LoadSubCommand( +        "xfer-size", +        CommandObjectSP( +            new CommandObjectProcessGDBRemotePacketXferSize(interpreter))); +    LoadSubCommand("speed-test", +                   CommandObjectSP(new CommandObjectProcessGDBRemoteSpeedTest( +                       interpreter))); +  } + +  ~CommandObjectProcessGDBRemotePacket() override {} +}; + +class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword { +public: +  CommandObjectMultiwordProcessGDBRemote(CommandInterpreter &interpreter) +      : CommandObjectMultiword( +            interpreter, "process plugin", +            "Commands for operating on a ProcessGDBRemote process.", +            "process plugin <subcommand> [<subcommand-options>]") { +    LoadSubCommand( +        "packet", +        CommandObjectSP(new CommandObjectProcessGDBRemotePacket(interpreter))); +  } + +  ~CommandObjectMultiwordProcessGDBRemote() override {} +}; + +CommandObject *ProcessGDBRemote::GetPluginCommandObject() { +  if (!m_command_sp) +    m_command_sp = std::make_shared<CommandObjectMultiwordProcessGDBRemote>( +        GetTarget().GetDebugger().GetCommandInterpreter()); +  return m_command_sp.get(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h new file mode 100644 index 000000000000..9c41fc2e5e98 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -0,0 +1,459 @@ +//===-- ProcessGDBRemote.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessGDBRemote_h_ +#define liblldb_ProcessGDBRemote_h_ + +#include <atomic> +#include <map> +#include <mutex> +#include <string> +#include <vector> + +#include "lldb/Core/LoadedModuleInfoList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Broadcaster.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamGDBRemote.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StringExtractor.h" +#include "lldb/Utility/StringList.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/lldb-private-forward.h" + +#include "GDBRemoteCommunicationClient.h" +#include "GDBRemoteCommunicationReplayServer.h" +#include "GDBRemoteRegisterContext.h" + +#include "llvm/ADT/DenseMap.h" + +namespace lldb_private { +namespace repro { +class Loader; +} +namespace process_gdb_remote { + +class ThreadGDBRemote; + +class ProcessGDBRemote : public Process, +                         private GDBRemoteClientBase::ContinueDelegate { +public: +  ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); + +  ~ProcessGDBRemote() override; + +  static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, +                                        lldb::ListenerSP listener_sp, +                                        const FileSpec *crash_file_path); + +  static void Initialize(); + +  static void DebuggerInitialize(Debugger &debugger); + +  static void Terminate(); + +  static ConstString GetPluginNameStatic(); + +  static const char *GetPluginDescriptionStatic(); + +  // Check if a given Process +  bool CanDebug(lldb::TargetSP target_sp, +                bool plugin_specified_by_name) override; + +  CommandObject *GetPluginCommandObject() override; + +  // Creating a new process, or attaching to an existing one +  Status WillLaunch(Module *module) override; + +  Status DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) override; + +  void DidLaunch() override; + +  Status WillAttachToProcessWithID(lldb::pid_t pid) override; + +  Status WillAttachToProcessWithName(const char *process_name, +                                     bool wait_for_launch) override; + +  Status DoConnectRemote(Stream *strm, llvm::StringRef remote_url) override; + +  Status WillLaunchOrAttach(); + +  Status DoAttachToProcessWithID(lldb::pid_t pid, +                                 const ProcessAttachInfo &attach_info) override; + +  Status +  DoAttachToProcessWithName(const char *process_name, +                            const ProcessAttachInfo &attach_info) override; + +  void DidAttach(ArchSpec &process_arch) override; + +  // PluginInterface protocol +  ConstString GetPluginName() override; + +  uint32_t GetPluginVersion() override; + +  // Process Control +  Status WillResume() override; + +  Status DoResume() override; + +  Status DoHalt(bool &caused_stop) override; + +  Status DoDetach(bool keep_stopped) override; + +  bool DetachRequiresHalt() override { return true; } + +  Status DoSignal(int signal) override; + +  Status DoDestroy() override; + +  void RefreshStateAfterStop() override; + +  void SetUnixSignals(const lldb::UnixSignalsSP &signals_sp); + +  // Process Queries +  bool IsAlive() override; + +  lldb::addr_t GetImageInfoAddress() override; + +  void WillPublicStop() override; + +  // Process Memory +  size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, +                      Status &error) override; + +  Status +  WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) override; + +  size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size, +                       Status &error) override; + +  lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, +                                Status &error) override; + +  Status GetMemoryRegionInfo(lldb::addr_t load_addr, +                             MemoryRegionInfo ®ion_info) override; + +  Status DoDeallocateMemory(lldb::addr_t ptr) override; + +  // Process STDIO +  size_t PutSTDIN(const char *buf, size_t buf_size, Status &error) override; + +  // Process Breakpoints +  Status EnableBreakpointSite(BreakpointSite *bp_site) override; + +  Status DisableBreakpointSite(BreakpointSite *bp_site) override; + +  // Process Watchpoints +  Status EnableWatchpoint(Watchpoint *wp, bool notify = true) override; + +  Status DisableWatchpoint(Watchpoint *wp, bool notify = true) override; + +  Status GetWatchpointSupportInfo(uint32_t &num) override; + +  lldb::user_id_t StartTrace(const TraceOptions &options, +                             Status &error) override; + +  Status StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) override; + +  Status GetData(lldb::user_id_t uid, lldb::tid_t thread_id, +                 llvm::MutableArrayRef<uint8_t> &buffer, +                 size_t offset = 0) override; + +  Status GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id, +                     llvm::MutableArrayRef<uint8_t> &buffer, +                     size_t offset = 0) override; + +  Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) override; + +  Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override; + +  bool StartNoticingNewThreads() override; + +  bool StopNoticingNewThreads() override; + +  GDBRemoteCommunicationClient &GetGDBRemote() { return m_gdb_comm; } + +  Status SendEventData(const char *data) override; + +  // Override DidExit so we can disconnect from the remote GDB server +  void DidExit() override; + +  void SetUserSpecifiedMaxMemoryTransferSize(uint64_t user_specified_max); + +  bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, +                     ModuleSpec &module_spec) override; + +  void PrefetchModuleSpecs(llvm::ArrayRef<FileSpec> module_file_specs, +                           const llvm::Triple &triple) override; + +  llvm::VersionTuple GetHostOSVersion() override; + +  size_t LoadModules(LoadedModuleInfoList &module_list) override; + +  size_t LoadModules() override; + +  Status GetFileLoadAddress(const FileSpec &file, bool &is_loaded, +                            lldb::addr_t &load_addr) override; + +  void ModulesDidLoad(ModuleList &module_list) override; + +  StructuredData::ObjectSP +  GetLoadedDynamicLibrariesInfos(lldb::addr_t image_list_address, +                                 lldb::addr_t image_count) override; + +  Status +  ConfigureStructuredData(ConstString type_name, +                          const StructuredData::ObjectSP &config_sp) override; + +  StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos() override; + +  StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos( +      const std::vector<lldb::addr_t> &load_addresses) override; + +  StructuredData::ObjectSP +  GetLoadedDynamicLibrariesInfos_sender(StructuredData::ObjectSP args); + +  StructuredData::ObjectSP GetSharedCacheInfo() override; + +  std::string HarmonizeThreadIdsForProfileData( +      StringExtractorGDBRemote &inputStringExtractor); + +protected: +  friend class ThreadGDBRemote; +  friend class GDBRemoteCommunicationClient; +  friend class GDBRemoteRegisterContext; + +  /// Broadcaster event bits definitions. +  enum { +    eBroadcastBitAsyncContinue = (1 << 0), +    eBroadcastBitAsyncThreadShouldExit = (1 << 1), +    eBroadcastBitAsyncThreadDidExit = (1 << 2) +  }; + +  GDBRemoteCommunicationClient m_gdb_comm; +  GDBRemoteCommunicationReplayServer m_gdb_replay_server; +  std::atomic<lldb::pid_t> m_debugserver_pid; +  std::vector<StringExtractorGDBRemote> m_stop_packet_stack; // The stop packet +                                                             // stack replaces +                                                             // the last stop +                                                             // packet variable +  std::recursive_mutex m_last_stop_packet_mutex; +  GDBRemoteDynamicRegisterInfo m_register_info; +  Broadcaster m_async_broadcaster; +  lldb::ListenerSP m_async_listener_sp; +  HostThread m_async_thread; +  std::recursive_mutex m_async_thread_state_mutex; +  typedef std::vector<lldb::tid_t> tid_collection; +  typedef std::vector<std::pair<lldb::tid_t, int>> tid_sig_collection; +  typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap; +  typedef std::map<uint32_t, std::string> ExpeditedRegisterMap; +  tid_collection m_thread_ids; // Thread IDs for all threads. This list gets +                               // updated after stopping +  std::vector<lldb::addr_t> m_thread_pcs;     // PC values for all the threads. +  StructuredData::ObjectSP m_jstopinfo_sp;    // Stop info only for any threads +                                              // that have valid stop infos +  StructuredData::ObjectSP m_jthreadsinfo_sp; // Full stop info, expedited +                                              // registers and memory for all +                                              // threads if "jThreadsInfo" +                                              // packet is supported +  tid_collection m_continue_c_tids;           // 'c' for continue +  tid_sig_collection m_continue_C_tids;       // 'C' for continue with signal +  tid_collection m_continue_s_tids;           // 's' for step +  tid_sig_collection m_continue_S_tids;       // 'S' for step with signal +  uint64_t m_max_memory_size; // The maximum number of bytes to read/write when +                              // reading and writing memory +  uint64_t m_remote_stub_max_memory_size; // The maximum memory size the remote +                                          // gdb stub can handle +  MMapMap m_addr_to_mmap_size; +  lldb::BreakpointSP m_thread_create_bp_sp; +  bool m_waiting_for_attach; +  bool m_destroy_tried_resuming; +  lldb::CommandObjectSP m_command_sp; +  int64_t m_breakpoint_pc_offset; +  lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach + +  bool m_replay_mode; +  bool m_allow_flash_writes; +  using FlashRangeVector = lldb_private::RangeVector<lldb::addr_t, size_t>; +  using FlashRange = FlashRangeVector::Entry; +  FlashRangeVector m_erased_flash_ranges; + +  // Accessors +  bool IsRunning(lldb::StateType state) { +    return state == lldb::eStateRunning || IsStepping(state); +  } + +  bool IsStepping(lldb::StateType state) { +    return state == lldb::eStateStepping; +  } + +  bool CanResume(lldb::StateType state) { return state == lldb::eStateStopped; } + +  bool HasExited(lldb::StateType state) { return state == lldb::eStateExited; } + +  bool ProcessIDIsValid() const; + +  void Clear(); + +  bool UpdateThreadList(ThreadList &old_thread_list, +                        ThreadList &new_thread_list) override; + +  Status ConnectToReplayServer(repro::Loader *loader); + +  Status EstablishConnectionIfNeeded(const ProcessInfo &process_info); + +  Status LaunchAndConnectToDebugserver(const ProcessInfo &process_info); + +  void KillDebugserverProcess(); + +  void BuildDynamicRegisterInfo(bool force); + +  void SetLastStopPacket(const StringExtractorGDBRemote &response); + +  bool ParsePythonTargetDefinition(const FileSpec &target_definition_fspec); + +  DataExtractor GetAuxvData() override; + +  StructuredData::ObjectSP GetExtendedInfoForThread(lldb::tid_t tid); + +  void GetMaxMemorySize(); + +  bool CalculateThreadStopInfo(ThreadGDBRemote *thread); + +  size_t UpdateThreadPCsFromStopReplyThreadsValue(std::string &value); + +  size_t UpdateThreadIDsFromStopReplyThreadsValue(std::string &value); + +  bool HandleNotifyPacket(StringExtractorGDBRemote &packet); + +  bool StartAsyncThread(); + +  void StopAsyncThread(); + +  static lldb::thread_result_t AsyncThread(void *arg); + +  static bool +  MonitorDebugserverProcess(std::weak_ptr<ProcessGDBRemote> process_wp, +                            lldb::pid_t pid, bool exited, int signo, +                            int exit_status); + +  lldb::StateType SetThreadStopInfo(StringExtractor &stop_packet); + +  bool +  GetThreadStopInfoFromJSON(ThreadGDBRemote *thread, +                            const StructuredData::ObjectSP &thread_infos_sp); + +  lldb::ThreadSP SetThreadStopInfo(StructuredData::Dictionary *thread_dict); + +  lldb::ThreadSP +  SetThreadStopInfo(lldb::tid_t tid, +                    ExpeditedRegisterMap &expedited_register_map, uint8_t signo, +                    const std::string &thread_name, const std::string &reason, +                    const std::string &description, uint32_t exc_type, +                    const std::vector<lldb::addr_t> &exc_data, +                    lldb::addr_t thread_dispatch_qaddr, bool queue_vars_valid, +                    lldb_private::LazyBool associated_with_libdispatch_queue, +                    lldb::addr_t dispatch_queue_t, std::string &queue_name, +                    lldb::QueueKind queue_kind, uint64_t queue_serial); + +  void HandleStopReplySequence(); + +  void ClearThreadIDList(); + +  bool UpdateThreadIDList(); + +  void DidLaunchOrAttach(ArchSpec &process_arch); + +  Status ConnectToDebugserver(llvm::StringRef host_port); + +  const char *GetDispatchQueueNameForThread(lldb::addr_t thread_dispatch_qaddr, +                                            std::string &dispatch_queue_name); + +  DynamicLoader *GetDynamicLoader() override; + +  bool GetGDBServerRegisterInfoXMLAndProcess(ArchSpec &arch_to_use, +                                             std::string xml_filename,  +                                             uint32_t &cur_reg_num, +                                             uint32_t ®_offset); + +  // Query remote GDBServer for register information +  bool GetGDBServerRegisterInfo(ArchSpec &arch); + +  // Query remote GDBServer for a detailed loaded library list +  Status GetLoadedModuleList(LoadedModuleInfoList &); + +  lldb::ModuleSP LoadModuleAtAddress(const FileSpec &file, +                                     lldb::addr_t link_map, +                                     lldb::addr_t base_addr, +                                     bool value_is_offset); + +  Status UpdateAutomaticSignalFiltering() override; + +  Status FlashErase(lldb::addr_t addr, size_t size); + +  Status FlashDone(); + +  bool HasErased(FlashRange range); + +private: +  // For ProcessGDBRemote only +  std::string m_partial_profile_data; +  std::map<uint64_t, uint32_t> m_thread_id_to_used_usec_map; +  uint64_t m_last_signals_version = 0; + +  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; +  void HandleAsyncStructuredDataPacket(llvm::StringRef data) override; + +  void SetThreadPc(const lldb::ThreadSP &thread_sp, uint64_t index); +  using ModuleCacheKey = std::pair<std::string, std::string>; +  // KeyInfo for the cached module spec DenseMap. +  // The invariant is that all real keys will have the file and architecture +  // set. +  // The empty key has an empty file and an empty arch. +  // The tombstone key has an invalid arch and an empty file. +  // The comparison and hash functions take the file name and architecture +  // triple into account. +  struct ModuleCacheInfo { +    static ModuleCacheKey getEmptyKey() { return ModuleCacheKey(); } + +    static ModuleCacheKey getTombstoneKey() { return ModuleCacheKey("", "T"); } + +    static unsigned getHashValue(const ModuleCacheKey &key) { +      return llvm::hash_combine(key.first, key.second); +    } + +    static bool isEqual(const ModuleCacheKey &LHS, const ModuleCacheKey &RHS) { +      return LHS == RHS; +    } +  }; + +  llvm::DenseMap<ModuleCacheKey, ModuleSpec, ModuleCacheInfo> +      m_cached_module_specs; + +  DISALLOW_COPY_AND_ASSIGN(ProcessGDBRemote); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_ProcessGDBRemote_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp new file mode 100644 index 000000000000..8cadc45824b3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp @@ -0,0 +1,43 @@ +//===-- ProcessGDBRemoteLog.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ProcessGDBRemoteLog.h" +#include "ProcessGDBRemote.h" +#include "llvm/Support/Threading.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +static constexpr Log::Category g_categories[] = { +    {{"async"}, {"log asynchronous activity"}, GDBR_LOG_ASYNC}, +    {{"break"}, {"log breakpoints"}, GDBR_LOG_BREAKPOINTS}, +    {{"comm"}, {"log communication activity"}, GDBR_LOG_COMM}, +    {{"packets"}, {"log gdb remote packets"}, GDBR_LOG_PACKETS}, +    {{"memory"}, {"log memory reads and writes"}, GDBR_LOG_MEMORY}, +    {{"data-short"}, +     {"log memory bytes for memory reads and writes for short transactions " +      "only"}, +     GDBR_LOG_MEMORY_DATA_SHORT}, +    {{"data-long"}, +     {"log memory bytes for memory reads and writes for all transactions"}, +     GDBR_LOG_MEMORY_DATA_LONG}, +    {{"process"}, {"log process events and activities"}, GDBR_LOG_PROCESS}, +    {{"step"}, {"log step related activities"}, GDBR_LOG_STEP}, +    {{"thread"}, {"log thread events and activities"}, GDBR_LOG_THREAD}, +    {{"watch"}, {"log watchpoint related activities"}, GDBR_LOG_WATCHPOINTS}, +}; + +Log::Channel ProcessGDBRemoteLog::g_channel(g_categories, GDBR_LOG_DEFAULT); + +void ProcessGDBRemoteLog::Initialize() { +  static llvm::once_flag g_once_flag; +  llvm::call_once(g_once_flag, []() { +    Log::Register("gdb-remote", g_channel); +  }); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h new file mode 100644 index 000000000000..d9b8d4536afe --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h @@ -0,0 +1,46 @@ +//===-- ProcessGDBRemoteLog.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessGDBRemoteLog_h_ +#define liblldb_ProcessGDBRemoteLog_h_ + + +#include "lldb/Utility/Log.h" + +#define GDBR_LOG_PROCESS (1u << 1) +#define GDBR_LOG_THREAD (1u << 2) +#define GDBR_LOG_PACKETS (1u << 3) +#define GDBR_LOG_MEMORY (1u << 4) // Log memory reads/writes calls +#define GDBR_LOG_MEMORY_DATA_SHORT                                             \ +  (1u << 5) // Log short memory reads/writes bytes +#define GDBR_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes +#define GDBR_LOG_BREAKPOINTS (1u << 7) +#define GDBR_LOG_WATCHPOINTS (1u << 8) +#define GDBR_LOG_STEP (1u << 9) +#define GDBR_LOG_COMM (1u << 10) +#define GDBR_LOG_ASYNC (1u << 11) +#define GDBR_LOG_ALL (UINT32_MAX) +#define GDBR_LOG_DEFAULT GDBR_LOG_PACKETS + +namespace lldb_private { +namespace process_gdb_remote { + +class ProcessGDBRemoteLog { +  static Log::Channel g_channel; + +public: +  static void Initialize(); + +  static Log *GetLogIfAllCategoriesSet(uint32_t mask) { return g_channel.GetLogIfAll(mask); } +  static Log *GetLogIfAnyCategoryIsSet(uint32_t mask) { return g_channel.GetLogIfAny(mask); } +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_ProcessGDBRemoteLog_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp new file mode 100644 index 000000000000..6607bce4488b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp @@ -0,0 +1,343 @@ +//===-- ThreadGDBRemote.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ThreadGDBRemote.h" + +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +// Thread Registers + +ThreadGDBRemote::ThreadGDBRemote(Process &process, lldb::tid_t tid) +    : Thread(process, tid), m_thread_name(), m_dispatch_queue_name(), +      m_thread_dispatch_qaddr(LLDB_INVALID_ADDRESS), +      m_dispatch_queue_t(LLDB_INVALID_ADDRESS), m_queue_kind(eQueueKindUnknown), +      m_queue_serial_number(LLDB_INVALID_QUEUE_ID), +      m_associated_with_libdispatch_queue(eLazyBoolCalculate) { +  Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD)); +  LLDB_LOG(log, "this = {0}, pid = {1}, tid = {2}", this, process.GetID(), +           GetID()); +} + +ThreadGDBRemote::~ThreadGDBRemote() { +  ProcessSP process_sp(GetProcess()); +  Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD)); +  LLDB_LOG(log, "this = {0}, pid = {1}, tid = {2}", this, +           process_sp ? process_sp->GetID() : LLDB_INVALID_PROCESS_ID, GetID()); +  DestroyThread(); +} + +const char *ThreadGDBRemote::GetName() { +  if (m_thread_name.empty()) +    return nullptr; +  return m_thread_name.c_str(); +} + +void ThreadGDBRemote::ClearQueueInfo() { +  m_dispatch_queue_name.clear(); +  m_queue_kind = eQueueKindUnknown; +  m_queue_serial_number = 0; +  m_dispatch_queue_t = LLDB_INVALID_ADDRESS; +  m_associated_with_libdispatch_queue = eLazyBoolCalculate; +} + +void ThreadGDBRemote::SetQueueInfo(std::string &&queue_name, +                                   QueueKind queue_kind, uint64_t queue_serial, +                                   addr_t dispatch_queue_t, +                                   LazyBool associated_with_libdispatch_queue) { +  m_dispatch_queue_name = queue_name; +  m_queue_kind = queue_kind; +  m_queue_serial_number = queue_serial; +  m_dispatch_queue_t = dispatch_queue_t; +  m_associated_with_libdispatch_queue = associated_with_libdispatch_queue; +} + +const char *ThreadGDBRemote::GetQueueName() { +  // If our cached queue info is valid, then someone called +  // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned +  // from the stop reply packet. In this case we trust that the info is valid +  // in m_dispatch_queue_name without refetching it +  if (CachedQueueInfoIsValid()) { +    if (m_dispatch_queue_name.empty()) +      return nullptr; +    else +      return m_dispatch_queue_name.c_str(); +  } +  // Always re-fetch the dispatch queue name since it can change + +  if (m_associated_with_libdispatch_queue == eLazyBoolNo) +    return nullptr; + +  if (m_thread_dispatch_qaddr != 0 && +      m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) { +    ProcessSP process_sp(GetProcess()); +    if (process_sp) { +      SystemRuntime *runtime = process_sp->GetSystemRuntime(); +      if (runtime) +        m_dispatch_queue_name = +            runtime->GetQueueNameFromThreadQAddress(m_thread_dispatch_qaddr); +      else +        m_dispatch_queue_name.clear(); + +      if (!m_dispatch_queue_name.empty()) +        return m_dispatch_queue_name.c_str(); +    } +  } +  return nullptr; +} + +QueueKind ThreadGDBRemote::GetQueueKind() { +  // If our cached queue info is valid, then someone called +  // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned +  // from the stop reply packet. In this case we trust that the info is valid +  // in m_dispatch_queue_name without refetching it +  if (CachedQueueInfoIsValid()) { +    return m_queue_kind; +  } + +  if (m_associated_with_libdispatch_queue == eLazyBoolNo) +    return eQueueKindUnknown; + +  if (m_thread_dispatch_qaddr != 0 && +      m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) { +    ProcessSP process_sp(GetProcess()); +    if (process_sp) { +      SystemRuntime *runtime = process_sp->GetSystemRuntime(); +      if (runtime) +        m_queue_kind = runtime->GetQueueKind(m_thread_dispatch_qaddr); +      return m_queue_kind; +    } +  } +  return eQueueKindUnknown; +} + +queue_id_t ThreadGDBRemote::GetQueueID() { +  // If our cached queue info is valid, then someone called +  // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned +  // from the stop reply packet. In this case we trust that the info is valid +  // in m_dispatch_queue_name without refetching it +  if (CachedQueueInfoIsValid()) +    return m_queue_serial_number; + +  if (m_associated_with_libdispatch_queue == eLazyBoolNo) +    return LLDB_INVALID_QUEUE_ID; + +  if (m_thread_dispatch_qaddr != 0 && +      m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) { +    ProcessSP process_sp(GetProcess()); +    if (process_sp) { +      SystemRuntime *runtime = process_sp->GetSystemRuntime(); +      if (runtime) { +        return runtime->GetQueueIDFromThreadQAddress(m_thread_dispatch_qaddr); +      } +    } +  } +  return LLDB_INVALID_QUEUE_ID; +} + +QueueSP ThreadGDBRemote::GetQueue() { +  queue_id_t queue_id = GetQueueID(); +  QueueSP queue; +  if (queue_id != LLDB_INVALID_QUEUE_ID) { +    ProcessSP process_sp(GetProcess()); +    if (process_sp) { +      queue = process_sp->GetQueueList().FindQueueByID(queue_id); +    } +  } +  return queue; +} + +addr_t ThreadGDBRemote::GetQueueLibdispatchQueueAddress() { +  if (m_dispatch_queue_t == LLDB_INVALID_ADDRESS) { +    if (m_thread_dispatch_qaddr != 0 && +        m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) { +      ProcessSP process_sp(GetProcess()); +      if (process_sp) { +        SystemRuntime *runtime = process_sp->GetSystemRuntime(); +        if (runtime) { +          m_dispatch_queue_t = +              runtime->GetLibdispatchQueueAddressFromThreadQAddress( +                  m_thread_dispatch_qaddr); +        } +      } +    } +  } +  return m_dispatch_queue_t; +} + +void ThreadGDBRemote::SetQueueLibdispatchQueueAddress( +    lldb::addr_t dispatch_queue_t) { +  m_dispatch_queue_t = dispatch_queue_t; +} + +bool ThreadGDBRemote::ThreadHasQueueInformation() const { +  return m_thread_dispatch_qaddr != 0 && +         m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS && +         m_dispatch_queue_t != LLDB_INVALID_ADDRESS && +         m_queue_kind != eQueueKindUnknown && m_queue_serial_number != 0; +} + +LazyBool ThreadGDBRemote::GetAssociatedWithLibdispatchQueue() { +  return m_associated_with_libdispatch_queue; +} + +void ThreadGDBRemote::SetAssociatedWithLibdispatchQueue( +    LazyBool associated_with_libdispatch_queue) { +  m_associated_with_libdispatch_queue = associated_with_libdispatch_queue; +} + +StructuredData::ObjectSP ThreadGDBRemote::FetchThreadExtendedInfo() { +  StructuredData::ObjectSP object_sp; +  const lldb::user_id_t tid = GetProtocolID(); +  Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD)); +  if (log) +    log->Printf("Fetching extended information for thread %4.4" PRIx64, tid); +  ProcessSP process_sp(GetProcess()); +  if (process_sp) { +    ProcessGDBRemote *gdb_process = +        static_cast<ProcessGDBRemote *>(process_sp.get()); +    object_sp = gdb_process->GetExtendedInfoForThread(tid); +  } +  return object_sp; +} + +void ThreadGDBRemote::WillResume(StateType resume_state) { +  int signo = GetResumeSignal(); +  const lldb::user_id_t tid = GetProtocolID(); +  Log *log(GetLogIfAnyCategoriesSet(GDBR_LOG_THREAD)); +  if (log) +    log->Printf("Resuming thread: %4.4" PRIx64 " with state: %s.", tid, +                StateAsCString(resume_state)); + +  ProcessSP process_sp(GetProcess()); +  if (process_sp) { +    ProcessGDBRemote *gdb_process = +        static_cast<ProcessGDBRemote *>(process_sp.get()); +    switch (resume_state) { +    case eStateSuspended: +    case eStateStopped: +      // Don't append anything for threads that should stay stopped. +      break; + +    case eStateRunning: +      if (gdb_process->GetUnixSignals()->SignalIsValid(signo)) +        gdb_process->m_continue_C_tids.push_back(std::make_pair(tid, signo)); +      else +        gdb_process->m_continue_c_tids.push_back(tid); +      break; + +    case eStateStepping: +      if (gdb_process->GetUnixSignals()->SignalIsValid(signo)) +        gdb_process->m_continue_S_tids.push_back(std::make_pair(tid, signo)); +      else +        gdb_process->m_continue_s_tids.push_back(tid); +      break; + +    default: +      break; +    } +  } +} + +void ThreadGDBRemote::RefreshStateAfterStop() { +  // Invalidate all registers in our register context. We don't set "force" to +  // true because the stop reply packet might have had some register values +  // that were expedited and these will already be copied into the register +  // context by the time this function gets called. The +  // GDBRemoteRegisterContext class has been made smart enough to detect when +  // it needs to invalidate which registers are valid by putting hooks in the +  // register read and register supply functions where they check the process +  // stop ID and do the right thing. +  const bool force = false; +  GetRegisterContext()->InvalidateIfNeeded(force); +} + +bool ThreadGDBRemote::ThreadIDIsValid(lldb::tid_t thread) { +  return thread != 0; +} + +void ThreadGDBRemote::Dump(Log *log, uint32_t index) {} + +bool ThreadGDBRemote::ShouldStop(bool &step_more) { return true; } +lldb::RegisterContextSP ThreadGDBRemote::GetRegisterContext() { +  if (!m_reg_context_sp) +    m_reg_context_sp = CreateRegisterContextForFrame(nullptr); +  return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadGDBRemote::CreateRegisterContextForFrame(StackFrame *frame) { +  lldb::RegisterContextSP reg_ctx_sp; +  uint32_t concrete_frame_idx = 0; + +  if (frame) +    concrete_frame_idx = frame->GetConcreteFrameIndex(); + +  if (concrete_frame_idx == 0) { +    ProcessSP process_sp(GetProcess()); +    if (process_sp) { +      ProcessGDBRemote *gdb_process = +          static_cast<ProcessGDBRemote *>(process_sp.get()); +      // read_all_registers_at_once will be true if 'p' packet is not +      // supported. +      bool read_all_registers_at_once = +          !gdb_process->GetGDBRemote().GetpPacketSupported(GetID()); +      reg_ctx_sp = std::make_shared<GDBRemoteRegisterContext>( +          *this, concrete_frame_idx, gdb_process->m_register_info, +          read_all_registers_at_once); +    } +  } else { +    Unwind *unwinder = GetUnwinder(); +    if (unwinder != nullptr) +      reg_ctx_sp = unwinder->CreateRegisterContextForFrame(frame); +  } +  return reg_ctx_sp; +} + +bool ThreadGDBRemote::PrivateSetRegisterValue(uint32_t reg, +                                              llvm::ArrayRef<uint8_t> data) { +  GDBRemoteRegisterContext *gdb_reg_ctx = +      static_cast<GDBRemoteRegisterContext *>(GetRegisterContext().get()); +  assert(gdb_reg_ctx); +  return gdb_reg_ctx->PrivateSetRegisterValue(reg, data); +} + +bool ThreadGDBRemote::PrivateSetRegisterValue(uint32_t reg, uint64_t regval) { +  GDBRemoteRegisterContext *gdb_reg_ctx = +      static_cast<GDBRemoteRegisterContext *>(GetRegisterContext().get()); +  assert(gdb_reg_ctx); +  return gdb_reg_ctx->PrivateSetRegisterValue(reg, regval); +} + +bool ThreadGDBRemote::CalculateStopInfo() { +  ProcessSP process_sp(GetProcess()); +  if (process_sp) +    return static_cast<ProcessGDBRemote *>(process_sp.get()) +        ->CalculateThreadStopInfo(this); +  return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h new file mode 100644 index 000000000000..c74be169abaf --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h @@ -0,0 +1,119 @@ +//===-- ThreadGDBRemote.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadGDBRemote_h_ +#define liblldb_ThreadGDBRemote_h_ + +#include <string> + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/StructuredData.h" + +class StringExtractor; + +namespace lldb_private { +class Process; + +namespace process_gdb_remote { + +class ProcessGDBRemote; + +class ThreadGDBRemote : public Thread { +public: +  ThreadGDBRemote(Process &process, lldb::tid_t tid); + +  ~ThreadGDBRemote() override; + +  void WillResume(lldb::StateType resume_state) override; + +  void RefreshStateAfterStop() override; + +  const char *GetName() override; + +  const char *GetQueueName() override; + +  lldb::QueueKind GetQueueKind() override; + +  lldb::queue_id_t GetQueueID() override; + +  lldb::QueueSP GetQueue() override; + +  lldb::addr_t GetQueueLibdispatchQueueAddress() override; + +  void SetQueueLibdispatchQueueAddress(lldb::addr_t dispatch_queue_t) override; + +  bool ThreadHasQueueInformation() const override; + +  lldb::RegisterContextSP GetRegisterContext() override; + +  lldb::RegisterContextSP +  CreateRegisterContextForFrame(StackFrame *frame) override; + +  void Dump(Log *log, uint32_t index); + +  static bool ThreadIDIsValid(lldb::tid_t thread); + +  bool ShouldStop(bool &step_more); + +  const char *GetBasicInfoAsString(); + +  void SetName(const char *name) override { +    if (name && name[0]) +      m_thread_name.assign(name); +    else +      m_thread_name.clear(); +  } + +  lldb::addr_t GetThreadDispatchQAddr() { return m_thread_dispatch_qaddr; } + +  void SetThreadDispatchQAddr(lldb::addr_t thread_dispatch_qaddr) { +    m_thread_dispatch_qaddr = thread_dispatch_qaddr; +  } + +  void ClearQueueInfo(); + +  void SetQueueInfo(std::string &&queue_name, lldb::QueueKind queue_kind, +                    uint64_t queue_serial, lldb::addr_t dispatch_queue_t, +                    lldb_private::LazyBool associated_with_libdispatch_queue); + +  lldb_private::LazyBool GetAssociatedWithLibdispatchQueue() override; + +  void SetAssociatedWithLibdispatchQueue( +      lldb_private::LazyBool associated_with_libdispatch_queue) override; + +  StructuredData::ObjectSP FetchThreadExtendedInfo() override; + +protected: +  friend class ProcessGDBRemote; + +  std::string m_thread_name; +  std::string m_dispatch_queue_name; +  lldb::addr_t m_thread_dispatch_qaddr; +  lldb::addr_t m_dispatch_queue_t; +  lldb::QueueKind +      m_queue_kind; // Queue info from stop reply/stop info for thread +  uint64_t +      m_queue_serial_number; // Queue info from stop reply/stop info for thread +  lldb_private::LazyBool m_associated_with_libdispatch_queue; + +  bool PrivateSetRegisterValue(uint32_t reg, llvm::ArrayRef<uint8_t> data); + +  bool PrivateSetRegisterValue(uint32_t reg, uint64_t regval); + +  bool CachedQueueInfoIsValid() const { +    return m_queue_kind != lldb::eQueueKindUnknown; +  } +  void SetStopInfoFromPacket(StringExtractor &stop_packet, uint32_t stop_id); + +  bool CalculateStopInfo() override; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // liblldb_ThreadGDBRemote_h_  | 
