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() : m_model_identifier() {}
70
71CoreSimulatorSupport::DeviceType::DeviceType(id d)
72    : m_dev(d), m_model_identifier() {}
73
74CoreSimulatorSupport::DeviceType::operator bool() { return m_dev != nil; }
75
76ConstString CoreSimulatorSupport::DeviceType::GetIdentifier() {
77  return ConstString([[m_dev identifier] UTF8String]);
78}
79
80ConstString CoreSimulatorSupport::DeviceType::GetProductFamily() {
81  return ConstString([[m_dev productFamily] UTF8String]);
82}
83
84CoreSimulatorSupport::DeviceType::ProductFamilyID
85CoreSimulatorSupport::DeviceType::GetProductFamilyID() {
86  return ProductFamilyID([m_dev productFamilyID]);
87}
88
89CoreSimulatorSupport::DeviceRuntime::DeviceRuntime() : m_os_version() {}
90
91CoreSimulatorSupport::DeviceRuntime::DeviceRuntime(id d)
92    : m_dev(d), m_os_version() {}
93
94CoreSimulatorSupport::DeviceRuntime::operator bool() { return m_dev != nil; }
95
96bool CoreSimulatorSupport::DeviceRuntime::IsAvailable() {
97  return [m_dev available];
98}
99
100CoreSimulatorSupport::Device::Device() : m_dev_type(), m_dev_runtime() {}
101
102CoreSimulatorSupport::Device::Device(id d)
103    : m_dev(d), m_dev_type(), m_dev_runtime() {}
104
105CoreSimulatorSupport::Device::operator bool() { return m_dev != nil; }
106
107CoreSimulatorSupport::Device::State CoreSimulatorSupport::Device::GetState() {
108  return (State)([m_dev state]);
109}
110
111CoreSimulatorSupport::ModelIdentifier::ModelIdentifier(const std::string &mi)
112    : m_family(), m_versions() {
113  bool first_digit = false;
114  unsigned int val = 0;
115
116  for (char c : mi) {
117    if (::isdigit(c)) {
118      if (!first_digit)
119        first_digit = true;
120      val = 10 * val + (c - '0');
121    } else if (c == ',') {
122      if (first_digit) {
123        m_versions.push_back(val);
124        val = 0;
125      } else
126        m_family.push_back(c);
127    } else {
128      if (first_digit) {
129        m_family.clear();
130        m_versions.clear();
131        return;
132      } else {
133        m_family.push_back(c);
134      }
135    }
136  }
137
138  if (first_digit)
139    m_versions.push_back(val);
140}
141
142CoreSimulatorSupport::ModelIdentifier::ModelIdentifier()
143    : ModelIdentifier("") {}
144
145CoreSimulatorSupport::OSVersion::OSVersion(const std::string &ver,
146                                           const std::string &build)
147    : m_versions(), m_build(build) {
148  bool any = false;
149  unsigned int val = 0;
150  for (char c : ver) {
151    if (c == '.') {
152      m_versions.push_back(val);
153      val = 0;
154    } else if (::isdigit(c)) {
155      val = 10 * val + (c - '0');
156      any = true;
157    } else {
158      m_versions.clear();
159      return;
160    }
161  }
162  if (any)
163    m_versions.push_back(val);
164}
165
166CoreSimulatorSupport::OSVersion::OSVersion() : OSVersion("", "") {}
167
168CoreSimulatorSupport::ModelIdentifier
169CoreSimulatorSupport::DeviceType::GetModelIdentifier() {
170  if (!m_model_identifier.has_value()) {
171    auto utf8_model_id = [[m_dev modelIdentifier] UTF8String];
172    if (utf8_model_id && *utf8_model_id)
173      m_model_identifier = ModelIdentifier(utf8_model_id);
174  }
175
176  if (m_model_identifier.has_value())
177    return m_model_identifier.value();
178  else
179    return ModelIdentifier();
180}
181
182CoreSimulatorSupport::OSVersion
183CoreSimulatorSupport::DeviceRuntime::GetVersion() {
184  if (!m_os_version.has_value()) {
185    auto utf8_ver_string = [[m_dev versionString] UTF8String];
186    auto utf8_build_ver = [[m_dev buildVersionString] UTF8String];
187    if (utf8_ver_string && *utf8_ver_string && utf8_build_ver &&
188        *utf8_build_ver) {
189      m_os_version = OSVersion(utf8_ver_string, utf8_build_ver);
190    }
191  }
192
193  if (m_os_version.has_value())
194    return m_os_version.value();
195  return OSVersion();
196}
197
198std::string CoreSimulatorSupport::DeviceType::GetName() {
199  auto utf8_name = [[m_dev name] UTF8String];
200  if (utf8_name)
201    return std::string(utf8_name);
202  return "";
203}
204
205std::string CoreSimulatorSupport::Device::GetName() const {
206  auto utf8_name = [[m_dev name] UTF8String];
207  if (utf8_name)
208    return std::string(utf8_name);
209  return "";
210}
211
212std::string CoreSimulatorSupport::Device::GetUDID() const {
213  auto utf8_udid = [[[m_dev UDID] UUIDString] UTF8String];
214  if (utf8_udid)
215    return std::string(utf8_udid);
216  else
217    return std::string();
218}
219
220CoreSimulatorSupport::DeviceType CoreSimulatorSupport::Device::GetDeviceType() {
221  if (!m_dev_type.has_value())
222    m_dev_type = DeviceType([m_dev deviceType]);
223
224  return m_dev_type.value();
225}
226
227CoreSimulatorSupport::DeviceRuntime
228CoreSimulatorSupport::Device::GetDeviceRuntime() {
229  if (!m_dev_runtime.has_value())
230    m_dev_runtime = DeviceRuntime([m_dev runtime]);
231
232  return m_dev_runtime.value();
233}
234
235bool CoreSimulatorSupport::
236operator>(const CoreSimulatorSupport::OSVersion &lhs,
237          const CoreSimulatorSupport::OSVersion &rhs) {
238  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
239    unsigned int l = lhs.GetVersionAtIndex(i);
240    unsigned int r = rhs.GetVersionAtIndex(i);
241    if (l > r)
242      return true;
243  }
244  return false;
245}
246
247bool CoreSimulatorSupport::
248operator>(const CoreSimulatorSupport::ModelIdentifier &lhs,
249          const CoreSimulatorSupport::ModelIdentifier &rhs) {
250  if (lhs.GetFamily() != rhs.GetFamily())
251    return false;
252  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
253    unsigned int l = lhs.GetVersionAtIndex(i);
254    unsigned int r = rhs.GetVersionAtIndex(i);
255    if (l > r)
256      return true;
257  }
258  return false;
259}
260
261bool CoreSimulatorSupport::
262operator<(const CoreSimulatorSupport::OSVersion &lhs,
263          const CoreSimulatorSupport::OSVersion &rhs) {
264  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
265    unsigned int l = lhs.GetVersionAtIndex(i);
266    unsigned int r = rhs.GetVersionAtIndex(i);
267    if (l < r)
268      return true;
269  }
270  return false;
271}
272
273bool CoreSimulatorSupport::
274operator<(const CoreSimulatorSupport::ModelIdentifier &lhs,
275          const CoreSimulatorSupport::ModelIdentifier &rhs) {
276  if (lhs.GetFamily() != rhs.GetFamily())
277    return false;
278
279  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
280    unsigned int l = lhs.GetVersionAtIndex(i);
281    unsigned int r = rhs.GetVersionAtIndex(i);
282    if (l < r)
283      return true;
284  }
285  return false;
286}
287
288bool CoreSimulatorSupport::
289operator==(const CoreSimulatorSupport::OSVersion &lhs,
290           const CoreSimulatorSupport::OSVersion &rhs) {
291  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
292    unsigned int l = lhs.GetVersionAtIndex(i);
293    unsigned int r = rhs.GetVersionAtIndex(i);
294    if (l != r)
295      return false;
296  }
297  return true;
298}
299
300bool CoreSimulatorSupport::
301operator==(const CoreSimulatorSupport::ModelIdentifier &lhs,
302           const CoreSimulatorSupport::ModelIdentifier &rhs) {
303  if (lhs.GetFamily() != rhs.GetFamily())
304    return false;
305
306  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
307    unsigned int l = lhs.GetVersionAtIndex(i);
308    unsigned int r = rhs.GetVersionAtIndex(i);
309    if (l != r)
310      return false;
311  }
312  return true;
313}
314
315bool CoreSimulatorSupport::
316operator!=(const CoreSimulatorSupport::OSVersion &lhs,
317           const CoreSimulatorSupport::OSVersion &rhs) {
318  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
319    unsigned int l = lhs.GetVersionAtIndex(i);
320    unsigned int r = rhs.GetVersionAtIndex(i);
321    if (l != r)
322      return true;
323  }
324  return false;
325}
326
327bool CoreSimulatorSupport::
328operator!=(const CoreSimulatorSupport::ModelIdentifier &lhs,
329           const CoreSimulatorSupport::ModelIdentifier &rhs) {
330  if (lhs.GetFamily() != rhs.GetFamily())
331    return false;
332
333  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
334    unsigned int l = lhs.GetVersionAtIndex(i);
335    unsigned int r = rhs.GetVersionAtIndex(i);
336    if (l != r)
337      return true;
338  }
339  return false;
340}
341
342bool CoreSimulatorSupport::Device::Boot(Status &err) {
343  if (m_dev == nil) {
344    err.SetErrorString("no valid simulator instance");
345    return false;
346  }
347
348#define kSimDeviceBootPersist                                                  \
349  @"persist" /* An NSNumber (boolean) indicating whether or not the session    \
350                should outlive the calling process (default false) */
351
352  NSDictionary *options = @{
353    kSimDeviceBootPersist : @NO,
354  };
355
356#undef kSimDeviceBootPersist
357
358  NSError *nserror;
359  if ([m_dev bootWithOptions:options error:&nserror]) {
360    err.Clear();
361    return true;
362  } else {
363    err.SetErrorString([[nserror description] UTF8String]);
364    return false;
365  }
366}
367
368bool CoreSimulatorSupport::Device::Shutdown(Status &err) {
369  NSError *nserror;
370  if ([m_dev shutdownWithError:&nserror]) {
371    err.Clear();
372    return true;
373  } else {
374    err.SetErrorString([[nserror description] UTF8String]);
375    return false;
376  }
377}
378
379static Status HandleFileAction(ProcessLaunchInfo &launch_info,
380                               NSMutableDictionary *options, NSString *key,
381                               const int fd, lldb::FileSP &file) {
382  Status error;
383  const FileAction *file_action = launch_info.GetFileActionForFD(fd);
384  if (file_action) {
385    switch (file_action->GetAction()) {
386    case FileAction::eFileActionNone:
387      break;
388
389    case FileAction::eFileActionClose:
390      error.SetErrorStringWithFormat("close file action for %i not supported",
391                                     fd);
392      break;
393
394    case FileAction::eFileActionDuplicate:
395      error.SetErrorStringWithFormat(
396          "duplication file action for %i not supported", fd);
397      break;
398
399    case FileAction::eFileActionOpen: {
400      FileSpec file_spec = file_action->GetFileSpec();
401      if (file_spec) {
402        const int primary_fd = launch_info.GetPTY().GetPrimaryFileDescriptor();
403        if (primary_fd != PseudoTerminal::invalid_fd) {
404          // Check in case our file action open wants to open the secondary
405          FileSpec secondary_spec(launch_info.GetPTY().GetSecondaryName());
406          if (file_spec == secondary_spec) {
407            int secondary_fd =
408                launch_info.GetPTY().GetSecondaryFileDescriptor();
409            if (secondary_fd == PseudoTerminal::invalid_fd) {
410              if (llvm::Error Err = launch_info.GetPTY().OpenSecondary(O_RDWR))
411                return Status(std::move(Err));
412            }
413            secondary_fd = launch_info.GetPTY().GetSecondaryFileDescriptor();
414            assert(secondary_fd != PseudoTerminal::invalid_fd);
415            [options setValue:[NSNumber numberWithInteger:secondary_fd]
416                       forKey:key];
417            return error; // Success
418          }
419        }
420        Status posix_error;
421        int oflag = file_action->GetActionArgument();
422        int created_fd =
423            open(file_spec.GetPath().c_str(), oflag, S_IRUSR | S_IWUSR);
424        if (created_fd >= 0) {
425          auto file_options = File::OpenOptions(0);
426          if (oflag & O_RDWR)
427            file_options |= File::eOpenOptionReadWrite;
428          else if (oflag & O_WRONLY)
429            file_options |= File::eOpenOptionWriteOnly;
430          else if (oflag & O_RDONLY)
431            file_options |= File::eOpenOptionReadOnly;
432          file = std::make_shared<NativeFile>(created_fd, file_options, 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
448CoreSimulatorSupport::Process
449CoreSimulatorSupport::Device::Spawn(ProcessLaunchInfo &launch_info) {
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#define kSimDeviceSpawnStandalone @"standalone"
464
465  NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
466
467  options[kSimDeviceSpawnStandalone] = @(YES);
468
469  if (launch_info.GetFlags().Test(lldb::eLaunchFlagDebug))
470    [options setObject:@YES forKey:kSimDeviceSpawnWaitForDebugger];
471
472  if (launch_info.GetArguments().GetArgumentCount()) {
473    const Args &args(launch_info.GetArguments());
474    NSMutableArray *args_array = [[NSMutableArray alloc] init];
475    for (size_t idx = 0; idx < args.GetArgumentCount(); idx++)
476      [args_array
477          addObject:[NSString
478                        stringWithUTF8String:args.GetArgumentAtIndex(idx)]];
479
480    [options setObject:args_array forKey:kSimDeviceSpawnArguments];
481  }
482
483  NSMutableDictionary *env_dict = [[NSMutableDictionary alloc] init];
484
485  for (const auto &KV : launch_info.GetEnvironment()) {
486    NSString *key_ns = [NSString stringWithUTF8String:KV.first().str().c_str()];
487    NSString *value_ns = [NSString stringWithUTF8String:KV.second.c_str()];
488
489    [env_dict setValue:value_ns forKey:key_ns];
490  }
491
492  [options setObject:env_dict forKey:kSimDeviceSpawnEnvironment];
493
494  Status error;
495  lldb::FileSP stdin_file;
496  lldb::FileSP stdout_file;
497  lldb::FileSP stderr_file;
498  error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdin,
499                           STDIN_FILENO, stdin_file);
500
501  if (error.Fail())
502    return CoreSimulatorSupport::Process(error);
503
504  error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdout,
505                           STDOUT_FILENO, stdout_file);
506
507  if (error.Fail())
508    return CoreSimulatorSupport::Process(error);
509
510  error = HandleFileAction(launch_info, options, kSimDeviceSpawnStderr,
511                           STDERR_FILENO, stderr_file);
512
513  if (error.Fail())
514    return CoreSimulatorSupport::Process(error);
515
516#undef kSimDeviceSpawnEnvironment
517#undef kSimDeviceSpawnStdin
518#undef kSimDeviceSpawnStdout
519#undef kSimDeviceSpawnStderr
520#undef kSimDeviceSpawnWaitForDebugger
521#undef kSimDeviceSpawnArguments
522
523  NSError *nserror;
524
525  pid_t pid;
526  BOOL success = [m_dev
527           spawnWithPath:[NSString stringWithUTF8String:launch_info
528                                                            .GetExecutableFile()
529                                                            .GetPath()
530                                                            .c_str()]
531                 options:options
532        terminationQueue:nil
533      terminationHandler:nil
534                     pid:&pid
535                   error:&nserror];
536
537  if (!success) {
538    const char *nserror_string = [[nserror description] UTF8String];
539    error.SetErrorString(nserror_string ? nserror_string : "unable to launch");
540  }
541
542  return CoreSimulatorSupport::Process(pid, error);
543}
544
545CoreSimulatorSupport::DeviceSet
546CoreSimulatorSupport::DeviceSet::GetAllDevices(const char *developer_dir) {
547  if (!developer_dir || !developer_dir[0])
548    return DeviceSet([NSArray new]);
549
550  Class SimServiceContextClass = NSClassFromString(@"SimServiceContext");
551  NSString *dev_dir = @(developer_dir);
552  NSError *error = nil;
553
554  id serviceContext =
555      [SimServiceContextClass sharedServiceContextForDeveloperDir:dev_dir
556                                                            error:&error];
557  if (!serviceContext)
558    return DeviceSet([NSArray new]);
559
560  return DeviceSet([[serviceContext defaultDeviceSetWithError:&error] devices]);
561}
562
563CoreSimulatorSupport::DeviceSet
564CoreSimulatorSupport::DeviceSet::GetAvailableDevices(
565    const char *developer_dir) {
566  return GetAllDevices(developer_dir).GetDevicesIf([](Device d) -> bool {
567    return (d && d.GetDeviceType() && d.GetDeviceRuntime() &&
568            d.GetDeviceRuntime().IsAvailable());
569  });
570}
571
572size_t CoreSimulatorSupport::DeviceSet::GetNumDevices() {
573  return [m_dev count];
574}
575
576CoreSimulatorSupport::Device
577CoreSimulatorSupport::DeviceSet::GetDeviceAtIndex(size_t idx) {
578  if (idx < GetNumDevices())
579    return Device([m_dev objectAtIndex:idx]);
580  return Device();
581}
582
583CoreSimulatorSupport::DeviceSet CoreSimulatorSupport::DeviceSet::GetDevicesIf(
584    std::function<bool(CoreSimulatorSupport::Device)> f) {
585  NSMutableArray *array = [[NSMutableArray alloc] init];
586  for (NSUInteger i = 0; i < GetNumDevices(); i++) {
587    Device d(GetDeviceAtIndex(i));
588    if (f(d))
589      [array addObject:(id)d.m_dev];
590  }
591
592  return DeviceSet(array);
593}
594
595void CoreSimulatorSupport::DeviceSet::ForEach(
596    std::function<bool(const Device &)> f) {
597  const size_t n = GetNumDevices();
598  for (NSUInteger i = 0; i < n; ++i) {
599    if (!f(GetDeviceAtIndex(i)))
600      break;
601  }
602}
603
604CoreSimulatorSupport::DeviceSet CoreSimulatorSupport::DeviceSet::GetDevices(
605    CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) {
606  NSMutableArray *array = [[NSMutableArray alloc] init];
607  const size_t n = GetNumDevices();
608  for (NSUInteger i = 0; i < n; ++i) {
609    Device d(GetDeviceAtIndex(i));
610    if (d && d.GetDeviceType() &&
611        d.GetDeviceType().GetProductFamilyID() == dev_id)
612      [array addObject:(id)d.m_dev];
613  }
614
615  return DeviceSet(array);
616}
617
618CoreSimulatorSupport::Device CoreSimulatorSupport::DeviceSet::GetFanciest(
619    CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) {
620  Device dev;
621
622  for (NSUInteger i = 0; i < GetNumDevices(); i++) {
623    Device d(GetDeviceAtIndex(i));
624    if (d && d.GetDeviceType() &&
625        d.GetDeviceType().GetProductFamilyID() == dev_id) {
626      if (!dev)
627        dev = d;
628      else {
629        if ((d.GetDeviceType().GetModelIdentifier() >
630             dev.GetDeviceType().GetModelIdentifier()) ||
631            d.GetDeviceRuntime().GetVersion() >
632                dev.GetDeviceRuntime().GetVersion())
633          dev = d;
634      }
635    }
636  }
637
638  return dev;
639}
640