15#include <CoreFoundation/CoreFoundation.h>
36#include "mach/machine.h"
38#include "llvm/ADT/ScopeExit.h"
39#include "llvm/Support/FileSystem.h"
45 CFUUIDRef uuid, CFURLRef exec_url) =
nullptr;
53 LLDB_LOGF(log,
"Spotlight lookup for .dSYM bundles is disabled.");
57 return_module_spec = module_spec;
68 void *handle = dlopen(
69 "/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols",
70 RTLD_LAZY | RTLD_LOCAL);
73 (CFURLRef(*)(CFUUIDRef, CFURLRef))dlsym(handle,
74 "DBGCopyFullDSYMURLForUUID");
76 handle,
"DBGCopyDSYMPropertyLists");
87 llvm::ArrayRef<uint8_t> module_uuid = uuid->
GetBytes();
88 if (module_uuid.size() == 16) {
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()) {
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),
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",
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",
144 CFDictionaryRef uuid_dict = NULL;
147 uuid_dict =
static_cast<CFDictionaryRef
>(
148 ::CFDictionaryGetValue(dict.
get(), uuid_cfstr.
get()));
157 module_sp.reset(
new Module(exe_spec));
158 if (module_sp && module_sp->GetObjectFile() &&
159 module_sp->MatchesModuleSpec(exe_spec)) {
162 LLDB_LOGF(log,
"using original binary filepath %s for UUID %s",
181 "using binary from shared cache for filepath %s for "
190 if (!success && uuid_dict) {
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",
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",
224 using namespace llvm::sys::fs;
225 switch (get_file_type(file_spec.
GetPath())) {
227 case file_type::directory_file:
232 if (bundle_exe_url.
get()) {
233 if (::CFURLGetFileSystemRepresentation(bundle_exe_url.
get(),
236 FileSpec bundle_exe_file_spec(path);
239 bundle_exe_file_spec, 0, 0, module_specs) &&
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:
256 case file_type::socket_file:
257 case file_type::file_not_found:
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:
269 matched_module_spec))
275 "Executable binary %s next to dSYM is "
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 =
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())
313 for (
size_t i = 0; i < module_specs.
GetSize(); ++i) {
317 if ((uuid ==
nullptr ||
334 const std::string &command) {
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()) {
346 std::string errorstr = command;
349 error.SetErrorString(errorstr);
354 (CFDictionaryRef)uuid_dict, CFSTR(
"DBGSymbolRichExecutable"));
355 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
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()) {
370 FileSpec::Style::native);
373 LLDB_LOGF(log,
"From dsymForUUID plist: dSYM is at '%s'", str.c_str());
377 std::string DBGBuildSourcePath;
378 std::string DBGSourcePath;
387 cf_dict = (CFDictionaryRef)CFDictionaryGetValue(
388 (CFDictionaryRef)uuid_dict, CFSTR(
"DBGSourcePathRemapping"));
389 if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {
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()) {
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) {
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()) {
428 if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
431 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
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());
443 DBGSourcePath = resolved_source_path.
GetPath();
450 DBGSourcePath,
true);
451 if (do_truncate_remapping_names) {
452 FileSpec build_path(DBGBuildSourcePath.c_str());
453 FileSpec source_path(DBGSourcePath.c_str());
473 cf_str = (
CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
474 CFSTR(
"DBGBuildSourcePath"));
475 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
479 cf_str = (
CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
480 CFSTR(
"DBGSourcePath"));
481 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
485 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
486 if (DBGSourcePath[0] ==
'~') {
487 FileSpec resolved_source_path(DBGSourcePath.c_str());
489 DBGSourcePath = resolved_source_path.
GetPath();
492 DBGSourcePath,
true);
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()) {
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;
527 if (
const char *dsymForUUID_env =
528 getenv(
"LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) {
529 FileSpec dsymForUUID_executable(dsymForUUID_env);
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, [&]() {
540 if (!dbgshell_command.empty()) {
541 g_dsymForUUID_executable =
FileSpec(dbgshell_command);
549 g_dsymForUUID_executable =
FileSpec(
"/usr/local/bin/dsymForUUID");
555 g_dsymForUUID_executable = {};
557 return g_dsymForUUID_executable;
562 bool copy_executable) {
573 if (!force_lookup && dbgshell_command.empty())
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() :
"";
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(),
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(),
615 int exit_status = -1;
617 std::string command_output;
624 std::chrono::seconds(
628 if (
error.Fail() || exit_status != 0 || command_output.empty()) {
629 LLDB_LOGF(log,
"'%s' failed (exit status: %d, error: '%s', output: '%s')",
631 command_output.c_str());
636 CFDataCreateWithBytesNoCopy(NULL, (
const UInt8 *)command_output.data(),
637 command_output.size(), kCFAllocatorNull));
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()) {
657 CFDictionaryRef uuid_dict =
658 (CFDictionaryRef)CFDictionaryGetValue(plist.get(), uuid_cfstr.get());
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) {
673 for (CFIndex i = 0; i < num_values; ++i) {
679 module_spec = curr_module_spec;
static llvm::raw_ostream & error(Stream &strm)
static llvm::StringRef GetDbgShellCommand()
It's expensive to check for the DBGShellCommands defaults setting.
int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec, ModuleSpec &return_module_spec)
static CFDictionaryRef(* g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url)
static CFURLRef(* g_dlsym_DBGCopyFullDSYMURLForUUID)(CFUUIDRef uuid, CFURLRef exec_url)
static FileSpec GetDsymForUUIDExecutable()
Get the dsymForUUID executable and cache the result so we don't end up stat'ing the binary over and o...
static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict, ModuleSpec &module_spec, Status &error, const std::string &command)
#define LLDB_LOGF(log,...)
CFURLRef CopyExecutableURL() const
static const char * FileSystemRepresentation(CFStringRef cf_str, std::string &str)
An architecture specification class.
bool IsCompatibleMatch(const ArchSpec &rhs) const
Shorthand for IsMatch(rhs, CompatibleMatch).
void SetFile(llvm::StringRef path, Style style)
Change the file specified with a new path.
bool RemoveLastPathComponent()
Removes the last path component by replacing the current path with its parent.
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
void Clear()
Clears the object state.
void Resolve(llvm::SmallVectorImpl< char > &path)
Resolve path to make it canonical.
bool Exists(const FileSpec &file_spec) const
Returns whether the given file exists.
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > GetVirtualFileSystem()
static FileSystem & Instance()
static Status RunShellCommand(llvm::StringRef command, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output, const Timeout< std::micro > &timeout, bool run_in_shell=true, bool hide_stderr=false)
Run a shell command.
static ModuleListProperties & GetGlobalModuleListProperties()
bool GetModuleSpecAtIndex(size_t i, ModuleSpec &module_spec) const
bool FindMatchingModuleSpec(const ModuleSpec &module_spec, ModuleSpec &match_module_spec) const
PathMappingList & GetSourceMappingList() const
ArchSpec & GetArchitecture()
FileSpec * GetFileSpecPtr()
FileSpec & GetSymbolFileSpec()
ArchSpec * GetArchitecturePtr()
A class that describes an executable image and its associated object and symbol files.
static size_t GetModuleSpecifications(const FileSpec &file, lldb::offset_t file_offset, lldb::offset_t file_size, ModuleSpecList &specs, lldb::DataBufferSP data_sp=lldb::DataBufferSP())
void Append(llvm::StringRef path, llvm::StringRef replacement, bool notify)
const char * GetData() const
llvm::StringRef GetString() const
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
static bool DownloadObjectAndSymbolFile(ModuleSpec &module_spec, Status &error, bool force_lookup=true, bool copy_executable=true)
static FileSpec FindSymbolFileInBundle(const FileSpec &dsym_bundle_fspec, const lldb_private::UUID *uuid, const ArchSpec *arch)
llvm::ArrayRef< uint8_t > GetBytes() const
std::string GetAsString(llvm::StringRef separator="-") const
#define UNUSED_IF_ASSERT_DISABLED(x)
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
std::shared_ptr< lldb_private::Module > ModuleSP