forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			286 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			286 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- GDBRemoteCommunicationReplayServer.cpp ----------------------------===//
 | 
						|
//
 | 
						|
// 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 "llvm/ADT/ScopeExit.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.
 | 
						|
    GDBRemotePacket 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 == GDBRemotePacket::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());
 | 
						|
#ifndef NDEBUG
 | 
						|
        // This behaves like a regular assert, but prints the expected and
 | 
						|
        // received packet before aborting.
 | 
						|
        printf("Reproducer expected packet: '%s'\n", entry.packet.data.c_str());
 | 
						|
        printf("Reproducer received packet: '%s'\n",
 | 
						|
               packet.GetStringRef().data());
 | 
						|
        llvm::report_fatal_error("Encountered unexpected packet during replay");
 | 
						|
#endif
 | 
						|
        return PacketResult::ErrorSendFailed;
 | 
						|
      }
 | 
						|
 | 
						|
      // Ignore QEnvironment packets as they're handled earlier.
 | 
						|
      if (entry.packet.data.find("QEnvironment") == 1) {
 | 
						|
        assert(m_packet_history.back().type ==
 | 
						|
               GDBRemotePacket::ePacketTypeRecv);
 | 
						|
        m_packet_history.pop_back();
 | 
						|
      }
 | 
						|
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (entry.type == GDBRemotePacket::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::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_ERROR(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST),
 | 
						|
                     async_thread.takeError(),
 | 
						|
                     "failed to launch host thread: {}");
 | 
						|
      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;
 | 
						|
  auto D = make_scope_exit([&]() { server->Disconnect(); });
 | 
						|
  EventSP event_sp;
 | 
						|
  bool done = false;
 | 
						|
  while (!done) {
 | 
						|
    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 {};
 | 
						|
}
 |