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