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;
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
62
64 : m_pid(LLDB_INVALID_PROCESS_ID), m_error(error) {}
65
67 : m_pid(p), m_error(error) {}
68
70
72 : m_dev(d), m_model_identifier() {}
73
74CoreSimulatorSupport::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
92 : m_dev(d), m_os_version() {}
93
94CoreSimulatorSupport::DeviceRuntime::operator bool() { return m_dev != nil; }
95
97 return [m_dev available];
98}
99
100CoreSimulatorSupport::Device::Device() : m_dev_type(), m_dev_runtime() {}
101
103 : m_dev(d), m_dev_type(), m_dev_runtime() {}
104
105CoreSimulatorSupport::Device::operator bool() { return m_dev != nil; }
106
108 return (State)([m_dev state]);
109}
110
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
143 : ModelIdentifier("") {}
144
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
167
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
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
199 auto utf8_name = [[m_dev name] UTF8String];
200 if (utf8_name)
201 return std::string(utf8_name);
202 return "";
203}
204
206 auto utf8_name = [[m_dev name] UTF8String];
207 if (utf8_name)
208 return std::string(utf8_name);
209 return "";
210}
211
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
221 if (!m_dev_type.has_value())
222 m_dev_type = DeviceType([m_dev deviceType]);
223
224 return m_dev_type.value();
225}
226
229 if (!m_dev_runtime.has_value())
230 m_dev_runtime = DeviceRuntime([m_dev runtime]);
231
232 return m_dev_runtime.value();
233}
234
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
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
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
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
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
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
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
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
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 = @{
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}
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}
379static Status HandleFileAction(ProcessLaunchInfo &launch_info,
380 NSMutableDictionary *options, NSString *key,
381 const int fd, lldb::FileSP &file) {
383 const FileAction *file_action = launch_info.GetFileActionForFD(fd);
384 if (file_action) {
385 switch (file_action->GetAction()) {
387 break;
388
390 error.SetErrorStringWithFormat("close file action for %i not supported",
391 fd);
392 break;
393
395 error.SetErrorStringWithFormat(
396 "duplication file action for %i not supported", fd);
397 break;
398
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
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
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())
503
504 error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdout,
505 STDOUT_FILENO, stdout_file);
506
507 if (error.Fail())
509
510 error = HandleFileAction(launch_info, options, kSimDeviceSpawnStderr,
511 STDERR_FILENO, stderr_file);
512
513 if (error.Fail())
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 }
543}
544
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]);
560 return DeviceSet([[serviceContext defaultDeviceSetWithError:&error] devices]);
561}
562
565 const char *developer_dir) {
566 return GetAllDevices(developer_dir).GetDevicesIf([](Device d) -> bool {
567 return (d && d.GetDeviceType() && d.GetDeviceRuntime() &&
569 });
570}
571
573 return [m_dev count];
574}
575
578 if (idx < GetNumDevices())
579 return Device([m_dev objectAtIndex:idx]);
580 return Device();
581}
582
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];
591
592 return DeviceSet(array);
593}
594
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
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];
614
615 return DeviceSet(array);
616}
617
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 {
630 dev.GetDeviceType().GetModelIdentifier()) ||
632 dev.GetDeviceRuntime().GetVersion())
633 dev = d;
634 }
635 }
636 }
637
638 return dev;
639}
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)
#define kSimDeviceSpawnWaitForDebugger
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)
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:116
const char * GetArgumentAtIndex(size_t idx) const
Gets the NULL terminated C string argument pointer for the argument at index idx.
Definition: Args.cpp:263
A uniqued constant string class.
Definition: ConstString.h:40
Action GetAction() const
Definition: FileAction.h:38
int GetActionArgument() const
Definition: FileAction.h:40
const FileSpec & GetFileSpec() const
Definition: FileAction.cpp:32
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:367
@ eOpenOptionReadOnly
Definition: File.h:51
@ eOpenOptionReadWrite
Definition: File.h:53
@ eOpenOptionWriteOnly
Definition: File.h:52
bool Test(ValueType bit) const
Test a single flag bit.
Definition: Flags.h:96
Environment & GetEnvironment()
Definition: ProcessInfo.h:87
const FileAction * GetFileActionForFD(int fd) const
llvm::Error OpenSecondary(int oflag)
Open the secondary for the current primary pseudo terminal.
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.
@ invalid_fd
Invalid file descriptor value.
An error handling class.
Definition: Status.h:44
void Clear()
Clear the object state.
Definition: Status.cpp:167
void SetErrorToErrno()
Set the current error to errno.
Definition: Status.cpp:215
const char * AsCString(const char *default_error_str="unknown error") const
Get the error string associated with the current error.
Definition: Status.cpp:130
void SetErrorString(llvm::StringRef err_str)
Set the current error string to err_str.
Definition: Status.cpp:233
#define LLDB_INVALID_PROCESS_ID
Definition: lldb-defines.h:89
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.
Definition: SBAttachInfo.h:14
uint64_t pid_t
Definition: lldb-types.h:81
std::shared_ptr< lldb_private::File > FileSP
Definition: lldb-forward.h:345
#define S_IRUSR
#define S_IWUSR