GDBRemoteCommunicationReplayServer.cpp revision 353358
1343181Sdim//===-- GDBRemoteCommunicationReplayServer.cpp ------------------*- C++ -*-===// 2343181Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6343181Sdim// 7343181Sdim//===----------------------------------------------------------------------===// 8343181Sdim 9343181Sdim#include <errno.h> 10343181Sdim 11343181Sdim#include "lldb/Host/Config.h" 12343181Sdim 13343181Sdim#include "GDBRemoteCommunicationReplayServer.h" 14343181Sdim#include "ProcessGDBRemoteLog.h" 15343181Sdim 16343181Sdim// C Includes 17343181Sdim// C++ Includes 18343181Sdim#include <cstring> 19343181Sdim 20343181Sdim// Project includes 21343181Sdim#include "lldb/Host/ThreadLauncher.h" 22343181Sdim#include "lldb/Utility/ConstString.h" 23343181Sdim#include "lldb/Utility/Event.h" 24343181Sdim#include "lldb/Utility/FileSpec.h" 25343181Sdim#include "lldb/Utility/StreamString.h" 26343181Sdim#include "lldb/Utility/StringExtractorGDBRemote.h" 27343181Sdim 28343181Sdimusing namespace llvm; 29343181Sdimusing namespace lldb; 30343181Sdimusing namespace lldb_private; 31343181Sdimusing namespace lldb_private::process_gdb_remote; 32343181Sdim 33353358Sdim/// Check if the given expected packet matches the actual packet. 34353358Sdimstatic bool unexpected(llvm::StringRef expected, llvm::StringRef actual) { 35353358Sdim // The 'expected' string contains the raw data, including the leading $ and 36353358Sdim // trailing checksum. The 'actual' string contains only the packet's content. 37353358Sdim if (expected.contains(actual)) 38353358Sdim return false; 39353358Sdim // Contains a PID which might be different. 40353358Sdim if (expected.contains("vAttach")) 41353358Sdim return false; 42353358Sdim // Contains a ascii-hex-path. 43353358Sdim if (expected.contains("QSetSTD")) 44353358Sdim return false; 45353358Sdim // Contains environment values. 46353358Sdim if (expected.contains("QEnvironment")) 47353358Sdim return false; 48353358Sdim 49353358Sdim return true; 50353358Sdim} 51353358Sdim 52353358Sdim/// Check if we should reply to the given packet. 53353358Sdimstatic bool skip(llvm::StringRef data) { 54353358Sdim assert(!data.empty() && "Empty packet?"); 55353358Sdim 56353358Sdim // We've already acknowledge the '+' packet so we're done here. 57353358Sdim if (data == "+") 58353358Sdim return true; 59353358Sdim 60353358Sdim /// Don't 't reply to ^C. We need this because of stop reply packets, which 61353358Sdim /// are only returned when the target halts. Reproducers synchronize these 62353358Sdim /// 'asynchronous' replies, by recording them as a regular replies to the 63353358Sdim /// previous packet (e.g. vCont). As a result, we should ignore real 64353358Sdim /// asynchronous requests. 65353358Sdim if (data.data()[0] == 0x03) 66353358Sdim return true; 67353358Sdim 68353358Sdim return false; 69353358Sdim} 70353358Sdim 71343181SdimGDBRemoteCommunicationReplayServer::GDBRemoteCommunicationReplayServer() 72353358Sdim : GDBRemoteCommunication("gdb-replay", "gdb-replay.rx_packet"), 73353358Sdim m_async_broadcaster(nullptr, "lldb.gdb-replay.async-broadcaster"), 74343181Sdim m_async_listener_sp( 75353358Sdim Listener::MakeListener("lldb.gdb-replay.async-listener")), 76343181Sdim m_async_thread_state_mutex(), m_skip_acks(false) { 77343181Sdim m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, 78343181Sdim "async thread continue"); 79343181Sdim m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, 80343181Sdim "async thread should exit"); 81343181Sdim 82343181Sdim const uint32_t async_event_mask = 83343181Sdim eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; 84343181Sdim m_async_listener_sp->StartListeningForEvents(&m_async_broadcaster, 85343181Sdim async_event_mask); 86343181Sdim} 87343181Sdim 88343181SdimGDBRemoteCommunicationReplayServer::~GDBRemoteCommunicationReplayServer() { 89343181Sdim StopAsyncThread(); 90343181Sdim} 91343181Sdim 92343181SdimGDBRemoteCommunication::PacketResult 93343181SdimGDBRemoteCommunicationReplayServer::GetPacketAndSendResponse( 94343181Sdim Timeout<std::micro> timeout, Status &error, bool &interrupt, bool &quit) { 95353358Sdim std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); 96353358Sdim 97343181Sdim StringExtractorGDBRemote packet; 98343181Sdim PacketResult packet_result = WaitForPacketNoLock(packet, timeout, false); 99343181Sdim 100343181Sdim if (packet_result != PacketResult::Success) { 101343181Sdim if (!IsConnected()) { 102343181Sdim error.SetErrorString("lost connection"); 103343181Sdim quit = true; 104343181Sdim } else { 105343181Sdim error.SetErrorString("timeout"); 106343181Sdim } 107343181Sdim return packet_result; 108343181Sdim } 109343181Sdim 110343181Sdim m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue); 111343181Sdim 112353358Sdim // Check if we should reply to this packet. 113353358Sdim if (skip(packet.GetStringRef())) 114353358Sdim return PacketResult::Success; 115353358Sdim 116353358Sdim // This completes the handshake. Since m_send_acks was true, we can unset it 117353358Sdim // already. 118353358Sdim if (packet.GetStringRef() == "QStartNoAckMode") 119343181Sdim m_send_acks = false; 120343181Sdim 121353358Sdim // A QEnvironment packet is sent for every environment variable. If the 122353358Sdim // number of environment variables is different during replay, the replies 123353358Sdim // become out of sync. 124353358Sdim if (packet.GetStringRef().find("QEnvironment") == 0) 125353358Sdim return SendRawPacketNoLock("$OK#9a"); 126353358Sdim 127353358Sdim Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); 128343181Sdim while (!m_packet_history.empty()) { 129343181Sdim // Pop last packet from the history. 130343181Sdim GDBRemoteCommunicationHistory::Entry entry = m_packet_history.back(); 131343181Sdim m_packet_history.pop_back(); 132343181Sdim 133353358Sdim // We've handled the handshake implicitly before. Skip the packet and move 134353358Sdim // on. 135353358Sdim if (entry.packet.data == "+") 136343181Sdim continue; 137343181Sdim 138353358Sdim if (entry.type == GDBRemoteCommunicationHistory::ePacketTypeSend) { 139353358Sdim if (unexpected(entry.packet.data, packet.GetStringRef())) { 140353358Sdim LLDB_LOG(log, 141353358Sdim "GDBRemoteCommunicationReplayServer expected packet: '{0}'", 142353358Sdim entry.packet.data); 143353358Sdim LLDB_LOG(log, "GDBRemoteCommunicationReplayServer actual packet: '{0}'", 144353358Sdim packet.GetStringRef()); 145353358Sdim assert(false && "Encountered unexpected packet during replay"); 146353358Sdim return PacketResult::ErrorSendFailed; 147353358Sdim } 148353358Sdim 149353358Sdim // Ignore QEnvironment packets as they're handled earlier. 150353358Sdim if (entry.packet.data.find("QEnvironment") == 1) { 151353358Sdim assert(m_packet_history.back().type == 152353358Sdim GDBRemoteCommunicationHistory::ePacketTypeRecv); 153353358Sdim m_packet_history.pop_back(); 154353358Sdim } 155353358Sdim 156353358Sdim continue; 157353358Sdim } 158353358Sdim 159353358Sdim if (entry.type == GDBRemoteCommunicationHistory::ePacketTypeInvalid) { 160353358Sdim LLDB_LOG( 161353358Sdim log, 162353358Sdim "GDBRemoteCommunicationReplayServer skipped invalid packet: '{0}'", 163353358Sdim packet.GetStringRef()); 164353358Sdim continue; 165353358Sdim } 166353358Sdim 167353358Sdim LLDB_LOG(log, 168353358Sdim "GDBRemoteCommunicationReplayServer replied to '{0}' with '{1}'", 169353358Sdim packet.GetStringRef(), entry.packet.data); 170353358Sdim return SendRawPacketNoLock(entry.packet.data); 171343181Sdim } 172343181Sdim 173343181Sdim quit = true; 174343181Sdim 175343181Sdim return packet_result; 176343181Sdim} 177343181Sdim 178343181SdimLLVM_YAML_IS_DOCUMENT_LIST_VECTOR( 179343181Sdim std::vector< 180343181Sdim lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry>) 181343181Sdim 182343181Sdimllvm::Error 183343181SdimGDBRemoteCommunicationReplayServer::LoadReplayHistory(const FileSpec &path) { 184343181Sdim auto error_or_file = MemoryBuffer::getFile(path.GetPath()); 185343181Sdim if (auto err = error_or_file.getError()) 186343181Sdim return errorCodeToError(err); 187343181Sdim 188343181Sdim yaml::Input yin((*error_or_file)->getBuffer()); 189343181Sdim yin >> m_packet_history; 190343181Sdim 191343181Sdim if (auto err = yin.error()) 192343181Sdim return errorCodeToError(err); 193343181Sdim 194343181Sdim // We want to manipulate the vector like a stack so we need to reverse the 195343181Sdim // order of the packets to have the oldest on at the back. 196343181Sdim std::reverse(m_packet_history.begin(), m_packet_history.end()); 197343181Sdim 198343181Sdim return Error::success(); 199343181Sdim} 200343181Sdim 201343181Sdimbool GDBRemoteCommunicationReplayServer::StartAsyncThread() { 202343181Sdim std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); 203343181Sdim if (!m_async_thread.IsJoinable()) { 204343181Sdim // Create a thread that watches our internal state and controls which 205343181Sdim // events make it to clients (into the DCProcess event queue). 206353358Sdim llvm::Expected<HostThread> async_thread = ThreadLauncher::LaunchThread( 207353358Sdim "<lldb.gdb-replay.async>", 208353358Sdim GDBRemoteCommunicationReplayServer::AsyncThread, this); 209353358Sdim if (!async_thread) { 210353358Sdim LLDB_LOG(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), 211353358Sdim "failed to launch host thread: {}", 212353358Sdim llvm::toString(async_thread.takeError())); 213353358Sdim return false; 214353358Sdim } 215353358Sdim m_async_thread = *async_thread; 216343181Sdim } 217343181Sdim 218343181Sdim // Wait for handshake. 219343181Sdim m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue); 220343181Sdim 221343181Sdim return m_async_thread.IsJoinable(); 222343181Sdim} 223343181Sdim 224343181Sdimvoid GDBRemoteCommunicationReplayServer::StopAsyncThread() { 225343181Sdim std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); 226343181Sdim 227343181Sdim if (!m_async_thread.IsJoinable()) 228343181Sdim return; 229343181Sdim 230343181Sdim // Request thread to stop. 231343181Sdim m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit); 232343181Sdim 233343181Sdim // Disconnect client. 234343181Sdim Disconnect(); 235343181Sdim 236343181Sdim // Stop the thread. 237343181Sdim m_async_thread.Join(nullptr); 238343181Sdim m_async_thread.Reset(); 239343181Sdim} 240343181Sdim 241343181Sdimvoid GDBRemoteCommunicationReplayServer::ReceivePacket( 242343181Sdim GDBRemoteCommunicationReplayServer &server, bool &done) { 243343181Sdim Status error; 244343181Sdim bool interrupt; 245343181Sdim auto packet_result = server.GetPacketAndSendResponse(std::chrono::seconds(1), 246343181Sdim error, interrupt, done); 247343181Sdim if (packet_result != GDBRemoteCommunication::PacketResult::Success && 248343181Sdim packet_result != 249343181Sdim GDBRemoteCommunication::PacketResult::ErrorReplyTimeout) { 250343181Sdim done = true; 251343181Sdim } else { 252343181Sdim server.m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue); 253343181Sdim } 254343181Sdim} 255343181Sdim 256343181Sdimthread_result_t GDBRemoteCommunicationReplayServer::AsyncThread(void *arg) { 257343181Sdim GDBRemoteCommunicationReplayServer *server = 258343181Sdim (GDBRemoteCommunicationReplayServer *)arg; 259343181Sdim 260343181Sdim EventSP event_sp; 261343181Sdim bool done = false; 262343181Sdim 263343181Sdim while (true) { 264343181Sdim if (server->m_async_listener_sp->GetEvent(event_sp, llvm::None)) { 265343181Sdim const uint32_t event_type = event_sp->GetType(); 266343181Sdim if (event_sp->BroadcasterIs(&server->m_async_broadcaster)) { 267343181Sdim switch (event_type) { 268343181Sdim case eBroadcastBitAsyncContinue: 269343181Sdim ReceivePacket(*server, done); 270343181Sdim if (done) 271343181Sdim return {}; 272343181Sdim break; 273343181Sdim case eBroadcastBitAsyncThreadShouldExit: 274343181Sdim default: 275343181Sdim return {}; 276343181Sdim } 277343181Sdim } 278343181Sdim } 279343181Sdim } 280343181Sdim 281343181Sdim return {}; 282343181Sdim} 283