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 any = false;
114  bool first_digit = false;
115  unsigned int val = 0;
116
117  for (char c : mi) {
118    any = true;
119    if (::isdigit(c)) {
120      if (!first_digit)
121        first_digit = true;
122      val = 10 * val + (c - '0');
123    } else if (c == ',') {
124      if (first_digit) {
125        m_versions.push_back(val);
126        val = 0;
127      } else
128        m_family.push_back(c);
129    } else {
130      if (first_digit) {
131        m_family.clear();
132        m_versions.clear();
133        return;
134      } else {
135        m_family.push_back(c);
136      }
137    }
138  }
139
140  if (first_digit)
141    m_versions.push_back(val);
142}
143
144CoreSimulatorSupport::ModelIdentifier::ModelIdentifier()
145    : ModelIdentifier("") {}
146
147CoreSimulatorSupport::OSVersion::OSVersion(const std::string &ver,
148                                           const std::string &build)
149    : m_versions(), m_build(build) {
150  bool any = false;
151  unsigned int val = 0;
152  for (char c : ver) {
153    if (c == '.') {
154      m_versions.push_back(val);
155      val = 0;
156    } else if (::isdigit(c)) {
157      val = 10 * val + (c - '0');
158      any = true;
159    } else {
160      m_versions.clear();
161      return;
162    }
163  }
164  if (any)
165    m_versions.push_back(val);
166}
167
168CoreSimulatorSupport::OSVersion::OSVersion() : OSVersion("", "") {}
169
170CoreSimulatorSupport::ModelIdentifier
171CoreSimulatorSupport::DeviceType::GetModelIdentifier() {
172  if (!m_model_identifier.hasValue()) {
173    auto utf8_model_id = [[m_dev modelIdentifier] UTF8String];
174    if (utf8_model_id && *utf8_model_id)
175      m_model_identifier = ModelIdentifier(utf8_model_id);
176  }
177
178  if (m_model_identifier.hasValue())
179    return m_model_identifier.getValue();
180  else
181    return ModelIdentifier();
182}
183
184CoreSimulatorSupport::OSVersion
185CoreSimulatorSupport::DeviceRuntime::GetVersion() {
186  if (!m_os_version.hasValue()) {
187    auto utf8_ver_string = [[m_dev versionString] UTF8String];
188    auto utf8_build_ver = [[m_dev buildVersionString] UTF8String];
189    if (utf8_ver_string && *utf8_ver_string && utf8_build_ver &&
190        *utf8_build_ver) {
191      m_os_version = OSVersion(utf8_ver_string, utf8_build_ver);
192    }
193  }
194
195  if (m_os_version.hasValue())
196    return m_os_version.getValue();
197  return OSVersion();
198}
199
200std::string CoreSimulatorSupport::DeviceType::GetName() {
201  auto utf8_name = [[m_dev name] UTF8String];
202  if (utf8_name)
203    return std::string(utf8_name);
204  return "";
205}
206
207std::string CoreSimulatorSupport::Device::GetName() const {
208  auto utf8_name = [[m_dev name] UTF8String];
209  if (utf8_name)
210    return std::string(utf8_name);
211  return "";
212}
213
214std::string CoreSimulatorSupport::Device::GetUDID() const {
215  auto utf8_udid = [[[m_dev UDID] UUIDString] UTF8String];
216  if (utf8_udid)
217    return std::string(utf8_udid);
218  else
219    return std::string();
220}
221
222CoreSimulatorSupport::DeviceType CoreSimulatorSupport::Device::GetDeviceType() {
223  if (!m_dev_type.hasValue())
224    m_dev_type = DeviceType([m_dev deviceType]);
225
226  return m_dev_type.getValue();
227}
228
229CoreSimulatorSupport::DeviceRuntime
230CoreSimulatorSupport::Device::GetDeviceRuntime() {
231  if (!m_dev_runtime.hasValue())
232    m_dev_runtime = DeviceRuntime([m_dev runtime]);
233
234  return m_dev_runtime.getValue();
235}
236
237bool CoreSimulatorSupport::
238operator>(const CoreSimulatorSupport::OSVersion &lhs,
239          const CoreSimulatorSupport::OSVersion &rhs) {
240  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
241    unsigned int l = lhs.GetVersionAtIndex(i);
242    unsigned int r = rhs.GetVersionAtIndex(i);
243    if (l > r)
244      return true;
245  }
246  return false;
247}
248
249bool CoreSimulatorSupport::
250operator>(const CoreSimulatorSupport::ModelIdentifier &lhs,
251          const CoreSimulatorSupport::ModelIdentifier &rhs) {
252  if (lhs.GetFamily() != rhs.GetFamily())
253    return false;
254  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
255    unsigned int l = lhs.GetVersionAtIndex(i);
256    unsigned int r = rhs.GetVersionAtIndex(i);
257    if (l > r)
258      return true;
259  }
260  return false;
261}
262
263bool CoreSimulatorSupport::
264operator<(const CoreSimulatorSupport::OSVersion &lhs,
265          const CoreSimulatorSupport::OSVersion &rhs) {
266  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
267    unsigned int l = lhs.GetVersionAtIndex(i);
268    unsigned int r = rhs.GetVersionAtIndex(i);
269    if (l < r)
270      return true;
271  }
272  return false;
273}
274
275bool CoreSimulatorSupport::
276operator<(const CoreSimulatorSupport::ModelIdentifier &lhs,
277          const CoreSimulatorSupport::ModelIdentifier &rhs) {
278  if (lhs.GetFamily() != rhs.GetFamily())
279    return false;
280
281  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
282    unsigned int l = lhs.GetVersionAtIndex(i);
283    unsigned int r = rhs.GetVersionAtIndex(i);
284    if (l < r)
285      return true;
286  }
287  return false;
288}
289
290bool CoreSimulatorSupport::
291operator==(const CoreSimulatorSupport::OSVersion &lhs,
292           const CoreSimulatorSupport::OSVersion &rhs) {
293  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
294    unsigned int l = lhs.GetVersionAtIndex(i);
295    unsigned int r = rhs.GetVersionAtIndex(i);
296    if (l != r)
297      return false;
298  }
299  return true;
300}
301
302bool CoreSimulatorSupport::
303operator==(const CoreSimulatorSupport::ModelIdentifier &lhs,
304           const CoreSimulatorSupport::ModelIdentifier &rhs) {
305  if (lhs.GetFamily() != rhs.GetFamily())
306    return false;
307
308  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
309    unsigned int l = lhs.GetVersionAtIndex(i);
310    unsigned int r = rhs.GetVersionAtIndex(i);
311    if (l != r)
312      return false;
313  }
314  return true;
315}
316
317bool CoreSimulatorSupport::
318operator!=(const CoreSimulatorSupport::OSVersion &lhs,
319           const CoreSimulatorSupport::OSVersion &rhs) {
320  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
321    unsigned int l = lhs.GetVersionAtIndex(i);
322    unsigned int r = rhs.GetVersionAtIndex(i);
323    if (l != r)
324      return true;
325  }
326  return false;
327}
328
329bool CoreSimulatorSupport::
330operator!=(const CoreSimulatorSupport::ModelIdentifier &lhs,
331           const CoreSimulatorSupport::ModelIdentifier &rhs) {
332  if (lhs.GetFamily() != rhs.GetFamily())
333    return false;
334
335  for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
336    unsigned int l = lhs.GetVersionAtIndex(i);
337    unsigned int r = rhs.GetVersionAtIndex(i);
338    if (l != r)
339      return true;
340  }
341  return false;
342}
343
344bool CoreSimulatorSupport::Device::Boot(Status &err) {
345  if (m_dev == nil) {
346    err.SetErrorString("no valid simulator instance");
347    return false;
348  }
349
350#define kSimDeviceBootPersist                                                  \
351  @"persist" /* An NSNumber (boolean) indicating whether or not the session    \
352                should outlive the calling process (default false) */
353
354  NSDictionary *options = @{
355    kSimDeviceBootPersist : @NO,
356  };
357
358#undef kSimDeviceBootPersist
359
360  NSError *nserror;
361  if ([m_dev bootWithOptions:options error:&nserror]) {
362    err.Clear();
363    return true;
364  } else {
365    err.SetErrorString([[nserror description] UTF8String]);
366    return false;
367  }
368}
369
370bool CoreSimulatorSupport::Device::Shutdown(Status &err) {
371  NSError *nserror;
372  if ([m_dev shutdownWithError:&nserror]) {
373    err.Clear();
374    return true;
375  } else {
376    err.SetErrorString([[nserror description] UTF8String]);
377    return false;
378  }
379}
380
381static Status HandleFileAction(ProcessLaunchInfo &launch_info,
382                               NSMutableDictionary *options, NSString *key,
383                               const int fd, lldb::FileSP &file) {
384  Status error;
385  const FileAction *file_action = launch_info.GetFileActionForFD(fd);
386  if (file_action) {
387    switch (file_action->GetAction()) {
388    case FileAction::eFileActionNone:
389      break;
390
391    case FileAction::eFileActionClose:
392      error.SetErrorStringWithFormat("close file action for %i not supported",
393                                     fd);
394      break;
395
396    case FileAction::eFileActionDuplicate:
397      error.SetErrorStringWithFormat(
398          "duplication file action for %i not supported", fd);
399      break;
400
401    case FileAction::eFileActionOpen: {
402      FileSpec file_spec = file_action->GetFileSpec();
403      if (file_spec) {
404        const int master_fd = launch_info.GetPTY().GetPrimaryFileDescriptor();
405        if (master_fd != PseudoTerminal::invalid_fd) {
406          // Check in case our file action open wants to open the secondary
407          FileSpec secondary_spec(launch_info.GetPTY().GetSecondaryName());
408          if (file_spec == secondary_spec) {
409            int secondary_fd =
410                launch_info.GetPTY().GetSecondaryFileDescriptor();
411            if (secondary_fd == PseudoTerminal::invalid_fd) {
412              if (llvm::Error Err = launch_info.GetPTY().OpenSecondary(O_RDWR))
413                return Status(std::move(Err));
414            }
415            secondary_fd = launch_info.GetPTY().GetSecondaryFileDescriptor();
416            assert(secondary_fd != PseudoTerminal::invalid_fd);
417            [options setValue:[NSNumber numberWithInteger:secondary_fd]
418                       forKey:key];
419            return error; // Success
420          }
421        }
422        Status posix_error;
423        int oflag = file_action->GetActionArgument();
424        int created_fd =
425            open(file_spec.GetPath().c_str(), oflag, S_IRUSR | S_IWUSR);
426        if (created_fd >= 0) {
427          auto file_options = File::OpenOptions(0);
428          if ((oflag & O_RDWR) || (oflag & O_RDONLY))
429            file_options |= File::eOpenOptionRead;
430          if ((oflag & O_RDWR) || (oflag & O_RDONLY))
431            file_options |= File::eOpenOptionWrite;
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