LLDB mainline
PlatformiOSSimulatorCoreSimulatorSupport.mm
Go to the documentation of this file.
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
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
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
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;
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
62
65
68
70
73
74CoreSimulatorSupport::DeviceType::operator bool() { return m_dev != nil; }
75
80
82
85
86CoreSimulatorSupport::DeviceRuntime::operator bool() { return m_dev != nil; }
87
89 return [m_dev available];
90}
91
93
96
97CoreSimulatorSupport::Device::operator bool() { return m_dev != nil; }
98
102
104 : m_family(), m_versions() {
105 bool first_digit = false;
106 unsigned int val = 0;
107
108 for (char c : mi) {
109 if (::isdigit(c)) {
110 if (!first_digit)
111 first_digit = true;
112 val = 10 * val + (c - '0');
113 } else if (c == ',') {
114 if (first_digit) {
115 m_versions.push_back(val);
116 val = 0;
117 } else
118 m_family.push_back(c);
119 } else {
120 if (first_digit) {
121 m_family.clear();
122 m_versions.clear();
123 return;
124 } else {
125 m_family.push_back(c);
126 }
127 }
128 }
129
130 if (first_digit)
131 m_versions.push_back(val);
132}
133
136
138 const std::string &build)
139 : m_versions(), m_build(build) {
140 bool any = false;
141 unsigned int val = 0;
142 for (char c : ver) {
143 if (c == '.') {
144 m_versions.push_back(val);
145 val = 0;
146 } else if (::isdigit(c)) {
147 val = 10 * val + (c - '0');
148 any = true;
149 } else {
150 m_versions.clear();
151 return;
152 }
153 }
154 if (any)
155 m_versions.push_back(val);
156}
157
159
162 if (!m_model_identifier.has_value()) {
163 auto utf8_model_id = [[m_dev modelIdentifier] UTF8String];
164 if (utf8_model_id && *utf8_model_id)
165 m_model_identifier = ModelIdentifier(utf8_model_id);
166 }
167
168 if (m_model_identifier.has_value())
169 return m_model_identifier.value();
170 else
171 return ModelIdentifier();
172}
173
176 if (!m_os_version.has_value()) {
177 auto utf8_ver_string = [[m_dev versionString] UTF8String];
178 auto utf8_build_ver = [[m_dev buildVersionString] UTF8String];
179 if (utf8_ver_string && *utf8_ver_string && utf8_build_ver &&
180 *utf8_build_ver) {
181 m_os_version = OSVersion(utf8_ver_string, utf8_build_ver);
182 }
183 }
184
185 if (m_os_version.has_value())
186 return m_os_version.value();
187 return OSVersion();
188}
189
191 auto utf8_name = [[m_dev name] UTF8String];
192 if (utf8_name)
193 return std::string(utf8_name);
194 return "";
195}
196
198 auto utf8_name = [[m_dev name] UTF8String];
199 if (utf8_name)
200 return std::string(utf8_name);
201 return "";
202}
203
205 auto utf8_udid = [[[m_dev UDID] UUIDString] UTF8String];
206 if (utf8_udid)
207 return std::string(utf8_udid);
208 else
209 return std::string();
210}
211
213 if (!m_dev_type.has_value())
214 m_dev_type = DeviceType([m_dev deviceType]);
215
216 return m_dev_type.value();
217}
218
221 if (!m_dev_runtime.has_value())
222 m_dev_runtime = DeviceRuntime([m_dev runtime]);
223
224 return m_dev_runtime.value();
225}
226
230 for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
231 unsigned int l = lhs.GetVersionAtIndex(i);
232 unsigned int r = rhs.GetVersionAtIndex(i);
233 if (l > r)
234 return true;
235 }
236 return false;
237}
238
242 if (lhs.GetFamily() != rhs.GetFamily())
243 return false;
244 for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
245 unsigned int l = lhs.GetVersionAtIndex(i);
246 unsigned int r = rhs.GetVersionAtIndex(i);
247 if (l > r)
248 return true;
249 }
250 return false;
251}
252
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
268 if (lhs.GetFamily() != rhs.GetFamily())
269 return false;
270
271 for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
272 unsigned int l = lhs.GetVersionAtIndex(i);
273 unsigned int r = rhs.GetVersionAtIndex(i);
274 if (l < r)
275 return true;
276 }
277 return false;
278}
279
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 false;
288 }
289 return true;
290}
291
295 if (lhs.GetFamily() != rhs.GetFamily())
296 return false;
297
298 for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
299 unsigned int l = lhs.GetVersionAtIndex(i);
300 unsigned int r = rhs.GetVersionAtIndex(i);
301 if (l != r)
302 return false;
303 }
304 return true;
305}
306
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 true;
315 }
316 return false;
317}
318
322 if (lhs.GetFamily() != rhs.GetFamily())
323 return false;
324
325 for (size_t i = 0; i < rhs.GetNumVersions(); i++) {
326 unsigned int l = lhs.GetVersionAtIndex(i);
327 unsigned int r = rhs.GetVersionAtIndex(i);
328 if (l != r)
329 return true;
330 }
331 return false;
332}
333
335 if (m_dev == nil) {
336 err = Status::FromErrorString("no valid simulator instance");
337 return false;
338 }
339
340#define kSimDeviceBootPersist \
341 @"persist" /* An NSNumber (boolean) indicating whether or not the session \
342 should outlive the calling process (default false) */
343
344 NSDictionary *options = @{
346 };
347
348#undef kSimDeviceBootPersist
349
350 NSError *nserror;
351 if ([m_dev bootWithOptions:options error:&nserror]) {
352 err.Clear();
353 return true;
354 } else {
355 err = Status::FromErrorString([[nserror description] UTF8String]);
356 return false;
357 }
358}
361 NSError *nserror;
362 if ([m_dev shutdownWithError:&nserror]) {
363 err.Clear();
364 return true;
365 } else {
366 err = Status::FromErrorString([[nserror description] UTF8String]);
367 return false;
368 }
369}
371static Status HandleFileAction(ProcessLaunchInfo &launch_info,
372 NSMutableDictionary *options, NSString *key,
373 const int fd, lldb::FileSP &file) {
375 const FileAction *file_action = launch_info.GetFileActionForFD(fd);
376 if (file_action) {
377 switch (file_action->GetAction()) {
379 break;
380
383 "close file action for %i not supported", fd);
384 break;
385
388 "duplication file action for %i not supported", fd);
389 break;
390
392 FileSpec file_spec = file_action->GetFileSpec();
393 if (file_spec) {
394 const int primary_fd = launch_info.GetPTY().GetPrimaryFileDescriptor();
395 if (primary_fd != PseudoTerminal::invalid_fd) {
396 // Check in case our file action open wants to open the secondary
397 FileSpec secondary_spec(launch_info.GetPTY().GetSecondaryName());
398 if (file_spec == secondary_spec) {
399 int secondary_fd =
400 launch_info.GetPTY().GetSecondaryFileDescriptor();
401 if (secondary_fd == PseudoTerminal::invalid_fd) {
402 if (llvm::Error Err = launch_info.GetPTY().OpenSecondary(O_RDWR))
403 return Status::FromError(std::move(Err));
404 }
405 secondary_fd = launch_info.GetPTY().GetSecondaryFileDescriptor();
406 assert(secondary_fd != PseudoTerminal::invalid_fd);
407 [options setValue:[NSNumber numberWithInteger:secondary_fd]
408 forKey:key];
409 return error; // Success
410 }
411 }
412 int oflag = file_action->GetActionArgument();
413 int created_fd =
414 open(file_spec.GetPath().c_str(), oflag, S_IRUSR | S_IWUSR);
415 if (created_fd >= 0) {
416 auto file_options = File::OpenOptions(0);
417 if (oflag & O_RDWR)
418 file_options |= File::eOpenOptionReadWrite;
419 else if (oflag & O_WRONLY)
420 file_options |= File::eOpenOptionWriteOnly;
421 else if (oflag & O_RDONLY)
422 file_options |= File::eOpenOptionReadOnly;
423 file = std::make_shared<NativeFile>(created_fd, file_options, true);
424 [options setValue:[NSNumber numberWithInteger:created_fd] forKey:key];
425 return error; // Success
426 } else {
427 Status posix_error = Status::FromErrno();
429 "unable to open file '{0}': {1}", file_spec.GetPath(),
430 posix_error.AsCString());
431 }
432 }
433 } break;
434 }
435 }
436 return error; // Success, no file action, nothing to do
437}
438
441#define kSimDeviceSpawnEnvironment \
442 @"environment" /* An NSDictionary (NSStrings -> NSStrings) of environment \
443 key/values */
444#define kSimDeviceSpawnStdin @"stdin" /* An NSNumber corresponding to a fd */
445#define kSimDeviceSpawnStdout @"stdout" /* An NSNumber corresponding to a fd \
446 */
447#define kSimDeviceSpawnStderr @"stderr" /* An NSNumber corresponding to a fd \
448 */
449#define kSimDeviceSpawnArguments \
450 @"arguments" /* An NSArray of strings to use as the argv array. If not \
451 provided, path will be argv[0] */
452#define kSimDeviceSpawnWaitForDebugger \
453 @"wait_for_debugger" /* An NSNumber (bool) */
454#define kSimDeviceSpawnStandalone @"standalone"
455
456 NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
457
458 options[kSimDeviceSpawnStandalone] = @(YES);
459
460 if (launch_info.GetFlags().Test(lldb::eLaunchFlagDebug))
461 [options setObject:@YES forKey:kSimDeviceSpawnWaitForDebugger];
462
463 if (launch_info.GetArguments().GetArgumentCount()) {
464 const Args &args(launch_info.GetArguments());
465 NSMutableArray *args_array = [[NSMutableArray alloc] init];
466 for (size_t idx = 0; idx < args.GetArgumentCount(); idx++)
467 [args_array
468 addObject:[NSString
469 stringWithUTF8String:args.GetArgumentAtIndex(idx)]];
470
471 [options setObject:args_array forKey:kSimDeviceSpawnArguments];
472 }
473
474 NSMutableDictionary *env_dict = [[NSMutableDictionary alloc] init];
475
476 for (const auto &KV : launch_info.GetEnvironment()) {
477 NSString *key_ns = [NSString stringWithUTF8String:KV.first().str().c_str()];
478 NSString *value_ns = [NSString stringWithUTF8String:KV.second.c_str()];
479
480 [env_dict setValue:value_ns forKey:key_ns];
481 }
482
483 [options setObject:env_dict forKey:kSimDeviceSpawnEnvironment];
484
486 lldb::FileSP stdin_file;
487 lldb::FileSP stdout_file;
488 lldb::FileSP stderr_file;
489 error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdin,
490 STDIN_FILENO, stdin_file);
491
492 if (error.Fail())
493 return CoreSimulatorSupport::Process(std::move(error));
494
495 error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdout,
496 STDOUT_FILENO, stdout_file);
497
498 if (error.Fail())
499 return CoreSimulatorSupport::Process(std::move(error));
500
501 error = HandleFileAction(launch_info, options, kSimDeviceSpawnStderr,
502 STDERR_FILENO, stderr_file);
503
504 if (error.Fail())
505 return CoreSimulatorSupport::Process(std::move(error));
506
507#undef kSimDeviceSpawnEnvironment
508#undef kSimDeviceSpawnStdin
509#undef kSimDeviceSpawnStdout
510#undef kSimDeviceSpawnStderr
511#undef kSimDeviceSpawnWaitForDebugger
512#undef kSimDeviceSpawnArguments
513
514 NSError *nserror;
515
516 pid_t pid;
517 BOOL success = [m_dev
518 spawnWithPath:[NSString stringWithUTF8String:launch_info
519 .GetExecutableFile()
520 .GetPath()
521 .c_str()]
522 options:options
523 terminationQueue:nil
524 terminationHandler:nil
525 pid:&pid
526 error:&nserror];
527
528 if (!success) {
529 const char *nserror_string = [[nserror description] UTF8String];
530 error = Status::FromErrorString(nserror_string ? nserror_string
531 : "unable to launch");
532 }
534 return CoreSimulatorSupport::Process(pid, std::move(error));
535}
536
538CoreSimulatorSupport::DeviceSet::GetAllDevices(const char *developer_dir) {
539 if (!developer_dir || !developer_dir[0])
540 return DeviceSet([NSArray new]);
541
542 Class SimServiceContextClass = NSClassFromString(@"SimServiceContext");
543 NSString *dev_dir = @(developer_dir);
544 NSError *error = nil;
545
546 id serviceContext =
547 [SimServiceContextClass sharedServiceContextForDeveloperDir:dev_dir
548 error:&error];
549 if (!serviceContext)
550 return DeviceSet([NSArray new]);
552 return DeviceSet([[serviceContext defaultDeviceSetWithError:&error] devices]);
553}
554
557 const char *developer_dir) {
558 return GetAllDevices(developer_dir).GetDevicesIf([](Device d) -> bool {
559 return (d && d.GetDeviceType() && d.GetDeviceRuntime() &&
561 });
562}
563
570 if (idx < GetNumDevices())
571 return Device([m_dev objectAtIndex:idx]);
572 return Device();
573}
574
576 std::function<bool(CoreSimulatorSupport::Device)> f) {
577 NSMutableArray *array = [[NSMutableArray alloc] init];
578 for (NSUInteger i = 0; i < GetNumDevices(); i++) {
579 Device d(GetDeviceAtIndex(i));
580 if (f(d))
581 [array addObject:(id)d.m_dev];
583
584 return DeviceSet(array);
585}
586
588 std::function<bool(const Device &)> f) {
589 const size_t n = GetNumDevices();
590 for (NSUInteger i = 0; i < n; ++i) {
592 break;
593 }
594}
595
598 NSMutableArray *array = [[NSMutableArray alloc] init];
599 const size_t n = GetNumDevices();
600 for (NSUInteger i = 0; i < n; ++i) {
601 Device d(GetDeviceAtIndex(i));
602 if (d && d.GetDeviceType() &&
603 d.GetDeviceType().GetProductFamilyID() == dev_id)
604 [array addObject:(id)d.m_dev];
606
607 return DeviceSet(array);
608}
609
612 Device dev;
613
614 for (NSUInteger i = 0; i < GetNumDevices(); i++) {
615 Device d(GetDeviceAtIndex(i));
616 if (d && d.GetDeviceType() &&
617 d.GetDeviceType().GetProductFamilyID() == dev_id) {
618 if (!dev)
619 dev = d;
620 else {
622 dev.GetDeviceType().GetModelIdentifier()) ||
624 dev.GetDeviceRuntime().GetVersion())
625 dev = d;
626 }
627 }
628 }
629
630 return dev;
631}
static llvm::raw_ostream & error(Stream &strm)
#define kSimDeviceSpawnStdin
#define kSimDeviceBootPersist
#define kSimDeviceSpawnStderr
#define kSimDeviceSpawnStdout
static Status HandleFileAction(ProcessLaunchInfo &launch_info, NSMutableDictionary *options, NSString *key, const int fd, lldb::FileSP &file)
static DeviceSet GetAvailableDevices(const char *developer_dir)
Device GetFanciest(DeviceType::ProductFamilyID dev_id)
void ForEach(std::function< bool(const Device &)> f)
DeviceSet GetDevices(DeviceType::ProductFamilyID dev_id)
static DeviceSet GetAllDevices(const char *developer_dir)
DeviceSet GetDevicesIf(std::function< bool(Device)> f)
Process Spawn(lldb_private::ProcessLaunchInfo &launch_info)
OSVersion(const std::string &ver, const std::string &build)
A command line argument class.
Definition Args.h:33
size_t GetArgumentCount() const
Gets the number of arguments left in this command object.
Definition Args.h:120
Represents a file descriptor action to be performed during process launch.
Definition FileAction.h:21
Action GetAction() const
Get the type of action.
Definition FileAction.h:59
int GetActionArgument() const
Get the action-specific argument.
Definition FileAction.h:65
const FileSpec & GetFileSpec() const
Get the file specification for open actions.
A file utility class.
Definition FileSpec.h:56
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition FileSpec.cpp:380
bool Test(ValueType bit) const
Test a single flag bit.
Definition Flags.h:96
Environment & GetEnvironment()
Definition ProcessInfo.h:86
const FileAction * GetFileActionForFD(int fd) const
llvm::Error OpenSecondary(int oflag)
Open the secondary for the current primary pseudo terminal.
@ invalid_fd
Invalid file descriptor value.
int GetPrimaryFileDescriptor() const
The primary file descriptor accessor.
std::string GetSecondaryName() const
Get the name of the secondary pseudo terminal.
int GetSecondaryFileDescriptor() const
The secondary file descriptor accessor.
An error handling class.
Definition Status.h:118
void Clear()
Clear the object state.
Definition Status.cpp:214
static Status FromErrno()
Set the current error to errno.
Definition Status.cpp:299
static Status FromErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Definition Status.cpp:106
static Status FromErrorString(const char *str)
Definition Status.h:141
const char * AsCString(const char *default_error_str="unknown error") const
Get the error string associated with the current error.
Definition Status.cpp:194
static Status static Status FromErrorStringWithFormatv(const char *format, Args &&...args)
Definition Status.h:151
static Status FromError(llvm::Error error)
Avoid using this in new code. Migrate APIs to llvm::Expected instead.
Definition Status.cpp:136
#define LLDB_INVALID_PROCESS_ID
bool operator>(const OSVersion &lhs, const OSVersion &rhs)
bool operator!=(const OSVersion &lhs, const OSVersion &rhs)
bool operator==(const OSVersion &lhs, const OSVersion &rhs)
bool operator<(const OSVersion &lhs, const OSVersion &rhs)
A class that represents a running process on the host machine.
uint64_t pid_t
Definition lldb-types.h:84
std::shared_ptr< lldb_private::File > FileSP
#define S_IRUSR
#define S_IWUSR