LLDB  mainline
GDBRemoteCommunicationReplayServer.cpp
Go to the documentation of this file.
1 //===-- GDBRemoteCommunicationReplayServer.cpp ----------------------------===//
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 
15 #include "ProcessGDBRemoteLog.h"
16 
17 // C Includes
18 // C++ Includes
19 #include <cstring>
20 
21 // Project includes
24 #include "lldb/Utility/Event.h"
25 #include "lldb/Utility/FileSpec.h"
28 
29 using namespace llvm;
30 using namespace lldb;
31 using namespace lldb_private;
32 using namespace lldb_private::process_gdb_remote;
33 
34 /// Check if the given expected packet matches the actual packet.
35 static 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.
54 static 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 
72 GDBRemoteCommunicationReplayServer::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) {
79  "async thread continue");
81  "async thread should exit");
82 
83  const uint32_t async_event_mask =
85  m_async_listener_sp->StartListeningForEvents(&m_async_broadcaster,
86  async_event_mask);
87 }
88 
91 }
92 
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 
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 
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 
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  // Decode run-length encoding.
135  const std::string expanded_data =
137 
138  // We've handled the handshake implicitly before. Skip the packet and move
139  // on.
140  if (entry.packet.data == "+")
141  continue;
142 
143  if (entry.type == GDBRemotePacket::ePacketTypeSend) {
144  if (unexpected(expanded_data, packet.GetStringRef())) {
145  LLDB_LOG(log,
146  "GDBRemoteCommunicationReplayServer expected packet: '{0}'",
147  expanded_data);
148  LLDB_LOG(log, "GDBRemoteCommunicationReplayServer actual packet: '{0}'",
149  packet.GetStringRef());
150 #ifndef NDEBUG
151  // This behaves like a regular assert, but prints the expected and
152  // received packet before aborting.
153  printf("Reproducer expected packet: '%s'\n", expanded_data.c_str());
154  printf("Reproducer received packet: '%s'\n",
155  packet.GetStringRef().data());
156  llvm::report_fatal_error("Encountered unexpected packet during replay");
157 #endif
159  }
160 
161  // Ignore QEnvironment packets as they're handled earlier.
162  if (expanded_data.find("QEnvironment") == 1) {
163  assert(m_packet_history.back().type ==
165  m_packet_history.pop_back();
166  }
167 
168  continue;
169  }
170 
172  LLDB_LOG(
173  log,
174  "GDBRemoteCommunicationReplayServer skipped invalid packet: '{0}'",
175  packet.GetStringRef());
176  continue;
177  }
178 
179  LLDB_LOG(log,
180  "GDBRemoteCommunicationReplayServer replied to '{0}' with '{1}'",
181  packet.GetStringRef(), entry.packet.data);
182  return SendRawPacketNoLock(entry.packet.data);
183  }
184 
185  quit = true;
186 
187  return packet_result;
188 }
189 
192  auto error_or_file = MemoryBuffer::getFile(path.GetPath());
193  if (auto err = error_or_file.getError())
194  return errorCodeToError(err);
195 
196  yaml::Input yin((*error_or_file)->getBuffer());
197  yin >> m_packet_history;
198 
199  if (auto err = yin.error())
200  return errorCodeToError(err);
201 
202  // We want to manipulate the vector like a stack so we need to reverse the
203  // order of the packets to have the oldest on at the back.
204  std::reverse(m_packet_history.begin(), m_packet_history.end());
205 
206  return Error::success();
207 }
208 
210  std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
211  if (!m_async_thread.IsJoinable()) {
212  // Create a thread that watches our internal state and controls which
213  // events make it to clients (into the DCProcess event queue).
214  llvm::Expected<HostThread> async_thread = ThreadLauncher::LaunchThread(
215  "<lldb.gdb-replay.async>",
217  if (!async_thread) {
219  async_thread.takeError(),
220  "failed to launch host thread: {}");
221  return false;
222  }
223  m_async_thread = *async_thread;
224  }
225 
226  // Wait for handshake.
228 
229  return m_async_thread.IsJoinable();
230 }
231 
233  std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
234 
235  if (!m_async_thread.IsJoinable())
236  return;
237 
238  // Request thread to stop.
240 
241  // Disconnect client.
242  Disconnect();
243 
244  // Stop the thread.
245  m_async_thread.Join(nullptr);
247 }
248 
250  GDBRemoteCommunicationReplayServer &server, bool &done) {
251  Status error;
252  bool interrupt;
253  auto packet_result = server.GetPacketAndSendResponse(std::chrono::seconds(1),
254  error, interrupt, done);
255  if (packet_result != GDBRemoteCommunication::PacketResult::Success &&
256  packet_result !=
258  done = true;
259  } else {
261  }
262 }
263 
267  auto D = make_scope_exit([&]() { server->Disconnect(); });
268  EventSP event_sp;
269  bool done = false;
270  while (!done) {
271  if (server->m_async_listener_sp->GetEvent(event_sp, llvm::None)) {
272  const uint32_t event_type = event_sp->GetType();
273  if (event_sp->BroadcasterIs(&server->m_async_broadcaster)) {
274  switch (event_type) {
276  ReceivePacket(*server, done);
277  if (done)
278  return {};
279  break;
281  default:
282  return {};
283  }
284  }
285  }
286  }
287 
288  return {};
289 }
290 
294  if (!loader)
295  return Status("No loader provided.");
296 
297  static std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>>
299  repro::Reproducer::Instance().GetLoader());
300  if (!multi_loader)
301  return Status("No gdb remote provider found.");
302 
303  llvm::Optional<std::string> history_file = multi_loader->GetNextFile();
304  if (!history_file)
305  return Status("No gdb remote packet log found.");
306 
307  if (auto error = LoadReplayHistory(FileSpec(*history_file)))
308  return Status("Unable to load replay history");
309 
310  if (auto error = GDBRemoteCommunication::ConnectLocally(client, *this))
311  return Status("Unable to connect to replay server");
312 
313  return {};
314 }
static bool unexpected(llvm::StringRef expected, llvm::StringRef actual)
Check if the given expected packet matches the actual packet.
void SetEventName(uint32_t event_mask, const char *name)
Set the name for an event bit.
Definition: Broadcaster.h:337
A class that represents a running process on the host machine.
Definition: Debugger.h:49
PacketResult GetPacketAndSendResponse(Timeout< std::micro > timeout, Status &error, bool &interrupt, bool &quit)
lldb::ConnectionStatus Disconnect(Status *error_ptr=nullptr)
Disconnect the communications connection if one is currently connected.
PacketResult WaitForPacketNoLock(StringExtractorGDBRemote &response, Timeout< std::micro > timeout, bool sync_on_timeout)
Status Connect(process_gdb_remote::GDBRemoteCommunicationClient &client)
llvm::Error Error
A file utility class.
Definition: FileSpec.h:56
static std::unique_ptr< MultiLoader > Create(Loader *loader)
Definition: Reproducer.h:437
Dummy GDB server that replays packets from the GDB Remote Communication history.
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition: Log.h:242
#define LLDB_LOG_ERROR(log, error,...)
Definition: Log.h:265
void BroadcastEvent(lldb::EventSP &event_sp)
Broadcast an event which has no associated data.
Definition: Broadcaster.h:262
#define LIBLLDB_LOG_HOST
Definition: Logging.h:28
#define GDBR_LOG_PROCESS
static bool skip(llvm::StringRef data)
Check if we should reply to the given packet.
static llvm::raw_ostream & error(Stream &strm)
Log * GetLogIfAllCategoriesSet(uint32_t mask)
Definition: Logging.cpp:58
static llvm::Error ConnectLocally(GDBRemoteCommunication &client, GDBRemoteCommunication &server)
void SetErrorString(llvm::StringRef err_str)
Set the current error string to err_str.
Definition: Status.cpp:242
static std::string ExpandRLE(std::string)
Expand GDB run-length encoding.
static llvm::Expected< HostThread > LaunchThread(llvm::StringRef name, lldb::thread_func_t thread_function, lldb::thread_arg_t thread_arg, size_t min_stack_byte_size=0)
Status Join(lldb::thread_result_t *result)
Definition: HostThread.cpp:20
bool IsConnected() const
Check if the connection is valid.
static Reproducer & Instance()
Definition: Reproducer.cpp:30
GDB remote packet as used by the reproducer and the GDB remote communication history.
Definition: GDBRemote.h:52
llvm::StringRef GetStringRef() const
Definition: SBAddress.h:15
static void ReceivePacket(GDBRemoteCommunicationReplayServer &server, bool &done)
PacketResult SendRawPacketNoLock(llvm::StringRef payload, bool skip_ack=false)
std::vector< GDBRemotePacket > m_packet_history
Replay history with the oldest packet at the end.
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:348
An error handling class.
Definition: Status.h:44
void * thread_result_t
Definition: lldb-types.h:62