1 | //===-- PlatformRemoteGDBServer.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 "PlatformRemoteGDBServer.h" |
10 | #include "lldb/Host/Config.h" |
11 | |
12 | #include "lldb/Breakpoint/BreakpointLocation.h" |
13 | #include "lldb/Core/Debugger.h" |
14 | #include "lldb/Core/Module.h" |
15 | #include "lldb/Core/ModuleList.h" |
16 | #include "lldb/Core/ModuleSpec.h" |
17 | #include "lldb/Core/PluginManager.h" |
18 | #include "lldb/Host/ConnectionFileDescriptor.h" |
19 | #include "lldb/Host/Host.h" |
20 | #include "lldb/Host/HostInfo.h" |
21 | #include "lldb/Host/PosixApi.h" |
22 | #include "lldb/Target/Process.h" |
23 | #include "lldb/Target/Target.h" |
24 | #include "lldb/Utility/FileSpec.h" |
25 | #include "lldb/Utility/LLDBLog.h" |
26 | #include "lldb/Utility/Log.h" |
27 | #include "lldb/Utility/ProcessInfo.h" |
28 | #include "lldb/Utility/Status.h" |
29 | #include "lldb/Utility/StreamString.h" |
30 | #include "lldb/Utility/UriParser.h" |
31 | #include "llvm/ADT/StringSet.h" |
32 | #include "llvm/Support/FormatAdapters.h" |
33 | |
34 | #include "Plugins/Process/Utility/GDBRemoteSignals.h" |
35 | #include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" |
36 | #include <mutex> |
37 | #include <optional> |
38 | |
39 | using namespace lldb; |
40 | using namespace lldb_private; |
41 | using namespace lldb_private::platform_gdb_server; |
42 | |
43 | LLDB_PLUGIN_DEFINE_ADV(PlatformRemoteGDBServer, PlatformGDB) |
44 | |
45 | static bool g_initialized = false; |
46 | // UnixSignals does not store the signal names or descriptions itself. |
47 | // It holds onto StringRefs. Becaue we may get signal information dynamically |
48 | // from the remote, these strings need persistent storage client-side. |
49 | static std::mutex g_signal_string_mutex; |
50 | static llvm::StringSet<> g_signal_string_storage; |
51 | |
52 | void PlatformRemoteGDBServer::Initialize() { |
53 | Platform::Initialize(); |
54 | |
55 | if (!g_initialized) { |
56 | g_initialized = true; |
57 | PluginManager::RegisterPlugin( |
58 | name: PlatformRemoteGDBServer::GetPluginNameStatic(), |
59 | description: PlatformRemoteGDBServer::GetDescriptionStatic(), |
60 | create_callback: PlatformRemoteGDBServer::CreateInstance); |
61 | } |
62 | } |
63 | |
64 | void PlatformRemoteGDBServer::Terminate() { |
65 | if (g_initialized) { |
66 | g_initialized = false; |
67 | PluginManager::UnregisterPlugin(create_callback: PlatformRemoteGDBServer::CreateInstance); |
68 | } |
69 | |
70 | Platform::Terminate(); |
71 | } |
72 | |
73 | PlatformSP PlatformRemoteGDBServer::CreateInstance(bool force, |
74 | const ArchSpec *arch) { |
75 | bool create = force; |
76 | if (!create) { |
77 | create = !arch->TripleVendorWasSpecified() && !arch->TripleOSWasSpecified(); |
78 | } |
79 | if (create) |
80 | return PlatformSP(new PlatformRemoteGDBServer()); |
81 | return PlatformSP(); |
82 | } |
83 | |
84 | llvm::StringRef PlatformRemoteGDBServer::GetDescriptionStatic() { |
85 | return "A platform that uses the GDB remote protocol as the communication " |
86 | "transport."; |
87 | } |
88 | |
89 | llvm::StringRef PlatformRemoteGDBServer::GetDescription() { |
90 | if (m_platform_description.empty()) { |
91 | if (IsConnected()) { |
92 | // Send the get description packet |
93 | } |
94 | } |
95 | |
96 | if (!m_platform_description.empty()) |
97 | return m_platform_description.c_str(); |
98 | return GetDescriptionStatic(); |
99 | } |
100 | |
101 | bool PlatformRemoteGDBServer::GetModuleSpec(const FileSpec &module_file_spec, |
102 | const ArchSpec &arch, |
103 | ModuleSpec &module_spec) { |
104 | Log *log = GetLog(mask: LLDBLog::Platform); |
105 | |
106 | const auto module_path = module_file_spec.GetPath(denormalize: false); |
107 | |
108 | if (!m_gdb_client_up || |
109 | !m_gdb_client_up->GetModuleInfo(module_file_spec, arch_spec: arch, module_spec)) { |
110 | LLDB_LOGF( |
111 | log, |
112 | "PlatformRemoteGDBServer::%s - failed to get module info for %s:%s", |
113 | __FUNCTION__, module_path.c_str(), |
114 | arch.GetTriple().getTriple().c_str()); |
115 | return false; |
116 | } |
117 | |
118 | if (log) { |
119 | StreamString stream; |
120 | module_spec.Dump(strm&: stream); |
121 | LLDB_LOGF(log, |
122 | "PlatformRemoteGDBServer::%s - got module info for (%s:%s) : %s", |
123 | __FUNCTION__, module_path.c_str(), |
124 | arch.GetTriple().getTriple().c_str(), stream.GetData()); |
125 | } |
126 | |
127 | return true; |
128 | } |
129 | |
130 | Status PlatformRemoteGDBServer::GetFileWithUUID(const FileSpec &platform_file, |
131 | const UUID *uuid_ptr, |
132 | FileSpec &local_file) { |
133 | // Default to the local case |
134 | local_file = platform_file; |
135 | return Status(); |
136 | } |
137 | |
138 | /// Default Constructor |
139 | PlatformRemoteGDBServer::PlatformRemoteGDBServer() |
140 | : Platform(/*is_host=*/false) {} |
141 | |
142 | /// Destructor. |
143 | /// |
144 | /// The destructor is virtual since this class is designed to be |
145 | /// inherited from by the plug-in instance. |
146 | PlatformRemoteGDBServer::~PlatformRemoteGDBServer() = default; |
147 | |
148 | size_t PlatformRemoteGDBServer::GetSoftwareBreakpointTrapOpcode( |
149 | Target &target, BreakpointSite *bp_site) { |
150 | // This isn't needed if the z/Z packets are supported in the GDB remote |
151 | // server. But we might need a packet to detect this. |
152 | return 0; |
153 | } |
154 | |
155 | bool PlatformRemoteGDBServer::GetRemoteOSVersion() { |
156 | if (m_gdb_client_up) |
157 | m_os_version = m_gdb_client_up->GetOSVersion(); |
158 | return !m_os_version.empty(); |
159 | } |
160 | |
161 | std::optional<std::string> PlatformRemoteGDBServer::GetRemoteOSBuildString() { |
162 | if (!m_gdb_client_up) |
163 | return std::nullopt; |
164 | return m_gdb_client_up->GetOSBuildString(); |
165 | } |
166 | |
167 | std::optional<std::string> |
168 | PlatformRemoteGDBServer::GetRemoteOSKernelDescription() { |
169 | if (!m_gdb_client_up) |
170 | return std::nullopt; |
171 | return m_gdb_client_up->GetOSKernelDescription(); |
172 | } |
173 | |
174 | // Remote Platform subclasses need to override this function |
175 | ArchSpec PlatformRemoteGDBServer::GetRemoteSystemArchitecture() { |
176 | if (!m_gdb_client_up) |
177 | return ArchSpec(); |
178 | return m_gdb_client_up->GetSystemArchitecture(); |
179 | } |
180 | |
181 | FileSpec PlatformRemoteGDBServer::GetRemoteWorkingDirectory() { |
182 | if (IsConnected()) { |
183 | Log *log = GetLog(mask: LLDBLog::Platform); |
184 | FileSpec working_dir; |
185 | if (m_gdb_client_up->GetWorkingDir(working_dir) && log) |
186 | LLDB_LOGF(log, |
187 | "PlatformRemoteGDBServer::GetRemoteWorkingDirectory() -> '%s'", |
188 | working_dir.GetPath().c_str()); |
189 | return working_dir; |
190 | } else { |
191 | return Platform::GetRemoteWorkingDirectory(); |
192 | } |
193 | } |
194 | |
195 | bool PlatformRemoteGDBServer::SetRemoteWorkingDirectory( |
196 | const FileSpec &working_dir) { |
197 | if (IsConnected()) { |
198 | // Clear the working directory it case it doesn't get set correctly. This |
199 | // will for use to re-read it |
200 | Log *log = GetLog(mask: LLDBLog::Platform); |
201 | LLDB_LOGF(log, "PlatformRemoteGDBServer::SetRemoteWorkingDirectory('%s')", |
202 | working_dir.GetPath().c_str()); |
203 | return m_gdb_client_up->SetWorkingDir(working_dir) == 0; |
204 | } else |
205 | return Platform::SetRemoteWorkingDirectory(working_dir); |
206 | } |
207 | |
208 | bool PlatformRemoteGDBServer::IsConnected() const { |
209 | return m_gdb_client_up && m_gdb_client_up->IsConnected(); |
210 | } |
211 | |
212 | Status PlatformRemoteGDBServer::ConnectRemote(Args &args) { |
213 | Status error; |
214 | if (IsConnected()) |
215 | return Status::FromErrorStringWithFormat( |
216 | format: "the platform is already connected to '%s', " |
217 | "execute 'platform disconnect' to close the " |
218 | "current connection", |
219 | GetHostname()); |
220 | |
221 | if (args.GetArgumentCount() != 1) |
222 | return Status::FromErrorString( |
223 | str: "\"platform connect\" takes a single argument: <connect-url>"); |
224 | |
225 | const char *url = args.GetArgumentAtIndex(idx: 0); |
226 | if (!url) |
227 | return Status::FromErrorString(str: "URL is null."); |
228 | |
229 | std::optional<URI> parsed_url = URI::Parse(uri: url); |
230 | if (!parsed_url) |
231 | return Status::FromErrorStringWithFormat(format: "Invalid URL: %s", url); |
232 | |
233 | // We're going to reuse the hostname when we connect to the debugserver. |
234 | m_platform_scheme = parsed_url->scheme.str(); |
235 | m_platform_hostname = parsed_url->hostname.str(); |
236 | |
237 | auto client_up = |
238 | std::make_unique<process_gdb_remote::GDBRemoteCommunicationClient>(); |
239 | client_up->SetPacketTimeout( |
240 | process_gdb_remote::ProcessGDBRemote::GetPacketTimeout()); |
241 | client_up->SetConnection(std::make_unique<ConnectionFileDescriptor>()); |
242 | client_up->Connect(url, error_ptr: &error); |
243 | |
244 | if (error.Fail()) |
245 | return error; |
246 | |
247 | if (client_up->HandshakeWithServer(error_ptr: &error)) { |
248 | m_gdb_client_up = std::move(client_up); |
249 | m_gdb_client_up->GetHostInfo(); |
250 | // If a working directory was set prior to connecting, send it down |
251 | // now. |
252 | if (m_working_dir) |
253 | m_gdb_client_up->SetWorkingDir(m_working_dir); |
254 | |
255 | m_supported_architectures.clear(); |
256 | ArchSpec remote_arch = m_gdb_client_up->GetSystemArchitecture(); |
257 | if (remote_arch) { |
258 | m_supported_architectures.push_back(x: remote_arch); |
259 | if (remote_arch.GetTriple().isArch64Bit()) |
260 | m_supported_architectures.push_back( |
261 | x: ArchSpec(remote_arch.GetTriple().get32BitArchVariant())); |
262 | } |
263 | } else { |
264 | client_up->Disconnect(); |
265 | if (error.Success()) |
266 | error = Status::FromErrorString(str: "handshake failed"); |
267 | } |
268 | return error; |
269 | } |
270 | |
271 | Status PlatformRemoteGDBServer::DisconnectRemote() { |
272 | Status error; |
273 | m_gdb_client_up.reset(); |
274 | m_remote_signals_sp.reset(); |
275 | return error; |
276 | } |
277 | |
278 | const char *PlatformRemoteGDBServer::GetHostname() { |
279 | if (m_gdb_client_up) |
280 | m_gdb_client_up->GetHostname(s&: m_hostname); |
281 | if (m_hostname.empty()) |
282 | return nullptr; |
283 | return m_hostname.c_str(); |
284 | } |
285 | |
286 | std::optional<std::string> |
287 | PlatformRemoteGDBServer::DoGetUserName(UserIDResolver::id_t uid) { |
288 | std::string name; |
289 | if (m_gdb_client_up && m_gdb_client_up->GetUserName(uid, name)) |
290 | return std::move(name); |
291 | return std::nullopt; |
292 | } |
293 | |
294 | std::optional<std::string> |
295 | PlatformRemoteGDBServer::DoGetGroupName(UserIDResolver::id_t gid) { |
296 | std::string name; |
297 | if (m_gdb_client_up && m_gdb_client_up->GetGroupName(gid, name)) |
298 | return std::move(name); |
299 | return std::nullopt; |
300 | } |
301 | |
302 | uint32_t PlatformRemoteGDBServer::FindProcesses( |
303 | const ProcessInstanceInfoMatch &match_info, |
304 | ProcessInstanceInfoList &process_infos) { |
305 | if (m_gdb_client_up) |
306 | return m_gdb_client_up->FindProcesses(process_match_info: match_info, process_infos); |
307 | return 0; |
308 | } |
309 | |
310 | bool PlatformRemoteGDBServer::GetProcessInfo( |
311 | lldb::pid_t pid, ProcessInstanceInfo &process_info) { |
312 | if (m_gdb_client_up) |
313 | return m_gdb_client_up->GetProcessInfo(pid, process_info); |
314 | return false; |
315 | } |
316 | |
317 | Status PlatformRemoteGDBServer::LaunchProcess(ProcessLaunchInfo &launch_info) { |
318 | Log *log = GetLog(mask: LLDBLog::Platform); |
319 | Status error; |
320 | |
321 | LLDB_LOGF(log, "PlatformRemoteGDBServer::%s() called", __FUNCTION__); |
322 | |
323 | if (!IsConnected()) |
324 | return Status::FromErrorStringWithFormat(format: "Not connected."); |
325 | auto num_file_actions = launch_info.GetNumFileActions(); |
326 | for (decltype(num_file_actions) i = 0; i < num_file_actions; ++i) { |
327 | const auto file_action = launch_info.GetFileActionAtIndex(idx: i); |
328 | if (file_action->GetAction() != FileAction::eFileActionOpen) |
329 | continue; |
330 | switch (file_action->GetFD()) { |
331 | case STDIN_FILENO: |
332 | m_gdb_client_up->SetSTDIN(file_action->GetFileSpec()); |
333 | break; |
334 | case STDOUT_FILENO: |
335 | m_gdb_client_up->SetSTDOUT(file_action->GetFileSpec()); |
336 | break; |
337 | case STDERR_FILENO: |
338 | m_gdb_client_up->SetSTDERR(file_action->GetFileSpec()); |
339 | break; |
340 | } |
341 | } |
342 | |
343 | m_gdb_client_up->SetDisableASLR( |
344 | launch_info.GetFlags().Test(bit: eLaunchFlagDisableASLR)); |
345 | m_gdb_client_up->SetDetachOnError( |
346 | launch_info.GetFlags().Test(bit: eLaunchFlagDetachOnError)); |
347 | |
348 | FileSpec working_dir = launch_info.GetWorkingDirectory(); |
349 | if (working_dir) { |
350 | m_gdb_client_up->SetWorkingDir(working_dir); |
351 | } |
352 | |
353 | // Send the environment and the program + arguments after we connect |
354 | m_gdb_client_up->SendEnvironment(env: launch_info.GetEnvironment()); |
355 | |
356 | ArchSpec arch_spec = launch_info.GetArchitecture(); |
357 | const char *arch_triple = arch_spec.GetTriple().str().c_str(); |
358 | |
359 | m_gdb_client_up->SendLaunchArchPacket(arch: arch_triple); |
360 | LLDB_LOGF( |
361 | log, |
362 | "PlatformRemoteGDBServer::%s() set launch architecture triple to '%s'", |
363 | __FUNCTION__, arch_triple ? arch_triple : "<NULL>"); |
364 | |
365 | { |
366 | // Scope for the scoped timeout object |
367 | process_gdb_remote::GDBRemoteCommunication::ScopedTimeout timeout( |
368 | *m_gdb_client_up, std::chrono::seconds(5)); |
369 | // Since we can't send argv0 separate from the executable path, we need to |
370 | // make sure to use the actual executable path found in the launch_info... |
371 | Args args = launch_info.GetArguments(); |
372 | if (FileSpec exe_file = launch_info.GetExecutableFile()) |
373 | args.ReplaceArgumentAtIndex(idx: 0, arg_str: exe_file.GetPath(denormalize: false)); |
374 | if (llvm::Error err = m_gdb_client_up->LaunchProcess(args)) { |
375 | error = Status::FromErrorStringWithFormatv( |
376 | format: "Cannot launch '{0}': {1}", args: args.GetArgumentAtIndex(idx: 0), |
377 | args: llvm::fmt_consume(Item: std::move(err))); |
378 | return error; |
379 | } |
380 | } |
381 | |
382 | const auto pid = m_gdb_client_up->GetCurrentProcessID(allow_lazy: false); |
383 | if (pid != LLDB_INVALID_PROCESS_ID) { |
384 | launch_info.SetProcessID(pid); |
385 | LLDB_LOGF(log, |
386 | "PlatformRemoteGDBServer::%s() pid %"PRIu64 |
387 | " launched successfully", |
388 | __FUNCTION__, pid); |
389 | } else { |
390 | LLDB_LOGF(log, |
391 | "PlatformRemoteGDBServer::%s() launch succeeded but we " |
392 | "didn't get a valid process id back!", |
393 | __FUNCTION__); |
394 | error = Status::FromErrorString(str: "failed to get PID"); |
395 | } |
396 | return error; |
397 | } |
398 | |
399 | Status PlatformRemoteGDBServer::KillProcess(const lldb::pid_t pid) { |
400 | if (!KillSpawnedProcess(pid)) |
401 | return Status::FromErrorStringWithFormat( |
402 | format: "failed to kill remote spawned process"); |
403 | return Status(); |
404 | } |
405 | |
406 | lldb::ProcessSP |
407 | PlatformRemoteGDBServer::DebugProcess(ProcessLaunchInfo &launch_info, |
408 | Debugger &debugger, Target &target, |
409 | Status &error) { |
410 | lldb::ProcessSP process_sp; |
411 | if (IsRemote()) { |
412 | if (IsConnected()) { |
413 | lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; |
414 | std::string connect_url; |
415 | if (!LaunchGDBServer(pid&: debugserver_pid, connect_url)) { |
416 | error = Status::FromErrorStringWithFormat( |
417 | format: "unable to launch a GDB server on '%s'", GetHostname()); |
418 | } else { |
419 | // The darwin always currently uses the GDB remote debugger plug-in |
420 | // so even when debugging locally we are debugging remotely! |
421 | process_sp = target.CreateProcess(listener_sp: launch_info.GetListener(), |
422 | plugin_name: "gdb-remote", crash_file: nullptr, can_connect: true); |
423 | |
424 | if (process_sp) { |
425 | process_sp->HijackProcessEvents(listener_sp: launch_info.GetHijackListener()); |
426 | process_sp->SetShadowListener(launch_info.GetShadowListener()); |
427 | |
428 | error = process_sp->ConnectRemote(remote_url: connect_url.c_str()); |
429 | // Retry the connect remote one time... |
430 | if (error.Fail()) |
431 | error = process_sp->ConnectRemote(remote_url: connect_url.c_str()); |
432 | if (error.Success()) |
433 | error = process_sp->Launch(launch_info); |
434 | else if (debugserver_pid != LLDB_INVALID_PROCESS_ID) { |
435 | printf(format: "error: connect remote failed (%s)\n", error.AsCString()); |
436 | KillSpawnedProcess(pid: debugserver_pid); |
437 | } |
438 | } |
439 | } |
440 | } else { |
441 | error = Status::FromErrorString(str: "not connected to remote gdb server"); |
442 | } |
443 | } |
444 | return process_sp; |
445 | } |
446 | |
447 | bool PlatformRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid, |
448 | std::string &connect_url) { |
449 | assert(IsConnected()); |
450 | |
451 | ArchSpec remote_arch = GetRemoteSystemArchitecture(); |
452 | llvm::Triple &remote_triple = remote_arch.GetTriple(); |
453 | |
454 | uint16_t port = 0; |
455 | std::string socket_name; |
456 | bool launch_result = false; |
457 | if (remote_triple.getVendor() == llvm::Triple::Apple && |
458 | remote_triple.getOS() == llvm::Triple::IOS) { |
459 | // When remote debugging to iOS, we use a USB mux that always talks to |
460 | // localhost, so we will need the remote debugserver to accept connections |
461 | // only from localhost, no matter what our current hostname is |
462 | launch_result = |
463 | m_gdb_client_up->LaunchGDBServer(remote_accept_hostname: "127.0.0.1", pid, port, socket_name); |
464 | } else { |
465 | // All other hosts should use their actual hostname |
466 | launch_result = |
467 | m_gdb_client_up->LaunchGDBServer(remote_accept_hostname: nullptr, pid, port, socket_name); |
468 | } |
469 | |
470 | if (!launch_result) |
471 | return false; |
472 | |
473 | connect_url = |
474 | MakeGdbServerUrl(platform_scheme: m_platform_scheme, platform_hostname: m_platform_hostname, port, |
475 | socket_name: (socket_name.empty()) ? nullptr : socket_name.c_str()); |
476 | return true; |
477 | } |
478 | |
479 | bool PlatformRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) { |
480 | assert(IsConnected()); |
481 | return m_gdb_client_up->KillSpawnedProcess(pid); |
482 | } |
483 | |
484 | lldb::ProcessSP PlatformRemoteGDBServer::Attach( |
485 | ProcessAttachInfo &attach_info, Debugger &debugger, |
486 | Target *target, // Can be NULL, if NULL create a new target, else use |
487 | // existing one |
488 | Status &error) { |
489 | lldb::ProcessSP process_sp; |
490 | if (IsRemote()) { |
491 | if (IsConnected()) { |
492 | lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; |
493 | std::string connect_url; |
494 | if (!LaunchGDBServer(pid&: debugserver_pid, connect_url)) { |
495 | error = Status::FromErrorStringWithFormat( |
496 | format: "unable to launch a GDB server on '%s'", GetHostname()); |
497 | } else { |
498 | if (target == nullptr) { |
499 | TargetSP new_target_sp; |
500 | |
501 | error = debugger.GetTargetList().CreateTarget( |
502 | debugger, user_exe_path: "", triple_str: "", get_dependent_modules: eLoadDependentsNo, platform_options: nullptr, target_sp&: new_target_sp); |
503 | target = new_target_sp.get(); |
504 | } else |
505 | error.Clear(); |
506 | |
507 | if (target && error.Success()) { |
508 | // The darwin always currently uses the GDB remote debugger plug-in |
509 | // so even when debugging locally we are debugging remotely! |
510 | process_sp = |
511 | target->CreateProcess(listener_sp: attach_info.GetListenerForProcess(debugger), |
512 | plugin_name: "gdb-remote", crash_file: nullptr, can_connect: true); |
513 | if (process_sp) { |
514 | error = process_sp->ConnectRemote(remote_url: connect_url.c_str()); |
515 | if (error.Success()) { |
516 | ListenerSP listener_sp = attach_info.GetHijackListener(); |
517 | if (listener_sp) |
518 | process_sp->HijackProcessEvents(listener_sp); |
519 | process_sp->SetShadowListener(attach_info.GetShadowListener()); |
520 | error = process_sp->Attach(attach_info); |
521 | } |
522 | |
523 | if (error.Fail() && debugserver_pid != LLDB_INVALID_PROCESS_ID) { |
524 | KillSpawnedProcess(pid: debugserver_pid); |
525 | } |
526 | } |
527 | } |
528 | } |
529 | } else { |
530 | error = Status::FromErrorString(str: "not connected to remote gdb server"); |
531 | } |
532 | } |
533 | return process_sp; |
534 | } |
535 | |
536 | Status PlatformRemoteGDBServer::MakeDirectory(const FileSpec &file_spec, |
537 | uint32_t mode) { |
538 | if (!IsConnected()) |
539 | return Status::FromErrorStringWithFormat(format: "Not connected."); |
540 | Status error = m_gdb_client_up->MakeDirectory(file_spec, mode); |
541 | Log *log = GetLog(mask: LLDBLog::Platform); |
542 | LLDB_LOGF(log, |
543 | "PlatformRemoteGDBServer::MakeDirectory(path='%s', mode=%o) " |
544 | "error = %u (%s)", |
545 | file_spec.GetPath().c_str(), mode, error.GetError(), |
546 | error.AsCString()); |
547 | return error; |
548 | } |
549 | |
550 | Status PlatformRemoteGDBServer::GetFilePermissions(const FileSpec &file_spec, |
551 | uint32_t &file_permissions) { |
552 | if (!IsConnected()) |
553 | return Status::FromErrorStringWithFormat(format: "Not connected."); |
554 | Status error = |
555 | m_gdb_client_up->GetFilePermissions(file_spec, file_permissions); |
556 | Log *log = GetLog(mask: LLDBLog::Platform); |
557 | LLDB_LOGF(log, |
558 | "PlatformRemoteGDBServer::GetFilePermissions(path='%s', " |
559 | "file_permissions=%o) error = %u (%s)", |
560 | file_spec.GetPath().c_str(), file_permissions, error.GetError(), |
561 | error.AsCString()); |
562 | return error; |
563 | } |
564 | |
565 | Status PlatformRemoteGDBServer::SetFilePermissions(const FileSpec &file_spec, |
566 | uint32_t file_permissions) { |
567 | if (!IsConnected()) |
568 | return Status::FromErrorStringWithFormat(format: "Not connected."); |
569 | Status error = |
570 | m_gdb_client_up->SetFilePermissions(file_spec, file_permissions); |
571 | Log *log = GetLog(mask: LLDBLog::Platform); |
572 | LLDB_LOGF(log, |
573 | "PlatformRemoteGDBServer::SetFilePermissions(path='%s', " |
574 | "file_permissions=%o) error = %u (%s)", |
575 | file_spec.GetPath().c_str(), file_permissions, error.GetError(), |
576 | error.AsCString()); |
577 | return error; |
578 | } |
579 | |
580 | lldb::user_id_t PlatformRemoteGDBServer::OpenFile(const FileSpec &file_spec, |
581 | File::OpenOptions flags, |
582 | uint32_t mode, |
583 | Status &error) { |
584 | if (IsConnected()) |
585 | return m_gdb_client_up->OpenFile(file_spec, flags, mode, error); |
586 | return LLDB_INVALID_UID; |
587 | } |
588 | |
589 | bool PlatformRemoteGDBServer::CloseFile(lldb::user_id_t fd, Status &error) { |
590 | if (IsConnected()) |
591 | return m_gdb_client_up->CloseFile(fd, error); |
592 | error = Status::FromErrorStringWithFormat(format: "Not connected."); |
593 | return false; |
594 | } |
595 | |
596 | lldb::user_id_t |
597 | PlatformRemoteGDBServer::GetFileSize(const FileSpec &file_spec) { |
598 | if (IsConnected()) |
599 | return m_gdb_client_up->GetFileSize(file_spec); |
600 | return LLDB_INVALID_UID; |
601 | } |
602 | |
603 | void PlatformRemoteGDBServer::AutoCompleteDiskFileOrDirectory( |
604 | CompletionRequest &request, bool only_dir) { |
605 | if (IsConnected()) |
606 | m_gdb_client_up->AutoCompleteDiskFileOrDirectory(request, only_dir); |
607 | } |
608 | |
609 | uint64_t PlatformRemoteGDBServer::ReadFile(lldb::user_id_t fd, uint64_t offset, |
610 | void *dst, uint64_t dst_len, |
611 | Status &error) { |
612 | if (IsConnected()) |
613 | return m_gdb_client_up->ReadFile(fd, offset, dst, dst_len, error); |
614 | error = Status::FromErrorStringWithFormat(format: "Not connected."); |
615 | return 0; |
616 | } |
617 | |
618 | uint64_t PlatformRemoteGDBServer::WriteFile(lldb::user_id_t fd, uint64_t offset, |
619 | const void *src, uint64_t src_len, |
620 | Status &error) { |
621 | if (IsConnected()) |
622 | return m_gdb_client_up->WriteFile(fd, offset, src, src_len, error); |
623 | error = Status::FromErrorStringWithFormat(format: "Not connected."); |
624 | return 0; |
625 | } |
626 | |
627 | Status PlatformRemoteGDBServer::PutFile(const FileSpec &source, |
628 | const FileSpec &destination, |
629 | uint32_t uid, uint32_t gid) { |
630 | return Platform::PutFile(source, destination, uid, gid); |
631 | } |
632 | |
633 | Status PlatformRemoteGDBServer::CreateSymlink( |
634 | const FileSpec &src, // The name of the link is in src |
635 | const FileSpec &dst) // The symlink points to dst |
636 | { |
637 | if (!IsConnected()) |
638 | return Status::FromErrorStringWithFormat(format: "Not connected."); |
639 | Status error = m_gdb_client_up->CreateSymlink(src, dst); |
640 | Log *log = GetLog(mask: LLDBLog::Platform); |
641 | LLDB_LOGF(log, |
642 | "PlatformRemoteGDBServer::CreateSymlink(src='%s', dst='%s') " |
643 | "error = %u (%s)", |
644 | src.GetPath().c_str(), dst.GetPath().c_str(), error.GetError(), |
645 | error.AsCString()); |
646 | return error; |
647 | } |
648 | |
649 | Status PlatformRemoteGDBServer::Unlink(const FileSpec &file_spec) { |
650 | if (!IsConnected()) |
651 | return Status::FromErrorStringWithFormat(format: "Not connected."); |
652 | Status error = m_gdb_client_up->Unlink(file_spec); |
653 | Log *log = GetLog(mask: LLDBLog::Platform); |
654 | LLDB_LOGF(log, "PlatformRemoteGDBServer::Unlink(path='%s') error = %u (%s)", |
655 | file_spec.GetPath().c_str(), error.GetError(), error.AsCString()); |
656 | return error; |
657 | } |
658 | |
659 | bool PlatformRemoteGDBServer::GetFileExists(const FileSpec &file_spec) { |
660 | if (IsConnected()) |
661 | return m_gdb_client_up->GetFileExists(file_spec); |
662 | return false; |
663 | } |
664 | |
665 | Status PlatformRemoteGDBServer::RunShellCommand( |
666 | llvm::StringRef shell, llvm::StringRef command, |
667 | const FileSpec & |
668 | working_dir, // Pass empty FileSpec to use the current working directory |
669 | int *status_ptr, // Pass NULL if you don't want the process exit status |
670 | int *signo_ptr, // Pass NULL if you don't want the signal that caused the |
671 | // process to exit |
672 | std::string |
673 | *command_output, // Pass NULL if you don't want the command output |
674 | const Timeout<std::micro> &timeout) { |
675 | if (!IsConnected()) |
676 | return Status::FromErrorStringWithFormat(format: "Not connected."); |
677 | return m_gdb_client_up->RunShellCommand(command, working_dir, status_ptr, |
678 | signo_ptr, command_output, timeout); |
679 | } |
680 | |
681 | llvm::ErrorOr<llvm::MD5::MD5Result> |
682 | PlatformRemoteGDBServer::CalculateMD5(const FileSpec &file_spec) { |
683 | if (!IsConnected()) |
684 | return std::make_error_code(e: std::errc::not_connected); |
685 | |
686 | return m_gdb_client_up->CalculateMD5(file_spec); |
687 | } |
688 | |
689 | void PlatformRemoteGDBServer::CalculateTrapHandlerSymbolNames() { |
690 | m_trap_handlers.push_back(x: ConstString("_sigtramp")); |
691 | } |
692 | |
693 | const UnixSignalsSP &PlatformRemoteGDBServer::GetRemoteUnixSignals() { |
694 | if (!IsConnected()) |
695 | return Platform::GetRemoteUnixSignals(); |
696 | |
697 | if (m_remote_signals_sp) |
698 | return m_remote_signals_sp; |
699 | |
700 | // If packet not implemented or JSON failed to parse, we'll guess the signal |
701 | // set based on the remote architecture. |
702 | m_remote_signals_sp = UnixSignals::Create(arch: GetRemoteSystemArchitecture()); |
703 | |
704 | StringExtractorGDBRemote response; |
705 | auto result = |
706 | m_gdb_client_up->SendPacketAndWaitForResponse(payload: "jSignalsInfo", response); |
707 | |
708 | if (result != decltype(result)::Success || |
709 | response.GetResponseType() != response.eResponse) |
710 | return m_remote_signals_sp; |
711 | |
712 | auto object_sp = StructuredData::ParseJSON(json_text: response.GetStringRef()); |
713 | if (!object_sp || !object_sp->IsValid()) |
714 | return m_remote_signals_sp; |
715 | |
716 | auto array_sp = object_sp->GetAsArray(); |
717 | if (!array_sp || !array_sp->IsValid()) |
718 | return m_remote_signals_sp; |
719 | |
720 | auto remote_signals_sp = std::make_shared<lldb_private::GDBRemoteSignals>(); |
721 | |
722 | bool done = array_sp->ForEach( |
723 | foreach_callback: [&remote_signals_sp](StructuredData::Object *object) -> bool { |
724 | if (!object || !object->IsValid()) |
725 | return false; |
726 | |
727 | auto dict = object->GetAsDictionary(); |
728 | if (!dict || !dict->IsValid()) |
729 | return false; |
730 | |
731 | // Signal number and signal name are required. |
732 | uint64_t signo; |
733 | if (!dict->GetValueForKeyAsInteger(key: "signo", result&: signo)) |
734 | return false; |
735 | |
736 | llvm::StringRef name; |
737 | if (!dict->GetValueForKeyAsString(key: "name", result&: name)) |
738 | return false; |
739 | |
740 | // We can live without short_name, description, etc. |
741 | bool suppress{false}; |
742 | auto object_sp = dict->GetValueForKey(key: "suppress"); |
743 | if (object_sp && object_sp->IsValid()) |
744 | suppress = object_sp->GetBooleanValue(); |
745 | |
746 | bool stop{false}; |
747 | object_sp = dict->GetValueForKey(key: "stop"); |
748 | if (object_sp && object_sp->IsValid()) |
749 | stop = object_sp->GetBooleanValue(); |
750 | |
751 | bool notify{false}; |
752 | object_sp = dict->GetValueForKey(key: "notify"); |
753 | if (object_sp && object_sp->IsValid()) |
754 | notify = object_sp->GetBooleanValue(); |
755 | |
756 | std::string description; |
757 | object_sp = dict->GetValueForKey(key: "description"); |
758 | if (object_sp && object_sp->IsValid()) |
759 | description = std::string(object_sp->GetStringValue()); |
760 | |
761 | llvm::StringRef name_backed, description_backed; |
762 | { |
763 | std::lock_guard<std::mutex> guard(g_signal_string_mutex); |
764 | name_backed = |
765 | g_signal_string_storage.insert(key: name).first->getKeyData(); |
766 | if (!description.empty()) |
767 | description_backed = |
768 | g_signal_string_storage.insert(key: description).first->getKeyData(); |
769 | } |
770 | |
771 | remote_signals_sp->AddSignal(signo, name: name_backed, default_suppress: suppress, default_stop: stop, default_notify: notify, |
772 | description: description_backed); |
773 | return true; |
774 | }); |
775 | |
776 | if (done) |
777 | m_remote_signals_sp = std::move(remote_signals_sp); |
778 | |
779 | return m_remote_signals_sp; |
780 | } |
781 | |
782 | std::string PlatformRemoteGDBServer::MakeGdbServerUrl( |
783 | const std::string &platform_scheme, const std::string &platform_hostname, |
784 | uint16_t port, const char *socket_name) { |
785 | const char *override_scheme = |
786 | getenv(name: "LLDB_PLATFORM_REMOTE_GDB_SERVER_SCHEME"); |
787 | const char *override_hostname = |
788 | getenv(name: "LLDB_PLATFORM_REMOTE_GDB_SERVER_HOSTNAME"); |
789 | const char *port_offset_c_str = |
790 | getenv(name: "LLDB_PLATFORM_REMOTE_GDB_SERVER_PORT_OFFSET"); |
791 | int port_offset = port_offset_c_str ? ::atoi(nptr: port_offset_c_str) : 0; |
792 | |
793 | return MakeUrl(scheme: override_scheme ? override_scheme : platform_scheme.c_str(), |
794 | hostname: override_hostname ? override_hostname |
795 | : platform_hostname.c_str(), |
796 | port: port + port_offset, path: socket_name); |
797 | } |
798 | |
799 | std::string PlatformRemoteGDBServer::MakeUrl(const char *scheme, |
800 | const char *hostname, |
801 | uint16_t port, const char *path) { |
802 | StreamString result; |
803 | result.Printf(format: "%s://", scheme); |
804 | if (strlen(s: hostname) > 0) |
805 | result.Printf(format: "[%s]", hostname); |
806 | |
807 | if (port != 0) |
808 | result.Printf(format: ":%u", port); |
809 | if (path) |
810 | result.Write(src: path, src_len: strlen(s: path)); |
811 | return std::string(result.GetString()); |
812 | } |
813 | |
814 | size_t PlatformRemoteGDBServer::ConnectToWaitingProcesses(Debugger &debugger, |
815 | Status &error) { |
816 | std::vector<std::string> connection_urls; |
817 | GetPendingGdbServerList(connection_urls); |
818 | |
819 | for (size_t i = 0; i < connection_urls.size(); ++i) { |
820 | ConnectProcess(connect_url: connection_urls[i].c_str(), plugin_name: "gdb-remote", debugger, target: nullptr, error); |
821 | if (error.Fail()) |
822 | return i; // We already connected to i process successfully |
823 | } |
824 | return connection_urls.size(); |
825 | } |
826 | |
827 | size_t PlatformRemoteGDBServer::GetPendingGdbServerList( |
828 | std::vector<std::string> &connection_urls) { |
829 | std::vector<std::pair<uint16_t, std::string>> remote_servers; |
830 | if (!IsConnected()) |
831 | return 0; |
832 | m_gdb_client_up->QueryGDBServer(connection_urls&: remote_servers); |
833 | for (const auto &gdbserver : remote_servers) { |
834 | const char *socket_name_cstr = |
835 | gdbserver.second.empty() ? nullptr : gdbserver.second.c_str(); |
836 | connection_urls.emplace_back( |
837 | args: MakeGdbServerUrl(platform_scheme: m_platform_scheme, platform_hostname: m_platform_hostname, |
838 | port: gdbserver.first, socket_name: socket_name_cstr)); |
839 | } |
840 | return connection_urls.size(); |
841 | } |
842 |
Definitions
- g_initialized
- g_signal_string_mutex
- g_signal_string_storage
- Initialize
- Terminate
- CreateInstance
- GetDescriptionStatic
- GetDescription
- GetModuleSpec
- GetFileWithUUID
- PlatformRemoteGDBServer
- ~PlatformRemoteGDBServer
- GetSoftwareBreakpointTrapOpcode
- GetRemoteOSVersion
- GetRemoteOSBuildString
- GetRemoteOSKernelDescription
- GetRemoteSystemArchitecture
- GetRemoteWorkingDirectory
- SetRemoteWorkingDirectory
- IsConnected
- ConnectRemote
- DisconnectRemote
- GetHostname
- DoGetUserName
- DoGetGroupName
- FindProcesses
- GetProcessInfo
- LaunchProcess
- KillProcess
- DebugProcess
- LaunchGDBServer
- KillSpawnedProcess
- Attach
- MakeDirectory
- GetFilePermissions
- SetFilePermissions
- OpenFile
- CloseFile
- GetFileSize
- AutoCompleteDiskFileOrDirectory
- ReadFile
- WriteFile
- PutFile
- CreateSymlink
- Unlink
- GetFileExists
- RunShellCommand
- CalculateMD5
- CalculateTrapHandlerSymbolNames
- GetRemoteUnixSignals
- MakeGdbServerUrl
- MakeUrl
- ConnectToWaitingProcesses
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more