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