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