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