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