LLDB  mainline
LocateSymbolFileMacOSX.cpp
Go to the documentation of this file.
1 //===-- LocateSymbolFileMacOSX.cpp ----------------------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
10 
11 #include <dirent.h>
12 #include <dlfcn.h>
13 #include <pwd.h>
14 
15 #include <CoreFoundation/CoreFoundation.h>
16 
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"
30 #include "lldb/Utility/Endian.h"
31 #include "lldb/Utility/LLDBLog.h"
32 #include "lldb/Utility/Log.h"
34 #include "lldb/Utility/Timer.h"
35 #include "lldb/Utility/UUID.h"
36 #include "mach/machine.h"
37 
38 #include "llvm/ADT/ScopeExit.h"
39 #include "llvm/Support/FileSystem.h"
40 
41 using namespace lldb;
42 using namespace lldb_private;
43 
45  CFUUIDRef uuid, CFURLRef exec_url) = nullptr;
46 static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) =
47  nullptr;
48 
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.");
54  return 0;
55  }
56 
57  return_module_spec = module_spec;
58  return_module_spec.GetFileSpec().Clear();
59  return_module_spec.GetSymbolFileSpec().Clear();
60 
61  const UUID *uuid = module_spec.GetUUIDPtr();
62  const ArchSpec *arch = module_spec.GetArchitecturePtr();
63 
64  int items_found = 0;
65 
66  if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
68  void *handle = dlopen(
69  "/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols",
70  RTLD_LAZY | RTLD_LOCAL);
71  if (handle) {
73  (CFURLRef(*)(CFUUIDRef, CFURLRef))dlsym(handle,
74  "DBGCopyFullDSYMURLForUUID");
75  g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef(*)(CFURLRef))dlsym(
76  handle, "DBGCopyDSYMPropertyLists");
77  }
78  }
79 
80  if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
82  return items_found;
83  }
84 
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]));
94 
95  if (module_uuid_ref.get()) {
96  CFCReleaser<CFURLRef> exec_url;
97  const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
98  if (exec_fspec) {
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),
103  FALSE));
104  }
105 
107  module_uuid_ref.get(), exec_url.get()));
108  char path[PATH_MAX];
109 
110  if (dsym_url.get()) {
111  if (::CFURLGetFileSystemRepresentation(
112  dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
113  LLDB_LOGF(log,
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);
118  if (path[0] == '~')
119  FileSystem::Instance().Resolve(dsym_filespec);
120 
121  if (FileSystem::Instance().IsDirectory(dsym_filespec)) {
122  dsym_filespec =
123  Symbols::FindSymbolFileInBundle(dsym_filespec, uuid, arch);
124  ++items_found;
125  } else {
126  ++items_found;
127  }
128  return_module_spec.GetSymbolFileSpec() = dsym_filespec;
129  }
130 
131  bool success = false;
132  if (log) {
133  if (::CFURLGetFileSystemRepresentation(
134  dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
135  LLDB_LOGF(log,
136  "DebugSymbols framework returned dSYM path of %s for "
137  "UUID %s -- looking for an exec file",
138  path, uuid->GetAsString().c_str());
139  }
140  }
141 
143  g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get()));
144  CFDictionaryRef uuid_dict = NULL;
145  if (dict.get()) {
146  CFCString uuid_cfstr(uuid->GetAsString().c_str());
147  uuid_dict = static_cast<CFDictionaryRef>(
148  ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get()));
149  }
150 
151  // Check to see if we have the file on the local filesystem.
152  if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
153  ModuleSpec exe_spec;
154  exe_spec.GetFileSpec() = module_spec.GetFileSpec();
155  exe_spec.GetUUID() = module_spec.GetUUID();
156  ModuleSP module_sp;
157  module_sp.reset(new Module(exe_spec));
158  if (module_sp && module_sp->GetObjectFile() &&
159  module_sp->MatchesModuleSpec(exe_spec)) {
160  success = true;
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());
165  ++items_found;
166  }
167  }
168 
169  // Check if the requested image is in our shared cache.
170  if (!success) {
171  SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo(
172  module_spec.GetFileSpec().GetPath());
173 
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)) {
178  success = true;
179  return_module_spec.GetFileSpec() = module_spec.GetFileSpec();
180  LLDB_LOGF(log,
181  "using binary from shared cache for filepath %s for "
182  "UUID %s",
183  module_spec.GetFileSpec().GetPath().c_str(),
184  uuid->GetAsString().c_str());
185  ++items_found;
186  }
187  }
188 
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());
198  ++items_found;
199  FileSpec exec_filespec(path);
200  if (path[0] == '~')
201  FileSystem::Instance().Resolve(exec_filespec);
202  if (FileSystem::Instance().Exists(exec_filespec)) {
203  success = true;
204  return_module_spec.GetFileSpec() = exec_filespec;
205  }
206  }
207  }
208 
209  // Look next to the dSYM for the binary file.
210  if (!success) {
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';
216  LLDB_LOGF(log,
217  "Looking for executable binary next to dSYM "
218  "bundle with name with name %s",
219  path);
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())) {
226 
227  case file_type::directory_file: // Bundle directory?
228  {
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(),
234  true, (UInt8 *)path,
235  sizeof(path) - 1)) {
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))
242 
243  {
244  ++items_found;
245  return_module_spec.GetFileSpec() = bundle_exe_file_spec;
246  LLDB_LOGF(log,
247  "Executable binary %s next to dSYM is "
248  "compatible; using",
249  path);
250  }
251  }
252  }
253  } break;
254 
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:
259  break;
260 
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,
267  module_specs) &&
268  module_specs.FindMatchingModuleSpec(module_spec,
269  matched_module_spec))
270 
271  {
272  ++items_found;
273  return_module_spec.GetFileSpec() = file_spec;
274  LLDB_LOGF(log,
275  "Executable binary %s next to dSYM is "
276  "compatible; using",
277  path);
278  }
279  break;
280  }
281  }
282  }
283  }
284  }
285  }
286  }
287  }
288 
289  return items_found;
290 }
291 
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");
298 
299  std::error_code EC;
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())
307  continue;
308 
309  FileSpec dsym_fspec(Iter->path());
310  ModuleSpecList module_specs;
311  if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) {
312  ModuleSpec spec;
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)) &&
319  (arch == nullptr ||
320  (spec.GetArchitecturePtr() &&
321  spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
322  return dsym_fspec;
323  }
324  }
325  }
326  }
327 
328  return {};
329 }
330 
331 static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,
332  ModuleSpec &module_spec,
333  Status &error) {
334  Log *log = GetLog(LLDBLog::Host);
335  bool success = false;
336  if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) {
337  std::string str;
338  CFStringRef cf_str;
339  CFDictionaryRef cf_dict;
340 
341  cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
342  CFSTR("DBGError"));
343  if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
344  if (CFCString::FileSystemRepresentation(cf_str, str)) {
345  error.SetErrorString(str);
346  }
347  }
348 
349  cf_str = (CFStringRef)CFDictionaryGetValue(
350  (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable"));
351  if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
352  if (CFCString::FileSystemRepresentation(cf_str, str)) {
353  module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native);
354  FileSystem::Instance().Resolve(module_spec.GetFileSpec());
355  LLDB_LOGF(log,
356  "From dsymForUUID plist: Symbol rich executable is at '%s'",
357  str.c_str());
358  }
359  }
360 
361  cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
362  CFSTR("DBGDSYMPath"));
363  if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
364  if (CFCString::FileSystemRepresentation(cf_str, str)) {
365  module_spec.GetSymbolFileSpec().SetFile(str.c_str(),
366  FileSpec::Style::native);
367  FileSystem::Instance().Resolve(module_spec.GetFileSpec());
368  success = true;
369  LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'", str.c_str());
370  }
371  }
372 
373  std::string DBGBuildSourcePath;
374  std::string DBGSourcePath;
375 
376  // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping.
377  // If DBGVersion 2, strip last two components of path remappings from
378  // entries to fix an issue with a specific set of
379  // DBGSourcePathRemapping entries that lldb worked
380  // with.
381  // If DBGVersion 3, trust & use the source path remappings as-is.
382  //
383  cf_dict = (CFDictionaryRef)CFDictionaryGetValue(
384  (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping"));
385  if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {
386  // If we see DBGVersion with a value of 2 or higher, this is a new style
387  // DBGSourcePathRemapping dictionary
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()) {
394  std::string version;
395  CFCString::FileSystemRepresentation(cf_str, version);
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;
400  }
401  if (version_number == 2) {
402  do_truncate_remapping_names = true;
403  }
404  }
405  }
406 
407  CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);
408  if (kv_pair_count > 0) {
409  CFStringRef *keys =
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,
415  (const void **)keys,
416  (const void **)values);
417  }
418  for (CFIndex i = 0; i < kv_pair_count; i++) {
419  DBGBuildSourcePath.clear();
420  DBGSourcePath.clear();
421  if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) {
422  CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath);
423  }
424  if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
425  CFCString::FileSystemRepresentation(values[i], DBGSourcePath);
426  }
427  if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
428  // In the "old style" DBGSourcePathRemapping dictionary, the
429  // DBGSourcePath values (the "values" half of key-value path pairs)
430  // were wrong. Ignore them and use the universal DBGSourcePath
431  // string from earlier.
432  if (new_style_source_remapping_dictionary &&
433  !original_DBGSourcePath_value.empty()) {
434  DBGSourcePath = original_DBGSourcePath_value;
435  }
436  if (DBGSourcePath[0] == '~') {
437  FileSpec resolved_source_path(DBGSourcePath.c_str());
438  FileSystem::Instance().Resolve(resolved_source_path);
439  DBGSourcePath = resolved_source_path.GetPath();
440  }
441  // With version 2 of DBGSourcePathRemapping, we can chop off the
442  // last two filename parts from the source remapping and get a more
443  // general source remapping that still works. Add this as another
444  // option in addition to the full source path remap.
445  module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,
446  DBGSourcePath, true);
447  if (do_truncate_remapping_names) {
448  FileSpec build_path(DBGBuildSourcePath.c_str());
449  FileSpec source_path(DBGSourcePath.c_str());
450  build_path.RemoveLastPathComponent();
451  build_path.RemoveLastPathComponent();
452  source_path.RemoveLastPathComponent();
453  source_path.RemoveLastPathComponent();
454  module_spec.GetSourceMappingList().Append(
455  build_path.GetPath(), source_path.GetPath(), true);
456  }
457  }
458  }
459  if (keys)
460  free(keys);
461  if (values)
462  free(values);
463  }
464  }
465 
466  // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the
467  // source remappings list.
468 
469  cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
470  CFSTR("DBGBuildSourcePath"));
471  if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
472  CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
473  }
474 
475  cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
476  CFSTR("DBGSourcePath"));
477  if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
478  CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
479  }
480 
481  if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
482  if (DBGSourcePath[0] == '~') {
483  FileSpec resolved_source_path(DBGSourcePath.c_str());
484  FileSystem::Instance().Resolve(resolved_source_path);
485  DBGSourcePath = resolved_source_path.GetPath();
486  }
487  module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,
488  DBGSourcePath, true);
489  }
490  }
491  return success;
492 }
493 
494 /// It's expensive to check for the DBGShellCommands defaults setting. Only do
495 /// it once per lldb run and cache the result.
496 static llvm::StringRef GetDbgShellCommand() {
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()) {
504  char buffer[PATH_MAX];
505  if (CFStringGetCString((CFStringRef)defaults_setting, buffer,
506  sizeof(buffer), kCFStringEncodingUTF8)) {
507  g_dbgshell_command = buffer;
508  }
509  }
510  if (defaults_setting) {
511  CFRelease(defaults_setting);
512  }
513  });
514  return g_dbgshell_command;
515 }
516 
517 /// Get the dsymForUUID executable and cache the result so we don't end up
518 /// stat'ing the binary over and over.
520  // The LLDB_APPLE_DSYMFORUUID_EXECUTABLE environment variable is used by the
521  // test suite to override the dsymForUUID location. Because we must be able
522  // to change the value within a single test, don't bother caching it.
523  if (const char *dsymForUUID_env =
524  getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) {
525  FileSpec dsymForUUID_executable(dsymForUUID_env);
526  FileSystem::Instance().Resolve(dsymForUUID_executable);
527  if (FileSystem::Instance().Exists(dsymForUUID_executable))
528  return dsymForUUID_executable;
529  }
530 
531  static std::once_flag g_once_flag;
532  static FileSpec g_dsymForUUID_executable;
533  std::call_once(g_once_flag, [&]() {
534  // Try the DBGShellCommand.
535  llvm::StringRef dbgshell_command = GetDbgShellCommand();
536  if (!dbgshell_command.empty()) {
537  g_dsymForUUID_executable = FileSpec(dbgshell_command);
538  FileSystem::Instance().Resolve(g_dsymForUUID_executable);
539  if (FileSystem::Instance().Exists(g_dsymForUUID_executable))
540  return;
541  }
542 
543  // Try dsymForUUID in /usr/local/bin
544  {
545  g_dsymForUUID_executable = FileSpec("/usr/local/bin/dsymForUUID");
546  if (FileSystem::Instance().Exists(g_dsymForUUID_executable))
547  return;
548  }
549 
550  // We couldn't find the dsymForUUID binary.
551  g_dsymForUUID_executable = {};
552  });
553  return g_dsymForUUID_executable;
554 }
555 
556 bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
557  Status &error, bool force_lookup,
558  bool copy_executable) {
559  const UUID *uuid_ptr = module_spec.GetUUIDPtr();
560  const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
561 
562  llvm::StringRef dbgshell_command = GetDbgShellCommand();
563 
564  // When dbgshell_command is empty, the user has not enabled the use of an
565  // external program to find the symbols, don't run it for them.
566  if (!force_lookup && dbgshell_command.empty())
567  return false;
568 
569  // We need a UUID or valid (existing FileSpec.
570  if (!uuid_ptr &&
571  (!file_spec_ptr || !FileSystem::Instance().Exists(*file_spec_ptr)))
572  return false;
573 
574  // We need a dsymForUUID binary or an equivalent executable/script.
575  FileSpec dsymForUUID_exe_spec = GetDsymForUUIDExecutable();
576  if (!dsymForUUID_exe_spec)
577  return false;
578 
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() : "";
583 
584  Log *log = GetLog(LLDBLog::Host);
585 
586  // Create the dsymForUUID command.
587  StreamString command;
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,
592  uuid_str.c_str());
593  LLDB_LOGF(log, "Calling %s with UUID %s to find dSYM: %s",
594  dsymForUUID_exe_path.c_str(), uuid_str.c_str(),
595  command.GetString().data());
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(),
602  command.GetString().data());
603  } else {
604  return false;
605  }
606 
607  // Invoke dsymForUUID.
608  int exit_status = -1;
609  int signo = -1;
610  std::string command_output;
611  error = Host::RunShellCommand(
612  command.GetData(),
613  FileSpec(), // current working directory
614  &exit_status, // Exit status
615  &signo, // Signal int *
616  &command_output, // Command output
617  std::chrono::seconds(
618  640), // Large timeout to allow for long dsym download times
619  false); // Don't run in a shell (we don't need shell expansion)
620 
621  if (error.Fail() || exit_status != 0 || command_output.empty()) {
622  LLDB_LOGF(log, "'%s' failed (exit status: %d, error: '%s', output: '%s')",
623  command.GetData(), exit_status, error.AsCString(),
624  command_output.c_str());
625  return false;
626  }
627 
628  CFCData data(
629  CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)command_output.data(),
630  command_output.size(), kCFAllocatorNull));
631 
633  (CFDictionaryRef)::CFPropertyListCreateWithData(
634  NULL, data.get(), kCFPropertyListImmutable, NULL, NULL));
635 
636  if (!plist.get()) {
637  LLDB_LOGF(log, "'%s' failed: output is not a valid plist",
638  command.GetData());
639  return false;
640  }
641 
642  if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {
643  LLDB_LOGF(log, "'%s' failed: output plist is not a valid CFDictionary",
644  command.GetData());
645  return false;
646  }
647 
648  if (!uuid_str.empty()) {
649  CFCString uuid_cfstr(uuid_str.c_str());
650  CFDictionaryRef uuid_dict =
651  (CFDictionaryRef)CFDictionaryGetValue(plist.get(), uuid_cfstr.get());
652  return GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec, error);
653  }
654 
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) {
661  return GetModuleSpecInfoFromUUIDDictionary(values[0], module_spec, error);
662  }
663 
664  for (CFIndex i = 0; i < num_values; ++i) {
665  ModuleSpec curr_module_spec;
666  if (GetModuleSpecInfoFromUUIDDictionary(values[i], curr_module_spec,
667  error)) {
668  if (module_spec.GetArchitecture().IsCompatibleMatch(
669  curr_module_spec.GetArchitecture())) {
670  module_spec = curr_module_spec;
671  return true;
672  }
673  }
674  }
675  }
676 
677  return false;
678 }
LocateMacOSXFilesUsingDebugSymbols
int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec, ModuleSpec &return_module_spec)
Definition: LocateSymbolFileMacOSX.cpp:49
lldb_private::UUID
Definition: UUID.h:23
lldb_private::ArchSpec
Definition: ArchSpec.h:32
UNUSED_IF_ASSERT_DISABLED
#define UNUSED_IF_ASSERT_DISABLED(x)
Definition: lldb-defines.h:126
lldb_private::ModuleSpec::GetUUIDPtr
UUID * GetUUIDPtr()
Definition: ModuleSpec.h:93
ModuleSpec.h
UUID.h
Host.h
lldb_private::UUID::IsValid
bool IsValid() const
Definition: UUID.h:69
GetDsymForUUIDExecutable
static FileSpec GetDsymForUUIDExecutable()
Get the dsymForUUID executable and cache the result so we don't end up stat'ing the binary over and o...
Definition: LocateSymbolFileMacOSX.cpp:519
lldb_private::ModuleSpecList
Definition: ModuleSpec.h:275
LLDB_LOGF
#define LLDB_LOGF(log,...)
Definition: Log.h:344
Module.h
lldb_private::Module
Definition: Module.h:86
LocateSymbolFile.h
CFCBundle.h
lldb_private::ArchSpec::IsCompatibleMatch
bool IsCompatibleMatch(const ArchSpec &rhs) const
Shorthand for IsMatch(rhs, CompatibleMatch).
Definition: ArchSpec.h:515
lldb_private::ModuleSpec::GetFileSpecPtr
FileSpec * GetFileSpecPtr()
Definition: ModuleSpec.h:47
lldb_private::UUID::GetAsString
std::string GetAsString(llvm::StringRef separator="-") const
Definition: UUID.cpp:49
CFCString
Definition: CFCString.h:16
lldb_private::StreamString::GetString
llvm::StringRef GetString() const
Definition: StreamString.cpp:51
lldb_private::ModuleSpec::GetSymbolFileSpec
FileSpec & GetSymbolFileSpec()
Definition: ModuleSpec.h:77
CFCReleaser
lldb_private::ModuleSpec::GetUUID
UUID & GetUUID()
Definition: ModuleSpec.h:99
lldb_private::ModuleSpec::GetSourceMappingList
PathMappingList & GetSourceMappingList() const
Definition: ModuleSpec.h:125
lldb_private::FileSpec
Definition: FileSpec.h:55
error
static llvm::raw_ostream & error(Stream &strm)
Definition: CommandReturnObject.cpp:17
ModuleList.h
CFCReleaser.h
Log.h
GetDbgShellCommand
static llvm::StringRef GetDbgShellCommand()
It's expensive to check for the DBGShellCommands defaults setting.
Definition: LocateSymbolFileMacOSX.cpp:496
lldb_private::StreamString::GetData
const char * GetData() const
Definition: StreamString.h:43
StreamString.h
lldb_private::StreamString
Definition: StreamString.h:23
Timer.h
CFCData.h
CFCBundle::CopyExecutableURL
CFURLRef CopyExecutableURL() const
Definition: CFCBundle.cpp:71
HostInfo.h
string
string(SUBSTRING ${p} 10 -1 pStripped) if($
Definition: Plugins/CMakeLists.txt:40
lldb_private::ModuleSpecList::FindMatchingModuleSpec
bool FindMatchingModuleSpec(const ModuleSpec &module_spec, ModuleSpec &match_module_spec) const
Definition: ModuleSpec.h:333
lldb_private::PathMappingList::Append
void Append(llvm::StringRef path, llvm::StringRef replacement, bool notify)
Definition: PathMappingList.cpp:60
g_dlsym_DBGCopyFullDSYMURLForUUID
static CFURLRef(* g_dlsym_DBGCopyFullDSYMURLForUUID)(CFUUIDRef uuid, CFURLRef exec_url)
Definition: LocateSymbolFileMacOSX.cpp:44
CFCBundle
Definition: CFCBundle.h:14
CFCData
Definition: CFCData.h:14
ObjectFile.h
lldb_private::FileSpec::Clear
void Clear()
Clears the object state.
Definition: FileSpec.cpp:257
lldb_private::ModuleSpec::GetFileSpec
FileSpec & GetFileSpec()
Definition: ModuleSpec.h:53
lldb_private::ModuleSpec
Definition: ModuleSpec.h:27
lldb_private::SharedCacheImageInfo
Definition: HostInfoBase.h:28
lldb_private::Status
Definition: Status.h:44
lldb_private::FileSpec::RemoveLastPathComponent
bool RemoveLastPathComponent()
Removes the last path component by replacing the current path with its parent.
Definition: FileSpec.cpp:462
lldb_private::ModuleSpec::GetArchitecturePtr
ArchSpec * GetArchitecturePtr()
Definition: ModuleSpec.h:81
lldb_private::SharedCacheImageInfo::uuid
UUID uuid
Definition: HostInfoBase.h:29
ArchSpec.h
GetModuleSpecInfoFromUUIDDictionary
static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict, ModuleSpec &module_spec, Status &error)
Definition: LocateSymbolFileMacOSX.cpp:331
lldb_private::ModuleSpec::GetArchitecture
ArchSpec & GetArchitecture()
Definition: ModuleSpec.h:89
DataExtractor.h
lldb_private::Stream::Printf
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:107
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
CFCString.h
lldb_private::ModuleSpecList::GetModuleSpecAtIndex
bool GetModuleSpecAtIndex(size_t i, ModuleSpec &module_spec) const
Definition: ModuleSpec.h:323
lldb_private::ModuleSpecList::GetSize
size_t GetSize() const
Definition: ModuleSpec.h:298
lldb_private::Log
Definition: Log.h:115
lldb_private::FileSpec::SetFile
void SetFile(llvm::StringRef path, Style style)
Change the file specified with a new path.
Definition: FileSpec.cpp:172
PATH_MAX
#define PATH_MAX
Definition: windows/PosixApi.h:25
lldb_private::GetLog
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition: Log.h:309
lldb_private::FileSpec::GetPath
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:364
DataBuffer.h
lldb
Definition: SBAddress.h:15
Endian.h
lldb_private::UUID::GetBytes
llvm::ArrayRef< uint8_t > GetBytes() const
Definition: UUID.h:66
LLDBLog.h
g_dlsym_DBGCopyDSYMPropertyLists
static CFDictionaryRef(* g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url)
Definition: LocateSymbolFileMacOSX.cpp:46
CFCString::FileSystemRepresentation
static const char * FileSystemRepresentation(CFStringRef cf_str, std::string &str)
Definition: CFCString.cpp:126