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