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