LLDB  mainline
PlatformiOSSimulatorCoreSimulatorSupport.mm
Go to the documentation of this file.
1 //===-- PlatformiOSSimulatorCoreSimulatorSupport.cpp ---------------*- C++
2 //-*-===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 #include <CoreFoundation/CoreFoundation.h>
16 #include <Foundation/Foundation.h>
17 // Project includes
19 #include "lldb/Host/FileAction.h"
20 
21 #include "llvm/ADT/StringRef.h"
22 
23 using namespace lldb_private;
24 // CoreSimulator lives as part of Xcode, which means we can't really link
25 // against it, so we dlopen()
26 // it at runtime, and error out nicely if that fails
27 @interface SimServiceContext {
28 }
29 + (id)sharedServiceContextForDeveloperDir:(NSString *)dir
30  error:(NSError **)error;
31 @end
32 // However, the drawback is that the compiler will not know about the selectors
33 // we're trying to use
34 // until runtime; to appease clang in this regard, define a fake protocol on
35 // NSObject that exposes
36 // the needed interface names for us
37 @protocol LLDBCoreSimulatorSupport <NSObject>
38 - (id)defaultDeviceSetWithError:(NSError **)error;
39 - (NSArray *)devices;
40 - (id)deviceType;
41 - (NSString *)name;
42 - (NSString *)identifier;
43 - (NSString *)modelIdentifier;
44 - (NSString *)productFamily;
45 - (int32_t)productFamilyID;
46 - (id)runtime;
47 - (BOOL)available;
48 - (NSString *)versionString;
49 - (NSString *)buildVersionString;
50 - (BOOL)bootWithOptions:(NSDictionary *)options error:(NSError **)error;
51 - (NSUInteger)state;
52 - (BOOL)shutdownWithError:(NSError **)error;
53 - (NSUUID *)UDID;
54 - (pid_t)spawnWithPath:(NSString *)path
55  options:(NSDictionary *)options
56  terminationHandler:(void (^)(int status))terminationHandler
57  error:(NSError **)error;
58 @end
59 
60 CoreSimulatorSupport::Process::Process(lldb::pid_t p) : m_pid(p), m_error() {}
61 
62 CoreSimulatorSupport::Process::Process(Status error)
63  : m_pid(LLDB_INVALID_PROCESS_ID), m_error(error) {}
64 
65 CoreSimulatorSupport::Process::Process(lldb::pid_t p, Status error)
66  : m_pid(p), m_error(error) {}
67 
69  : m_dev(nil), m_model_identifier() {}
70 
72  : m_dev(d), m_model_identifier() {}
73 
74 CoreSimulatorSupport::DeviceType::operator bool() { return m_dev != nil; }
75 
77  return ConstString([[m_dev identifier] UTF8String]);
78 }
79 
81  return ConstString([[m_dev productFamily] UTF8String]);
82 }
83 
86  return ProductFamilyID([m_dev productFamilyID]);
87 }
88 
90  : m_dev(nil), m_os_version() {}
91 
93  : m_dev(d), m_os_version() {}
94 
95 CoreSimulatorSupport::DeviceRuntime::operator bool() { return m_dev != nil; }
96 
98  return [m_dev available];
99 }
100 
102  : m_dev(nil), m_dev_type(), m_dev_runtime() {}
103 
105  : m_dev(d), m_dev_type(), m_dev_runtime() {}
106 
107 CoreSimulatorSupport::Device::operator bool() { return m_dev != nil; }
108 
110  return (State)([m_dev state]);
111 }
112 
114  : m_family(), m_versions() {
115  bool any = false;
116  bool first_digit = false;
117  unsigned int val = 0;
118 
119  for (char c : mi) {
120  any = true;
121  if (::isdigit(c)) {
122  if (!first_digit)
123  first_digit = true;
124  val = 10 * val + (c - '0');
125  } else if (c == ',') {
126  if (first_digit) {
127  m_versions.push_back(val);
128  val = 0;
129  } else
130  m_family.push_back(c);
131  } else {
132  if (first_digit) {
133  m_family.clear();
134  m_versions.clear();
135  return;
136  } else {
137  m_family.push_back(c);
138  }
139  }
140  }
141 
142  if (first_digit)
143  m_versions.push_back(val);
144 }
145 
147  : ModelIdentifier("") {}
148 
150  const std::string &build)
151  : m_versions(), m_build(build) {
152  bool any = false;
153  unsigned int val = 0;
154  for (char c : ver) {
155  if (c == '.') {
156  m_versions.push_back(val);
157  val = 0;
158  } else if (::isdigit(c)) {
159  val = 10 * val + (c - '0');
160  any = true;
161  } else {
162  m_versions.clear();
163  return;
164  }
165  }
166  if (any)
167  m_versions.push_back(val);
168 }
169 
171 
174  if (!m_model_identifier.hasValue()) {
175  auto utf8_model_id = [[m_dev modelIdentifier] UTF8String];
176  if (utf8_model_id && *utf8_model_id)
177  m_model_identifier = ModelIdentifier(utf8_model_id);
178  }
179 
180  if (m_model_identifier.hasValue())
181  return m_model_identifier.getValue();
182  else
183  return ModelIdentifier();
184 }
185 
188  if (!m_os_version.hasValue()) {
189  auto utf8_ver_string = [[m_dev versionString] UTF8String];
190  auto utf8_build_ver = [[m_dev buildVersionString] UTF8String];
191  if (utf8_ver_string && *utf8_ver_string && utf8_build_ver &&
192  *utf8_build_ver) {
193  m_os_version = OSVersion(utf8_ver_string, utf8_build_ver);
194  }
195  }
196 
197  if (m_os_version.hasValue())
198  return m_os_version.getValue();
199  return OSVersion();
200 }
201 
203  auto utf8_name = [[m_dev name] UTF8String];
204  if (utf8_name)
205  return std::string(utf8_name);
206  return "";
207 }
208 
210  auto utf8_name = [[m_dev name] UTF8String];
211  if (utf8_name)
212  return std::string(utf8_name);
213  return "";
214 }
215 
217  auto utf8_udid = [[[m_dev UDID] UUIDString] UTF8String];
218  if (utf8_udid)
219  return std::string(utf8_udid);
220  else
221  return std::string();
222 }
223 
225  if (!m_dev_type.hasValue())
226  m_dev_type = DeviceType([m_dev deviceType]);
227 
228  return m_dev_type.getValue();
229 }
230 
233  if (!m_dev_runtime.hasValue())
234  m_dev_runtime = DeviceRuntime([m_dev runtime]);
235 
236  return m_dev_runtime.getValue();
237 }
238 
241  const CoreSimulatorSupport::OSVersion &rhs) {
242  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
243  unsigned int l = lhs.GetVersionAtIndex(i);
244  unsigned int r = rhs.GetVersionAtIndex(i);
245  if (l > r)
246  return true;
247  }
248  return false;
249 }
250 
254  if (lhs.GetFamily() != rhs.GetFamily())
255  return false;
256  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
257  unsigned int l = lhs.GetVersionAtIndex(i);
258  unsigned int r = rhs.GetVersionAtIndex(i);
259  if (l > r)
260  return true;
261  }
262  return false;
263 }
264 
267  const CoreSimulatorSupport::OSVersion &rhs) {
268  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
269  unsigned int l = lhs.GetVersionAtIndex(i);
270  unsigned int r = rhs.GetVersionAtIndex(i);
271  if (l < r)
272  return true;
273  }
274  return false;
275 }
276 
280  if (lhs.GetFamily() != rhs.GetFamily())
281  return false;
282 
283  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
284  unsigned int l = lhs.GetVersionAtIndex(i);
285  unsigned int r = rhs.GetVersionAtIndex(i);
286  if (l < r)
287  return true;
288  }
289  return false;
290 }
291 
294  const CoreSimulatorSupport::OSVersion &rhs) {
295  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
296  unsigned int l = lhs.GetVersionAtIndex(i);
297  unsigned int r = rhs.GetVersionAtIndex(i);
298  if (l != r)
299  return false;
300  }
301  return true;
302 }
303 
307  if (lhs.GetFamily() != rhs.GetFamily())
308  return false;
309 
310  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
311  unsigned int l = lhs.GetVersionAtIndex(i);
312  unsigned int r = rhs.GetVersionAtIndex(i);
313  if (l != r)
314  return false;
315  }
316  return true;
317 }
318 
321  const CoreSimulatorSupport::OSVersion &rhs) {
322  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
323  unsigned int l = lhs.GetVersionAtIndex(i);
324  unsigned int r = rhs.GetVersionAtIndex(i);
325  if (l != r)
326  return true;
327  }
328  return false;
329 }
330 
334  if (lhs.GetFamily() != rhs.GetFamily())
335  return false;
336 
337  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
338  unsigned int l = lhs.GetVersionAtIndex(i);
339  unsigned int r = rhs.GetVersionAtIndex(i);
340  if (l != r)
341  return true;
342  }
343  return false;
344 }
345 
347  if (m_dev == nil) {
348  err.SetErrorString("no valid simulator instance");
349  return false;
350  }
351 
352 #define kSimDeviceBootPersist \
353  @"persist" /* An NSNumber (boolean) indicating whether or not the session \
354  should outlive the calling process (default false) */
355 
356  NSDictionary *options = @{
357  kSimDeviceBootPersist : @NO,
358  };
359 
360 #undef kSimDeviceBootPersist
361 
362  NSError *nserror;
363  if ([m_dev bootWithOptions:options error:&nserror]) {
364  err.Clear();
365  return true;
366  } else {
367  err.SetErrorString([[nserror description] UTF8String]);
368  return false;
369  }
370 }
371 
373  NSError *nserror;
374  if ([m_dev shutdownWithError:&nserror]) {
375  err.Clear();
376  return true;
377  } else {
378  err.SetErrorString([[nserror description] UTF8String]);
379  return false;
380  }
381 }
382 
384  NSMutableDictionary *options, NSString *key,
385  const int fd, File &file) {
386  Status error;
387  const FileAction *file_action = launch_info.GetFileActionForFD(fd);
388  if (file_action) {
389  switch (file_action->GetAction()) {
390  case FileAction::eFileActionNone:
391  break;
392 
393  case FileAction::eFileActionClose:
394  error.SetErrorStringWithFormat("close file action for %i not supported",
395  fd);
396  break;
397 
398  case FileAction::eFileActionDuplicate:
400  "duplication file action for %i not supported", fd);
401  break;
402 
403  case FileAction::eFileActionOpen: {
404  FileSpec file_spec = file_action->GetFileSpec();
405  if (file_spec) {
406  const int master_fd = launch_info.GetPTY().GetMasterFileDescriptor();
407  if (master_fd != PseudoTerminal::invalid_fd) {
408  // Check in case our file action open wants to open the slave
409  const char *slave_path = launch_info.GetPTY().GetSlaveName(NULL, 0);
410  if (slave_path) {
411  FileSpec slave_spec(slave_path);
412  if (file_spec == slave_spec) {
413  int slave_fd = launch_info.GetPTY().GetSlaveFileDescriptor();
414  if (slave_fd == PseudoTerminal::invalid_fd)
415  slave_fd = launch_info.GetPTY().OpenSlave(O_RDWR, nullptr, 0);
416  if (slave_fd == PseudoTerminal::invalid_fd) {
417  error.SetErrorStringWithFormat("unable to open slave pty '%s'",
418  slave_path);
419  return error; // Failure
420  }
421  [options setValue:[NSNumber numberWithInteger:slave_fd]
422  forKey:key];
423  return error; // Success
424  }
425  }
426  }
427  Status posix_error;
428  int created_fd =
429  open(file_spec.GetPath().c_str(), file_action->GetActionArgument(),
430  S_IRUSR | S_IWUSR);
431  if (created_fd >= 0) {
432  file.SetDescriptor(created_fd, true);
433  [options setValue:[NSNumber numberWithInteger:created_fd] forKey:key];
434  return error; // Success
435  } else {
436  posix_error.SetErrorToErrno();
437  error.SetErrorStringWithFormat("unable to open file '%s': %s",
438  file_spec.GetPath().c_str(),
439  posix_error.AsCString());
440  }
441  }
442  } break;
443  }
444  }
445  return error; // Success, no file action, nothing to do
446 }
447 
450 #define kSimDeviceSpawnEnvironment \
451  @"environment" /* An NSDictionary (NSStrings -> NSStrings) of environment \
452  key/values */
453 #define kSimDeviceSpawnStdin @"stdin" /* An NSNumber corresponding to a fd */
454 #define kSimDeviceSpawnStdout @"stdout" /* An NSNumber corresponding to a fd \
455  */
456 #define kSimDeviceSpawnStderr @"stderr" /* An NSNumber corresponding to a fd \
457  */
458 #define kSimDeviceSpawnArguments \
459  @"arguments" /* An NSArray of strings to use as the argv array. If not \
460  provided, path will be argv[0] */
461 #define kSimDeviceSpawnWaitForDebugger \
462  @"wait_for_debugger" /* An NSNumber (bool) */
463 
464  NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
465 
466  if (launch_info.GetFlags().Test(lldb::eLaunchFlagDebug))
467  [options setObject:@YES forKey:kSimDeviceSpawnWaitForDebugger];
468 
469  if (launch_info.GetArguments().GetArgumentCount()) {
470  const Args &args(launch_info.GetArguments());
471  NSMutableArray *args_array = [[NSMutableArray alloc] init];
472  for (size_t idx = 0; idx < args.GetArgumentCount(); idx++)
473  [args_array
474  addObject:[NSString
475  stringWithUTF8String:args.GetArgumentAtIndex(idx)]];
476 
477  [options setObject:args_array forKey:kSimDeviceSpawnArguments];
478  }
479 
480  NSMutableDictionary *env_dict = [[NSMutableDictionary alloc] init];
481 
482  for (const auto &KV : launch_info.GetEnvironment()) {
483  NSString *key_ns = [NSString stringWithUTF8String:KV.first().str().c_str()];
484  NSString *value_ns = [NSString stringWithUTF8String:KV.second.c_str()];
485 
486  [env_dict setValue:value_ns forKey:key_ns];
487  }
488 
489  [options setObject:env_dict forKey:kSimDeviceSpawnEnvironment];
490 
491  Status error;
492  File stdin_file;
493  File stdout_file;
494  File stderr_file;
495  error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdin,
496  STDIN_FILENO, stdin_file);
497 
498  if (error.Fail())
499  return CoreSimulatorSupport::Process(error);
500 
501  error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdout,
502  STDOUT_FILENO, stdout_file);
503 
504  if (error.Fail())
505  return CoreSimulatorSupport::Process(error);
506 
507  error = HandleFileAction(launch_info, options, kSimDeviceSpawnStderr,
508  STDERR_FILENO, stderr_file);
509 
510  if (error.Fail())
511  return CoreSimulatorSupport::Process(error);
512 
513 #undef kSimDeviceSpawnEnvironment
514 #undef kSimDeviceSpawnStdin
515 #undef kSimDeviceSpawnStdout
516 #undef kSimDeviceSpawnStderr
517 #undef kSimDeviceSpawnWaitForDebugger
518 #undef kSimDeviceSpawnArguments
519 
520  NSError *nserror;
521 
522  pid_t pid = [m_dev
523  spawnWithPath:[NSString stringWithUTF8String:launch_info
524  .GetExecutableFile()
525  .GetPath()
526  .c_str()]
527  options:options
528  terminationHandler:nil
529  error:&nserror];
530 
531  if (pid < 0) {
532  const char *nserror_string = [[nserror description] UTF8String];
533  error.SetErrorString(nserror_string ? nserror_string : "unable to launch");
534  }
535 
536  return CoreSimulatorSupport::Process(pid, error);
537 }
538 
541  if (!developer_dir || !developer_dir[0])
542  return DeviceSet([NSArray new]);
543 
544  Class SimServiceContextClass = NSClassFromString(@"SimServiceContext");
545  NSString *dev_dir = @(developer_dir);
546  NSError *error = nil;
547 
548  id serviceContext =
549  [SimServiceContextClass sharedServiceContextForDeveloperDir:dev_dir
550  error:&error];
551  if (!serviceContext)
552  return DeviceSet([NSArray new]);
553 
554  return DeviceSet([[serviceContext defaultDeviceSetWithError:&error] devices]);
555 }
556 
559  const char *developer_dir) {
560  return GetAllDevices(developer_dir).GetDevicesIf([](Device d) -> bool {
561  return (d && d.GetDeviceType() && d.GetDeviceRuntime() &&
563  });
564 }
565 
567  return [m_dev count];
568 }
569 
572  if (idx < GetNumDevices())
573  return Device([m_dev objectAtIndex:idx]);
574  return Device();
575 }
576 
578  std::function<bool(CoreSimulatorSupport::Device)> f) {
579  NSMutableArray *array = [[NSMutableArray alloc] init];
580  for (NSUInteger i = 0; i < GetNumDevices(); i++) {
581  Device d(GetDeviceAtIndex(i));
582  if (f(d))
583  [array addObject:(id)d.m_dev];
584  }
585 
586  return DeviceSet(array);
587 }
588 
590  std::function<bool(const Device &)> f) {
591  const size_t n = GetNumDevices();
592  for (NSUInteger i = 0; i < n; ++i) {
593  if (!f(GetDeviceAtIndex(i)))
594  break;
595  }
596 }
597 
600  NSMutableArray *array = [[NSMutableArray alloc] init];
601  const size_t n = GetNumDevices();
602  for (NSUInteger i = 0; i < n; ++i) {
603  Device d(GetDeviceAtIndex(i));
604  if (d && d.GetDeviceType() &&
605  d.GetDeviceType().GetProductFamilyID() == dev_id)
606  [array addObject:(id)d.m_dev];
607  }
608 
609  return DeviceSet(array);
610 }
611 
614  Device dev;
615 
616  for (NSUInteger i = 0; i < GetNumDevices(); i++) {
617  Device d(GetDeviceAtIndex(i));
618  if (d && d.GetDeviceType() &&
619  d.GetDeviceType().GetProductFamilyID() == dev_id) {
620  if (!dev)
621  dev = d;
622  else {
623  if ((d.GetDeviceType().GetModelIdentifier() >
627  dev = d;
628  }
629  }
630  }
631 
632  return dev;
633 }
#define kSimDeviceBootPersist
A command line argument class.
Definition: Args.h:32
static DeviceSet GetAllDevices(const char *developer_dir)
Enumerations for broadcasting.
Definition: SBLaunchInfo.h:14
#define kSimDeviceSpawnStdin
#define LLDB_INVALID_PROCESS_ID
Definition: lldb-defines.h:92
bool operator<(const OSVersion &lhs, const OSVersion &rhs)
Process Spawn(lldb_private::ProcessLaunchInfo &launch_info)
bool OpenSlave(int oflag, char *error_str, size_t error_len)
Open the slave for the current master pseudo terminal.
DeviceSet GetDevices(DeviceType::ProductFamilyID dev_id)
#define kSimDeviceSpawnStdout
const char * GetSlaveName(char *error_str, size_t error_len) const
Get the name of the slave pseudo terminal.
size_t GetArgumentCount() const
Gets the number of arguments left in this command object.
Definition: Args.cpp:254
A file utility class.
Definition: FileSpec.h:55
bool operator==(const OSVersion &lhs, const OSVersion &rhs)
Action GetAction() const
Definition: FileAction.h:38
A file class.
Definition: File.h:29
void SetErrorToErrno()
Set the current error to errno.
Definition: Status.cpp:223
bool operator!=(const OSVersion &lhs, const OSVersion &rhs)
static DeviceSet GetAvailableDevices(const char *developer_dir)
int GetMasterFileDescriptor() const
The master file descriptor accessor.
bool Test(ValueType bit) const
Test a single flag bit.
Definition: Flags.h:107
void Clear()
Clear the object state.
Definition: Status.cpp:167
void SetErrorString(llvm::StringRef err_str)
Set the current error string to err_str.
Definition: Status.cpp:241
Environment & GetEnvironment()
Definition: ProcessInfo.h:88
Device GetFanciest(DeviceType::ProductFamilyID dev_id)
const FileAction * GetFileActionForFD(int fd) const
int GetSlaveFileDescriptor() const
The slave file descriptor accessor.
#define kSimDeviceSpawnStderr
const FileSpec & GetFileSpec() const
Definition: FileAction.cpp:31
static Status HandleFileAction(ProcessLaunchInfo &launch_info, NSMutableDictionary *options, NSString *key, const int fd, File &file)
A uniqued constant string class.
Definition: ConstString.h:38
bool Fail() const
Test for error condition.
Definition: Status.cpp:181
uint64_t pid_t
Definition: lldb-types.h:85
#define kSimDeviceSpawnWaitForDebugger
void SetDescriptor(int fd, bool transfer_ownership)
Definition: File.cpp:96
int SetErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Set the current error string to a formatted error string.
Definition: Status.cpp:255
const char * AsCString(const char *default_error_str="unknown error") const
Get the error string associated with the current error.
Definition: Status.cpp:130
int GetActionArgument() const
Definition: FileAction.h:40
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:376
bool operator>(const OSVersion &lhs, const OSVersion &rhs)
An error handling class.
Definition: Status.h:44
DeviceSet GetDevicesIf(std::function< bool(Device)> f)
void ForEach(std::function< bool(const Device &)> f)