| //===-- PlatformAppleSimulator.cpp ------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "PlatformAppleSimulator.h" |
| |
| // C Includes |
| #if defined(__APPLE__) |
| #include <dlfcn.h> |
| #endif |
| |
| // C++ Includes |
| #include <mutex> |
| #include <thread> |
| // Other libraries and framework includes |
| // Project includes |
| #include "lldb/Core/Error.h" |
| #include "lldb/Core/StreamString.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Utility/LLDBAssert.h" |
| #include "lldb/Utility/PseudoTerminal.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_simulator_framework_path() |
| { |
| } |
| |
| //------------------------------------------------------------------ |
| /// Destructor. |
| /// |
| /// The destructor is virtual since this class is designed to be |
| /// inherited from by the plug-in instance. |
| //------------------------------------------------------------------ |
| PlatformAppleSimulator::~PlatformAppleSimulator() |
| { |
| } |
| |
| lldb_private::Error |
| PlatformAppleSimulator::LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info) |
| { |
| #if defined(__APPLE__) |
| LoadCoreSimulator(); |
| CoreSimulatorSupport::Device device(GetSimulatorDevice()); |
| |
| if (device.GetState() != CoreSimulatorSupport::Device::State::Booted) |
| { |
| Error 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 Error(); |
| } |
| else |
| return spawned.GetError(); |
| #else |
| Error 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(); |
| 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 <ARG>\" where <ARG> 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 <ARG>\" where <ARG> 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 |
| } |
| |
| Error |
| PlatformAppleSimulator::ConnectRemote (Args& args) |
| { |
| #if defined(__APPLE__) |
| Error 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(); |
| 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 |
| Error err; |
| err.SetErrorString(UNSUPPORTED_ERROR); |
| return err; |
| #endif |
| } |
| |
| Error |
| PlatformAppleSimulator::DisconnectRemote () |
| { |
| #if defined(__APPLE__) |
| m_device.reset(); |
| return Error(); |
| #else |
| Error 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 |
| Error &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 != lldb_utility::PseudoTerminal::invalid_fd) |
| { |
| process_sp->SetSTDIOFileDescriptor(pty_fd); |
| } |
| } |
| } |
| } |
| |
| return process_sp; |
| #else |
| return ProcessSP(); |
| #endif |
| } |
| |
| FileSpec |
| PlatformAppleSimulator::GetCoreSimulatorPath() |
| { |
| #if defined(__APPLE__) |
| std::lock_guard<std::mutex> guard(m_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); |
| const bool resolve_path = true; |
| m_core_simulator_framework_path = FileSpec(cs_path.GetData(), resolve_path); |
| } |
| } |
| |
| return m_core_simulator_framework_path.getValue(); |
| #else |
| return FileSpec(); |
| #endif |
| } |
| |
| void |
| PlatformAppleSimulator::LoadCoreSimulator () |
| { |
| #if defined(__APPLE__) |
| static std::once_flag g_load_core_sim_flag; |
| std::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().GetFanciest(dev_id); |
| } |
| |
| if (m_device.hasValue()) |
| return m_device.getValue(); |
| else |
| return CoreSimulatorSupport::Device(); |
| } |
| #endif |
| |