1 //===-- PlatformRemoteGDBServer.cpp -----------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "PlatformRemoteGDBServer.h"
11 
12 // C Includes
13 #include <sys/sysctl.h>
14 
15 // C++ Includes
16 // Other libraries and framework includes
17 // Project includes
18 #include "lldb/Breakpoint/BreakpointLocation.h"
19 #include "lldb/Core/ConnectionFileDescriptor.h"
20 #include "lldb/Core/Debugger.h"
21 #include "lldb/Core/Error.h"
22 #include "lldb/Core/Module.h"
23 #include "lldb/Core/ModuleList.h"
24 #include "lldb/Core/PluginManager.h"
25 #include "lldb/Core/StreamString.h"
26 #include "lldb/Host/FileSpec.h"
27 #include "lldb/Host/Host.h"
28 #include "lldb/Target/Process.h"
29 #include "lldb/Target/Target.h"
30 
31 using namespace lldb;
32 using namespace lldb_private;
33 
34 static bool g_initialized = false;
35 
36 void
37 PlatformRemoteGDBServer::Initialize ()
38 {
39     if (g_initialized == false)
40     {
41         g_initialized = true;
42         PluginManager::RegisterPlugin (PlatformRemoteGDBServer::GetShortPluginNameStatic(),
43                                        PlatformRemoteGDBServer::GetDescriptionStatic(),
44                                        PlatformRemoteGDBServer::CreateInstance);
45     }
46 }
47 
48 void
49 PlatformRemoteGDBServer::Terminate ()
50 {
51     if (g_initialized)
52     {
53         g_initialized = false;
54         PluginManager::UnregisterPlugin (PlatformRemoteGDBServer::CreateInstance);
55     }
56 }
57 
58 Platform*
59 PlatformRemoteGDBServer::CreateInstance (bool force, const lldb_private::ArchSpec *arch)
60 {
61     return new PlatformRemoteGDBServer ();
62 }
63 
64 const char *
65 PlatformRemoteGDBServer::GetShortPluginNameStatic()
66 {
67     return "remote-gdb-server";
68 }
69 
70 const char *
71 PlatformRemoteGDBServer::GetDescriptionStatic()
72 {
73     return "A platform that uses the GDB remote protocol as the communication transport.";
74 }
75 
76 const char *
77 PlatformRemoteGDBServer::GetDescription ()
78 {
79     if (m_platform_description.empty())
80     {
81         if (IsConnected())
82         {
83             // Send the get description packet
84         }
85     }
86 
87     if (!m_platform_description.empty())
88         return m_platform_description.c_str();
89     return GetDescriptionStatic();
90 }
91 
92 Error
93 PlatformRemoteGDBServer::ResolveExecutable (const FileSpec &exe_file,
94                                             const ArchSpec &exe_arch,
95                                             lldb::ModuleSP &exe_module_sp,
96                                             const FileSpecList *module_search_paths_ptr)
97 {
98     Error error;
99     error.SetErrorString ("PlatformRemoteGDBServer::ResolveExecutable() is unimplemented");
100     return error;
101 }
102 
103 Error
104 PlatformRemoteGDBServer::GetFile (const FileSpec &platform_file,
105                                   const UUID *uuid_ptr,
106                                   FileSpec &local_file)
107 {
108     // Default to the local case
109     local_file = platform_file;
110     return Error();
111 }
112 
113 //------------------------------------------------------------------
114 /// Default Constructor
115 //------------------------------------------------------------------
116 PlatformRemoteGDBServer::PlatformRemoteGDBServer () :
117     Platform(false), // This is a remote platform
118     m_gdb_client(true)
119 {
120 }
121 
122 //------------------------------------------------------------------
123 /// Destructor.
124 ///
125 /// The destructor is virtual since this class is designed to be
126 /// inherited from by the plug-in instance.
127 //------------------------------------------------------------------
128 PlatformRemoteGDBServer::~PlatformRemoteGDBServer()
129 {
130 }
131 
132 bool
133 PlatformRemoteGDBServer::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch)
134 {
135     return false;
136 }
137 
138 size_t
139 PlatformRemoteGDBServer::GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite *bp_site)
140 {
141     // This isn't needed if the z/Z packets are supported in the GDB remote
142     // server. But we might need a packet to detect this.
143     return 0;
144 }
145 
146 bool
147 PlatformRemoteGDBServer::GetRemoteOSVersion ()
148 {
149     uint32_t major, minor, update;
150     if (m_gdb_client.GetOSVersion (major, minor, update))
151     {
152         m_major_os_version = major;
153         m_minor_os_version = minor;
154         m_update_os_version = update;
155         return true;
156     }
157     return false;
158 }
159 
160 bool
161 PlatformRemoteGDBServer::GetRemoteOSBuildString (std::string &s)
162 {
163     return m_gdb_client.GetOSBuildString (s);
164 }
165 
166 bool
167 PlatformRemoteGDBServer::GetRemoteOSKernelDescription (std::string &s)
168 {
169     return m_gdb_client.GetOSKernelDescription (s);
170 }
171 
172 // Remote Platform subclasses need to override this function
173 ArchSpec
174 PlatformRemoteGDBServer::GetRemoteSystemArchitecture ()
175 {
176     return m_gdb_client.GetSystemArchitecture();
177 }
178 
179 bool
180 PlatformRemoteGDBServer::IsConnected () const
181 {
182     return m_gdb_client.IsConnected();
183 }
184 
185 Error
186 PlatformRemoteGDBServer::ConnectRemote (Args& args)
187 {
188     Error error;
189     if (IsConnected())
190     {
191         error.SetErrorStringWithFormat ("the platform is already connected to '%s', execute 'platform disconnect' to close the current connection",
192                                         GetHostname());
193     }
194     else
195     {
196         if (args.GetArgumentCount() == 1)
197         {
198             const char *url = args.GetArgumentAtIndex(0);
199             m_gdb_client.SetConnection (new ConnectionFileDescriptor());
200             const ConnectionStatus status = m_gdb_client.Connect(url, &error);
201             if (status == eConnectionStatusSuccess)
202             {
203                 if (m_gdb_client.HandshakeWithServer(&error))
204                 {
205                     m_gdb_client.QueryNoAckModeSupported();
206                     m_gdb_client.GetHostInfo();
207 #if 0
208                     m_gdb_client.TestPacketSpeed(10000);
209 #endif
210                 }
211                 else
212                 {
213                     m_gdb_client.Disconnect();
214                 }
215             }
216         }
217         else
218         {
219             error.SetErrorString ("\"platform connect\" takes a single argument: <connect-url>");
220         }
221     }
222 
223     return error;
224 }
225 
226 Error
227 PlatformRemoteGDBServer::DisconnectRemote ()
228 {
229     Error error;
230     m_gdb_client.Disconnect(&error);
231     return error;
232 }
233 
234 const char *
235 PlatformRemoteGDBServer::GetHostname ()
236 {
237     m_gdb_client.GetHostname (m_name);
238     if (m_name.empty())
239         return NULL;
240     return m_name.c_str();
241 }
242 
243 const char *
244 PlatformRemoteGDBServer::GetUserName (uint32_t uid)
245 {
246     // Try and get a cache user name first
247     const char *cached_user_name = Platform::GetUserName(uid);
248     if (cached_user_name)
249         return cached_user_name;
250     std::string name;
251     if (m_gdb_client.GetUserName(uid, name))
252         return SetCachedUserName(uid, name.c_str(), name.size());
253 
254     SetUserNameNotFound(uid); // Negative cache so we don't keep sending packets
255     return NULL;
256 }
257 
258 const char *
259 PlatformRemoteGDBServer::GetGroupName (uint32_t gid)
260 {
261     const char *cached_group_name = Platform::GetGroupName(gid);
262     if (cached_group_name)
263         return cached_group_name;
264     std::string name;
265     if (m_gdb_client.GetGroupName(gid, name))
266         return SetCachedGroupName(gid, name.c_str(), name.size());
267 
268     SetGroupNameNotFound(gid); // Negative cache so we don't keep sending packets
269     return NULL;
270 }
271 
272 uint32_t
273 PlatformRemoteGDBServer::FindProcesses (const ProcessInstanceInfoMatch &match_info,
274                                         ProcessInstanceInfoList &process_infos)
275 {
276     return m_gdb_client.FindProcesses (match_info, process_infos);
277 }
278 
279 bool
280 PlatformRemoteGDBServer::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
281 {
282     return m_gdb_client.GetProcessInfo (pid, process_info);
283 }
284 
285 
286 Error
287 PlatformRemoteGDBServer::LaunchProcess (ProcessLaunchInfo &launch_info)
288 {
289     Error error;
290     lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
291 
292     m_gdb_client.SetSTDIN ("/dev/null");
293     m_gdb_client.SetSTDOUT ("/dev/null");
294     m_gdb_client.SetSTDERR ("/dev/null");
295     m_gdb_client.SetDisableASLR (launch_info.GetFlags().Test (eLaunchFlagDisableASLR));
296 
297     const char *working_dir = launch_info.GetWorkingDirectory();
298     if (working_dir && working_dir[0])
299     {
300         m_gdb_client.SetWorkingDir (working_dir);
301     }
302 
303     // Send the environment and the program + arguments after we connect
304     const char **argv = launch_info.GetArguments().GetConstArgumentVector();
305     const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector();
306 
307     if (envp)
308     {
309         const char *env_entry;
310         for (int i=0; (env_entry = envp[i]); ++i)
311         {
312             if (m_gdb_client.SendEnvironmentPacket(env_entry) != 0)
313                 break;
314         }
315     }
316     const uint32_t old_packet_timeout = m_gdb_client.SetPacketTimeout (5);
317     int arg_packet_err = m_gdb_client.SendArgumentsPacket (argv);
318     m_gdb_client.SetPacketTimeout (old_packet_timeout);
319     if (arg_packet_err == 0)
320     {
321         std::string error_str;
322         if (m_gdb_client.GetLaunchSuccess (error_str))
323         {
324             pid = m_gdb_client.GetCurrentProcessID ();
325             if (pid != LLDB_INVALID_PROCESS_ID)
326                 launch_info.SetProcessID (pid);
327         }
328         else
329         {
330             error.SetErrorString (error_str.c_str());
331         }
332     }
333     else
334     {
335         error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err);
336     }
337     return error;
338 }
339 
340 lldb::ProcessSP
341 PlatformRemoteGDBServer::Attach (lldb_private::ProcessAttachInfo &attach_info,
342                                  Debugger &debugger,
343                                  Target *target,       // Can be NULL, if NULL create a new target, else use existing one
344                                  Listener &listener,
345                                  Error &error)
346 {
347     lldb::ProcessSP process_sp;
348     if (IsRemote())
349     {
350         if (IsConnected())
351         {
352             uint16_t port = m_gdb_client.LaunchGDBserverAndGetPort();
353 
354             if (port == 0)
355             {
356                 error.SetErrorStringWithFormat ("unable to launch a GDB server on '%s'", GetHostname ());
357             }
358             else
359             {
360                 if (target == NULL)
361                 {
362                     TargetSP new_target_sp;
363                     FileSpec emptyFileSpec;
364 
365                     error = debugger.GetTargetList().CreateTarget (debugger,
366                                                                    emptyFileSpec,
367                                                                    NULL,
368                                                                    false,
369                                                                    NULL,
370                                                                    new_target_sp);
371                     target = new_target_sp.get();
372                 }
373                 else
374                     error.Clear();
375 
376                 if (target && error.Success())
377                 {
378                     debugger.GetTargetList().SetSelectedTarget(target);
379 
380                     // The darwin always currently uses the GDB remote debugger plug-in
381                     // so even when debugging locally we are debugging remotely!
382                     process_sp = target->CreateProcess (listener, "gdb-remote", NULL);
383 
384                     if (process_sp)
385                     {
386                         char connect_url[256];
387                         const int connect_url_len = ::snprintf (connect_url,
388                                                                 sizeof(connect_url),
389                                                                 "connect://%s:%u",
390                                                                 GetHostname (),
391                                                                 port);
392                         assert (connect_url_len < sizeof(connect_url));
393                         error = process_sp->ConnectRemote (connect_url);
394                         if (error.Success())
395                             error = process_sp->Attach(attach_info);
396                     }
397                 }
398             }
399         }
400         else
401         {
402             error.SetErrorString("not connected to remote gdb server");
403         }
404     }
405     return process_sp;
406 }
407 
408 
409