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