1 //===-- PlatformAppleSimulator.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 "PlatformAppleSimulator.h"
11 
12 // C Includes
13 #if defined(__APPLE__)
14 #include <dlfcn.h>
15 #endif
16 
17 // C++ Includes
18 #include <mutex>
19 #include <thread>
20 // Other libraries and framework includes
21 // Project includes
22 #include "lldb/Core/Error.h"
23 #include "lldb/Core/StreamString.h"
24 #include "lldb/Target/Process.h"
25 #include "lldb/Utility/LLDBAssert.h"
26 #include "lldb/Utility/PseudoTerminal.h"
27 
28 using namespace lldb;
29 using namespace lldb_private;
30 
31 #if !defined(__APPLE__)
32 #define UNSUPPORTED_ERROR ("Apple simulators aren't supported on this platform")
33 #endif
34 
35 //------------------------------------------------------------------
36 // Static Functions
37 //------------------------------------------------------------------
38 void
39 PlatformAppleSimulator::Initialize ()
40 {
41     PlatformDarwin::Initialize ();
42 }
43 
44 void
45 PlatformAppleSimulator::Terminate ()
46 {
47     PlatformDarwin::Terminate ();
48 }
49 
50 //------------------------------------------------------------------
51 /// Default Constructor
52 //------------------------------------------------------------------
53 PlatformAppleSimulator::PlatformAppleSimulator () :
54     PlatformDarwin (true),
55     m_core_simulator_framework_path()
56 {
57 }
58 
59 //------------------------------------------------------------------
60 /// Destructor.
61 ///
62 /// The destructor is virtual since this class is designed to be
63 /// inherited from by the plug-in instance.
64 //------------------------------------------------------------------
65 PlatformAppleSimulator::~PlatformAppleSimulator()
66 {
67 }
68 
69 lldb_private::Error
70 PlatformAppleSimulator::LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info)
71 {
72 #if defined(__APPLE__)
73     LoadCoreSimulator();
74     CoreSimulatorSupport::Device device(GetSimulatorDevice());
75 
76     if (device.GetState() != CoreSimulatorSupport::Device::State::Booted)
77     {
78         Error boot_err;
79         device.Boot(boot_err);
80         if (boot_err.Fail())
81             return boot_err;
82     }
83 
84     auto spawned = device.Spawn(launch_info);
85 
86     if (spawned)
87     {
88         launch_info.SetProcessID(spawned.GetPID());
89         return Error();
90     }
91     else
92         return spawned.GetError();
93 #else
94     Error err;
95     err.SetErrorString(UNSUPPORTED_ERROR);
96     return err;
97 #endif
98 }
99 
100 void
101 PlatformAppleSimulator::GetStatus (Stream &strm)
102 {
103 #if defined(__APPLE__)
104     // This will get called by subclasses, so just output status on the
105     // current simulator
106     PlatformAppleSimulator::LoadCoreSimulator();
107 
108     CoreSimulatorSupport::DeviceSet devices = CoreSimulatorSupport::DeviceSet::GetAvailableDevices();
109     const size_t num_devices = devices.GetNumDevices();
110     if (num_devices)
111     {
112         strm.Printf("Available devices:\n");
113         for (size_t i=0; i<num_devices; ++i)
114         {
115             CoreSimulatorSupport::Device device = devices.GetDeviceAtIndex(i);
116             strm.Printf("   %s: %s\n", device.GetUDID().c_str(), device.GetName().c_str());
117         }
118 
119         if (m_device.hasValue() && m_device->operator bool())
120         {
121             strm.Printf("Current device: %s: %s", m_device->GetUDID().c_str(), m_device->GetName().c_str());
122             if (m_device->GetState() == CoreSimulatorSupport::Device::State::Booted)
123             {
124                 strm.Printf(" state = booted");
125             }
126             strm.Printf("\nType \"platform connect <ARG>\" where <ARG> is a device UDID or a device name to disconnect and connect to a different device.\n");
127 
128         }
129         else
130         {
131             strm.Printf("No current device is selected, \"platform connect <ARG>\" where <ARG> is a device UDID or a device name to connect to a specific device.\n");
132         }
133 
134     }
135     else
136     {
137         strm.Printf("No devices are available.\n");
138     }
139 #else
140     strm.Printf(UNSUPPORTED_ERROR);
141 #endif
142 }
143 
144 Error
145 PlatformAppleSimulator::ConnectRemote (Args& args)
146 {
147 #if defined(__APPLE__)
148     Error error;
149     if (args.GetArgumentCount() == 1)
150     {
151         if (m_device)
152             DisconnectRemote ();
153         PlatformAppleSimulator::LoadCoreSimulator();
154         const char *arg_cstr = args.GetArgumentAtIndex(0);
155         if (arg_cstr)
156         {
157             std::string arg_str(arg_cstr);
158             CoreSimulatorSupport::DeviceSet devices = CoreSimulatorSupport::DeviceSet::GetAvailableDevices();
159             devices.ForEach([this, &arg_str](const CoreSimulatorSupport::Device &device) -> bool {
160                 if (arg_str == device.GetUDID() || arg_str == device.GetName())
161                 {
162                     m_device = device;
163                     return false; // Stop iterating
164                 }
165                 else
166                 {
167                     return true; // Keep iterating
168                 }
169             });
170             if (!m_device)
171                 error.SetErrorStringWithFormat("no device with UDID or name '%s' was found", arg_cstr);
172         }
173     }
174     else
175     {
176         error.SetErrorString("this command take a single UDID argument of the device you want to connect to.");
177     }
178     return error;
179 #else
180     Error err;
181     err.SetErrorString(UNSUPPORTED_ERROR);
182     return err;
183 #endif
184 }
185 
186 Error
187 PlatformAppleSimulator::DisconnectRemote ()
188 {
189 #if defined(__APPLE__)
190     m_device.reset();
191     return Error();
192 #else
193     Error err;
194     err.SetErrorString(UNSUPPORTED_ERROR);
195     return err;
196 #endif
197 }
198 
199 
200 lldb::ProcessSP
201 PlatformAppleSimulator::DebugProcess (ProcessLaunchInfo &launch_info,
202                                       Debugger &debugger,
203                                       Target *target,       // Can be NULL, if NULL create a new target, else use existing one
204                                       Error &error)
205 {
206 #if defined(__APPLE__)
207     ProcessSP process_sp;
208     // Make sure we stop at the entry point
209     launch_info.GetFlags ().Set (eLaunchFlagDebug);
210     // We always launch the process we are going to debug in a separate process
211     // group, since then we can handle ^C interrupts ourselves w/o having to worry
212     // about the target getting them as well.
213     launch_info.SetLaunchInSeparateProcessGroup(true);
214 
215     error = LaunchProcess (launch_info);
216     if (error.Success())
217     {
218         if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
219         {
220             ProcessAttachInfo attach_info (launch_info);
221             process_sp = Attach (attach_info, debugger, target, error);
222             if (process_sp)
223             {
224                 launch_info.SetHijackListener(attach_info.GetHijackListener());
225 
226                 // Since we attached to the process, it will think it needs to detach
227                 // if the process object just goes away without an explicit call to
228                 // Process::Kill() or Process::Detach(), so let it know to kill the
229                 // process if this happens.
230                 process_sp->SetShouldDetach (false);
231 
232                 // If we didn't have any file actions, the pseudo terminal might
233                 // have been used where the slave side was given as the file to
234                 // open for stdin/out/err after we have already opened the master
235                 // so we can read/write stdin/out/err.
236                 int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
237                 if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd)
238                 {
239                     process_sp->SetSTDIOFileDescriptor(pty_fd);
240                 }
241             }
242         }
243     }
244 
245     return process_sp;
246 #else
247     return ProcessSP();
248 #endif
249 }
250 
251 FileSpec
252 PlatformAppleSimulator::GetCoreSimulatorPath()
253 {
254 #if defined(__APPLE__)
255     Mutex::Locker locker (m_mutex);
256     if (!m_core_simulator_framework_path.hasValue())
257     {
258         const char *developer_dir = GetDeveloperDirectory();
259         if (developer_dir)
260         {
261             StreamString cs_path;
262             cs_path.Printf("%s/Library/PrivateFrameworks/CoreSimulator.framework/CoreSimulator", developer_dir);
263             const bool resolve_path = true;
264             m_core_simulator_framework_path = FileSpec(cs_path.GetData(), resolve_path);
265         }
266     }
267 
268     return m_core_simulator_framework_path.getValue();
269 #else
270     return FileSpec();
271 #endif
272 }
273 
274 void
275 PlatformAppleSimulator::LoadCoreSimulator ()
276 {
277 #if defined(__APPLE__)
278     static std::once_flag g_load_core_sim_flag;
279     std::call_once(g_load_core_sim_flag, [this] {
280         const std::string core_sim_path(GetCoreSimulatorPath().GetPath());
281         if (core_sim_path.size())
282             dlopen(core_sim_path.c_str(), RTLD_LAZY);
283     });
284 #endif
285 }
286 
287 #if defined(__APPLE__)
288 CoreSimulatorSupport::Device
289 PlatformAppleSimulator::GetSimulatorDevice ()
290 {
291     if (!m_device.hasValue())
292     {
293         const CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id = CoreSimulatorSupport::DeviceType::ProductFamilyID::iPhone;
294         m_device = CoreSimulatorSupport::DeviceSet::GetAvailableDevices().GetFanciest(dev_id);
295     }
296 
297     if (m_device.hasValue())
298         return m_device.getValue();
299     else
300         return CoreSimulatorSupport::Device();
301 }
302 #endif
303 
304