LLDB  mainline
PlatformAppleSimulator.cpp
Go to the documentation of this file.
1 //===-- PlatformAppleSimulator.cpp ------------------------------*- C++ -*-===//
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 
10 
11 #if defined(__APPLE__)
12 #include <dlfcn.h>
13 #endif
14 
15 #include <mutex>
16 #include <thread>
18 #include "lldb/Target/Process.h"
20 #include "lldb/Utility/Status.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
33 
35 
36 /// Default Constructor
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.
46 
48  lldb_private::ProcessLaunchInfo &launch_info) {
49 #if defined(__APPLE__)
51  CoreSimulatorSupport::Device device(GetSimulatorDevice());
52 
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;
70  return err;
71 #endif
72 }
73 
75 #if defined(__APPLE__)
76  // This will get called by subclasses, so just output status on the current
77  // simulator
79 
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) {
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());
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
113 #endif
114 }
115 
117 #if defined(__APPLE__)
118  Status error;
119  if (args.GetArgumentCount() == 1) {
120  if (m_device)
123  const char *arg_cstr = args.GetArgumentAtIndex(0);
124  if (arg_cstr) {
125  std::string arg_str(arg_cstr);
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)
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;
150  return err;
151 #endif
152 }
153 
155 #if defined(__APPLE__)
156  m_device.reset();
157  return Status();
158 #else
159  Status err;
161  return err;
162 #endif
163 }
164 
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 
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);
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 
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()) {
250  .GetFanciest(dev_id);
251  }
252 
253  if (m_device.hasValue())
254  return m_device.getValue();
255  else
257 }
258 #endif
A class to manage flag bits.
Definition: Debugger.h:82
A command line argument class.
Definition: Args.h:32
Enumerations for broadcasting.
Definition: SBLaunchInfo.h:14
A stream class that can stream formatted output to a file.
Definition: Stream.h:28
lldb_private::Status LaunchProcess(lldb_private::ProcessLaunchInfo &launch_info) override
Launch a new process on a platform, not necessarily for debugging, it could be just for running the p...
lldb::ProcessSP Attach(lldb_private::ProcessAttachInfo &attach_info, lldb_private::Debugger &debugger, lldb_private::Target *target, lldb_private::Status &error) override
Attach to an existing process using a process ID.
#define LLDB_INVALID_PROCESS_ID
Definition: lldb-defines.h:92
Process Spawn(lldb_private::ProcessLaunchInfo &launch_info)
size_t GetArgumentCount() const
Gets the number of arguments left in this command object.
Definition: Args.cpp:254
void SetProcessID(lldb::pid_t pid)
Definition: ProcessInfo.h:70
A file utility class.
Definition: FileSpec.h:55
llvm::Optional< lldb_private::FileSpec > m_core_simulator_framework_path
static void Terminate()
Definition: Platform.cpp:142
const char * GetData() const
Definition: StreamString.h:43
int ReleaseMasterFileDescriptor()
Release the master file descriptor.
const char * GetArgumentAtIndex(size_t idx) const
Gets the NULL terminated C string argument pointer for the argument at index idx. ...
Definition: Args.cpp:256
const char * GetDeveloperDirectory()
lldb_private::FileSpec GetCoreSimulatorPath()
static DeviceSet GetAvailableDevices(const char *developer_dir)
void GetStatus(lldb_private::Stream &strm) override
Report the current status for this platform.
PlatformAppleSimulator()
Default Constructor.
static void Initialize()
Definition: Platform.cpp:140
void SetHijackListener(const lldb::ListenerSP &listener_sp)
void SetLaunchInSeparateProcessGroup(bool separate)
void SetErrorString(llvm::StringRef err_str)
Set the current error string to err_str.
Definition: Status.cpp:241
Device GetFanciest(DeviceType::ProductFamilyID dev_id)
#define UNSUPPORTED_ERROR
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:106
bool Success() const
Test for success condition.
Definition: Status.cpp:287
ValueType Set(ValueType mask)
Set one or more flags by logical OR&#39;ing mask with the current flags.
Definition: Flags.h:84
lldb_private::Status ConnectRemote(lldb_private::Args &args) override
lldb_private::Status DisconnectRemote() override
llvm::Optional< CoreSimulatorSupport::Device > m_device
bool Fail() const
Test for error condition.
Definition: Status.cpp:181
virtual ~PlatformAppleSimulator()
Destructor.
Definition: SBAddress.h:15
lldb::ProcessSP DebugProcess(lldb_private::ProcessLaunchInfo &launch_info, lldb_private::Debugger &debugger, lldb_private::Target *target, lldb_private::Status &error) override
Subclasses do not need to implement this function as it uses the Platform::LaunchProcess() followed b...
int SetErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Set the current error string to a formatted error string.
Definition: Status.cpp:255
lldb::ListenerSP GetHijackListener() const
Definition: Process.h:165
lldb::pid_t GetProcessID() const
Definition: ProcessInfo.h:68
An error handling class.
Definition: Status.h:44
void ForEach(std::function< bool(const Device &)> f)