1 //===-- LocateSymbolFileMacOSX.cpp ----------------------------------------===//
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/Module.h"
22 #include "lldb/Core/ModuleList.h"
23 #include "lldb/Core/ModuleSpec.h"
24 #include "lldb/Host/Host.h"
25 #include "lldb/Host/HostInfo.h"
26 #include "lldb/Symbol/ObjectFile.h"
27 #include "lldb/Utility/ArchSpec.h"
28 #include "lldb/Utility/DataBuffer.h"
29 #include "lldb/Utility/DataExtractor.h"
30 #include "lldb/Utility/Endian.h"
31 #include "lldb/Utility/LLDBLog.h"
32 #include "lldb/Utility/Log.h"
33 #include "lldb/Utility/StreamString.h"
34 #include "lldb/Utility/Timer.h"
35 #include "lldb/Utility/UUID.h"
36 #include "mach/machine.h"
38 #include "llvm/ADT/ScopeExit.h"
39 #include "llvm/Support/FileSystem.h"
42 using namespace lldb_private
;
44 static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID
)(
45 CFUUIDRef uuid
, CFURLRef exec_url
) = nullptr;
46 static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists
)(CFURLRef dsym_url
) =
49 int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec
&module_spec
,
50 ModuleSpec
&return_module_spec
) {
51 Log
*log
= GetLog(LLDBLog::Host
);
52 if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
53 LLDB_LOGF(log
, "Spotlight lookup for .dSYM bundles is disabled.");
57 return_module_spec
= module_spec
;
58 return_module_spec
.GetFileSpec().Clear();
59 return_module_spec
.GetSymbolFileSpec().Clear();
61 const UUID
*uuid
= module_spec
.GetUUIDPtr();
62 const ArchSpec
*arch
= module_spec
.GetArchitecturePtr();
66 if (g_dlsym_DBGCopyFullDSYMURLForUUID
== nullptr ||
67 g_dlsym_DBGCopyDSYMPropertyLists
== nullptr) {
68 void *handle
= dlopen(
69 "/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols",
70 RTLD_LAZY
| RTLD_LOCAL
);
72 g_dlsym_DBGCopyFullDSYMURLForUUID
=
73 (CFURLRef(*)(CFUUIDRef
, CFURLRef
))dlsym(handle
,
74 "DBGCopyFullDSYMURLForUUID");
75 g_dlsym_DBGCopyDSYMPropertyLists
= (CFDictionaryRef(*)(CFURLRef
))dlsym(
76 handle
, "DBGCopyDSYMPropertyLists");
80 if (g_dlsym_DBGCopyFullDSYMURLForUUID
== nullptr ||
81 g_dlsym_DBGCopyDSYMPropertyLists
== nullptr) {
85 if (uuid
&& uuid
->IsValid()) {
86 // Try and locate the dSYM file using DebugSymbols first
87 llvm::ArrayRef
<uint8_t> module_uuid
= uuid
->GetBytes();
88 if (module_uuid
.size() == 16) {
89 CFCReleaser
<CFUUIDRef
> module_uuid_ref(::CFUUIDCreateWithBytes(
90 NULL
, module_uuid
[0], module_uuid
[1], module_uuid
[2], module_uuid
[3],
91 module_uuid
[4], module_uuid
[5], module_uuid
[6], module_uuid
[7],
92 module_uuid
[8], module_uuid
[9], module_uuid
[10], module_uuid
[11],
93 module_uuid
[12], module_uuid
[13], module_uuid
[14], module_uuid
[15]));
95 if (module_uuid_ref
.get()) {
96 CFCReleaser
<CFURLRef
> exec_url
;
97 const FileSpec
*exec_fspec
= module_spec
.GetFileSpecPtr();
99 char exec_cf_path
[PATH_MAX
];
100 if (exec_fspec
->GetPath(exec_cf_path
, sizeof(exec_cf_path
)))
101 exec_url
.reset(::CFURLCreateFromFileSystemRepresentation(
102 NULL
, (const UInt8
*)exec_cf_path
, strlen(exec_cf_path
),
106 CFCReleaser
<CFURLRef
> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID(
107 module_uuid_ref
.get(), exec_url
.get()));
110 if (dsym_url
.get()) {
111 if (::CFURLGetFileSystemRepresentation(
112 dsym_url
.get(), true, (UInt8
*)path
, sizeof(path
) - 1)) {
114 "DebugSymbols framework returned dSYM path of %s for "
115 "UUID %s -- looking for the dSYM",
116 path
, uuid
->GetAsString().c_str());
117 FileSpec
dsym_filespec(path
);
119 FileSystem::Instance().Resolve(dsym_filespec
);
121 if (FileSystem::Instance().IsDirectory(dsym_filespec
)) {
123 Symbols::FindSymbolFileInBundle(dsym_filespec
, uuid
, arch
);
128 return_module_spec
.GetSymbolFileSpec() = dsym_filespec
;
131 bool success
= false;
133 if (::CFURLGetFileSystemRepresentation(
134 dsym_url
.get(), true, (UInt8
*)path
, sizeof(path
) - 1)) {
136 "DebugSymbols framework returned dSYM path of %s for "
137 "UUID %s -- looking for an exec file",
138 path
, uuid
->GetAsString().c_str());
142 CFCReleaser
<CFDictionaryRef
> dict(
143 g_dlsym_DBGCopyDSYMPropertyLists(dsym_url
.get()));
144 CFDictionaryRef uuid_dict
= NULL
;
146 CFCString
uuid_cfstr(uuid
->GetAsString().c_str());
147 uuid_dict
= static_cast<CFDictionaryRef
>(
148 ::CFDictionaryGetValue(dict
.get(), uuid_cfstr
.get()));
151 // Check to see if we have the file on the local filesystem.
152 if (FileSystem::Instance().Exists(module_spec
.GetFileSpec())) {
154 exe_spec
.GetFileSpec() = module_spec
.GetFileSpec();
155 exe_spec
.GetUUID() = module_spec
.GetUUID();
157 module_sp
.reset(new Module(exe_spec
));
158 if (module_sp
&& module_sp
->GetObjectFile() &&
159 module_sp
->MatchesModuleSpec(exe_spec
)) {
161 return_module_spec
.GetFileSpec() = module_spec
.GetFileSpec();
162 LLDB_LOGF(log
, "using original binary filepath %s for UUID %s",
163 module_spec
.GetFileSpec().GetPath().c_str(),
164 uuid
->GetAsString().c_str());
169 // Check if the requested image is in our shared cache.
171 SharedCacheImageInfo image_info
= HostInfo::GetSharedCacheImageInfo(
172 module_spec
.GetFileSpec().GetPath());
174 // If we found it and it has the correct UUID, let's proceed with
175 // creating a module from the memory contents.
176 if (image_info
.uuid
&& (!module_spec
.GetUUID() ||
177 module_spec
.GetUUID() == image_info
.uuid
)) {
179 return_module_spec
.GetFileSpec() = module_spec
.GetFileSpec();
181 "using binary from shared cache for filepath %s for "
183 module_spec
.GetFileSpec().GetPath().c_str(),
184 uuid
->GetAsString().c_str());
189 // Use the DBGSymbolRichExecutable filepath if present
190 if (!success
&& uuid_dict
) {
191 CFStringRef exec_cf_path
=
192 static_cast<CFStringRef
>(::CFDictionaryGetValue(
193 uuid_dict
, CFSTR("DBGSymbolRichExecutable")));
194 if (exec_cf_path
&& ::CFStringGetFileSystemRepresentation(
195 exec_cf_path
, path
, sizeof(path
))) {
196 LLDB_LOGF(log
, "plist bundle has exec path of %s for UUID %s",
197 path
, uuid
->GetAsString().c_str());
199 FileSpec
exec_filespec(path
);
201 FileSystem::Instance().Resolve(exec_filespec
);
202 if (FileSystem::Instance().Exists(exec_filespec
)) {
204 return_module_spec
.GetFileSpec() = exec_filespec
;
209 // Look next to the dSYM for the binary file.
211 if (::CFURLGetFileSystemRepresentation(
212 dsym_url
.get(), true, (UInt8
*)path
, sizeof(path
) - 1)) {
213 char *dsym_extension_pos
= ::strstr(path
, ".dSYM");
214 if (dsym_extension_pos
) {
215 *dsym_extension_pos
= '\0';
217 "Looking for executable binary next to dSYM "
218 "bundle with name with name %s",
220 FileSpec
file_spec(path
);
221 FileSystem::Instance().Resolve(file_spec
);
222 ModuleSpecList module_specs
;
223 ModuleSpec matched_module_spec
;
224 using namespace llvm::sys::fs
;
225 switch (get_file_type(file_spec
.GetPath())) {
227 case file_type::directory_file
: // Bundle directory?
229 CFCBundle
bundle(path
);
230 CFCReleaser
<CFURLRef
> bundle_exe_url(
231 bundle
.CopyExecutableURL());
232 if (bundle_exe_url
.get()) {
233 if (::CFURLGetFileSystemRepresentation(bundle_exe_url
.get(),
236 FileSpec
bundle_exe_file_spec(path
);
237 FileSystem::Instance().Resolve(bundle_exe_file_spec
);
238 if (ObjectFile::GetModuleSpecifications(
239 bundle_exe_file_spec
, 0, 0, module_specs
) &&
240 module_specs
.FindMatchingModuleSpec(
241 module_spec
, matched_module_spec
))
245 return_module_spec
.GetFileSpec() = bundle_exe_file_spec
;
247 "Executable binary %s next to dSYM is "
255 case file_type::fifo_file
: // Forget pipes
256 case file_type::socket_file
: // We can't process socket files
257 case file_type::file_not_found
: // File doesn't exist...
258 case file_type::status_error
:
261 case file_type::type_unknown
:
262 case file_type::regular_file
:
263 case file_type::symlink_file
:
264 case file_type::block_file
:
265 case file_type::character_file
:
266 if (ObjectFile::GetModuleSpecifications(file_spec
, 0, 0,
268 module_specs
.FindMatchingModuleSpec(module_spec
,
269 matched_module_spec
))
273 return_module_spec
.GetFileSpec() = file_spec
;
275 "Executable binary %s next to dSYM is "
292 FileSpec
Symbols::FindSymbolFileInBundle(const FileSpec
&dsym_bundle_fspec
,
293 const lldb_private::UUID
*uuid
,
294 const ArchSpec
*arch
) {
295 std::string dsym_bundle_path
= dsym_bundle_fspec
.GetPath();
296 llvm::SmallString
<128> buffer(dsym_bundle_path
);
297 llvm::sys::path::append(buffer
, "Contents", "Resources", "DWARF");
300 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> vfs
=
301 FileSystem::Instance().GetVirtualFileSystem();
302 llvm::vfs::recursive_directory_iterator
Iter(*vfs
, buffer
.str(), EC
);
303 llvm::vfs::recursive_directory_iterator End
;
304 for (; Iter
!= End
&& !EC
; Iter
.increment(EC
)) {
305 llvm::ErrorOr
<llvm::vfs::Status
> Status
= vfs
->status(Iter
->path());
306 if (Status
->isDirectory())
309 FileSpec
dsym_fspec(Iter
->path());
310 ModuleSpecList module_specs
;
311 if (ObjectFile::GetModuleSpecifications(dsym_fspec
, 0, 0, module_specs
)) {
313 for (size_t i
= 0; i
< module_specs
.GetSize(); ++i
) {
314 bool got_spec
= module_specs
.GetModuleSpecAtIndex(i
, spec
);
315 assert(got_spec
); // The call has side-effects so can't be inlined.
316 UNUSED_IF_ASSERT_DISABLED(got_spec
);
317 if ((uuid
== nullptr ||
318 (spec
.GetUUIDPtr() && spec
.GetUUID() == *uuid
)) &&
320 (spec
.GetArchitecturePtr() &&
321 spec
.GetArchitecture().IsCompatibleMatch(*arch
)))) {
331 static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict
,
332 ModuleSpec
&module_spec
,
334 const std::string
&command
) {
335 Log
*log
= GetLog(LLDBLog::Host
);
336 bool success
= false;
337 if (uuid_dict
!= NULL
&& CFGetTypeID(uuid_dict
) == CFDictionaryGetTypeID()) {
340 CFDictionaryRef cf_dict
;
342 cf_str
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)uuid_dict
,
344 if (cf_str
&& CFGetTypeID(cf_str
) == CFStringGetTypeID()) {
345 if (CFCString::FileSystemRepresentation(cf_str
, str
)) {
346 std::string errorstr
= command
;
349 error
.SetErrorString(errorstr
);
353 cf_str
= (CFStringRef
)CFDictionaryGetValue(
354 (CFDictionaryRef
)uuid_dict
, CFSTR("DBGSymbolRichExecutable"));
355 if (cf_str
&& CFGetTypeID(cf_str
) == CFStringGetTypeID()) {
356 if (CFCString::FileSystemRepresentation(cf_str
, str
)) {
357 module_spec
.GetFileSpec().SetFile(str
.c_str(), FileSpec::Style::native
);
358 FileSystem::Instance().Resolve(module_spec
.GetFileSpec());
360 "From dsymForUUID plist: Symbol rich executable is at '%s'",
365 cf_str
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)uuid_dict
,
366 CFSTR("DBGDSYMPath"));
367 if (cf_str
&& CFGetTypeID(cf_str
) == CFStringGetTypeID()) {
368 if (CFCString::FileSystemRepresentation(cf_str
, str
)) {
369 module_spec
.GetSymbolFileSpec().SetFile(str
.c_str(),
370 FileSpec::Style::native
);
371 FileSystem::Instance().Resolve(module_spec
.GetFileSpec());
373 LLDB_LOGF(log
, "From dsymForUUID plist: dSYM is at '%s'", str
.c_str());
377 std::string DBGBuildSourcePath
;
378 std::string DBGSourcePath
;
380 // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping.
381 // If DBGVersion 2, strip last two components of path remappings from
382 // entries to fix an issue with a specific set of
383 // DBGSourcePathRemapping entries that lldb worked
385 // If DBGVersion 3, trust & use the source path remappings as-is.
387 cf_dict
= (CFDictionaryRef
)CFDictionaryGetValue(
388 (CFDictionaryRef
)uuid_dict
, CFSTR("DBGSourcePathRemapping"));
389 if (cf_dict
&& CFGetTypeID(cf_dict
) == CFDictionaryGetTypeID()) {
390 // If we see DBGVersion with a value of 2 or higher, this is a new style
391 // DBGSourcePathRemapping dictionary
392 bool new_style_source_remapping_dictionary
= false;
393 bool do_truncate_remapping_names
= false;
394 std::string original_DBGSourcePath_value
= DBGSourcePath
;
395 cf_str
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)uuid_dict
,
396 CFSTR("DBGVersion"));
397 if (cf_str
&& CFGetTypeID(cf_str
) == CFStringGetTypeID()) {
399 CFCString::FileSystemRepresentation(cf_str
, version
);
400 if (!version
.empty() && isdigit(version
[0])) {
401 int version_number
= atoi(version
.c_str());
402 if (version_number
> 1) {
403 new_style_source_remapping_dictionary
= true;
405 if (version_number
== 2) {
406 do_truncate_remapping_names
= true;
411 CFIndex kv_pair_count
= CFDictionaryGetCount((CFDictionaryRef
)uuid_dict
);
412 if (kv_pair_count
> 0) {
414 (CFStringRef
*)malloc(kv_pair_count
* sizeof(CFStringRef
));
415 CFStringRef
*values
=
416 (CFStringRef
*)malloc(kv_pair_count
* sizeof(CFStringRef
));
417 if (keys
!= nullptr && values
!= nullptr) {
418 CFDictionaryGetKeysAndValues((CFDictionaryRef
)uuid_dict
,
420 (const void **)values
);
422 for (CFIndex i
= 0; i
< kv_pair_count
; i
++) {
423 DBGBuildSourcePath
.clear();
424 DBGSourcePath
.clear();
425 if (keys
[i
] && CFGetTypeID(keys
[i
]) == CFStringGetTypeID()) {
426 CFCString::FileSystemRepresentation(keys
[i
], DBGBuildSourcePath
);
428 if (values
[i
] && CFGetTypeID(values
[i
]) == CFStringGetTypeID()) {
429 CFCString::FileSystemRepresentation(values
[i
], DBGSourcePath
);
431 if (!DBGBuildSourcePath
.empty() && !DBGSourcePath
.empty()) {
432 // In the "old style" DBGSourcePathRemapping dictionary, the
433 // DBGSourcePath values (the "values" half of key-value path pairs)
434 // were wrong. Ignore them and use the universal DBGSourcePath
435 // string from earlier.
436 if (new_style_source_remapping_dictionary
&&
437 !original_DBGSourcePath_value
.empty()) {
438 DBGSourcePath
= original_DBGSourcePath_value
;
440 if (DBGSourcePath
[0] == '~') {
441 FileSpec
resolved_source_path(DBGSourcePath
.c_str());
442 FileSystem::Instance().Resolve(resolved_source_path
);
443 DBGSourcePath
= resolved_source_path
.GetPath();
445 // With version 2 of DBGSourcePathRemapping, we can chop off the
446 // last two filename parts from the source remapping and get a more
447 // general source remapping that still works. Add this as another
448 // option in addition to the full source path remap.
449 module_spec
.GetSourceMappingList().Append(DBGBuildSourcePath
,
450 DBGSourcePath
, true);
451 if (do_truncate_remapping_names
) {
452 FileSpec
build_path(DBGBuildSourcePath
.c_str());
453 FileSpec
source_path(DBGSourcePath
.c_str());
454 build_path
.RemoveLastPathComponent();
455 build_path
.RemoveLastPathComponent();
456 source_path
.RemoveLastPathComponent();
457 source_path
.RemoveLastPathComponent();
458 module_spec
.GetSourceMappingList().Append(
459 build_path
.GetPath(), source_path
.GetPath(), true);
470 // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the
471 // source remappings list.
473 cf_str
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)uuid_dict
,
474 CFSTR("DBGBuildSourcePath"));
475 if (cf_str
&& CFGetTypeID(cf_str
) == CFStringGetTypeID()) {
476 CFCString::FileSystemRepresentation(cf_str
, DBGBuildSourcePath
);
479 cf_str
= (CFStringRef
)CFDictionaryGetValue((CFDictionaryRef
)uuid_dict
,
480 CFSTR("DBGSourcePath"));
481 if (cf_str
&& CFGetTypeID(cf_str
) == CFStringGetTypeID()) {
482 CFCString::FileSystemRepresentation(cf_str
, DBGSourcePath
);
485 if (!DBGBuildSourcePath
.empty() && !DBGSourcePath
.empty()) {
486 if (DBGSourcePath
[0] == '~') {
487 FileSpec
resolved_source_path(DBGSourcePath
.c_str());
488 FileSystem::Instance().Resolve(resolved_source_path
);
489 DBGSourcePath
= resolved_source_path
.GetPath();
491 module_spec
.GetSourceMappingList().Append(DBGBuildSourcePath
,
492 DBGSourcePath
, true);
498 /// It's expensive to check for the DBGShellCommands defaults setting. Only do
499 /// it once per lldb run and cache the result.
500 static llvm::StringRef
GetDbgShellCommand() {
501 static std::once_flag g_once_flag
;
502 static std::string g_dbgshell_command
;
503 std::call_once(g_once_flag
, [&]() {
504 CFTypeRef defaults_setting
= CFPreferencesCopyAppValue(
505 CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols"));
506 if (defaults_setting
&&
507 CFGetTypeID(defaults_setting
) == CFStringGetTypeID()) {
508 char buffer
[PATH_MAX
];
509 if (CFStringGetCString((CFStringRef
)defaults_setting
, buffer
,
510 sizeof(buffer
), kCFStringEncodingUTF8
)) {
511 g_dbgshell_command
= buffer
;
514 if (defaults_setting
) {
515 CFRelease(defaults_setting
);
518 return g_dbgshell_command
;
521 /// Get the dsymForUUID executable and cache the result so we don't end up
522 /// stat'ing the binary over and over.
523 static FileSpec
GetDsymForUUIDExecutable() {
524 // The LLDB_APPLE_DSYMFORUUID_EXECUTABLE environment variable is used by the
525 // test suite to override the dsymForUUID location. Because we must be able
526 // to change the value within a single test, don't bother caching it.
527 if (const char *dsymForUUID_env
=
528 getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) {
529 FileSpec
dsymForUUID_executable(dsymForUUID_env
);
530 FileSystem::Instance().Resolve(dsymForUUID_executable
);
531 if (FileSystem::Instance().Exists(dsymForUUID_executable
))
532 return dsymForUUID_executable
;
535 static std::once_flag g_once_flag
;
536 static FileSpec g_dsymForUUID_executable
;
537 std::call_once(g_once_flag
, [&]() {
538 // Try the DBGShellCommand.
539 llvm::StringRef dbgshell_command
= GetDbgShellCommand();
540 if (!dbgshell_command
.empty()) {
541 g_dsymForUUID_executable
= FileSpec(dbgshell_command
);
542 FileSystem::Instance().Resolve(g_dsymForUUID_executable
);
543 if (FileSystem::Instance().Exists(g_dsymForUUID_executable
))
547 // Try dsymForUUID in /usr/local/bin
549 g_dsymForUUID_executable
= FileSpec("/usr/local/bin/dsymForUUID");
550 if (FileSystem::Instance().Exists(g_dsymForUUID_executable
))
554 // We couldn't find the dsymForUUID binary.
555 g_dsymForUUID_executable
= {};
557 return g_dsymForUUID_executable
;
560 bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec
&module_spec
,
561 Status
&error
, bool force_lookup
,
562 bool copy_executable
) {
563 const UUID
*uuid_ptr
= module_spec
.GetUUIDPtr();
564 const FileSpec
*file_spec_ptr
= module_spec
.GetFileSpecPtr();
566 // If \a dbgshell_command is set, the user has specified
567 // forced symbol lookup via that command. We'll get the
568 // path back from GetDsymForUUIDExecutable() later.
569 llvm::StringRef dbgshell_command
= GetDbgShellCommand();
571 // If forced lookup isn't set, by the user's \a dbgshell_command or
572 // by the \a force_lookup argument, exit this method.
573 if (!force_lookup
&& dbgshell_command
.empty())
576 // We need a UUID or valid existing FileSpec.
578 (!file_spec_ptr
|| !FileSystem::Instance().Exists(*file_spec_ptr
)))
581 // We need a dsymForUUID binary or an equivalent executable/script.
582 FileSpec dsymForUUID_exe_spec
= GetDsymForUUIDExecutable();
583 if (!dsymForUUID_exe_spec
)
586 const std::string dsymForUUID_exe_path
= dsymForUUID_exe_spec
.GetPath();
587 const std::string uuid_str
= uuid_ptr
? uuid_ptr
->GetAsString() : "";
588 const std::string file_path_str
=
589 file_spec_ptr
? file_spec_ptr
->GetPath() : "";
591 Log
*log
= GetLog(LLDBLog::Host
);
593 // Create the dsymForUUID command.
594 StreamString command
;
595 const char *copy_executable_arg
= copy_executable
? "--copyExecutable " : "";
596 if (!uuid_str
.empty()) {
597 command
.Printf("%s --ignoreNegativeCache %s%s",
598 dsymForUUID_exe_path
.c_str(), copy_executable_arg
,
600 LLDB_LOGF(log
, "Calling %s with UUID %s to find dSYM: %s",
601 dsymForUUID_exe_path
.c_str(), uuid_str
.c_str(),
602 command
.GetString().data());
603 } else if (!file_path_str
.empty()) {
604 command
.Printf("%s --ignoreNegativeCache %s%s",
605 dsymForUUID_exe_path
.c_str(), copy_executable_arg
,
606 file_path_str
.c_str());
607 LLDB_LOGF(log
, "Calling %s with file %s to find dSYM: %s",
608 dsymForUUID_exe_path
.c_str(), file_path_str
.c_str(),
609 command
.GetString().data());
614 // Invoke dsymForUUID.
615 int exit_status
= -1;
617 std::string command_output
;
618 error
= Host::RunShellCommand(
620 FileSpec(), // current working directory
621 &exit_status
, // Exit status
622 &signo
, // Signal int *
623 &command_output
, // Command output
624 std::chrono::seconds(
625 640), // Large timeout to allow for long dsym download times
626 false); // Don't run in a shell (we don't need shell expansion)
628 if (error
.Fail() || exit_status
!= 0 || command_output
.empty()) {
629 LLDB_LOGF(log
, "'%s' failed (exit status: %d, error: '%s', output: '%s')",
630 command
.GetData(), exit_status
, error
.AsCString(),
631 command_output
.c_str());
636 CFDataCreateWithBytesNoCopy(NULL
, (const UInt8
*)command_output
.data(),
637 command_output
.size(), kCFAllocatorNull
));
639 CFCReleaser
<CFDictionaryRef
> plist(
640 (CFDictionaryRef
)::CFPropertyListCreateWithData(
641 NULL
, data
.get(), kCFPropertyListImmutable
, NULL
, NULL
));
644 LLDB_LOGF(log
, "'%s' failed: output is not a valid plist",
649 if (CFGetTypeID(plist
.get()) != CFDictionaryGetTypeID()) {
650 LLDB_LOGF(log
, "'%s' failed: output plist is not a valid CFDictionary",
655 if (!uuid_str
.empty()) {
656 CFCString
uuid_cfstr(uuid_str
.c_str());
657 CFDictionaryRef uuid_dict
=
658 (CFDictionaryRef
)CFDictionaryGetValue(plist
.get(), uuid_cfstr
.get());
659 return GetModuleSpecInfoFromUUIDDictionary(uuid_dict
, module_spec
, error
,
663 if (const CFIndex num_values
= ::CFDictionaryGetCount(plist
.get())) {
664 std::vector
<CFStringRef
> keys(num_values
, NULL
);
665 std::vector
<CFDictionaryRef
> values(num_values
, NULL
);
666 ::CFDictionaryGetKeysAndValues(plist
.get(), NULL
,
667 (const void **)&values
[0]);
668 if (num_values
== 1) {
669 return GetModuleSpecInfoFromUUIDDictionary(values
[0], module_spec
, error
,
673 for (CFIndex i
= 0; i
< num_values
; ++i
) {
674 ModuleSpec curr_module_spec
;
675 if (GetModuleSpecInfoFromUUIDDictionary(values
[i
], curr_module_spec
,
676 error
, command
.GetData())) {
677 if (module_spec
.GetArchitecture().IsCompatibleMatch(
678 curr_module_spec
.GetArchitecture())) {
679 module_spec
= curr_module_spec
;