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