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() 70 : m_dev(nil), m_model_identifier() {} 71 72CoreSimulatorSupport::DeviceType::DeviceType(id d) 73 : m_dev(d), m_model_identifier() {} 74 75CoreSimulatorSupport::DeviceType::operator bool() { return m_dev != nil; } 76 77ConstString CoreSimulatorSupport::DeviceType::GetIdentifier() { 78 return ConstString([[m_dev identifier] UTF8String]); 79} 80 81ConstString CoreSimulatorSupport::DeviceType::GetProductFamily() { 82 return ConstString([[m_dev productFamily] UTF8String]); 83} 84 85CoreSimulatorSupport::DeviceType::ProductFamilyID 86CoreSimulatorSupport::DeviceType::GetProductFamilyID() { 87 return ProductFamilyID([m_dev productFamilyID]); 88} 89 90CoreSimulatorSupport::DeviceRuntime::DeviceRuntime() 91 : m_dev(nil), m_os_version() {} 92 93CoreSimulatorSupport::DeviceRuntime::DeviceRuntime(id d) 94 : m_dev(d), m_os_version() {} 95 96CoreSimulatorSupport::DeviceRuntime::operator bool() { return m_dev != nil; } 97 98bool CoreSimulatorSupport::DeviceRuntime::IsAvailable() { 99 return [m_dev available]; 100} 101 102CoreSimulatorSupport::Device::Device() 103 : m_dev(nil), m_dev_type(), m_dev_runtime() {} 104 105CoreSimulatorSupport::Device::Device(id d) 106 : m_dev(d), m_dev_type(), m_dev_runtime() {} 107 108CoreSimulatorSupport::Device::operator bool() { return m_dev != nil; } 109 110CoreSimulatorSupport::Device::State CoreSimulatorSupport::Device::GetState() { 111 return (State)([m_dev state]); 112} 113 114CoreSimulatorSupport::ModelIdentifier::ModelIdentifier(const std::string &mi) 115 : m_family(), m_versions() { 116 bool any = false; 117 bool first_digit = false; 118 unsigned int val = 0; 119 120 for (char c : mi) { 121 any = true; 122 if (::isdigit(c)) { 123 if (!first_digit) 124 first_digit = true; 125 val = 10 * val + (c - '0'); 126 } else if (c == ',') { 127 if (first_digit) { 128 m_versions.push_back(val); 129 val = 0; 130 } else 131 m_family.push_back(c); 132 } else { 133 if (first_digit) { 134 m_family.clear(); 135 m_versions.clear(); 136 return; 137 } else { 138 m_family.push_back(c); 139 } 140 } 141 } 142 143 if (first_digit) 144 m_versions.push_back(val); 145} 146 147CoreSimulatorSupport::ModelIdentifier::ModelIdentifier() 148 : ModelIdentifier("") {} 149 150CoreSimulatorSupport::OSVersion::OSVersion(const std::string &ver, 151 const std::string &build) 152 : m_versions(), m_build(build) { 153 bool any = false; 154 unsigned int val = 0; 155 for (char c : ver) { 156 if (c == '.') { 157 m_versions.push_back(val); 158 val = 0; 159 } else if (::isdigit(c)) { 160 val = 10 * val + (c - '0'); 161 any = true; 162 } else { 163 m_versions.clear(); 164 return; 165 } 166 } 167 if (any) 168 m_versions.push_back(val); 169} 170 171CoreSimulatorSupport::OSVersion::OSVersion() : OSVersion("", "") {} 172 173CoreSimulatorSupport::ModelIdentifier 174CoreSimulatorSupport::DeviceType::GetModelIdentifier() { 175 if (!m_model_identifier.hasValue()) { 176 auto utf8_model_id = [[m_dev modelIdentifier] UTF8String]; 177 if (utf8_model_id && *utf8_model_id) 178 m_model_identifier = ModelIdentifier(utf8_model_id); 179 } 180 181 if (m_model_identifier.hasValue()) 182 return m_model_identifier.getValue(); 183 else 184 return ModelIdentifier(); 185} 186 187CoreSimulatorSupport::OSVersion 188CoreSimulatorSupport::DeviceRuntime::GetVersion() { 189 if (!m_os_version.hasValue()) { 190 auto utf8_ver_string = [[m_dev versionString] UTF8String]; 191 auto utf8_build_ver = [[m_dev buildVersionString] UTF8String]; 192 if (utf8_ver_string && *utf8_ver_string && utf8_build_ver && 193 *utf8_build_ver) { 194 m_os_version = OSVersion(utf8_ver_string, utf8_build_ver); 195 } 196 } 197 198 if (m_os_version.hasValue()) 199 return m_os_version.getValue(); 200 return OSVersion(); 201} 202 203std::string CoreSimulatorSupport::DeviceType::GetName() { 204 auto utf8_name = [[m_dev name] UTF8String]; 205 if (utf8_name) 206 return std::string(utf8_name); 207 return ""; 208} 209 210std::string CoreSimulatorSupport::Device::GetName() const { 211 auto utf8_name = [[m_dev name] UTF8String]; 212 if (utf8_name) 213 return std::string(utf8_name); 214 return ""; 215} 216 217std::string CoreSimulatorSupport::Device::GetUDID() const { 218 auto utf8_udid = [[[m_dev UDID] UUIDString] UTF8String]; 219 if (utf8_udid) 220 return std::string(utf8_udid); 221 else 222 return std::string(); 223} 224 225CoreSimulatorSupport::DeviceType CoreSimulatorSupport::Device::GetDeviceType() { 226 if (!m_dev_type.hasValue()) 227 m_dev_type = DeviceType([m_dev deviceType]); 228 229 return m_dev_type.getValue(); 230} 231 232CoreSimulatorSupport::DeviceRuntime 233CoreSimulatorSupport::Device::GetDeviceRuntime() { 234 if (!m_dev_runtime.hasValue()) 235 m_dev_runtime = DeviceRuntime([m_dev runtime]); 236 237 return m_dev_runtime.getValue(); 238} 239 240bool CoreSimulatorSupport:: 241operator>(const CoreSimulatorSupport::OSVersion &lhs, 242 const CoreSimulatorSupport::OSVersion &rhs) { 243 for (size_t i = 0; i < rhs.GetNumVersions(); i++) { 244 unsigned int l = lhs.GetVersionAtIndex(i); 245 unsigned int r = rhs.GetVersionAtIndex(i); 246 if (l > r) 247 return true; 248 } 249 return false; 250} 251 252bool CoreSimulatorSupport:: 253operator>(const CoreSimulatorSupport::ModelIdentifier &lhs, 254 const CoreSimulatorSupport::ModelIdentifier &rhs) { 255 if (lhs.GetFamily() != rhs.GetFamily()) 256 return false; 257 for (size_t i = 0; i < rhs.GetNumVersions(); i++) { 258 unsigned int l = lhs.GetVersionAtIndex(i); 259 unsigned int r = rhs.GetVersionAtIndex(i); 260 if (l > r) 261 return true; 262 } 263 return false; 264} 265 266bool CoreSimulatorSupport:: 267operator<(const CoreSimulatorSupport::OSVersion &lhs, 268 const CoreSimulatorSupport::OSVersion &rhs) { 269 for (size_t i = 0; i < rhs.GetNumVersions(); i++) { 270 unsigned int l = lhs.GetVersionAtIndex(i); 271 unsigned int r = rhs.GetVersionAtIndex(i); 272 if (l < r) 273 return true; 274 } 275 return false; 276} 277 278bool CoreSimulatorSupport:: 279operator<(const CoreSimulatorSupport::ModelIdentifier &lhs, 280 const CoreSimulatorSupport::ModelIdentifier &rhs) { 281 if (lhs.GetFamily() != rhs.GetFamily()) 282 return false; 283 284 for (size_t i = 0; i < rhs.GetNumVersions(); i++) { 285 unsigned int l = lhs.GetVersionAtIndex(i); 286 unsigned int r = rhs.GetVersionAtIndex(i); 287 if (l < r) 288 return true; 289 } 290 return false; 291} 292 293bool CoreSimulatorSupport:: 294operator==(const CoreSimulatorSupport::OSVersion &lhs, 295 const CoreSimulatorSupport::OSVersion &rhs) { 296 for (size_t i = 0; i < rhs.GetNumVersions(); i++) { 297 unsigned int l = lhs.GetVersionAtIndex(i); 298 unsigned int r = rhs.GetVersionAtIndex(i); 299 if (l != r) 300 return false; 301 } 302 return true; 303} 304 305bool CoreSimulatorSupport:: 306operator==(const CoreSimulatorSupport::ModelIdentifier &lhs, 307 const CoreSimulatorSupport::ModelIdentifier &rhs) { 308 if (lhs.GetFamily() != rhs.GetFamily()) 309 return false; 310 311 for (size_t i = 0; i < rhs.GetNumVersions(); i++) { 312 unsigned int l = lhs.GetVersionAtIndex(i); 313 unsigned int r = rhs.GetVersionAtIndex(i); 314 if (l != r) 315 return false; 316 } 317 return true; 318} 319 320bool CoreSimulatorSupport:: 321operator!=(const CoreSimulatorSupport::OSVersion &lhs, 322 const CoreSimulatorSupport::OSVersion &rhs) { 323 for (size_t i = 0; i < rhs.GetNumVersions(); i++) { 324 unsigned int l = lhs.GetVersionAtIndex(i); 325 unsigned int r = rhs.GetVersionAtIndex(i); 326 if (l != r) 327 return true; 328 } 329 return false; 330} 331 332bool CoreSimulatorSupport:: 333operator!=(const CoreSimulatorSupport::ModelIdentifier &lhs, 334 const CoreSimulatorSupport::ModelIdentifier &rhs) { 335 if (lhs.GetFamily() != rhs.GetFamily()) 336 return false; 337 338 for (size_t i = 0; i < rhs.GetNumVersions(); i++) { 339 unsigned int l = lhs.GetVersionAtIndex(i); 340 unsigned int r = rhs.GetVersionAtIndex(i); 341 if (l != r) 342 return true; 343 } 344 return false; 345} 346 347bool CoreSimulatorSupport::Device::Boot(Status &err) { 348 if (m_dev == nil) { 349 err.SetErrorString("no valid simulator instance"); 350 return false; 351 } 352 353#define kSimDeviceBootPersist \ 354 @"persist" /* An NSNumber (boolean) indicating whether or not the session \ 355 should outlive the calling process (default false) */ 356 357 NSDictionary *options = @{ 358 kSimDeviceBootPersist : @NO, 359 }; 360 361#undef kSimDeviceBootPersist 362 363 NSError *nserror; 364 if ([m_dev bootWithOptions:options error:&nserror]) { 365 err.Clear(); 366 return true; 367 } else { 368 err.SetErrorString([[nserror description] UTF8String]); 369 return false; 370 } 371} 372 373bool CoreSimulatorSupport::Device::Shutdown(Status &err) { 374 NSError *nserror; 375 if ([m_dev shutdownWithError:&nserror]) { 376 err.Clear(); 377 return true; 378 } else { 379 err.SetErrorString([[nserror description] UTF8String]); 380 return false; 381 } 382} 383 384static Status HandleFileAction(ProcessLaunchInfo &launch_info, 385 NSMutableDictionary *options, NSString *key, 386 const int fd, lldb::FileSP &file) { 387 Status error; 388 const FileAction *file_action = launch_info.GetFileActionForFD(fd); 389 if (file_action) { 390 switch (file_action->GetAction()) { 391 case FileAction::eFileActionNone: 392 break; 393 394 case FileAction::eFileActionClose: 395 error.SetErrorStringWithFormat("close file action for %i not supported", 396 fd); 397 break; 398 399 case FileAction::eFileActionDuplicate: 400 error.SetErrorStringWithFormat( 401 "duplication file action for %i not supported", fd); 402 break; 403 404 case FileAction::eFileActionOpen: { 405 FileSpec file_spec = file_action->GetFileSpec(); 406 if (file_spec) { 407 const int master_fd = launch_info.GetPTY().GetPrimaryFileDescriptor(); 408 if (master_fd != PseudoTerminal::invalid_fd) { 409 // Check in case our file action open wants to open the secondary 410 FileSpec secondary_spec(launch_info.GetPTY().GetSecondaryName()); 411 if (file_spec == secondary_spec) { 412 int secondary_fd = 413 launch_info.GetPTY().GetSecondaryFileDescriptor(); 414 if (secondary_fd == PseudoTerminal::invalid_fd) { 415 if (llvm::Error Err = launch_info.GetPTY().OpenSecondary(O_RDWR)) 416 return Status(std::move(Err)); 417 } 418 secondary_fd = launch_info.GetPTY().GetSecondaryFileDescriptor(); 419 assert(secondary_fd != PseudoTerminal::invalid_fd); 420 [options setValue:[NSNumber numberWithInteger:secondary_fd] 421 forKey:key]; 422 return error; // Success 423 } 424 } 425 Status posix_error; 426 int oflag = file_action->GetActionArgument(); 427 int created_fd = 428 open(file_spec.GetPath().c_str(), oflag, S_IRUSR | S_IWUSR); 429 if (created_fd >= 0) { 430 auto file_options = File::OpenOptions(0); 431 if ((oflag & O_RDWR) || (oflag & O_RDONLY)) 432 file_options |= File::eOpenOptionRead; 433 if ((oflag & O_RDWR) || (oflag & O_RDONLY)) 434 file_options |= File::eOpenOptionWrite; 435 file = std::make_shared<NativeFile>(created_fd, file_options, true); 436 [options setValue:[NSNumber numberWithInteger:created_fd] forKey:key]; 437 return error; // Success 438 } else { 439 posix_error.SetErrorToErrno(); 440 error.SetErrorStringWithFormat("unable to open file '%s': %s", 441 file_spec.GetPath().c_str(), 442 posix_error.AsCString()); 443 } 444 } 445 } break; 446 } 447 } 448 return error; // Success, no file action, nothing to do 449} 450 451CoreSimulatorSupport::Process 452CoreSimulatorSupport::Device::Spawn(ProcessLaunchInfo &launch_info) { 453#define kSimDeviceSpawnEnvironment \ 454 @"environment" /* An NSDictionary (NSStrings -> NSStrings) of environment \ 455 key/values */ 456#define kSimDeviceSpawnStdin @"stdin" /* An NSNumber corresponding to a fd */ 457#define kSimDeviceSpawnStdout @"stdout" /* An NSNumber corresponding to a fd \ 458 */ 459#define kSimDeviceSpawnStderr @"stderr" /* An NSNumber corresponding to a fd \ 460 */ 461#define kSimDeviceSpawnArguments \ 462 @"arguments" /* An NSArray of strings to use as the argv array. If not \ 463 provided, path will be argv[0] */ 464#define kSimDeviceSpawnWaitForDebugger \ 465 @"wait_for_debugger" /* An NSNumber (bool) */ 466#define kSimDeviceSpawnStandalone @"standalone" 467 468 NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; 469 470 options[kSimDeviceSpawnStandalone] = @(YES); 471 472 if (launch_info.GetFlags().Test(lldb::eLaunchFlagDebug)) 473 [options setObject:@YES forKey:kSimDeviceSpawnWaitForDebugger]; 474 475 if (launch_info.GetArguments().GetArgumentCount()) { 476 const Args &args(launch_info.GetArguments()); 477 NSMutableArray *args_array = [[NSMutableArray alloc] init]; 478 for (size_t idx = 0; idx < args.GetArgumentCount(); idx++) 479 [args_array 480 addObject:[NSString 481 stringWithUTF8String:args.GetArgumentAtIndex(idx)]]; 482 483 [options setObject:args_array forKey:kSimDeviceSpawnArguments]; 484 } 485 486 NSMutableDictionary *env_dict = [[NSMutableDictionary alloc] init]; 487 488 for (const auto &KV : launch_info.GetEnvironment()) { 489 NSString *key_ns = [NSString stringWithUTF8String:KV.first().str().c_str()]; 490 NSString *value_ns = [NSString stringWithUTF8String:KV.second.c_str()]; 491 492 [env_dict setValue:value_ns forKey:key_ns]; 493 } 494 495 [options setObject:env_dict forKey:kSimDeviceSpawnEnvironment]; 496 497 Status error; 498 lldb::FileSP stdin_file; 499 lldb::FileSP stdout_file; 500 lldb::FileSP stderr_file; 501 error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdin, 502 STDIN_FILENO, stdin_file); 503 504 if (error.Fail()) 505 return CoreSimulatorSupport::Process(error); 506 507 error = HandleFileAction(launch_info, options, kSimDeviceSpawnStdout, 508 STDOUT_FILENO, stdout_file); 509 510 if (error.Fail()) 511 return CoreSimulatorSupport::Process(error); 512 513 error = HandleFileAction(launch_info, options, kSimDeviceSpawnStderr, 514 STDERR_FILENO, stderr_file); 515 516 if (error.Fail()) 517 return CoreSimulatorSupport::Process(error); 518 519#undef kSimDeviceSpawnEnvironment 520#undef kSimDeviceSpawnStdin 521#undef kSimDeviceSpawnStdout 522#undef kSimDeviceSpawnStderr 523#undef kSimDeviceSpawnWaitForDebugger 524#undef kSimDeviceSpawnArguments 525 526 NSError *nserror; 527 528 pid_t pid; 529 BOOL success = [m_dev 530 spawnWithPath:[NSString stringWithUTF8String:launch_info 531 .GetExecutableFile() 532 .GetPath() 533 .c_str()] 534 options:options 535 terminationQueue:nil 536 terminationHandler:nil 537 pid:&pid 538 error:&nserror]; 539 540 if (!success) { 541 const char *nserror_string = [[nserror description] UTF8String]; 542 error.SetErrorString(nserror_string ? nserror_string : "unable to launch"); 543 } 544 545 return CoreSimulatorSupport::Process(pid, error); 546} 547 548CoreSimulatorSupport::DeviceSet 549CoreSimulatorSupport::DeviceSet::GetAllDevices(const char *developer_dir) { 550 if (!developer_dir || !developer_dir[0]) 551 return DeviceSet([NSArray new]); 552 553 Class SimServiceContextClass = NSClassFromString(@"SimServiceContext"); 554 NSString *dev_dir = @(developer_dir); 555 NSError *error = nil; 556 557 id serviceContext = 558 [SimServiceContextClass sharedServiceContextForDeveloperDir:dev_dir 559 error:&error]; 560 if (!serviceContext) 561 return DeviceSet([NSArray new]); 562 563 return DeviceSet([[serviceContext defaultDeviceSetWithError:&error] devices]); 564} 565 566CoreSimulatorSupport::DeviceSet 567CoreSimulatorSupport::DeviceSet::GetAvailableDevices( 568 const char *developer_dir) { 569 return GetAllDevices(developer_dir).GetDevicesIf([](Device d) -> bool { 570 return (d && d.GetDeviceType() && d.GetDeviceRuntime() && 571 d.GetDeviceRuntime().IsAvailable()); 572 }); 573} 574 575size_t CoreSimulatorSupport::DeviceSet::GetNumDevices() { 576 return [m_dev count]; 577} 578 579CoreSimulatorSupport::Device 580CoreSimulatorSupport::DeviceSet::GetDeviceAtIndex(size_t idx) { 581 if (idx < GetNumDevices()) 582 return Device([m_dev objectAtIndex:idx]); 583 return Device(); 584} 585 586CoreSimulatorSupport::DeviceSet CoreSimulatorSupport::DeviceSet::GetDevicesIf( 587 std::function<bool(CoreSimulatorSupport::Device)> f) { 588 NSMutableArray *array = [[NSMutableArray alloc] init]; 589 for (NSUInteger i = 0; i < GetNumDevices(); i++) { 590 Device d(GetDeviceAtIndex(i)); 591 if (f(d)) 592 [array addObject:(id)d.m_dev]; 593 } 594 595 return DeviceSet(array); 596} 597 598void CoreSimulatorSupport::DeviceSet::ForEach( 599 std::function<bool(const Device &)> f) { 600 const size_t n = GetNumDevices(); 601 for (NSUInteger i = 0; i < n; ++i) { 602 if (!f(GetDeviceAtIndex(i))) 603 break; 604 } 605} 606 607CoreSimulatorSupport::DeviceSet CoreSimulatorSupport::DeviceSet::GetDevices( 608 CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) { 609 NSMutableArray *array = [[NSMutableArray alloc] init]; 610 const size_t n = GetNumDevices(); 611 for (NSUInteger i = 0; i < n; ++i) { 612 Device d(GetDeviceAtIndex(i)); 613 if (d && d.GetDeviceType() && 614 d.GetDeviceType().GetProductFamilyID() == dev_id) 615 [array addObject:(id)d.m_dev]; 616 } 617 618 return DeviceSet(array); 619} 620 621CoreSimulatorSupport::Device CoreSimulatorSupport::DeviceSet::GetFanciest( 622 CoreSimulatorSupport::DeviceType::ProductFamilyID dev_id) { 623 Device dev; 624 625 for (NSUInteger i = 0; i < GetNumDevices(); i++) { 626 Device d(GetDeviceAtIndex(i)); 627 if (d && d.GetDeviceType() && 628 d.GetDeviceType().GetProductFamilyID() == dev_id) { 629 if (!dev) 630 dev = d; 631 else { 632 if ((d.GetDeviceType().GetModelIdentifier() > 633 dev.GetDeviceType().GetModelIdentifier()) || 634 d.GetDeviceRuntime().GetVersion() > 635 dev.GetDeviceRuntime().GetVersion()) 636 dev = d; 637 } 638 } 639 } 640 641 return dev; 642} 643