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 | if (m_gdb_client_up) { |
210 | assert(m_gdb_client_up->IsConnected()); |
211 | return true; |
212 | } |
213 | return false; |
214 | } |
215 | |
216 | Status PlatformRemoteGDBServer::ConnectRemote(Args &args) { |
217 | Status error; |
218 | if (IsConnected()) { |
219 | error.SetErrorStringWithFormat("the platform is already connected to '%s', " |
220 | "execute 'platform disconnect' to close the " |
221 | "current connection" , |
222 | GetHostname()); |
223 | return error; |
224 | } |
225 | |
226 | if (args.GetArgumentCount() != 1) { |
227 | error.SetErrorString( |
228 | "\"platform connect\" takes a single argument: <connect-url>" ); |
229 | return error; |
230 | } |
231 | |
232 | const char *url = args.GetArgumentAtIndex(idx: 0); |
233 | if (!url) |
234 | return Status("URL is null." ); |
235 | |
236 | std::optional<URI> parsed_url = URI::Parse(uri: url); |
237 | if (!parsed_url) |
238 | return Status("Invalid URL: %s" , url); |
239 | |
240 | // We're going to reuse the hostname when we connect to the debugserver. |
241 | m_platform_scheme = parsed_url->scheme.str(); |
242 | m_platform_hostname = parsed_url->hostname.str(); |
243 | |
244 | auto client_up = |
245 | std::make_unique<process_gdb_remote::GDBRemoteCommunicationClient>(); |
246 | client_up->SetPacketTimeout( |
247 | process_gdb_remote::ProcessGDBRemote::GetPacketTimeout()); |
248 | client_up->SetConnection(std::make_unique<ConnectionFileDescriptor>()); |
249 | client_up->Connect(url, error_ptr: &error); |
250 | |
251 | if (error.Fail()) |
252 | return error; |
253 | |
254 | if (client_up->HandshakeWithServer(error_ptr: &error)) { |
255 | m_gdb_client_up = std::move(client_up); |
256 | m_gdb_client_up->GetHostInfo(); |
257 | // If a working directory was set prior to connecting, send it down |
258 | // now. |
259 | if (m_working_dir) |
260 | m_gdb_client_up->SetWorkingDir(m_working_dir); |
261 | |
262 | m_supported_architectures.clear(); |
263 | ArchSpec remote_arch = m_gdb_client_up->GetSystemArchitecture(); |
264 | if (remote_arch) { |
265 | m_supported_architectures.push_back(x: remote_arch); |
266 | if (remote_arch.GetTriple().isArch64Bit()) |
267 | m_supported_architectures.push_back( |
268 | x: ArchSpec(remote_arch.GetTriple().get32BitArchVariant())); |
269 | } |
270 | } else { |
271 | client_up->Disconnect(); |
272 | if (error.Success()) |
273 | error.SetErrorString("handshake failed" ); |
274 | } |
275 | return error; |
276 | } |
277 | |
278 | Status PlatformRemoteGDBServer::DisconnectRemote() { |
279 | Status error; |
280 | m_gdb_client_up.reset(); |
281 | m_remote_signals_sp.reset(); |
282 | return error; |
283 | } |
284 | |
285 | const char *PlatformRemoteGDBServer::GetHostname() { |
286 | if (m_gdb_client_up) |
287 | m_gdb_client_up->GetHostname(s&: m_hostname); |
288 | if (m_hostname.empty()) |
289 | return nullptr; |
290 | return m_hostname.c_str(); |
291 | } |
292 | |
293 | std::optional<std::string> |
294 | PlatformRemoteGDBServer::DoGetUserName(UserIDResolver::id_t uid) { |
295 | std::string name; |
296 | if (m_gdb_client_up && m_gdb_client_up->GetUserName(uid, name)) |
297 | return std::move(name); |
298 | return std::nullopt; |
299 | } |
300 | |
301 | std::optional<std::string> |
302 | PlatformRemoteGDBServer::DoGetGroupName(UserIDResolver::id_t gid) { |
303 | std::string name; |
304 | if (m_gdb_client_up && m_gdb_client_up->GetGroupName(gid, name)) |
305 | return std::move(name); |
306 | return std::nullopt; |
307 | } |
308 | |
309 | uint32_t PlatformRemoteGDBServer::FindProcesses( |
310 | const ProcessInstanceInfoMatch &match_info, |
311 | ProcessInstanceInfoList &process_infos) { |
312 | if (m_gdb_client_up) |
313 | return m_gdb_client_up->FindProcesses(process_match_info: match_info, process_infos); |
314 | return 0; |
315 | } |
316 | |
317 | bool PlatformRemoteGDBServer::GetProcessInfo( |
318 | lldb::pid_t pid, ProcessInstanceInfo &process_info) { |
319 | if (m_gdb_client_up) |
320 | return m_gdb_client_up->GetProcessInfo(pid, process_info); |
321 | return false; |
322 | } |
323 | |
324 | Status PlatformRemoteGDBServer::LaunchProcess(ProcessLaunchInfo &launch_info) { |
325 | Log *log = GetLog(mask: LLDBLog::Platform); |
326 | Status error; |
327 | |
328 | LLDB_LOGF(log, "PlatformRemoteGDBServer::%s() called" , __FUNCTION__); |
329 | |
330 | if (!IsConnected()) |
331 | return Status("Not connected." ); |
332 | auto num_file_actions = launch_info.GetNumFileActions(); |
333 | for (decltype(num_file_actions) i = 0; i < num_file_actions; ++i) { |
334 | const auto file_action = launch_info.GetFileActionAtIndex(idx: i); |
335 | if (file_action->GetAction() != FileAction::eFileActionOpen) |
336 | continue; |
337 | switch (file_action->GetFD()) { |
338 | case STDIN_FILENO: |
339 | m_gdb_client_up->SetSTDIN(file_action->GetFileSpec()); |
340 | break; |
341 | case STDOUT_FILENO: |
342 | m_gdb_client_up->SetSTDOUT(file_action->GetFileSpec()); |
343 | break; |
344 | case STDERR_FILENO: |
345 | m_gdb_client_up->SetSTDERR(file_action->GetFileSpec()); |
346 | break; |
347 | } |
348 | } |
349 | |
350 | m_gdb_client_up->SetDisableASLR( |
351 | launch_info.GetFlags().Test(bit: eLaunchFlagDisableASLR)); |
352 | m_gdb_client_up->SetDetachOnError( |
353 | launch_info.GetFlags().Test(bit: eLaunchFlagDetachOnError)); |
354 | |
355 | FileSpec working_dir = launch_info.GetWorkingDirectory(); |
356 | if (working_dir) { |
357 | m_gdb_client_up->SetWorkingDir(working_dir); |
358 | } |
359 | |
360 | // Send the environment and the program + arguments after we connect |
361 | m_gdb_client_up->SendEnvironment(env: launch_info.GetEnvironment()); |
362 | |
363 | ArchSpec arch_spec = launch_info.GetArchitecture(); |
364 | const char *arch_triple = arch_spec.GetTriple().str().c_str(); |
365 | |
366 | m_gdb_client_up->SendLaunchArchPacket(arch: arch_triple); |
367 | LLDB_LOGF( |
368 | log, |
369 | "PlatformRemoteGDBServer::%s() set launch architecture triple to '%s'" , |
370 | __FUNCTION__, arch_triple ? arch_triple : "<NULL>" ); |
371 | |
372 | { |
373 | // Scope for the scoped timeout object |
374 | process_gdb_remote::GDBRemoteCommunication::ScopedTimeout timeout( |
375 | *m_gdb_client_up, std::chrono::seconds(5)); |
376 | // Since we can't send argv0 separate from the executable path, we need to |
377 | // make sure to use the actual executable path found in the launch_info... |
378 | Args args = launch_info.GetArguments(); |
379 | if (FileSpec exe_file = launch_info.GetExecutableFile()) |
380 | args.ReplaceArgumentAtIndex(idx: 0, arg_str: exe_file.GetPath(denormalize: false)); |
381 | if (llvm::Error err = m_gdb_client_up->LaunchProcess(args)) { |
382 | error.SetErrorStringWithFormatv(format: "Cannot launch '{0}': {1}" , |
383 | args: args.GetArgumentAtIndex(idx: 0), |
384 | args: llvm::fmt_consume(Item: std::move(err))); |
385 | return error; |
386 | } |
387 | } |
388 | |
389 | const auto pid = m_gdb_client_up->GetCurrentProcessID(allow_lazy: false); |
390 | if (pid != LLDB_INVALID_PROCESS_ID) { |
391 | launch_info.SetProcessID(pid); |
392 | LLDB_LOGF(log, |
393 | "PlatformRemoteGDBServer::%s() pid %" PRIu64 |
394 | " launched successfully" , |
395 | __FUNCTION__, pid); |
396 | } else { |
397 | LLDB_LOGF(log, |
398 | "PlatformRemoteGDBServer::%s() launch succeeded but we " |
399 | "didn't get a valid process id back!" , |
400 | __FUNCTION__); |
401 | error.SetErrorString("failed to get PID" ); |
402 | } |
403 | return error; |
404 | } |
405 | |
406 | Status PlatformRemoteGDBServer::KillProcess(const lldb::pid_t pid) { |
407 | if (!KillSpawnedProcess(pid)) |
408 | return Status("failed to kill remote spawned process" ); |
409 | return Status(); |
410 | } |
411 | |
412 | lldb::ProcessSP |
413 | PlatformRemoteGDBServer::DebugProcess(ProcessLaunchInfo &launch_info, |
414 | Debugger &debugger, Target &target, |
415 | Status &error) { |
416 | lldb::ProcessSP process_sp; |
417 | if (IsRemote()) { |
418 | if (IsConnected()) { |
419 | lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; |
420 | std::string connect_url; |
421 | if (!LaunchGDBServer(pid&: debugserver_pid, connect_url)) { |
422 | error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'" , |
423 | GetHostname()); |
424 | } else { |
425 | // The darwin always currently uses the GDB remote debugger plug-in |
426 | // so even when debugging locally we are debugging remotely! |
427 | process_sp = target.CreateProcess(listener_sp: launch_info.GetListener(), |
428 | plugin_name: "gdb-remote" , crash_file: nullptr, can_connect: true); |
429 | |
430 | if (process_sp) { |
431 | process_sp->HijackProcessEvents(listener_sp: launch_info.GetHijackListener()); |
432 | process_sp->SetShadowListener(launch_info.GetShadowListener()); |
433 | |
434 | error = process_sp->ConnectRemote(remote_url: connect_url.c_str()); |
435 | // Retry the connect remote one time... |
436 | if (error.Fail()) |
437 | error = process_sp->ConnectRemote(remote_url: connect_url.c_str()); |
438 | if (error.Success()) |
439 | error = process_sp->Launch(launch_info); |
440 | else if (debugserver_pid != LLDB_INVALID_PROCESS_ID) { |
441 | printf(format: "error: connect remote failed (%s)\n" , error.AsCString()); |
442 | KillSpawnedProcess(pid: debugserver_pid); |
443 | } |
444 | } |
445 | } |
446 | } else { |
447 | error.SetErrorString("not connected to remote gdb server" ); |
448 | } |
449 | } |
450 | return process_sp; |
451 | } |
452 | |
453 | bool PlatformRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid, |
454 | std::string &connect_url) { |
455 | assert(IsConnected()); |
456 | |
457 | ArchSpec remote_arch = GetRemoteSystemArchitecture(); |
458 | llvm::Triple &remote_triple = remote_arch.GetTriple(); |
459 | |
460 | uint16_t port = 0; |
461 | std::string socket_name; |
462 | bool launch_result = false; |
463 | if (remote_triple.getVendor() == llvm::Triple::Apple && |
464 | remote_triple.getOS() == llvm::Triple::IOS) { |
465 | // When remote debugging to iOS, we use a USB mux that always talks to |
466 | // localhost, so we will need the remote debugserver to accept connections |
467 | // only from localhost, no matter what our current hostname is |
468 | launch_result = |
469 | m_gdb_client_up->LaunchGDBServer(remote_accept_hostname: "127.0.0.1" , pid, port, socket_name); |
470 | } else { |
471 | // All other hosts should use their actual hostname |
472 | launch_result = |
473 | m_gdb_client_up->LaunchGDBServer(remote_accept_hostname: nullptr, pid, port, socket_name); |
474 | } |
475 | |
476 | if (!launch_result) |
477 | return false; |
478 | |
479 | connect_url = |
480 | MakeGdbServerUrl(platform_scheme: m_platform_scheme, platform_hostname: m_platform_hostname, port, |
481 | socket_name: (socket_name.empty()) ? nullptr : socket_name.c_str()); |
482 | return true; |
483 | } |
484 | |
485 | bool PlatformRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) { |
486 | assert(IsConnected()); |
487 | return m_gdb_client_up->KillSpawnedProcess(pid); |
488 | } |
489 | |
490 | lldb::ProcessSP PlatformRemoteGDBServer::Attach( |
491 | ProcessAttachInfo &attach_info, Debugger &debugger, |
492 | Target *target, // Can be NULL, if NULL create a new target, else use |
493 | // existing one |
494 | Status &error) { |
495 | lldb::ProcessSP process_sp; |
496 | if (IsRemote()) { |
497 | if (IsConnected()) { |
498 | lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; |
499 | std::string connect_url; |
500 | if (!LaunchGDBServer(pid&: debugserver_pid, connect_url)) { |
501 | error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'" , |
502 | GetHostname()); |
503 | } else { |
504 | if (target == nullptr) { |
505 | TargetSP new_target_sp; |
506 | |
507 | error = debugger.GetTargetList().CreateTarget( |
508 | debugger, user_exe_path: "" , triple_str: "" , get_dependent_modules: eLoadDependentsNo, platform_options: nullptr, target_sp&: new_target_sp); |
509 | target = new_target_sp.get(); |
510 | } else |
511 | error.Clear(); |
512 | |
513 | if (target && error.Success()) { |
514 | // The darwin always currently uses the GDB remote debugger plug-in |
515 | // so even when debugging locally we are debugging remotely! |
516 | process_sp = |
517 | target->CreateProcess(listener_sp: attach_info.GetListenerForProcess(debugger), |
518 | plugin_name: "gdb-remote" , crash_file: nullptr, can_connect: true); |
519 | if (process_sp) { |
520 | error = process_sp->ConnectRemote(remote_url: connect_url.c_str()); |
521 | if (error.Success()) { |
522 | ListenerSP listener_sp = attach_info.GetHijackListener(); |
523 | if (listener_sp) |
524 | process_sp->HijackProcessEvents(listener_sp); |
525 | process_sp->SetShadowListener(attach_info.GetShadowListener()); |
526 | error = process_sp->Attach(attach_info); |
527 | } |
528 | |
529 | if (error.Fail() && debugserver_pid != LLDB_INVALID_PROCESS_ID) { |
530 | KillSpawnedProcess(pid: debugserver_pid); |
531 | } |
532 | } |
533 | } |
534 | } |
535 | } else { |
536 | error.SetErrorString("not connected to remote gdb server" ); |
537 | } |
538 | } |
539 | return process_sp; |
540 | } |
541 | |
542 | Status PlatformRemoteGDBServer::MakeDirectory(const FileSpec &file_spec, |
543 | uint32_t mode) { |
544 | if (!IsConnected()) |
545 | return Status("Not connected." ); |
546 | Status error = m_gdb_client_up->MakeDirectory(file_spec, mode); |
547 | Log *log = GetLog(mask: LLDBLog::Platform); |
548 | LLDB_LOGF(log, |
549 | "PlatformRemoteGDBServer::MakeDirectory(path='%s', mode=%o) " |
550 | "error = %u (%s)" , |
551 | file_spec.GetPath().c_str(), mode, error.GetError(), |
552 | error.AsCString()); |
553 | return error; |
554 | } |
555 | |
556 | Status PlatformRemoteGDBServer::GetFilePermissions(const FileSpec &file_spec, |
557 | uint32_t &file_permissions) { |
558 | if (!IsConnected()) |
559 | return Status("Not connected." ); |
560 | Status error = |
561 | m_gdb_client_up->GetFilePermissions(file_spec, file_permissions); |
562 | Log *log = GetLog(mask: LLDBLog::Platform); |
563 | LLDB_LOGF(log, |
564 | "PlatformRemoteGDBServer::GetFilePermissions(path='%s', " |
565 | "file_permissions=%o) error = %u (%s)" , |
566 | file_spec.GetPath().c_str(), file_permissions, error.GetError(), |
567 | error.AsCString()); |
568 | return error; |
569 | } |
570 | |
571 | Status PlatformRemoteGDBServer::SetFilePermissions(const FileSpec &file_spec, |
572 | uint32_t file_permissions) { |
573 | if (!IsConnected()) |
574 | return Status("Not connected." ); |
575 | Status error = |
576 | m_gdb_client_up->SetFilePermissions(file_spec, file_permissions); |
577 | Log *log = GetLog(mask: LLDBLog::Platform); |
578 | LLDB_LOGF(log, |
579 | "PlatformRemoteGDBServer::SetFilePermissions(path='%s', " |
580 | "file_permissions=%o) error = %u (%s)" , |
581 | file_spec.GetPath().c_str(), file_permissions, error.GetError(), |
582 | error.AsCString()); |
583 | return error; |
584 | } |
585 | |
586 | lldb::user_id_t PlatformRemoteGDBServer::OpenFile(const FileSpec &file_spec, |
587 | File::OpenOptions flags, |
588 | uint32_t mode, |
589 | Status &error) { |
590 | if (IsConnected()) |
591 | return m_gdb_client_up->OpenFile(file_spec, flags, mode, error); |
592 | return LLDB_INVALID_UID; |
593 | } |
594 | |
595 | bool PlatformRemoteGDBServer::CloseFile(lldb::user_id_t fd, Status &error) { |
596 | if (IsConnected()) |
597 | return m_gdb_client_up->CloseFile(fd, error); |
598 | error = Status("Not connected." ); |
599 | return false; |
600 | } |
601 | |
602 | lldb::user_id_t |
603 | PlatformRemoteGDBServer::GetFileSize(const FileSpec &file_spec) { |
604 | if (IsConnected()) |
605 | return m_gdb_client_up->GetFileSize(file_spec); |
606 | return LLDB_INVALID_UID; |
607 | } |
608 | |
609 | void PlatformRemoteGDBServer::AutoCompleteDiskFileOrDirectory( |
610 | CompletionRequest &request, bool only_dir) { |
611 | if (IsConnected()) |
612 | m_gdb_client_up->AutoCompleteDiskFileOrDirectory(request, only_dir); |
613 | } |
614 | |
615 | uint64_t PlatformRemoteGDBServer::ReadFile(lldb::user_id_t fd, uint64_t offset, |
616 | void *dst, uint64_t dst_len, |
617 | Status &error) { |
618 | if (IsConnected()) |
619 | return m_gdb_client_up->ReadFile(fd, offset, dst, dst_len, error); |
620 | error = Status("Not connected." ); |
621 | return 0; |
622 | } |
623 | |
624 | uint64_t PlatformRemoteGDBServer::WriteFile(lldb::user_id_t fd, uint64_t offset, |
625 | const void *src, uint64_t src_len, |
626 | Status &error) { |
627 | if (IsConnected()) |
628 | return m_gdb_client_up->WriteFile(fd, offset, src, src_len, error); |
629 | error = Status("Not connected." ); |
630 | return 0; |
631 | } |
632 | |
633 | Status PlatformRemoteGDBServer::PutFile(const FileSpec &source, |
634 | const FileSpec &destination, |
635 | uint32_t uid, uint32_t gid) { |
636 | return Platform::PutFile(source, destination, uid, gid); |
637 | } |
638 | |
639 | Status PlatformRemoteGDBServer::CreateSymlink( |
640 | const FileSpec &src, // The name of the link is in src |
641 | const FileSpec &dst) // The symlink points to dst |
642 | { |
643 | if (!IsConnected()) |
644 | return Status("Not connected." ); |
645 | Status error = m_gdb_client_up->CreateSymlink(src, dst); |
646 | Log *log = GetLog(mask: LLDBLog::Platform); |
647 | LLDB_LOGF(log, |
648 | "PlatformRemoteGDBServer::CreateSymlink(src='%s', dst='%s') " |
649 | "error = %u (%s)" , |
650 | src.GetPath().c_str(), dst.GetPath().c_str(), error.GetError(), |
651 | error.AsCString()); |
652 | return error; |
653 | } |
654 | |
655 | Status PlatformRemoteGDBServer::Unlink(const FileSpec &file_spec) { |
656 | if (!IsConnected()) |
657 | return Status("Not connected." ); |
658 | Status error = m_gdb_client_up->Unlink(file_spec); |
659 | Log *log = GetLog(mask: LLDBLog::Platform); |
660 | LLDB_LOGF(log, "PlatformRemoteGDBServer::Unlink(path='%s') error = %u (%s)" , |
661 | file_spec.GetPath().c_str(), error.GetError(), error.AsCString()); |
662 | return error; |
663 | } |
664 | |
665 | bool PlatformRemoteGDBServer::GetFileExists(const FileSpec &file_spec) { |
666 | if (IsConnected()) |
667 | return m_gdb_client_up->GetFileExists(file_spec); |
668 | return false; |
669 | } |
670 | |
671 | Status PlatformRemoteGDBServer::RunShellCommand( |
672 | llvm::StringRef shell, llvm::StringRef command, |
673 | const FileSpec & |
674 | working_dir, // Pass empty FileSpec to use the current working directory |
675 | int *status_ptr, // Pass NULL if you don't want the process exit status |
676 | int *signo_ptr, // Pass NULL if you don't want the signal that caused the |
677 | // process to exit |
678 | std::string |
679 | *command_output, // Pass NULL if you don't want the command output |
680 | const Timeout<std::micro> &timeout) { |
681 | if (!IsConnected()) |
682 | return Status("Not connected." ); |
683 | return m_gdb_client_up->RunShellCommand(command, working_dir, status_ptr, |
684 | signo_ptr, command_output, timeout); |
685 | } |
686 | |
687 | bool PlatformRemoteGDBServer::CalculateMD5(const FileSpec &file_spec, |
688 | uint64_t &low, uint64_t &high) { |
689 | if (!IsConnected()) |
690 | return false; |
691 | |
692 | return m_gdb_client_up->CalculateMD5(file_spec, low, high); |
693 | } |
694 | |
695 | void PlatformRemoteGDBServer::CalculateTrapHandlerSymbolNames() { |
696 | m_trap_handlers.push_back(x: ConstString("_sigtramp" )); |
697 | } |
698 | |
699 | const UnixSignalsSP &PlatformRemoteGDBServer::GetRemoteUnixSignals() { |
700 | if (!IsConnected()) |
701 | return Platform::GetRemoteUnixSignals(); |
702 | |
703 | if (m_remote_signals_sp) |
704 | return m_remote_signals_sp; |
705 | |
706 | // If packet not implemented or JSON failed to parse, we'll guess the signal |
707 | // set based on the remote architecture. |
708 | m_remote_signals_sp = UnixSignals::Create(arch: GetRemoteSystemArchitecture()); |
709 | |
710 | StringExtractorGDBRemote response; |
711 | auto result = |
712 | m_gdb_client_up->SendPacketAndWaitForResponse(payload: "jSignalsInfo" , response); |
713 | |
714 | if (result != decltype(result)::Success || |
715 | response.GetResponseType() != response.eResponse) |
716 | return m_remote_signals_sp; |
717 | |
718 | auto object_sp = StructuredData::ParseJSON(json_text: response.GetStringRef()); |
719 | if (!object_sp || !object_sp->IsValid()) |
720 | return m_remote_signals_sp; |
721 | |
722 | auto array_sp = object_sp->GetAsArray(); |
723 | if (!array_sp || !array_sp->IsValid()) |
724 | return m_remote_signals_sp; |
725 | |
726 | auto remote_signals_sp = std::make_shared<lldb_private::GDBRemoteSignals>(); |
727 | |
728 | bool done = array_sp->ForEach( |
729 | foreach_callback: [&remote_signals_sp](StructuredData::Object *object) -> bool { |
730 | if (!object || !object->IsValid()) |
731 | return false; |
732 | |
733 | auto dict = object->GetAsDictionary(); |
734 | if (!dict || !dict->IsValid()) |
735 | return false; |
736 | |
737 | // Signal number and signal name are required. |
738 | uint64_t signo; |
739 | if (!dict->GetValueForKeyAsInteger(key: "signo" , result&: signo)) |
740 | return false; |
741 | |
742 | llvm::StringRef name; |
743 | if (!dict->GetValueForKeyAsString(key: "name" , result&: name)) |
744 | return false; |
745 | |
746 | // We can live without short_name, description, etc. |
747 | bool suppress{false}; |
748 | auto object_sp = dict->GetValueForKey(key: "suppress" ); |
749 | if (object_sp && object_sp->IsValid()) |
750 | suppress = object_sp->GetBooleanValue(); |
751 | |
752 | bool stop{false}; |
753 | object_sp = dict->GetValueForKey(key: "stop" ); |
754 | if (object_sp && object_sp->IsValid()) |
755 | stop = object_sp->GetBooleanValue(); |
756 | |
757 | bool notify{false}; |
758 | object_sp = dict->GetValueForKey(key: "notify" ); |
759 | if (object_sp && object_sp->IsValid()) |
760 | notify = object_sp->GetBooleanValue(); |
761 | |
762 | std::string description; |
763 | object_sp = dict->GetValueForKey(key: "description" ); |
764 | if (object_sp && object_sp->IsValid()) |
765 | description = std::string(object_sp->GetStringValue()); |
766 | |
767 | llvm::StringRef name_backed, description_backed; |
768 | { |
769 | std::lock_guard<std::mutex> guard(g_signal_string_mutex); |
770 | name_backed = |
771 | g_signal_string_storage.insert(key: name).first->getKeyData(); |
772 | if (!description.empty()) |
773 | description_backed = |
774 | g_signal_string_storage.insert(key: description).first->getKeyData(); |
775 | } |
776 | |
777 | remote_signals_sp->AddSignal(signo, name: name_backed, default_suppress: suppress, default_stop: stop, default_notify: notify, |
778 | description: description_backed); |
779 | return true; |
780 | }); |
781 | |
782 | if (done) |
783 | m_remote_signals_sp = std::move(remote_signals_sp); |
784 | |
785 | return m_remote_signals_sp; |
786 | } |
787 | |
788 | std::string PlatformRemoteGDBServer::MakeGdbServerUrl( |
789 | const std::string &platform_scheme, const std::string &platform_hostname, |
790 | uint16_t port, const char *socket_name) { |
791 | const char *override_scheme = |
792 | getenv(name: "LLDB_PLATFORM_REMOTE_GDB_SERVER_SCHEME" ); |
793 | const char *override_hostname = |
794 | getenv(name: "LLDB_PLATFORM_REMOTE_GDB_SERVER_HOSTNAME" ); |
795 | const char *port_offset_c_str = |
796 | getenv(name: "LLDB_PLATFORM_REMOTE_GDB_SERVER_PORT_OFFSET" ); |
797 | int port_offset = port_offset_c_str ? ::atoi(nptr: port_offset_c_str) : 0; |
798 | |
799 | return MakeUrl(scheme: override_scheme ? override_scheme : platform_scheme.c_str(), |
800 | hostname: override_hostname ? override_hostname |
801 | : platform_hostname.c_str(), |
802 | port: port + port_offset, path: socket_name); |
803 | } |
804 | |
805 | std::string PlatformRemoteGDBServer::MakeUrl(const char *scheme, |
806 | const char *hostname, |
807 | uint16_t port, const char *path) { |
808 | StreamString result; |
809 | result.Printf(format: "%s://[%s]" , scheme, hostname); |
810 | if (port != 0) |
811 | result.Printf(format: ":%u" , port); |
812 | if (path) |
813 | result.Write(src: path, src_len: strlen(s: path)); |
814 | return std::string(result.GetString()); |
815 | } |
816 | |
817 | size_t PlatformRemoteGDBServer::ConnectToWaitingProcesses(Debugger &debugger, |
818 | Status &error) { |
819 | std::vector<std::string> connection_urls; |
820 | GetPendingGdbServerList(connection_urls); |
821 | |
822 | for (size_t i = 0; i < connection_urls.size(); ++i) { |
823 | ConnectProcess(connect_url: connection_urls[i].c_str(), plugin_name: "gdb-remote" , debugger, target: nullptr, error); |
824 | if (error.Fail()) |
825 | return i; // We already connected to i process successfully |
826 | } |
827 | return connection_urls.size(); |
828 | } |
829 | |
830 | size_t PlatformRemoteGDBServer::GetPendingGdbServerList( |
831 | std::vector<std::string> &connection_urls) { |
832 | std::vector<std::pair<uint16_t, std::string>> remote_servers; |
833 | if (!IsConnected()) |
834 | return 0; |
835 | m_gdb_client_up->QueryGDBServer(connection_urls&: remote_servers); |
836 | for (const auto &gdbserver : remote_servers) { |
837 | const char *socket_name_cstr = |
838 | gdbserver.second.empty() ? nullptr : gdbserver.second.c_str(); |
839 | connection_urls.emplace_back( |
840 | args: MakeGdbServerUrl(platform_scheme: m_platform_scheme, platform_hostname: m_platform_hostname, |
841 | port: gdbserver.first, socket_name: socket_name_cstr)); |
842 | } |
843 | return connection_urls.size(); |
844 | } |
845 | |