Index: lldb/include/lldb/Utility/Reproducer.h =================================================================== --- lldb/include/lldb/Utility/Reproducer.h +++ lldb/include/lldb/Utility/Reproducer.h @@ -22,6 +22,7 @@ #include namespace lldb_private { +class UUID; namespace repro { class Reproducer; @@ -200,6 +201,40 @@ static char ID; }; +class SymbolFileProvider : public Provider { +public: + SymbolFileProvider(const FileSpec &directory) + : Provider(directory), m_symbol_files() {} + + void AddSymbolFile(const UUID *uuid, const FileSpec &module_path, + const FileSpec &symbol_path); + void Keep() override; + + struct Entry { + Entry() = default; + Entry(std::string uuid) : uuid(std::move(uuid)) {} + Entry(std::string uuid, std::string module_path, std::string symbol_path) + : uuid(std::move(uuid)), module_path(std::move(module_path)), + symbol_path(std::move(symbol_path)) {} + + bool operator==(const Entry &rhs) const { return uuid == rhs.uuid; } + bool operator<(const Entry &rhs) const { return uuid < rhs.uuid; } + + std::string uuid; + std::string module_path; + std::string symbol_path; + }; + + struct Info { + static const char *name; + static const char *file; + }; + static char ID; + +private: + std::vector m_symbol_files; +}; + /// The recorder is a small object handed out by a provider to record data. It /// is commonly used in combination with a MultiProvider which is meant to /// record information for multiple instances of the same source of data. @@ -514,6 +549,16 @@ unsigned m_index = 0; }; +class SymbolFileLoader { +public: + SymbolFileLoader(Loader *loader); + std::pair GetPaths(const UUID *uuid) const; + +private: + // Sorted list of UUID to path mappings. + std::vector m_symbol_files; +}; + /// Helper to read directories written by the DirectoryProvider. template llvm::Expected GetDirectoryFrom(repro::Loader *loader) { @@ -526,4 +571,20 @@ } // namespace repro } // namespace lldb_private +LLVM_YAML_IS_SEQUENCE_VECTOR(lldb_private::repro::SymbolFileProvider::Entry) + +namespace llvm { +namespace yaml { +template <> +struct MappingTraits { + static void mapping(IO &io, + lldb_private::repro::SymbolFileProvider::Entry &entry) { + io.mapRequired("uuid", entry.uuid); + io.mapRequired("module-path", entry.module_path); + io.mapRequired("symbol-path", entry.symbol_path); + } +}; +} // namespace yaml +} // namespace llvm + #endif // LLDB_UTILITY_REPRODUCER_H Index: lldb/source/Commands/CommandObjectReproducer.cpp =================================================================== --- lldb/source/Commands/CommandObjectReproducer.cpp +++ lldb/source/Commands/CommandObjectReproducer.cpp @@ -27,6 +27,7 @@ enum ReproducerProvider { eReproducerProviderCommands, eReproducerProviderFiles, + eReproducerProviderSymbolFiles, eReproducerProviderGDB, eReproducerProviderProcessInfo, eReproducerProviderVersion, @@ -46,6 +47,11 @@ "files", "Files", }, + { + eReproducerProviderSymbolFiles, + "symbol-files", + "Symbol Files", + }, { eReproducerProviderGDB, "gdb", @@ -427,6 +433,29 @@ result.SetStatus(eReturnStatusSuccessFinishResult); return true; } + case eReproducerProviderSymbolFiles: { + Expected symbol_files = + loader->LoadBuffer(); + if (!symbol_files) { + SetError(result, symbol_files.takeError()); + return false; + } + + std::vector entries; + llvm::yaml::Input yin(*symbol_files); + yin >> entries; + + for (const auto &entry : entries) { + result.AppendMessageWithFormat("- uuid: %s\n", + entry.uuid.c_str()); + result.AppendMessageWithFormat(" module path: %s\n", + entry.module_path.c_str()); + result.AppendMessageWithFormat(" symbol path: %s\n", + entry.symbol_path.c_str()); + } + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } case eReproducerProviderVersion: { Expected version = loader->LoadBuffer(); if (!version) { Index: lldb/source/Symbol/LocateSymbolFile.cpp =================================================================== --- lldb/source/Symbol/LocateSymbolFile.cpp +++ lldb/source/Symbol/LocateSymbolFile.cpp @@ -16,6 +16,7 @@ #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/Reproducer.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" #include "lldb/Utility/UUID.h" @@ -225,6 +226,7 @@ } else { dsym_module_spec.GetSymbolFileSpec() = symbol_fspec; } + return dsym_module_spec.GetSymbolFileSpec(); } @@ -248,6 +250,7 @@ } else { LocateMacOSXFilesUsingDebugSymbols(module_spec, result); } + return result; } Index: lldb/source/Symbol/LocateSymbolFileMacOSX.cpp =================================================================== --- lldb/source/Symbol/LocateSymbolFileMacOSX.cpp +++ lldb/source/Symbol/LocateSymbolFileMacOSX.cpp @@ -27,6 +27,7 @@ #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Endian.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/Reproducer.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" #include "lldb/Utility/UUID.h" @@ -53,6 +54,17 @@ return_module_spec.GetFileSpec().Clear(); return_module_spec.GetSymbolFileSpec().Clear(); + const UUID *uuid = module_spec.GetUUIDPtr(); + const ArchSpec *arch = module_spec.GetArchitecturePtr(); + + if (repro::Loader *l = repro::Reproducer::Instance().GetLoader()) { + static repro::SymbolFileLoader symbol_file_loader(l); + std::pair paths = symbol_file_loader.GetPaths(uuid); + return_module_spec.GetFileSpec() = paths.first; + return_module_spec.GetSymbolFileSpec() = paths.second; + return 1; + } + int items_found = 0; if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr || @@ -69,9 +81,6 @@ return items_found; } - const UUID *uuid = module_spec.GetUUIDPtr(); - const ArchSpec *arch = module_spec.GetArchitecturePtr(); - if (uuid && uuid->IsValid()) { // Try and locate the dSYM file using DebugSymbols first llvm::ArrayRef module_uuid = uuid->GetBytes(); @@ -247,6 +256,12 @@ } } + if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) { + g->GetOrCreate().AddSymbolFile( + uuid, return_module_spec.GetFileSpec(), + return_module_spec.GetSymbolFileSpec()); + } + return items_found; } @@ -464,6 +479,25 @@ const UUID *uuid_ptr = module_spec.GetUUIDPtr(); const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr(); + if (repro::Loader *l = repro::Reproducer::Instance().GetLoader()) { + static repro::SymbolFileLoader symbol_file_loader(l); + std::pair paths = symbol_file_loader.GetPaths(uuid_ptr); + if (paths.first) + module_spec.GetFileSpec() = paths.first; + if (paths.second) + module_spec.GetSymbolFileSpec() = paths.second; + return true; + } + + // Lambda to capture the state of module_spec before returning from this + // function. + auto RecordResult = [&]() { + if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) { + g->GetOrCreate().AddSymbolFile( + uuid_ptr, module_spec.GetFileSpec(), module_spec.GetSymbolFileSpec()); + } + }; + // It's expensive to check for the DBGShellCommands defaults setting, only do // it once per lldb run and cache the result. static bool g_have_checked_for_dbgshell_command = false; @@ -489,6 +523,7 @@ // When g_dbgshell_command is NULL, the user has not enabled the use of an // external program to find the symbols, don't run it for them. if (!force_lookup && g_dbgshell_command == NULL) { + RecordResult(); return false; } @@ -613,8 +648,10 @@ ::CFDictionaryGetKeysAndValues(plist.get(), NULL, (const void **)&values[0]); if (num_values == 1) { - return GetModuleSpecInfoFromUUIDDictionary(values[0], - module_spec); + success = GetModuleSpecInfoFromUUIDDictionary(values[0], + module_spec); + RecordResult(); + return success; } else { for (CFIndex i = 0; i < num_values; ++i) { ModuleSpec curr_module_spec; @@ -623,6 +660,7 @@ if (module_spec.GetArchitecture().IsCompatibleMatch( curr_module_spec.GetArchitecture())) { module_spec = curr_module_spec; + RecordResult(); return true; } } @@ -644,5 +682,6 @@ } } } + RecordResult(); return success; } Index: lldb/source/Utility/Reproducer.cpp =================================================================== --- lldb/source/Utility/Reproducer.cpp +++ lldb/source/Utility/Reproducer.cpp @@ -8,6 +8,7 @@ #include "lldb/Utility/Reproducer.h" #include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/UUID.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Threading.h" @@ -301,6 +302,61 @@ m_collector->addDirectory(dir); } +void SymbolFileProvider::AddSymbolFile(const UUID *uuid, + const FileSpec &module_file, + const FileSpec &symbol_file) { + if (!uuid || (!module_file && !symbol_file)) + return; + m_symbol_files.emplace_back(uuid->GetAsString(), module_file.GetPath(), + symbol_file.GetPath()); +} + +void SymbolFileProvider::Keep() { + FileSpec file = this->GetRoot().CopyByAppendingPathComponent(Info::file); + std::error_code ec; + llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); + if (ec) + return; + + // Remove duplicates. + llvm::sort(m_symbol_files.begin(), m_symbol_files.end()); + m_symbol_files.erase( + std::unique(m_symbol_files.begin(), m_symbol_files.end()), + m_symbol_files.end()); + + llvm::yaml::Output yout(os); + yout << m_symbol_files; +} + +SymbolFileLoader::SymbolFileLoader(Loader *loader) { + if (!loader) + return; + + FileSpec file = loader->GetFile(); + if (!file) + return; + + auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); + if (auto err = error_or_file.getError()) + return; + + llvm::yaml::Input yin((*error_or_file)->getBuffer()); + yin >> m_symbol_files; +} + +std::pair +SymbolFileLoader::GetPaths(const UUID *uuid) const { + if (!uuid) + return {}; + + auto it = std::lower_bound(m_symbol_files.begin(), m_symbol_files.end(), + SymbolFileProvider::Entry(uuid->GetAsString())); + if (it == m_symbol_files.end()) + return {}; + return std::make_pair(FileSpec(it->module_path), + FileSpec(it->symbol_path)); +} + void ProviderBase::anchor() {} char CommandProvider::ID = 0; char FileProvider::ID = 0; @@ -308,6 +364,7 @@ char VersionProvider::ID = 0; char WorkingDirectoryProvider::ID = 0; char HomeDirectoryProvider::ID = 0; +char SymbolFileProvider::ID = 0; const char *CommandProvider::Info::file = "command-interpreter.yaml"; const char *CommandProvider::Info::name = "command-interpreter"; const char *FileProvider::Info::file = "files.yaml"; @@ -318,3 +375,5 @@ const char *WorkingDirectoryProvider::Info::name = "cwd"; const char *HomeDirectoryProvider::Info::file = "home.txt"; const char *HomeDirectoryProvider::Info::name = "home"; +const char *SymbolFileProvider::Info::file = "symbol-files.yaml"; +const char *SymbolFileProvider::Info::name = "symbol-files"; Index: lldb/test/Shell/Reproducer/Inputs/dsymforuuid.sh =================================================================== --- /dev/null +++ lldb/test/Shell/Reproducer/Inputs/dsymforuuid.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +echo "" +echo "" +echo "" +echo "" +echo " AD52358C-94F8-3796-ADD6-B20FFAC00E5C" +echo " " +echo " DBGArchitecture" +echo " x86_64" +echo " DBGBuildSourcePath" +echo " /path/to/build/sources" +echo " DBGSourcePath" +echo " /path/to/actual/sources" +echo " DBGDSYMPath" +echo " /path/to/foo.dSYM/Contents/Resources/DWARF/foo" +echo " DBGSymbolRichExecutable" +echo " /path/to/unstripped/executable" +echo " " +echo "" +echo "" Index: lldb/test/Shell/Reproducer/TestDebugSymbols.test =================================================================== --- /dev/null +++ lldb/test/Shell/Reproducer/TestDebugSymbols.test @@ -0,0 +1,13 @@ +# REQUIRES: system-darwin + +# RUN: rm -rf %t.repro +# RUN: env LLDB_APPLE_DSYMFORUUID_EXECUTABLE=%S/Inputs/dsymforuuid.sh %lldb --capture --capture-path %t.repro -c %S/Inputs/core -o 'reproducer generate' + +# RUN: cat %t.repro/symbol-files.yaml | FileCheck %s +# CHECK: AD52358C-94F8-3796-ADD6-B20FFAC00E5C + +# RUN: %lldb -b -o 'reproducer dump -p symbol-files -f %t.repro' | FileCheck %s --check-prefix DUMP + +# DUMP: uuid: AD52358C-94F8-3796-ADD6-B20FFAC00E5C +# DUMP-NEXT: module path: /path/to/unstripped/executable +# DUMP-nEXT: symbol path: /path/to/foo.dSYM/Contents/Resources/DWARF/foo