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