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) {
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",
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 ||
335 bool success =
false;
336 if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) {
339 CFDictionaryRef cf_dict;
341 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
343 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
345 error.SetErrorString(str);
349 cf_str = (CFStringRef)CFDictionaryGetValue(
350 (CFDictionaryRef)uuid_dict, CFSTR(
"DBGSymbolRichExecutable"));
351 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
356 "From dsymForUUID plist: Symbol rich executable is at '%s'",
361 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
362 CFSTR(
"DBGDSYMPath"));
363 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
366 FileSpec::Style::native);
369 LLDB_LOGF(log,
"From dsymForUUID plist: dSYM is at '%s'", str.c_str());
373 std::string DBGBuildSourcePath;
374 std::string DBGSourcePath;
383 cf_dict = (CFDictionaryRef)CFDictionaryGetValue(
384 (CFDictionaryRef)uuid_dict, CFSTR(
"DBGSourcePathRemapping"));
385 if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {
388 bool new_style_source_remapping_dictionary =
false;
389 bool do_truncate_remapping_names =
false;
390 std::string original_DBGSourcePath_value = DBGSourcePath;
391 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
392 CFSTR(
"DBGVersion"));
393 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
396 if (!version.empty() && isdigit(version[0])) {
397 int version_number = atoi(version.c_str());
398 if (version_number > 1) {
399 new_style_source_remapping_dictionary =
true;
401 if (version_number == 2) {
402 do_truncate_remapping_names =
true;
407 CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);
408 if (kv_pair_count > 0) {
410 (CFStringRef *)malloc(kv_pair_count *
sizeof(CFStringRef));
411 CFStringRef *values =
412 (CFStringRef *)malloc(kv_pair_count *
sizeof(CFStringRef));
413 if (keys !=
nullptr && values !=
nullptr) {
414 CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict,
416 (
const void **)values);
418 for (CFIndex i = 0; i < kv_pair_count; i++) {
419 DBGBuildSourcePath.clear();
420 DBGSourcePath.clear();
421 if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) {
424 if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
427 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
432 if (new_style_source_remapping_dictionary &&
433 !original_DBGSourcePath_value.empty()) {
434 DBGSourcePath = original_DBGSourcePath_value;
436 if (DBGSourcePath[0] ==
'~') {
437 FileSpec resolved_source_path(DBGSourcePath.c_str());
439 DBGSourcePath = resolved_source_path.
GetPath();
446 DBGSourcePath,
true);
447 if (do_truncate_remapping_names) {
448 FileSpec build_path(DBGBuildSourcePath.c_str());
449 FileSpec source_path(DBGSourcePath.c_str());
469 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
470 CFSTR(
"DBGBuildSourcePath"));
471 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
475 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
476 CFSTR(
"DBGSourcePath"));
477 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
481 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
482 if (DBGSourcePath[0] ==
'~') {
483 FileSpec resolved_source_path(DBGSourcePath.c_str());
485 DBGSourcePath = resolved_source_path.
GetPath();
488 DBGSourcePath,
true);
497 static std::once_flag g_once_flag;
498 static std::string g_dbgshell_command;
499 std::call_once(g_once_flag, [&]() {
500 CFTypeRef defaults_setting = CFPreferencesCopyAppValue(
501 CFSTR(
"DBGShellCommands"), CFSTR(
"com.apple.DebugSymbols"));
502 if (defaults_setting &&
503 CFGetTypeID(defaults_setting) == CFStringGetTypeID()) {
505 if (CFStringGetCString((CFStringRef)defaults_setting, buffer,
506 sizeof(buffer), kCFStringEncodingUTF8)) {
507 g_dbgshell_command = buffer;
510 if (defaults_setting) {
511 CFRelease(defaults_setting);
514 return g_dbgshell_command;
523 if (
const char *dsymForUUID_env =
524 getenv(
"LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) {
525 FileSpec dsymForUUID_executable(dsymForUUID_env);
528 return dsymForUUID_executable;
531 static std::once_flag g_once_flag;
532 static FileSpec g_dsymForUUID_executable;
533 std::call_once(g_once_flag, [&]() {
536 if (!dbgshell_command.empty()) {
537 g_dsymForUUID_executable =
FileSpec(dbgshell_command);
545 g_dsymForUUID_executable =
FileSpec(
"/usr/local/bin/dsymForUUID");
551 g_dsymForUUID_executable = {};
553 return g_dsymForUUID_executable;
558 bool copy_executable) {
566 if (!force_lookup && dbgshell_command.empty())
576 if (!dsymForUUID_exe_spec)
579 const std::string dsymForUUID_exe_path = dsymForUUID_exe_spec.
GetPath();
580 const std::string uuid_str = uuid_ptr ? uuid_ptr->
GetAsString() :
"";
581 const std::string file_path_str =
582 file_spec_ptr ? file_spec_ptr->
GetPath() :
"";
588 const char *copy_executable_arg = copy_executable ?
"--copyExecutable " :
"";
589 if (!uuid_str.empty()) {
590 command.
Printf(
"%s --ignoreNegativeCache %s%s",
591 dsymForUUID_exe_path.c_str(), copy_executable_arg,
593 LLDB_LOGF(log,
"Calling %s with UUID %s to find dSYM: %s",
594 dsymForUUID_exe_path.c_str(), uuid_str.c_str(),
596 }
else if (!file_path_str.empty()) {
597 command.
Printf(
"%s --ignoreNegativeCache %s%s",
598 dsymForUUID_exe_path.c_str(), copy_executable_arg,
599 file_path_str.c_str());
600 LLDB_LOGF(log,
"Calling %s with file %s to find dSYM: %s",
601 dsymForUUID_exe_path.c_str(), file_path_str.c_str(),
608 int exit_status = -1;
610 std::string command_output;
617 std::chrono::seconds(
621 if (
error.Fail() || exit_status != 0 || command_output.empty()) {
622 LLDB_LOGF(log,
"'%s' failed (exit status: %d, error: '%s', output: '%s')",
624 command_output.c_str());
629 CFDataCreateWithBytesNoCopy(NULL, (
const UInt8 *)command_output.data(),
630 command_output.size(), kCFAllocatorNull));
633 (CFDictionaryRef)::CFPropertyListCreateWithData(
634 NULL, data.get(), kCFPropertyListImmutable, NULL, NULL));
637 LLDB_LOGF(log,
"'%s' failed: output is not a valid plist",
642 if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {
643 LLDB_LOGF(log,
"'%s' failed: output plist is not a valid CFDictionary",
648 if (!uuid_str.empty()) {
650 CFDictionaryRef uuid_dict =
651 (CFDictionaryRef)CFDictionaryGetValue(plist.get(), uuid_cfstr.get());
655 if (
const CFIndex num_values = ::CFDictionaryGetCount(plist.get())) {
656 std::vector<CFStringRef> keys(num_values, NULL);
657 std::vector<CFDictionaryRef> values(num_values, NULL);
658 ::CFDictionaryGetKeysAndValues(plist.get(), NULL,
659 (
const void **)&values[0]);
660 if (num_values == 1) {
664 for (CFIndex i = 0; i < num_values; ++i) {
670 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 bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict, ModuleSpec &module_spec, Status &error)
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...
#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.