diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:06:29 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:06:29 +0000 | 
| commit | 94994d372d014ce4c8758b9605d63fae651bd8aa (patch) | |
| tree | 51c0b708bd59f205d6b35cb2a8c24d62f0c33d77 /source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp | |
| parent | 39be7ce23363d12ae3e49aeb1fdb2bfeb892e836 (diff) | |
Notes
Diffstat (limited to 'source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp')
| -rw-r--r-- | source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp | 204 | 
1 files changed, 204 insertions, 0 deletions
diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp new file mode 100644 index 000000000000..6a78eb20992e --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp @@ -0,0 +1,204 @@ +//===-- GDBRemoteCommunicationReplayServer.cpp ------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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; + +GDBRemoteCommunicationReplayServer::GDBRemoteCommunicationReplayServer() +    : GDBRemoteCommunication("gdb-remote.server", +                             "gdb-remote.server.rx_packet"), +      m_async_broadcaster(nullptr, "lldb.gdb-remote.server.async-broadcaster"), +      m_async_listener_sp( +          Listener::MakeListener("lldb.gdb-remote.server.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) { +  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); + +  if (m_skip_acks) { +    const StringExtractorGDBRemote::ServerPacketType packet_type = +        packet.GetServerPacketType(); +    switch (packet_type) { +    case StringExtractorGDBRemote::eServerPacketType_nack: +    case StringExtractorGDBRemote::eServerPacketType_ack: +      return PacketResult::Success; +    default: +      break; +    } +  } else if (packet.GetStringRef() == "QStartNoAckMode") { +    m_skip_acks = true; +    m_send_acks = false; +  } + +  while (!m_packet_history.empty()) { +    // Pop last packet from the history. +    GDBRemoteCommunicationHistory::Entry entry = m_packet_history.back(); +    m_packet_history.pop_back(); + +    // We only care about what we received from the server. Skip everything +    // the client sent. +    if (entry.type != GDBRemoteCommunicationHistory::ePacketTypeRecv) +      continue; + +    return SendRawPacketNoLock(entry.packet.data, true); +  } + +  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). +    m_async_thread = ThreadLauncher::LaunchThread( +        "<lldb.gdb-remote.server.async>", +        GDBRemoteCommunicationReplayServer::AsyncThread, this, nullptr); +  } + +  // 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 {}; +}  | 
