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