1 //===-- LocateSymbolFileMacOSX.cpp ------------------------------*- C++ -*-===// 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 "lldb/Symbol/LocateSymbolFile.h" 10 11 #include <dirent.h> 12 #include <pwd.h> 13 14 #include <CoreFoundation/CoreFoundation.h> 15 16 #include "Host/macosx/cfcpp/CFCBundle.h" 17 #include "Host/macosx/cfcpp/CFCData.h" 18 #include "Host/macosx/cfcpp/CFCReleaser.h" 19 #include "Host/macosx/cfcpp/CFCString.h" 20 #include "lldb/Core/ModuleList.h" 21 #include "lldb/Core/ModuleSpec.h" 22 #include "lldb/Host/Host.h" 23 #include "lldb/Symbol/ObjectFile.h" 24 #include "lldb/Utility/ArchSpec.h" 25 #include "lldb/Utility/CleanUp.h" 26 #include "lldb/Utility/DataBuffer.h" 27 #include "lldb/Utility/DataExtractor.h" 28 #include "lldb/Utility/Endian.h" 29 #include "lldb/Utility/Log.h" 30 #include "lldb/Utility/StreamString.h" 31 #include "lldb/Utility/Timer.h" 32 #include "lldb/Utility/UUID.h" 33 #include "mach/machine.h" 34 35 #include "llvm/Support/FileSystem.h" 36 37 using namespace lldb; 38 using namespace lldb_private; 39 40 #if !defined(__arm__) && !defined(__arm64__) && \ 41 !defined(__aarch64__) // No DebugSymbols on the iOS devices 42 extern "C" { 43 44 CFURLRef DBGCopyFullDSYMURLForUUID(CFUUIDRef uuid, CFURLRef exec_url); 45 CFDictionaryRef DBGCopyDSYMPropertyLists(CFURLRef dsym_url); 46 } 47 #endif 48 49 int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec, 50 ModuleSpec &return_module_spec) { 51 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); 52 if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) { 53 if (log) 54 log->Printf("Spotlight lookup for .dSYM bundles is disabled."); 55 return 0; 56 } 57 58 return_module_spec = module_spec; 59 return_module_spec.GetFileSpec().Clear(); 60 return_module_spec.GetSymbolFileSpec().Clear(); 61 62 int items_found = 0; 63 64 #if !defined(__arm__) && !defined(__arm64__) && \ 65 !defined(__aarch64__) // No DebugSymbols on the iOS devices 66 67 const UUID *uuid = module_spec.GetUUIDPtr(); 68 const ArchSpec *arch = module_spec.GetArchitecturePtr(); 69 70 if (uuid && uuid->IsValid()) { 71 // Try and locate the dSYM file using DebugSymbols first 72 llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes(); 73 if (module_uuid.size() == 16) { 74 CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes( 75 NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3], 76 module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7], 77 module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11], 78 module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15])); 79 80 if (module_uuid_ref.get()) { 81 CFCReleaser<CFURLRef> exec_url; 82 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); 83 if (exec_fspec) { 84 char exec_cf_path[PATH_MAX]; 85 if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path))) 86 exec_url.reset(::CFURLCreateFromFileSystemRepresentation( 87 NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path), 88 FALSE)); 89 } 90 91 CFCReleaser<CFURLRef> dsym_url( 92 ::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get())); 93 char path[PATH_MAX]; 94 95 if (dsym_url.get()) { 96 if (::CFURLGetFileSystemRepresentation( 97 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { 98 if (log) { 99 log->Printf("DebugSymbols framework returned dSYM path of %s for " 100 "UUID %s -- looking for the dSYM", 101 path, uuid->GetAsString().c_str()); 102 } 103 FileSpec dsym_filespec(path); 104 if (path[0] == '~') 105 FileSystem::Instance().Resolve(dsym_filespec); 106 107 if (FileSystem::Instance().IsDirectory(dsym_filespec)) { 108 dsym_filespec = 109 Symbols::FindSymbolFileInBundle(dsym_filespec, uuid, arch); 110 ++items_found; 111 } else { 112 ++items_found; 113 } 114 return_module_spec.GetSymbolFileSpec() = dsym_filespec; 115 } 116 117 bool success = false; 118 if (log) { 119 if (::CFURLGetFileSystemRepresentation( 120 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { 121 log->Printf("DebugSymbols framework returned dSYM path of %s for " 122 "UUID %s -- looking for an exec file", 123 path, uuid->GetAsString().c_str()); 124 } 125 } 126 127 CFCReleaser<CFDictionaryRef> dict( 128 ::DBGCopyDSYMPropertyLists(dsym_url.get())); 129 CFDictionaryRef uuid_dict = NULL; 130 if (dict.get()) { 131 CFCString uuid_cfstr(uuid->GetAsString().c_str()); 132 uuid_dict = static_cast<CFDictionaryRef>( 133 ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get())); 134 } 135 if (uuid_dict) { 136 CFStringRef exec_cf_path = 137 static_cast<CFStringRef>(::CFDictionaryGetValue( 138 uuid_dict, CFSTR("DBGSymbolRichExecutable"))); 139 if (exec_cf_path && ::CFStringGetFileSystemRepresentation( 140 exec_cf_path, path, sizeof(path))) { 141 if (log) { 142 log->Printf("plist bundle has exec path of %s for UUID %s", 143 path, uuid->GetAsString().c_str()); 144 } 145 ++items_found; 146 FileSpec exec_filespec(path); 147 if (path[0] == '~') 148 FileSystem::Instance().Resolve(exec_filespec); 149 if (FileSystem::Instance().Exists(exec_filespec)) { 150 success = true; 151 return_module_spec.GetFileSpec() = exec_filespec; 152 } 153 } 154 } 155 156 if (!success) { 157 // No dictionary, check near the dSYM bundle for an executable that 158 // matches... 159 if (::CFURLGetFileSystemRepresentation( 160 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { 161 char *dsym_extension_pos = ::strstr(path, ".dSYM"); 162 if (dsym_extension_pos) { 163 *dsym_extension_pos = '\0'; 164 if (log) { 165 log->Printf("Looking for executable binary next to dSYM " 166 "bundle with name with name %s", 167 path); 168 } 169 FileSpec file_spec(path); 170 FileSystem::Instance().Resolve(file_spec); 171 ModuleSpecList module_specs; 172 ModuleSpec matched_module_spec; 173 using namespace llvm::sys::fs; 174 switch (get_file_type(file_spec.GetPath())) { 175 176 case file_type::directory_file: // Bundle directory? 177 { 178 CFCBundle bundle(path); 179 CFCReleaser<CFURLRef> bundle_exe_url( 180 bundle.CopyExecutableURL()); 181 if (bundle_exe_url.get()) { 182 if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(), 183 true, (UInt8 *)path, 184 sizeof(path) - 1)) { 185 FileSpec bundle_exe_file_spec(path); 186 FileSystem::Instance().Resolve(bundle_exe_file_spec); 187 if (ObjectFile::GetModuleSpecifications( 188 bundle_exe_file_spec, 0, 0, module_specs) && 189 module_specs.FindMatchingModuleSpec( 190 module_spec, matched_module_spec)) 191 192 { 193 ++items_found; 194 return_module_spec.GetFileSpec() = bundle_exe_file_spec; 195 if (log) { 196 log->Printf("Executable binary %s next to dSYM is " 197 "compatible; using", 198 path); 199 } 200 } 201 } 202 } 203 } break; 204 205 case file_type::fifo_file: // Forget pipes 206 case file_type::socket_file: // We can't process socket files 207 case file_type::file_not_found: // File doesn't exist... 208 case file_type::status_error: 209 break; 210 211 case file_type::type_unknown: 212 case file_type::regular_file: 213 case file_type::symlink_file: 214 case file_type::block_file: 215 case file_type::character_file: 216 if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, 217 module_specs) && 218 module_specs.FindMatchingModuleSpec(module_spec, 219 matched_module_spec)) 220 221 { 222 ++items_found; 223 return_module_spec.GetFileSpec() = file_spec; 224 if (log) { 225 log->Printf("Executable binary %s next to dSYM is " 226 "compatible; using", 227 path); 228 } 229 } 230 break; 231 } 232 } 233 } 234 } 235 } 236 } 237 } 238 } 239 #endif // #if !defined (__arm__) && !defined (__arm64__) && !defined 240 // (__aarch64__) 241 242 return items_found; 243 } 244 245 FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &dsym_bundle_fspec, 246 const lldb_private::UUID *uuid, 247 const ArchSpec *arch) { 248 char path[PATH_MAX]; 249 if (dsym_bundle_fspec.GetPath(path, sizeof(path)) == 0) 250 return {}; 251 252 ::strncat(path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1); 253 254 DIR *dirp = opendir(path); 255 if (!dirp) 256 return {}; 257 258 // Make sure we close the directory before exiting this scope. 259 CleanUp cleanup_dir(closedir, dirp); 260 261 FileSpec dsym_fspec; 262 dsym_fspec.GetDirectory().SetCString(path); 263 struct dirent *dp; 264 while ((dp = readdir(dirp)) != NULL) { 265 // Only search directories 266 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) { 267 if (dp->d_namlen == 1 && dp->d_name[0] == '.') 268 continue; 269 270 if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') 271 continue; 272 } 273 274 if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) { 275 dsym_fspec.GetFilename().SetCString(dp->d_name); 276 ModuleSpecList module_specs; 277 if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) { 278 ModuleSpec spec; 279 for (size_t i = 0; i < module_specs.GetSize(); ++i) { 280 bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec); 281 UNUSED_IF_ASSERT_DISABLED(got_spec); 282 assert(got_spec); 283 if ((uuid == NULL || 284 (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) && 285 (arch == NULL || 286 (spec.GetArchitecturePtr() && 287 spec.GetArchitecture().IsCompatibleMatch(*arch)))) { 288 return dsym_fspec; 289 } 290 } 291 } 292 } 293 } 294 295 return {}; 296 } 297 298 static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict, 299 ModuleSpec &module_spec) { 300 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); 301 bool success = false; 302 if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) { 303 std::string str; 304 CFStringRef cf_str; 305 CFDictionaryRef cf_dict; 306 307 cf_str = (CFStringRef)CFDictionaryGetValue( 308 (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable")); 309 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 310 if (CFCString::FileSystemRepresentation(cf_str, str)) { 311 module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native); 312 FileSystem::Instance().Resolve(module_spec.GetFileSpec()); 313 if (log) { 314 log->Printf( 315 "From dsymForUUID plist: Symbol rich executable is at '%s'", 316 str.c_str()); 317 } 318 } 319 } 320 321 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 322 CFSTR("DBGDSYMPath")); 323 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 324 if (CFCString::FileSystemRepresentation(cf_str, str)) { 325 module_spec.GetSymbolFileSpec().SetFile(str.c_str(), 326 FileSpec::Style::native); 327 FileSystem::Instance().Resolve(module_spec.GetFileSpec()); 328 success = true; 329 if (log) { 330 log->Printf("From dsymForUUID plist: dSYM is at '%s'", str.c_str()); 331 } 332 } 333 } 334 335 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 336 CFSTR("DBGArchitecture")); 337 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 338 if (CFCString::FileSystemRepresentation(cf_str, str)) 339 module_spec.GetArchitecture().SetTriple(str.c_str()); 340 } 341 342 std::string DBGBuildSourcePath; 343 std::string DBGSourcePath; 344 345 // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping. 346 // If DBGVersion 2, strip last two components of path remappings from 347 // entries to fix an issue with a specific set of 348 // DBGSourcePathRemapping entries that lldb worked 349 // with. 350 // If DBGVersion 3, trust & use the source path remappings as-is. 351 // 352 cf_dict = (CFDictionaryRef)CFDictionaryGetValue( 353 (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping")); 354 if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) { 355 // If we see DBGVersion with a value of 2 or higher, this is a new style 356 // DBGSourcePathRemapping dictionary 357 bool new_style_source_remapping_dictionary = false; 358 bool do_truncate_remapping_names = false; 359 std::string original_DBGSourcePath_value = DBGSourcePath; 360 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 361 CFSTR("DBGVersion")); 362 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 363 std::string version; 364 CFCString::FileSystemRepresentation(cf_str, version); 365 if (!version.empty() && isdigit(version[0])) { 366 int version_number = atoi(version.c_str()); 367 if (version_number > 1) { 368 new_style_source_remapping_dictionary = true; 369 } 370 if (version_number == 2) { 371 do_truncate_remapping_names = true; 372 } 373 } 374 } 375 376 CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict); 377 if (kv_pair_count > 0) { 378 CFStringRef *keys = 379 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef)); 380 CFStringRef *values = 381 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef)); 382 if (keys != nullptr && values != nullptr) { 383 CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict, 384 (const void **)keys, 385 (const void **)values); 386 } 387 for (CFIndex i = 0; i < kv_pair_count; i++) { 388 DBGBuildSourcePath.clear(); 389 DBGSourcePath.clear(); 390 if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) { 391 CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath); 392 } 393 if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) { 394 CFCString::FileSystemRepresentation(values[i], DBGSourcePath); 395 } 396 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) { 397 // In the "old style" DBGSourcePathRemapping dictionary, the 398 // DBGSourcePath values (the "values" half of key-value path pairs) 399 // were wrong. Ignore them and use the universal DBGSourcePath 400 // string from earlier. 401 if (new_style_source_remapping_dictionary && 402 !original_DBGSourcePath_value.empty()) { 403 DBGSourcePath = original_DBGSourcePath_value; 404 } 405 if (DBGSourcePath[0] == '~') { 406 FileSpec resolved_source_path(DBGSourcePath.c_str()); 407 FileSystem::Instance().Resolve(resolved_source_path); 408 DBGSourcePath = resolved_source_path.GetPath(); 409 } 410 // With version 2 of DBGSourcePathRemapping, we can chop off the 411 // last two filename parts from the source remapping and get a more 412 // general source remapping that still works. Add this as another 413 // option in addition to the full source path remap. 414 module_spec.GetSourceMappingList().Append( 415 ConstString(DBGBuildSourcePath.c_str()), 416 ConstString(DBGSourcePath.c_str()), true); 417 if (do_truncate_remapping_names) { 418 FileSpec build_path(DBGBuildSourcePath.c_str()); 419 FileSpec source_path(DBGSourcePath.c_str()); 420 build_path.RemoveLastPathComponent(); 421 build_path.RemoveLastPathComponent(); 422 source_path.RemoveLastPathComponent(); 423 source_path.RemoveLastPathComponent(); 424 module_spec.GetSourceMappingList().Append( 425 ConstString(build_path.GetPath().c_str()), 426 ConstString(source_path.GetPath().c_str()), true); 427 } 428 } 429 } 430 if (keys) 431 free(keys); 432 if (values) 433 free(values); 434 } 435 } 436 437 // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the 438 // source remappings list. 439 440 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 441 CFSTR("DBGBuildSourcePath")); 442 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 443 CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath); 444 } 445 446 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 447 CFSTR("DBGSourcePath")); 448 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 449 CFCString::FileSystemRepresentation(cf_str, DBGSourcePath); 450 } 451 452 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) { 453 if (DBGSourcePath[0] == '~') { 454 FileSpec resolved_source_path(DBGSourcePath.c_str()); 455 FileSystem::Instance().Resolve(resolved_source_path); 456 DBGSourcePath = resolved_source_path.GetPath(); 457 } 458 module_spec.GetSourceMappingList().Append( 459 ConstString(DBGBuildSourcePath.c_str()), 460 ConstString(DBGSourcePath.c_str()), true); 461 } 462 } 463 return success; 464 } 465 466 bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec, 467 bool force_lookup) { 468 bool success = false; 469 const UUID *uuid_ptr = module_spec.GetUUIDPtr(); 470 const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr(); 471 472 // It's expensive to check for the DBGShellCommands defaults setting, only do 473 // it once per lldb run and cache the result. 474 static bool g_have_checked_for_dbgshell_command = false; 475 static const char *g_dbgshell_command = NULL; 476 if (!g_have_checked_for_dbgshell_command) { 477 g_have_checked_for_dbgshell_command = true; 478 CFTypeRef defaults_setting = CFPreferencesCopyAppValue( 479 CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols")); 480 if (defaults_setting && 481 CFGetTypeID(defaults_setting) == CFStringGetTypeID()) { 482 char cstr_buf[PATH_MAX]; 483 if (CFStringGetCString((CFStringRef)defaults_setting, cstr_buf, 484 sizeof(cstr_buf), kCFStringEncodingUTF8)) { 485 g_dbgshell_command = 486 strdup(cstr_buf); // this malloc'ed memory will never be freed 487 } 488 } 489 if (defaults_setting) { 490 CFRelease(defaults_setting); 491 } 492 } 493 494 // When g_dbgshell_command is NULL, the user has not enabled the use of an 495 // external program to find the symbols, don't run it for them. 496 if (!force_lookup && g_dbgshell_command == NULL) { 497 return false; 498 } 499 500 if (uuid_ptr || 501 (file_spec_ptr && FileSystem::Instance().Exists(*file_spec_ptr))) { 502 static bool g_located_dsym_for_uuid_exe = false; 503 static bool g_dsym_for_uuid_exe_exists = false; 504 static char g_dsym_for_uuid_exe_path[PATH_MAX]; 505 if (!g_located_dsym_for_uuid_exe) { 506 g_located_dsym_for_uuid_exe = true; 507 const char *dsym_for_uuid_exe_path_cstr = 508 getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE"); 509 FileSpec dsym_for_uuid_exe_spec; 510 if (dsym_for_uuid_exe_path_cstr) { 511 dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr, 512 FileSpec::Style::native); 513 FileSystem::Instance().Resolve(dsym_for_uuid_exe_spec); 514 g_dsym_for_uuid_exe_exists = 515 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec); 516 } 517 518 if (!g_dsym_for_uuid_exe_exists) { 519 dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID", 520 FileSpec::Style::native); 521 g_dsym_for_uuid_exe_exists = 522 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec); 523 if (!g_dsym_for_uuid_exe_exists) { 524 long bufsize; 525 if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1) { 526 char buffer[bufsize]; 527 struct passwd pwd; 528 struct passwd *tilde_rc = NULL; 529 // we are a library so we need to use the reentrant version of 530 // getpwnam() 531 if (getpwnam_r("rc", &pwd, buffer, bufsize, &tilde_rc) == 0 && 532 tilde_rc && tilde_rc->pw_dir) { 533 std::string dsymforuuid_path(tilde_rc->pw_dir); 534 dsymforuuid_path += "/bin/dsymForUUID"; 535 dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(), 536 FileSpec::Style::native); 537 g_dsym_for_uuid_exe_exists = 538 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec); 539 } 540 } 541 } 542 } 543 if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL) { 544 dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command, 545 FileSpec::Style::native); 546 FileSystem::Instance().Resolve(dsym_for_uuid_exe_spec); 547 g_dsym_for_uuid_exe_exists = 548 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec); 549 } 550 551 if (g_dsym_for_uuid_exe_exists) 552 dsym_for_uuid_exe_spec.GetPath(g_dsym_for_uuid_exe_path, 553 sizeof(g_dsym_for_uuid_exe_path)); 554 } 555 if (g_dsym_for_uuid_exe_exists) { 556 std::string uuid_str; 557 char file_path[PATH_MAX]; 558 file_path[0] = '\0'; 559 560 if (uuid_ptr) 561 uuid_str = uuid_ptr->GetAsString(); 562 563 if (file_spec_ptr) 564 file_spec_ptr->GetPath(file_path, sizeof(file_path)); 565 566 StreamString command; 567 if (!uuid_str.empty()) 568 command.Printf("%s --ignoreNegativeCache --copyExecutable %s", 569 g_dsym_for_uuid_exe_path, uuid_str.c_str()); 570 else if (file_path[0] != '\0') 571 command.Printf("%s --ignoreNegativeCache --copyExecutable %s", 572 g_dsym_for_uuid_exe_path, file_path); 573 574 if (!command.GetString().empty()) { 575 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); 576 int exit_status = -1; 577 int signo = -1; 578 std::string command_output; 579 if (log) { 580 if (!uuid_str.empty()) 581 log->Printf("Calling %s with UUID %s to find dSYM", 582 g_dsym_for_uuid_exe_path, uuid_str.c_str()); 583 else if (file_path[0] != '\0') 584 log->Printf("Calling %s with file %s to find dSYM", 585 g_dsym_for_uuid_exe_path, file_path); 586 } 587 Status error = Host::RunShellCommand( 588 command.GetData(), 589 NULL, // current working directory 590 &exit_status, // Exit status 591 &signo, // Signal int * 592 &command_output, // Command output 593 std::chrono::seconds( 594 120), // Large timeout to allow for long dsym download times 595 false); // Don't run in a shell (we don't need shell expansion) 596 if (error.Success() && exit_status == 0 && !command_output.empty()) { 597 CFCData data(CFDataCreateWithBytesNoCopy( 598 NULL, (const UInt8 *)command_output.data(), command_output.size(), 599 kCFAllocatorNull)); 600 601 CFCReleaser<CFDictionaryRef> plist( 602 (CFDictionaryRef)::CFPropertyListCreateFromXMLData( 603 NULL, data.get(), kCFPropertyListImmutable, NULL)); 604 605 if (plist.get() && 606 CFGetTypeID(plist.get()) == CFDictionaryGetTypeID()) { 607 if (!uuid_str.empty()) { 608 CFCString uuid_cfstr(uuid_str.c_str()); 609 CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue( 610 plist.get(), uuid_cfstr.get()); 611 success = 612 GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec); 613 } else { 614 const CFIndex num_values = ::CFDictionaryGetCount(plist.get()); 615 if (num_values > 0) { 616 std::vector<CFStringRef> keys(num_values, NULL); 617 std::vector<CFDictionaryRef> values(num_values, NULL); 618 ::CFDictionaryGetKeysAndValues(plist.get(), NULL, 619 (const void **)&values[0]); 620 if (num_values == 1) { 621 return GetModuleSpecInfoFromUUIDDictionary(values[0], 622 module_spec); 623 } else { 624 for (CFIndex i = 0; i < num_values; ++i) { 625 ModuleSpec curr_module_spec; 626 if (GetModuleSpecInfoFromUUIDDictionary(values[i], 627 curr_module_spec)) { 628 if (module_spec.GetArchitecture().IsCompatibleMatch( 629 curr_module_spec.GetArchitecture())) { 630 module_spec = curr_module_spec; 631 return true; 632 } 633 } 634 } 635 } 636 } 637 } 638 } 639 } else { 640 if (log) { 641 if (!uuid_str.empty()) 642 log->Printf("Called %s on %s, no matches", 643 g_dsym_for_uuid_exe_path, uuid_str.c_str()); 644 else if (file_path[0] != '\0') 645 log->Printf("Called %s on %s, no matches", 646 g_dsym_for_uuid_exe_path, file_path); 647 } 648 } 649 } 650 } 651 } 652 return success; 653 } 654