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