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