//===-- PlatformAppleSimulator.cpp ------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "PlatformAppleSimulator.h" #if defined(__APPLE__) #include #endif #include #include #include "lldb/Host/PseudoTerminal.h" #include "lldb/Target/Process.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" #include "llvm/Support/Threading.h" using namespace lldb; using namespace lldb_private; #if !defined(__APPLE__) #define UNSUPPORTED_ERROR ("Apple simulators aren't supported on this platform") #endif // Static Functions void PlatformAppleSimulator::Initialize() { PlatformDarwin::Initialize(); } void PlatformAppleSimulator::Terminate() { PlatformDarwin::Terminate(); } /// Default Constructor PlatformAppleSimulator::PlatformAppleSimulator() : PlatformDarwin(true), m_core_sim_path_mutex(), m_core_simulator_framework_path(), m_device() {} /// Destructor. /// /// The destructor is virtual since this class is designed to be /// inherited from by the plug-in instance. PlatformAppleSimulator::~PlatformAppleSimulator() {} lldb_private::Status PlatformAppleSimulator::LaunchProcess( lldb_private::ProcessLaunchInfo &launch_info) { #if defined(__APPLE__) LoadCoreSimulator(); CoreSimulatorSupport::Device device(GetSimulatorDevice()); if (device.GetState() != CoreSimulatorSupport::Device::State::Booted) { Status boot_err; device.Boot(boot_err); if (boot_err.Fail()) return boot_err; } auto spawned = device.Spawn(launch_info); if (spawned) { launch_info.SetProcessID(spawned.GetPID()); return Status(); } else return spawned.GetError(); #else Status err; err.SetErrorString(UNSUPPORTED_ERROR); return err; #endif } void PlatformAppleSimulator::GetStatus(Stream &strm) { #if defined(__APPLE__) // This will get called by subclasses, so just output status on the current // simulator PlatformAppleSimulator::LoadCoreSimulator(); CoreSimulatorSupport::DeviceSet devices = CoreSimulatorSupport::DeviceSet::GetAvailableDevices( GetDeveloperDirectory()); const size_t num_devices = devices.GetNumDevices(); if (num_devices) { strm.Printf("Available devices:\n"); for (size_t i = 0; i < num_devices; ++i) { CoreSimulatorSupport::Device device = devices.GetDeviceAtIndex(i); strm.Printf(" %s: %s\n", device.GetUDID().c_str(), device.GetName().c_str()); } if (m_device.hasValue() && m_device->operator bool()) { strm.Printf("Current device: %s: %s", m_device->GetUDID().c_str(), m_device->GetName().c_str()); if (m_device->GetState() == CoreSimulatorSupport::Device::State::Booted) { strm.Printf(" state = booted"); } strm.Printf("\nType \"platform connect \" where is a device " "UDID or a device name to disconnect and connect to a " "different device.\n"); } else { strm.Printf("No current device is selected, \"platform connect \" " "where is a device UDID or a device name to connect to " "a specific device.\n"); } } else { strm.Printf("No devices are available.\n"); } #else strm.Printf(UNSUPPORTED_ERROR); #endif } Status PlatformAppleSimulator::ConnectRemote(Args &args) { #if defined(__APPLE__) Status error; if (args.GetArgumentCount() == 1) { if (m_device) DisconnectRemote(); PlatformAppleSimulator::LoadCoreSimulator(); const char *arg_cstr = args.GetArgumentAtIndex(0); if (arg_cstr) { std::string arg_str(arg_cstr); CoreSimulatorSupport::DeviceSet devices = CoreSimulatorSupport::DeviceSet::GetAvailableDevices( GetDeveloperDirectory()); devices.ForEach( [this, &arg_str](const CoreSimulatorSupport::Device &device) -> bool { if (arg_str == device.GetUDID() || arg_str == device.GetName()) { m_device = device; return false; // Stop iterating } else { return true; // Keep iterating } }); if (!m_device) error.SetErrorStringWithFormat( "no device with UDID or name '%s' was found", arg_cstr); } } else { error.SetErrorString("this command take a single UDID argument of the " "device you want to connect to."); } return error; #else Status err; err.SetErrorString(UNSUPPORTED_ERROR); return err; #endif } Status PlatformAppleSimulator::DisconnectRemote() { #if defined(__APPLE__) m_device.reset(); return Status(); #else Status err; err.SetErrorString(UNSUPPORTED_ERROR); return err; #endif } lldb::ProcessSP PlatformAppleSimulator::DebugProcess( ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use // existing one Status &error) { #if defined(__APPLE__) ProcessSP process_sp; // Make sure we stop at the entry point launch_info.GetFlags().Set(eLaunchFlagDebug); // We always launch the process we are going to debug in a separate process // group, since then we can handle ^C interrupts ourselves w/o having to // worry about the target getting them as well. launch_info.SetLaunchInSeparateProcessGroup(true); error = LaunchProcess(launch_info); if (error.Success()) { if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { ProcessAttachInfo attach_info(launch_info); process_sp = Attach(attach_info, debugger, target, error); if (process_sp) { launch_info.SetHijackListener(attach_info.GetHijackListener()); // Since we attached to the process, it will think it needs to detach // if the process object just goes away without an explicit call to // Process::Kill() or Process::Detach(), so let it know to kill the // process if this happens. process_sp->SetShouldDetach(false); // If we didn't have any file actions, the pseudo terminal might have // been used where the slave side was given as the file to open for // stdin/out/err after we have already opened the master so we can // read/write stdin/out/err. int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); if (pty_fd != PseudoTerminal::invalid_fd) { process_sp->SetSTDIOFileDescriptor(pty_fd); } } } } return process_sp; #else return ProcessSP(); #endif } FileSpec PlatformAppleSimulator::GetCoreSimulatorPath() { #if defined(__APPLE__) std::lock_guard guard(m_core_sim_path_mutex); if (!m_core_simulator_framework_path.hasValue()) { const char *developer_dir = GetDeveloperDirectory(); if (developer_dir) { StreamString cs_path; cs_path.Printf( "%s/Library/PrivateFrameworks/CoreSimulator.framework/CoreSimulator", developer_dir); m_core_simulator_framework_path = FileSpec(cs_path.GetData()); FileSystem::Instance().Resolve(*m_core_simulator_framework_path); } } return m_core_simulator_framework_path.getValue(); #else return FileSpec(); #endif } void PlatformAppleSimulator::LoadCoreSimulator() { #if defined(__APPLE__) static llvm::once_flag g_load_core_sim_flag; llvm::call_once(g_load_core_sim_flag, [this] { const std::string core_sim_path(GetCoreSimulatorPath().GetPath()); if (core_sim_path.size()) dlopen(core_sim_path.c_str(), RTLD_LAZY); }); #endif } #if defined(__APPLE__) CoreSimulatorSupport::Device PlatformAppleSimulator::GetSimulatorDevice() { if (!m_device.hasValue()) { const CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id = CoreSimulatorSupport::DeviceType::ProductFamilyID::iPhone; m_device = CoreSimulatorSupport::DeviceSet::GetAvailableDevices( GetDeveloperDirectory()) .GetFanciest(dev_id); } if (m_device.hasValue()) return m_device.getValue(); else return CoreSimulatorSupport::Device(); } #endif