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 CoreSimulatorSupport::DeviceSet devices = 81 CoreSimulatorSupport::DeviceSet::GetAvailableDevices( 82 GetDeveloperDirectory()); 83 const size_t num_devices = devices.GetNumDevices(); 84 if (num_devices) { 85 strm.Printf("Available devices:\n"); 86 for (size_t i = 0; i < num_devices; ++i) { 87 CoreSimulatorSupport::Device device = devices.GetDeviceAtIndex(i); 88 strm.Printf(" %s: %s\n", device.GetUDID().c_str(), 89 device.GetName().c_str()); 90 } 91 92 if (m_device.hasValue() && m_device->operator bool()) { 93 strm.Printf("Current device: %s: %s", m_device->GetUDID().c_str(), 94 m_device->GetName().c_str()); 95 if (m_device->GetState() == CoreSimulatorSupport::Device::State::Booted) { 96 strm.Printf(" state = booted"); 97 } 98 strm.Printf("\nType \"platform connect <ARG>\" where <ARG> is a device " 99 "UDID or a device name to disconnect and connect to a " 100 "different device.\n"); 101 102 } else { 103 strm.Printf("No current device is selected, \"platform connect <ARG>\" " 104 "where <ARG> is a device UDID or a device name to connect to " 105 "a specific device.\n"); 106 } 107 108 } else { 109 strm.Printf("No devices are available.\n"); 110 } 111 #else 112 strm.Printf(UNSUPPORTED_ERROR); 113 #endif 114 } 115 116 Status PlatformAppleSimulator::ConnectRemote(Args &args) { 117 #if defined(__APPLE__) 118 Status error; 119 if (args.GetArgumentCount() == 1) { 120 if (m_device) 121 DisconnectRemote(); 122 PlatformAppleSimulator::LoadCoreSimulator(); 123 const char *arg_cstr = args.GetArgumentAtIndex(0); 124 if (arg_cstr) { 125 std::string arg_str(arg_cstr); 126 CoreSimulatorSupport::DeviceSet devices = 127 CoreSimulatorSupport::DeviceSet::GetAvailableDevices( 128 GetDeveloperDirectory()); 129 devices.ForEach( 130 [this, &arg_str](const CoreSimulatorSupport::Device &device) -> bool { 131 if (arg_str == device.GetUDID() || arg_str == device.GetName()) { 132 m_device = device; 133 return false; // Stop iterating 134 } else { 135 return true; // Keep iterating 136 } 137 }); 138 if (!m_device) 139 error.SetErrorStringWithFormat( 140 "no device with UDID or name '%s' was found", arg_cstr); 141 } 142 } else { 143 error.SetErrorString("this command take a single UDID argument of the " 144 "device you want to connect to."); 145 } 146 return error; 147 #else 148 Status err; 149 err.SetErrorString(UNSUPPORTED_ERROR); 150 return err; 151 #endif 152 } 153 154 Status PlatformAppleSimulator::DisconnectRemote() { 155 #if defined(__APPLE__) 156 m_device.reset(); 157 return Status(); 158 #else 159 Status err; 160 err.SetErrorString(UNSUPPORTED_ERROR); 161 return err; 162 #endif 163 } 164 165 lldb::ProcessSP PlatformAppleSimulator::DebugProcess( 166 ProcessLaunchInfo &launch_info, Debugger &debugger, 167 Target *target, // Can be NULL, if NULL create a new target, else use 168 // existing one 169 Status &error) { 170 #if defined(__APPLE__) 171 ProcessSP process_sp; 172 // Make sure we stop at the entry point 173 launch_info.GetFlags().Set(eLaunchFlagDebug); 174 // We always launch the process we are going to debug in a separate process 175 // group, since then we can handle ^C interrupts ourselves w/o having to 176 // worry about the target getting them as well. 177 launch_info.SetLaunchInSeparateProcessGroup(true); 178 179 error = LaunchProcess(launch_info); 180 if (error.Success()) { 181 if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { 182 ProcessAttachInfo attach_info(launch_info); 183 process_sp = Attach(attach_info, debugger, target, error); 184 if (process_sp) { 185 launch_info.SetHijackListener(attach_info.GetHijackListener()); 186 187 // Since we attached to the process, it will think it needs to detach 188 // if the process object just goes away without an explicit call to 189 // Process::Kill() or Process::Detach(), so let it know to kill the 190 // process if this happens. 191 process_sp->SetShouldDetach(false); 192 193 // If we didn't have any file actions, the pseudo terminal might have 194 // been used where the slave side was given as the file to open for 195 // stdin/out/err after we have already opened the master so we can 196 // read/write stdin/out/err. 197 int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); 198 if (pty_fd != PseudoTerminal::invalid_fd) { 199 process_sp->SetSTDIOFileDescriptor(pty_fd); 200 } 201 } 202 } 203 } 204 205 return process_sp; 206 #else 207 return ProcessSP(); 208 #endif 209 } 210 211 FileSpec PlatformAppleSimulator::GetCoreSimulatorPath() { 212 #if defined(__APPLE__) 213 std::lock_guard<std::mutex> guard(m_core_sim_path_mutex); 214 if (!m_core_simulator_framework_path.hasValue()) { 215 const char *developer_dir = GetDeveloperDirectory(); 216 if (developer_dir) { 217 StreamString cs_path; 218 cs_path.Printf( 219 "%s/Library/PrivateFrameworks/CoreSimulator.framework/CoreSimulator", 220 developer_dir); 221 m_core_simulator_framework_path = FileSpec(cs_path.GetData()); 222 FileSystem::Instance().Resolve(*m_core_simulator_framework_path); 223 } 224 } 225 226 return m_core_simulator_framework_path.getValue(); 227 #else 228 return FileSpec(); 229 #endif 230 } 231 232 void PlatformAppleSimulator::LoadCoreSimulator() { 233 #if defined(__APPLE__) 234 static llvm::once_flag g_load_core_sim_flag; 235 llvm::call_once(g_load_core_sim_flag, [this] { 236 const std::string core_sim_path(GetCoreSimulatorPath().GetPath()); 237 if (core_sim_path.size()) 238 dlopen(core_sim_path.c_str(), RTLD_LAZY); 239 }); 240 #endif 241 } 242 243 #if defined(__APPLE__) 244 CoreSimulatorSupport::Device PlatformAppleSimulator::GetSimulatorDevice() { 245 if (!m_device.hasValue()) { 246 const CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id = 247 CoreSimulatorSupport::DeviceType::ProductFamilyID::iPhone; 248 m_device = CoreSimulatorSupport::DeviceSet::GetAvailableDevices( 249 GetDeveloperDirectory()) 250 .GetFanciest(dev_id); 251 } 252 253 if (m_device.hasValue()) 254 return m_device.getValue(); 255 else 256 return CoreSimulatorSupport::Device(); 257 } 258 #endif 259