GDBRemoteCommunicationReplayServer.cpp revision 360784
1//===-- GDBRemoteCommunicationReplayServer.cpp ------------------*- C++ -*-===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8 9#include <errno.h> 10 11#include "lldb/Host/Config.h" 12#include "llvm/ADT/ScopeExit.h" 13 14#include "GDBRemoteCommunicationReplayServer.h" 15#include "ProcessGDBRemoteLog.h" 16 17// C Includes 18// C++ Includes 19#include <cstring> 20 21// Project includes 22#include "lldb/Host/ThreadLauncher.h" 23#include "lldb/Utility/ConstString.h" 24#include "lldb/Utility/Event.h" 25#include "lldb/Utility/FileSpec.h" 26#include "lldb/Utility/StreamString.h" 27#include "lldb/Utility/StringExtractorGDBRemote.h" 28 29using namespace llvm; 30using namespace lldb; 31using namespace lldb_private; 32using namespace lldb_private::process_gdb_remote; 33 34/// Check if the given expected packet matches the actual packet. 35static bool unexpected(llvm::StringRef expected, llvm::StringRef actual) { 36 // The 'expected' string contains the raw data, including the leading $ and 37 // trailing checksum. The 'actual' string contains only the packet's content. 38 if (expected.contains(actual)) 39 return false; 40 // Contains a PID which might be different. 41 if (expected.contains("vAttach")) 42 return false; 43 // Contains a ascii-hex-path. 44 if (expected.contains("QSetSTD")) 45 return false; 46 // Contains environment values. 47 if (expected.contains("QEnvironment")) 48 return false; 49 50 return true; 51} 52 53/// Check if we should reply to the given packet. 54static bool skip(llvm::StringRef data) { 55 assert(!data.empty() && "Empty packet?"); 56 57 // We've already acknowledge the '+' packet so we're done here. 58 if (data == "+") 59 return true; 60 61 /// Don't 't reply to ^C. We need this because of stop reply packets, which 62 /// are only returned when the target halts. Reproducers synchronize these 63 /// 'asynchronous' replies, by recording them as a regular replies to the 64 /// previous packet (e.g. vCont). As a result, we should ignore real 65 /// asynchronous requests. 66 if (data.data()[0] == 0x03) 67 return true; 68 69 return false; 70} 71 72GDBRemoteCommunicationReplayServer::GDBRemoteCommunicationReplayServer() 73 : GDBRemoteCommunication("gdb-replay", "gdb-replay.rx_packet"), 74 m_async_broadcaster(nullptr, "lldb.gdb-replay.async-broadcaster"), 75 m_async_listener_sp( 76 Listener::MakeListener("lldb.gdb-replay.async-listener")), 77 m_async_thread_state_mutex(), m_skip_acks(false) { 78 m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, 79 "async thread continue"); 80 m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, 81 "async thread should exit"); 82 83 const uint32_t async_event_mask = 84 eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; 85 m_async_listener_sp->StartListeningForEvents(&m_async_broadcaster, 86 async_event_mask); 87} 88 89GDBRemoteCommunicationReplayServer::~GDBRemoteCommunicationReplayServer() { 90 StopAsyncThread(); 91} 92 93GDBRemoteCommunication::PacketResult 94GDBRemoteCommunicationReplayServer::GetPacketAndSendResponse( 95 Timeout<std::micro> timeout, Status &error, bool &interrupt, bool &quit) { 96 std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); 97 98 StringExtractorGDBRemote packet; 99 PacketResult packet_result = WaitForPacketNoLock(packet, timeout, false); 100 101 if (packet_result != PacketResult::Success) { 102 if (!IsConnected()) { 103 error.SetErrorString("lost connection"); 104 quit = true; 105 } else { 106 error.SetErrorString("timeout"); 107 } 108 return packet_result; 109 } 110 111 m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue); 112 113 // Check if we should reply to this packet. 114 if (skip(packet.GetStringRef())) 115 return PacketResult::Success; 116 117 // This completes the handshake. Since m_send_acks was true, we can unset it 118 // already. 119 if (packet.GetStringRef() == "QStartNoAckMode") 120 m_send_acks = false; 121 122 // A QEnvironment packet is sent for every environment variable. If the 123 // number of environment variables is different during replay, the replies 124 // become out of sync. 125 if (packet.GetStringRef().find("QEnvironment") == 0) 126 return SendRawPacketNoLock("$OK#9a"); 127 128 Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); 129 while (!m_packet_history.empty()) { 130 // Pop last packet from the history. 131 GDBRemotePacket entry = m_packet_history.back(); 132 m_packet_history.pop_back(); 133 134 // We've handled the handshake implicitly before. Skip the packet and move 135 // on. 136 if (entry.packet.data == "+") 137 continue; 138 139 if (entry.type == GDBRemotePacket::ePacketTypeSend) { 140 if (unexpected(entry.packet.data, packet.GetStringRef())) { 141 LLDB_LOG(log, 142 "GDBRemoteCommunicationReplayServer expected packet: '{0}'", 143 entry.packet.data); 144 LLDB_LOG(log, "GDBRemoteCommunicationReplayServer actual packet: '{0}'", 145 packet.GetStringRef()); 146#ifndef NDEBUG 147 // This behaves like a regular assert, but prints the expected and 148 // received packet before aborting. 149 printf("Reproducer expected packet: '%s'\n", entry.packet.data.c_str()); 150 printf("Reproducer received packet: '%s'\n", 151 packet.GetStringRef().data()); 152 llvm::report_fatal_error("Encountered unexpected packet during replay"); 153#endif 154 return PacketResult::ErrorSendFailed; 155 } 156 157 // Ignore QEnvironment packets as they're handled earlier. 158 if (entry.packet.data.find("QEnvironment") == 1) { 159 assert(m_packet_history.back().type == 160 GDBRemotePacket::ePacketTypeRecv); 161 m_packet_history.pop_back(); 162 } 163 164 continue; 165 } 166 167 if (entry.type == GDBRemotePacket::ePacketTypeInvalid) { 168 LLDB_LOG( 169 log, 170 "GDBRemoteCommunicationReplayServer skipped invalid packet: '{0}'", 171 packet.GetStringRef()); 172 continue; 173 } 174 175 LLDB_LOG(log, 176 "GDBRemoteCommunicationReplayServer replied to '{0}' with '{1}'", 177 packet.GetStringRef(), entry.packet.data); 178 return SendRawPacketNoLock(entry.packet.data); 179 } 180 181 quit = true; 182 183 return packet_result; 184} 185 186llvm::Error 187GDBRemoteCommunicationReplayServer::LoadReplayHistory(const FileSpec &path) { 188 auto error_or_file = MemoryBuffer::getFile(path.GetPath()); 189 if (auto err = error_or_file.getError()) 190 return errorCodeToError(err); 191 192 yaml::Input yin((*error_or_file)->getBuffer()); 193 yin >> m_packet_history; 194 195 if (auto err = yin.error()) 196 return errorCodeToError(err); 197 198 // We want to manipulate the vector like a stack so we need to reverse the 199 // order of the packets to have the oldest on at the back. 200 std::reverse(m_packet_history.begin(), m_packet_history.end()); 201 202 return Error::success(); 203} 204 205bool GDBRemoteCommunicationReplayServer::StartAsyncThread() { 206 std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); 207 if (!m_async_thread.IsJoinable()) { 208 // Create a thread that watches our internal state and controls which 209 // events make it to clients (into the DCProcess event queue). 210 llvm::Expected<HostThread> async_thread = ThreadLauncher::LaunchThread( 211 "<lldb.gdb-replay.async>", 212 GDBRemoteCommunicationReplayServer::AsyncThread, this); 213 if (!async_thread) { 214 LLDB_LOG_ERROR(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST), 215 async_thread.takeError(), 216 "failed to launch host thread: {}"); 217 return false; 218 } 219 m_async_thread = *async_thread; 220 } 221 222 // Wait for handshake. 223 m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue); 224 225 return m_async_thread.IsJoinable(); 226} 227 228void GDBRemoteCommunicationReplayServer::StopAsyncThread() { 229 std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); 230 231 if (!m_async_thread.IsJoinable()) 232 return; 233 234 // Request thread to stop. 235 m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit); 236 237 // Disconnect client. 238 Disconnect(); 239 240 // Stop the thread. 241 m_async_thread.Join(nullptr); 242 m_async_thread.Reset(); 243} 244 245void GDBRemoteCommunicationReplayServer::ReceivePacket( 246 GDBRemoteCommunicationReplayServer &server, bool &done) { 247 Status error; 248 bool interrupt; 249 auto packet_result = server.GetPacketAndSendResponse(std::chrono::seconds(1), 250 error, interrupt, done); 251 if (packet_result != GDBRemoteCommunication::PacketResult::Success && 252 packet_result != 253 GDBRemoteCommunication::PacketResult::ErrorReplyTimeout) { 254 done = true; 255 } else { 256 server.m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue); 257 } 258} 259 260thread_result_t GDBRemoteCommunicationReplayServer::AsyncThread(void *arg) { 261 GDBRemoteCommunicationReplayServer *server = 262 (GDBRemoteCommunicationReplayServer *)arg; 263 auto D = make_scope_exit([&]() { server->Disconnect(); }); 264 EventSP event_sp; 265 bool done = false; 266 while (!done) { 267 if (server->m_async_listener_sp->GetEvent(event_sp, llvm::None)) { 268 const uint32_t event_type = event_sp->GetType(); 269 if (event_sp->BroadcasterIs(&server->m_async_broadcaster)) { 270 switch (event_type) { 271 case eBroadcastBitAsyncContinue: 272 ReceivePacket(*server, done); 273 if (done) 274 return {}; 275 break; 276 case eBroadcastBitAsyncThreadShouldExit: 277 default: 278 return {}; 279 } 280 } 281 } 282 } 283 284 return {}; 285} 286