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/CleanUp.h"
27 #include "lldb/Utility/DataBuffer.h"
28 #include "lldb/Utility/DataExtractor.h"
29 #include "lldb/Utility/Endian.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"
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()) {
49 log->Printf("Spotlight lookup for .dSYM bundles is disabled.");
53 return_module_spec = module_spec;
54 return_module_spec.GetFileSpec().Clear();
55 return_module_spec.GetSymbolFileSpec().Clear();
59 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
60 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
61 void *handle = dlopen ("/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols", RTLD_LAZY | RTLD_LOCAL);
63 g_dlsym_DBGCopyFullDSYMURLForUUID = (CFURLRef (*)(CFUUIDRef, CFURLRef)) dlsym (handle, "DBGCopyFullDSYMURLForUUID");
64 g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef (*)(CFURLRef)) dlsym (handle, "DBGCopyDSYMPropertyLists");
68 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
69 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
73 const UUID *uuid = module_spec.GetUUIDPtr();
74 const ArchSpec *arch = module_spec.GetArchitecturePtr();
76 if (uuid && uuid->IsValid()) {
77 // Try and locate the dSYM file using DebugSymbols first
78 llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes();
79 if (module_uuid.size() == 16) {
80 CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes(
81 NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3],
82 module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7],
83 module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11],
84 module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15]));
86 if (module_uuid_ref.get()) {
87 CFCReleaser<CFURLRef> exec_url;
88 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
90 char exec_cf_path[PATH_MAX];
91 if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
92 exec_url.reset(::CFURLCreateFromFileSystemRepresentation(
93 NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path),
97 CFCReleaser<CFURLRef> dsym_url(
98 g_dlsym_DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get()));
101 if (dsym_url.get()) {
102 if (::CFURLGetFileSystemRepresentation(
103 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
105 log->Printf("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)) {
127 log->Printf("DebugSymbols framework returned dSYM path of %s for "
128 "UUID %s -- looking for an exec file",
129 path, uuid->GetAsString().c_str());
133 CFCReleaser<CFDictionaryRef> dict(
134 g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get()));
135 CFDictionaryRef uuid_dict = NULL;
137 CFCString uuid_cfstr(uuid->GetAsString().c_str());
138 uuid_dict = static_cast<CFDictionaryRef>(
139 ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get()));
142 CFStringRef exec_cf_path =
143 static_cast<CFStringRef>(::CFDictionaryGetValue(
144 uuid_dict, CFSTR("DBGSymbolRichExecutable")));
145 if (exec_cf_path && ::CFStringGetFileSystemRepresentation(
146 exec_cf_path, path, sizeof(path))) {
148 log->Printf("plist bundle has exec path of %s for UUID %s",
149 path, uuid->GetAsString().c_str());
152 FileSpec exec_filespec(path);
154 FileSystem::Instance().Resolve(exec_filespec);
155 if (FileSystem::Instance().Exists(exec_filespec)) {
157 return_module_spec.GetFileSpec() = exec_filespec;
163 // No dictionary, check near the dSYM bundle for an executable that
165 if (::CFURLGetFileSystemRepresentation(
166 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
167 char *dsym_extension_pos = ::strstr(path, ".dSYM");
168 if (dsym_extension_pos) {
169 *dsym_extension_pos = '\0';
171 log->Printf("Looking for executable binary next to dSYM "
172 "bundle with name with name %s",
175 FileSpec file_spec(path);
176 FileSystem::Instance().Resolve(file_spec);
177 ModuleSpecList module_specs;
178 ModuleSpec matched_module_spec;
179 using namespace llvm::sys::fs;
180 switch (get_file_type(file_spec.GetPath())) {
182 case file_type::directory_file: // Bundle directory?
184 CFCBundle bundle(path);
185 CFCReleaser<CFURLRef> bundle_exe_url(
186 bundle.CopyExecutableURL());
187 if (bundle_exe_url.get()) {
188 if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(),
191 FileSpec bundle_exe_file_spec(path);
192 FileSystem::Instance().Resolve(bundle_exe_file_spec);
193 if (ObjectFile::GetModuleSpecifications(
194 bundle_exe_file_spec, 0, 0, module_specs) &&
195 module_specs.FindMatchingModuleSpec(
196 module_spec, matched_module_spec))
200 return_module_spec.GetFileSpec() = bundle_exe_file_spec;
202 log->Printf("Executable binary %s next to dSYM is "
211 case file_type::fifo_file: // Forget pipes
212 case file_type::socket_file: // We can't process socket files
213 case file_type::file_not_found: // File doesn't exist...
214 case file_type::status_error:
217 case file_type::type_unknown:
218 case file_type::regular_file:
219 case file_type::symlink_file:
220 case file_type::block_file:
221 case file_type::character_file:
222 if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0,
224 module_specs.FindMatchingModuleSpec(module_spec,
225 matched_module_spec))
229 return_module_spec.GetFileSpec() = file_spec;
231 log->Printf("Executable binary %s next to dSYM is "
249 FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &dsym_bundle_fspec,
250 const lldb_private::UUID *uuid,
251 const ArchSpec *arch) {
253 if (dsym_bundle_fspec.GetPath(path, sizeof(path)) == 0)
256 ::strncat(path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1);
258 DIR *dirp = opendir(path);
262 // Make sure we close the directory before exiting this scope.
263 CleanUp cleanup_dir(closedir, dirp);
266 dsym_fspec.GetDirectory().SetCString(path);
268 while ((dp = readdir(dirp)) != NULL) {
269 // Only search directories
270 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) {
271 if (dp->d_namlen == 1 && dp->d_name[0] == '.')
274 if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')
278 if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) {
279 dsym_fspec.GetFilename().SetCString(dp->d_name);
280 ModuleSpecList module_specs;
281 if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) {
283 for (size_t i = 0; i < module_specs.GetSize(); ++i) {
284 bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
285 UNUSED_IF_ASSERT_DISABLED(got_spec);
288 (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
290 (spec.GetArchitecturePtr() &&
291 spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
302 static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,
303 ModuleSpec &module_spec) {
304 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
305 bool success = false;
306 if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) {
309 CFDictionaryRef cf_dict;
311 cf_str = (CFStringRef)CFDictionaryGetValue(
312 (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable"));
313 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
314 if (CFCString::FileSystemRepresentation(cf_str, str)) {
315 module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native);
316 FileSystem::Instance().Resolve(module_spec.GetFileSpec());
319 "From dsymForUUID plist: Symbol rich executable is at '%s'",
325 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
326 CFSTR("DBGDSYMPath"));
327 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
328 if (CFCString::FileSystemRepresentation(cf_str, str)) {
329 module_spec.GetSymbolFileSpec().SetFile(str.c_str(),
330 FileSpec::Style::native);
331 FileSystem::Instance().Resolve(module_spec.GetFileSpec());
334 log->Printf("From dsymForUUID plist: dSYM is at '%s'", str.c_str());
339 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
340 CFSTR("DBGArchitecture"));
341 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
342 if (CFCString::FileSystemRepresentation(cf_str, str))
343 module_spec.GetArchitecture().SetTriple(str.c_str());
346 std::string DBGBuildSourcePath;
347 std::string DBGSourcePath;
349 // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping.
350 // If DBGVersion 2, strip last two components of path remappings from
351 // entries to fix an issue with a specific set of
352 // DBGSourcePathRemapping entries that lldb worked
354 // If DBGVersion 3, trust & use the source path remappings as-is.
356 cf_dict = (CFDictionaryRef)CFDictionaryGetValue(
357 (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping"));
358 if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {
359 // If we see DBGVersion with a value of 2 or higher, this is a new style
360 // DBGSourcePathRemapping dictionary
361 bool new_style_source_remapping_dictionary = false;
362 bool do_truncate_remapping_names = false;
363 std::string original_DBGSourcePath_value = DBGSourcePath;
364 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
365 CFSTR("DBGVersion"));
366 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
368 CFCString::FileSystemRepresentation(cf_str, version);
369 if (!version.empty() && isdigit(version[0])) {
370 int version_number = atoi(version.c_str());
371 if (version_number > 1) {
372 new_style_source_remapping_dictionary = true;
374 if (version_number == 2) {
375 do_truncate_remapping_names = true;
380 CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);
381 if (kv_pair_count > 0) {
383 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
384 CFStringRef *values =
385 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
386 if (keys != nullptr && values != nullptr) {
387 CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict,
389 (const void **)values);
391 for (CFIndex i = 0; i < kv_pair_count; i++) {
392 DBGBuildSourcePath.clear();
393 DBGSourcePath.clear();
394 if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) {
395 CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath);
397 if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
398 CFCString::FileSystemRepresentation(values[i], DBGSourcePath);
400 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
401 // In the "old style" DBGSourcePathRemapping dictionary, the
402 // DBGSourcePath values (the "values" half of key-value path pairs)
403 // were wrong. Ignore them and use the universal DBGSourcePath
404 // string from earlier.
405 if (new_style_source_remapping_dictionary &&
406 !original_DBGSourcePath_value.empty()) {
407 DBGSourcePath = original_DBGSourcePath_value;
409 if (DBGSourcePath[0] == '~') {
410 FileSpec resolved_source_path(DBGSourcePath.c_str());
411 FileSystem::Instance().Resolve(resolved_source_path);
412 DBGSourcePath = resolved_source_path.GetPath();
414 // With version 2 of DBGSourcePathRemapping, we can chop off the
415 // last two filename parts from the source remapping and get a more
416 // general source remapping that still works. Add this as another
417 // option in addition to the full source path remap.
418 module_spec.GetSourceMappingList().Append(
419 ConstString(DBGBuildSourcePath.c_str()),
420 ConstString(DBGSourcePath.c_str()), 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 ConstString(build_path.GetPath().c_str()),
430 ConstString(source_path.GetPath().c_str()), true);
441 // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the
442 // source remappings list.
444 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
445 CFSTR("DBGBuildSourcePath"));
446 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
447 CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
450 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
451 CFSTR("DBGSourcePath"));
452 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
453 CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
456 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
457 if (DBGSourcePath[0] == '~') {
458 FileSpec resolved_source_path(DBGSourcePath.c_str());
459 FileSystem::Instance().Resolve(resolved_source_path);
460 DBGSourcePath = resolved_source_path.GetPath();
462 module_spec.GetSourceMappingList().Append(
463 ConstString(DBGBuildSourcePath.c_str()),
464 ConstString(DBGSourcePath.c_str()), true);
470 bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
472 bool success = false;
473 const UUID *uuid_ptr = module_spec.GetUUIDPtr();
474 const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
476 // It's expensive to check for the DBGShellCommands defaults setting, only do
477 // it once per lldb run and cache the result.
478 static bool g_have_checked_for_dbgshell_command = false;
479 static const char *g_dbgshell_command = NULL;
480 if (!g_have_checked_for_dbgshell_command) {
481 g_have_checked_for_dbgshell_command = true;
482 CFTypeRef defaults_setting = CFPreferencesCopyAppValue(
483 CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols"));
484 if (defaults_setting &&
485 CFGetTypeID(defaults_setting) == CFStringGetTypeID()) {
486 char cstr_buf[PATH_MAX];
487 if (CFStringGetCString((CFStringRef)defaults_setting, cstr_buf,
488 sizeof(cstr_buf), kCFStringEncodingUTF8)) {
490 strdup(cstr_buf); // this malloc'ed memory will never be freed
493 if (defaults_setting) {
494 CFRelease(defaults_setting);
498 // When g_dbgshell_command is NULL, the user has not enabled the use of an
499 // external program to find the symbols, don't run it for them.
500 if (!force_lookup && g_dbgshell_command == NULL) {
505 (file_spec_ptr && FileSystem::Instance().Exists(*file_spec_ptr))) {
506 static bool g_located_dsym_for_uuid_exe = false;
507 static bool g_dsym_for_uuid_exe_exists = false;
508 static char g_dsym_for_uuid_exe_path[PATH_MAX];
509 if (!g_located_dsym_for_uuid_exe) {
510 g_located_dsym_for_uuid_exe = true;
511 const char *dsym_for_uuid_exe_path_cstr =
512 getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE");
513 FileSpec dsym_for_uuid_exe_spec;
514 if (dsym_for_uuid_exe_path_cstr) {
515 dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr,
516 FileSpec::Style::native);
517 FileSystem::Instance().Resolve(dsym_for_uuid_exe_spec);
518 g_dsym_for_uuid_exe_exists =
519 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
522 if (!g_dsym_for_uuid_exe_exists) {
523 dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID",
524 FileSpec::Style::native);
525 g_dsym_for_uuid_exe_exists =
526 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
527 if (!g_dsym_for_uuid_exe_exists) {
529 if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1) {
530 char buffer[bufsize];
532 struct passwd *tilde_rc = NULL;
533 // we are a library so we need to use the reentrant version of
535 if (getpwnam_r("rc", &pwd, buffer, bufsize, &tilde_rc) == 0 &&
536 tilde_rc && tilde_rc->pw_dir) {
537 std::string dsymforuuid_path(tilde_rc->pw_dir);
538 dsymforuuid_path += "/bin/dsymForUUID";
539 dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(),
540 FileSpec::Style::native);
541 g_dsym_for_uuid_exe_exists =
542 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
547 if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL) {
548 dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command,
549 FileSpec::Style::native);
550 FileSystem::Instance().Resolve(dsym_for_uuid_exe_spec);
551 g_dsym_for_uuid_exe_exists =
552 FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
555 if (g_dsym_for_uuid_exe_exists)
556 dsym_for_uuid_exe_spec.GetPath(g_dsym_for_uuid_exe_path,
557 sizeof(g_dsym_for_uuid_exe_path));
559 if (g_dsym_for_uuid_exe_exists) {
560 std::string uuid_str;
561 char file_path[PATH_MAX];
565 uuid_str = uuid_ptr->GetAsString();
568 file_spec_ptr->GetPath(file_path, sizeof(file_path));
570 StreamString command;
571 if (!uuid_str.empty())
572 command.Printf("%s --ignoreNegativeCache --copyExecutable %s",
573 g_dsym_for_uuid_exe_path, uuid_str.c_str());
574 else if (file_path[0] != '\0')
575 command.Printf("%s --ignoreNegativeCache --copyExecutable %s",
576 g_dsym_for_uuid_exe_path, file_path);
578 if (!command.GetString().empty()) {
579 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
580 int exit_status = -1;
582 std::string command_output;
584 if (!uuid_str.empty())
585 log->Printf("Calling %s with UUID %s to find dSYM",
586 g_dsym_for_uuid_exe_path, uuid_str.c_str());
587 else if (file_path[0] != '\0')
588 log->Printf("Calling %s with file %s to find dSYM",
589 g_dsym_for_uuid_exe_path, file_path);
591 Status error = Host::RunShellCommand(
593 NULL, // current working directory
594 &exit_status, // Exit status
595 &signo, // Signal int *
596 &command_output, // Command output
597 std::chrono::seconds(
598 120), // Large timeout to allow for long dsym download times
599 false); // Don't run in a shell (we don't need shell expansion)
600 if (error.Success() && exit_status == 0 && !command_output.empty()) {
601 CFCData data(CFDataCreateWithBytesNoCopy(
602 NULL, (const UInt8 *)command_output.data(), command_output.size(),
605 CFCReleaser<CFDictionaryRef> plist(
606 (CFDictionaryRef)::CFPropertyListCreateFromXMLData(
607 NULL, data.get(), kCFPropertyListImmutable, NULL));
610 CFGetTypeID(plist.get()) == CFDictionaryGetTypeID()) {
611 if (!uuid_str.empty()) {
612 CFCString uuid_cfstr(uuid_str.c_str());
613 CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue(
614 plist.get(), uuid_cfstr.get());
616 GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec);
618 const CFIndex num_values = ::CFDictionaryGetCount(plist.get());
619 if (num_values > 0) {
620 std::vector<CFStringRef> keys(num_values, NULL);
621 std::vector<CFDictionaryRef> values(num_values, NULL);
622 ::CFDictionaryGetKeysAndValues(plist.get(), NULL,
623 (const void **)&values[0]);
624 if (num_values == 1) {
625 return GetModuleSpecInfoFromUUIDDictionary(values[0],
628 for (CFIndex i = 0; i < num_values; ++i) {
629 ModuleSpec curr_module_spec;
630 if (GetModuleSpecInfoFromUUIDDictionary(values[i],
632 if (module_spec.GetArchitecture().IsCompatibleMatch(
633 curr_module_spec.GetArchitecture())) {
634 module_spec = curr_module_spec;
645 if (!uuid_str.empty())
646 log->Printf("Called %s on %s, no matches",
647 g_dsym_for_uuid_exe_path, uuid_str.c_str());
648 else if (file_path[0] != '\0')
649 log->Printf("Called %s on %s, no matches",
650 g_dsym_for_uuid_exe_path, file_path);