1 //===-- LocateSymbolFileMacOSX.cpp ------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Symbol/LocateSymbolFile.h"
15 #include <CoreFoundation/CoreFoundation.h>
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"
35 #include "llvm/ADT/ScopeExit.h"
36 #include "llvm/Support/FileSystem.h"
39 using namespace lldb_private;
41 static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)(CFUUIDRef uuid, CFURLRef exec_url) = nullptr;
42 static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) = nullptr;
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.");
52 return_module_spec = module_spec;
53 return_module_spec.GetFileSpec().Clear();
54 return_module_spec.GetSymbolFileSpec().Clear();
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);
62 g_dlsym_DBGCopyFullDSYMURLForUUID = (CFURLRef (*)(CFUUIDRef, CFURLRef)) dlsym (handle, "DBGCopyFullDSYMURLForUUID");
63 g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef (*)(CFURLRef)) dlsym (handle, "DBGCopyDSYMPropertyLists");
67 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
68 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
72 const UUID *uuid = module_spec.GetUUIDPtr();
73 const ArchSpec *arch = module_spec.GetArchitecturePtr();
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]));
85 if (module_uuid_ref.get()) {
86 CFCReleaser<CFURLRef> exec_url;
87 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
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),
96 CFCReleaser<CFURLRef> dsym_url(
97 g_dlsym_DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get()));
100 if (dsym_url.get()) {
101 if (::CFURLGetFileSystemRepresentation(
102 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
105 "DebugSymbols framework returned dSYM path of %s for "
106 "UUID %s -- looking for the dSYM",
107 path, uuid->GetAsString().c_str());
109 FileSpec dsym_filespec(path);
111 FileSystem::Instance().Resolve(dsym_filespec);
113 if (FileSystem::Instance().IsDirectory(dsym_filespec)) {
115 Symbols::FindSymbolFileInBundle(dsym_filespec, uuid, arch);
120 return_module_spec.GetSymbolFileSpec() = dsym_filespec;
123 bool success = false;
125 if (::CFURLGetFileSystemRepresentation(
126 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
128 "DebugSymbols framework returned dSYM path of %s for "
129 "UUID %s -- looking for an exec file",
130 path, uuid->GetAsString().c_str());
134 CFCReleaser<CFDictionaryRef> dict(
135 g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get()));
136 CFDictionaryRef uuid_dict = NULL;
138 CFCString uuid_cfstr(uuid->GetAsString().c_str());
139 uuid_dict = static_cast<CFDictionaryRef>(
140 ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get()));
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))) {
149 LLDB_LOGF(log, "plist bundle has exec path of %s for UUID %s",
150 path, uuid->GetAsString().c_str());
153 FileSpec exec_filespec(path);
155 FileSystem::Instance().Resolve(exec_filespec);
156 if (FileSystem::Instance().Exists(exec_filespec)) {
158 return_module_spec.GetFileSpec() = exec_filespec;
164 // No dictionary, check near the dSYM bundle for an executable that
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';
173 "Looking for executable binary next to dSYM "
174 "bundle with name with name %s",
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())) {
184 case file_type::directory_file: // Bundle directory?
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(),
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))
202 return_module_spec.GetFileSpec() = bundle_exe_file_spec;
205 "Executable binary %s next to dSYM is "
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:
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,
227 module_specs.FindMatchingModuleSpec(module_spec,
228 matched_module_spec))
232 return_module_spec.GetFileSpec() = file_spec;
235 "Executable binary %s next to dSYM is "
253 FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &dsym_bundle_fspec,
254 const lldb_private::UUID *uuid,
255 const ArchSpec *arch) {
257 if (dsym_bundle_fspec.GetPath(path, sizeof(path)) == 0)
260 ::strncat(path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1);
262 DIR *dirp = opendir(path);
266 // Make sure we close the directory before exiting this scope.
267 auto cleanup_dir = llvm::make_scope_exit([&]() { closedir(dirp); });
270 dsym_fspec.GetDirectory().SetCString(path);
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] == '.')
278 if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
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)) {
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);
292 (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
294 (spec.GetArchitecturePtr() &&
295 spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
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()) {
313 CFDictionaryRef cf_dict;
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());
323 "From dsymForUUID plist: Symbol rich executable is at '%s'",
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());
338 LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'",
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());
351 std::string DBGBuildSourcePath;
352 std::string DBGSourcePath;
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
359 // If DBGVersion 3, trust & use the source path remappings as-is.
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()) {
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;
379 if (version_number == 2) {
380 do_truncate_remapping_names = true;
385 CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);
386 if (kv_pair_count > 0) {
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,
394 (const void **)values);
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);
402 if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
403 CFCString::FileSystemRepresentation(values[i], DBGSourcePath);
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;
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();
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);
446 // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the
447 // source remappings list.
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);
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);
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();
467 module_spec.GetSourceMappingList().Append(
468 ConstString(DBGBuildSourcePath.c_str()),
469 ConstString(DBGSourcePath.c_str()), true);
475 bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
477 bool success = false;
478 const UUID *uuid_ptr = module_spec.GetUUIDPtr();
479 const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
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)) {
495 strdup(cstr_buf); // this malloc'ed memory will never be freed
498 if (defaults_setting) {
499 CFRelease(defaults_setting);
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) {
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);
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) {
534 if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1) {
535 char buffer[bufsize];
537 struct passwd *tilde_rc = NULL;
538 // we are a library so we need to use the reentrant version of
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);
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);
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));
564 if (g_dsym_for_uuid_exe_exists) {
565 std::string uuid_str;
566 char file_path[PATH_MAX];
570 uuid_str = uuid_ptr->GetAsString();
573 file_spec_ptr->GetPath(file_path, sizeof(file_path));
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);
583 if (!command.GetString().empty()) {
584 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
585 int exit_status = -1;
587 std::string command_output;
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);
596 Status error = Host::RunShellCommand(
598 NULL, // 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(),
610 CFCReleaser<CFDictionaryRef> plist(
611 (CFDictionaryRef)::CFPropertyListCreateFromXMLData(
612 NULL, data.get(), kCFPropertyListImmutable, NULL));
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());
621 GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec);
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],
633 for (CFIndex i = 0; i < num_values; ++i) {
634 ModuleSpec curr_module_spec;
635 if (GetModuleSpecInfoFromUUIDDictionary(values[i],
637 if (module_spec.GetArchitecture().IsCompatibleMatch(
638 curr_module_spec.GetArchitecture())) {
639 module_spec = curr_module_spec;
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);