summaryrefslogtreecommitdiffstats
path: root/gnu/llvm/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2020-08-03 14:33:06 +0000
committerpatrick <patrick@openbsd.org>2020-08-03 14:33:06 +0000
commit061da546b983eb767bad15e67af1174fb0bcf31c (patch)
tree83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp
parentImport LLVM 10.0.0 release including clang, lld and lldb. (diff)
downloadwireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz
wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp')
-rw-r--r--gnu/llvm/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp285
1 files changed, 285 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp b/gnu/llvm/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp
new file mode 100644
index 00000000000..15c73e78bd4
--- /dev/null
+++ b/gnu/llvm/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp
@@ -0,0 +1,285 @@
+//===-- 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 "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 {};
+}