diff --git a/lldb/cmake/modules/FindDebuginfod.cmake b/lldb/cmake/modules/FindDebuginfod.cmake new file mode 100644 --- /dev/null +++ b/lldb/cmake/modules/FindDebuginfod.cmake @@ -0,0 +1,58 @@ +#.rst: +# FindDebuginfod +# ----------- +# +# Find debuginfod library and headers +# +# The module defines the following variables: +# +# :: +# +# Debuginfod_FOUND - true if debuginfod was found +# Debuginfod_INCLUDE_DIRS - include search path +# Debuginfod_LIBRARIES - libraries to link +# Debuginfod_VERSION_STRING - version number +# +# TODO(kwk): Debuginfod_VERSION_STRING is only set if pkg-config file is +# available. Trying to see if we can get a MAJOR, MINOR, PATCH define in the +# debuginfod.h file. + +if(Debuginfod_INCLUDE_DIRS AND Debuginfod_LIBRARIES) + set(Debuginfod_FOUND TRUE) +else() + # Utilize package config (e.g. /usr/lib64/pkgconfig/libdebuginfod.pc) to fetch + # version information. + find_package(PkgConfig QUIET) + pkg_check_modules(PC_Debuginfod QUIET libdebuginfod) + + find_path(Debuginfod_INCLUDE_DIRS + NAMES + elfutils/debuginfod.h + HINTS + /usr/include + ${PC_Debuginfod_INCLUDEDIR} + ${PC_Debuginfod_INCLUDE_DIRS} + ${CMAKE_INSTALL_FULL_INCLUDEDIR}) + find_library(Debuginfod_LIBRARIES + NAMES + debuginfod + HINTS + ${PC_Debuginfod_LIBDIR} + ${PC_Debuginfod_LIBRARY_DIRS} + ${CMAKE_INSTALL_FULL_LIBDIR}) + + if(Debuginfod_INCLUDE_DIRS AND EXISTS "${Debuginfod_INCLUDE_DIRS}/debuginfod.h") + set(Debuginfod_VERSION_STRING "${PC_Debuginfod_VERSION}") + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Debuginfod + FOUND_VAR + Debuginfod_FOUND + REQUIRED_VARS + Debuginfod_INCLUDE_DIRS + Debuginfod_LIBRARIES + VERSION_VAR + Debuginfod_VERSION_STRING) + mark_as_advanced(Debuginfod_INCLUDE_DIRS Debuginfod_LIBRARIES) +endif() \ No newline at end of file diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake --- a/lldb/cmake/modules/LLDBConfig.cmake +++ b/lldb/cmake/modules/LLDBConfig.cmake @@ -58,6 +58,7 @@ add_optional_dependency(LLDB_ENABLE_LUA "Enable Lua scripting support in LLDB" LuaAndSwig LUAANDSWIG_FOUND) add_optional_dependency(LLDB_ENABLE_PYTHON "Enable Python scripting support in LLDB" PythonInterpAndLibs PYTHONINTERPANDLIBS_FOUND) add_optional_dependency(LLDB_ENABLE_LIBXML2 "Enable Libxml 2 support in LLDB" LibXml2 LIBXML2_FOUND VERSION 2.8) +add_optional_dependency(LLDB_ENABLE_DEBUGINFOD "Enable Debuginfod support in LLDB" Debuginfod Debuginfod_FOUND) option(LLDB_USE_SYSTEM_SIX "Use six.py shipped with system and do not install a copy of it" OFF) option(LLDB_USE_ENTITLEMENTS "When codesigning, use entitlements if available" ON) @@ -233,6 +234,10 @@ include_directories(${LIBLZMA_INCLUDE_DIRS}) endif() +if (LLDB_ENABLE_DEBUGINFOD) + include_directories(${Debuginfod_INCLUDE_DIRS}) +endif() + if (LLDB_ENABLE_LIBXML2) list(APPEND system_libs ${LIBXML2_LIBRARIES}) include_directories(${LIBXML2_INCLUDE_DIR}) diff --git a/lldb/include/lldb/Host/Config.h.cmake b/lldb/include/lldb/Host/Config.h.cmake --- a/lldb/include/lldb/Host/Config.h.cmake +++ b/lldb/include/lldb/Host/Config.h.cmake @@ -36,6 +36,8 @@ #cmakedefine01 LLDB_ENABLE_LZMA +#cmakedefine01 LLDB_ENABLE_DEBUGINFOD + #cmakedefine01 LLDB_ENABLE_CURSES #cmakedefine01 LLDB_ENABLE_LIBEDIT diff --git a/lldb/include/lldb/Host/DebugInfoD.h b/lldb/include/lldb/Host/DebugInfoD.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/Host/DebugInfoD.h @@ -0,0 +1,33 @@ +//===-- DebugInfoD.h --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_HOST_DEBUGINFOD_H +#define LLDB_HOST_DEBUGINFOD_H + +#include "lldb/Utility/UUID.h" + +namespace llvm { +class Error; +} // End of namespace llvm + +namespace lldb_private { + +namespace debuginfod { + +bool isAvailable(); + +UUID getBuildIDFromModule(const lldb::ModuleSP &module); + +llvm::Error findSource(UUID buildID, const std::string &path, + std::string &result_path); + +} // End of namespace debuginfod + +} // End of namespace lldb_private + +#endif // LLDB_HOST_DEBUGINFOD_H diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py --- a/lldb/packages/Python/lldbsuite/test/lldbtest.py +++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py @@ -1,7 +1,7 @@ """ LLDB module which provides the abstract base class of lldb test case. -The concrete subclass can override lldbtest.TesBase in order to inherit the +The concrete subclass can override lldbtest.TestBase in order to inherit the common behavior for unitest.TestCase.setUp/tearDown implemented in this file. The subclass should override the attribute mydir in order for the python runtime diff --git a/lldb/source/Core/SourceManager.cpp b/lldb/source/Core/SourceManager.cpp --- a/lldb/source/Core/SourceManager.cpp +++ b/lldb/source/Core/SourceManager.cpp @@ -29,6 +29,7 @@ #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" #include "lldb/lldb-enumerations.h" +#include "lldb/Host/DebugInfoD.h" #include "llvm/ADT/Twine.h" @@ -402,7 +403,9 @@ if (target) { m_source_map_mod_id = target->GetSourcePathMap().GetModificationID(); - if (!file_spec.GetDirectory() && file_spec.GetFilename()) { + SymbolContext sc; + if ((!file_spec.GetDirectory() && file_spec.GetFilename()) || + !FileSystem::Instance().Exists(m_file_spec)) { // If this is just a file name, lets see if we can find it in the // target: bool check_inlines = false; @@ -416,7 +419,7 @@ bool got_multiple = false; if (num_matches != 0) { if (num_matches > 1) { - SymbolContext sc; + // SymbolContext sc; CompileUnit *test_cu = nullptr; for (unsigned i = 0; i < num_matches; i++) { @@ -432,11 +435,12 @@ } } if (!got_multiple) { - SymbolContext sc; + // SymbolContext sc; sc_list.GetContextAtIndex(0, sc); if (sc.comp_unit) m_file_spec = sc.comp_unit->GetPrimaryFile(); - m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec); + m_mod_time = + FileSystem::Instance().GetModificationTime(m_file_spec); } } } @@ -452,6 +456,24 @@ m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec); } } + + // Try finding the file using elfutils' debuginfod + if (!FileSystem::Instance().Exists(m_file_spec) && + debuginfod::isAvailable() && sc.module_sp) { + UUID buildID = debuginfod::getBuildIDFromModule(sc.module_sp); + std::string cache_path; + llvm::Error err = + debuginfod::findSource(buildID, file_spec.GetCString(), cache_path); + if (err) { + sc.module_sp->ReportWarning("An error occurred while finding the " + "source file %s using debuginfod: %s", + file_spec.GetCString(), + llvm::toString(std::move(err)).c_str()); + } else { + m_file_spec = FileSpec(cache_path); + m_mod_time = FileSystem::Instance().GetModificationTime(cache_path); + } + } } } diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -30,6 +30,7 @@ common/HostThread.cpp common/LockFileBase.cpp common/LZMA.cpp + common/DebugInfoD.cpp common/MainLoop.cpp common/MonitoringProcessLauncher.cpp common/NativeProcessProtocol.cpp @@ -161,6 +162,9 @@ if (LLDB_ENABLE_LZMA) list(APPEND EXTRA_LIBS ${LIBLZMA_LIBRARIES}) endif() +if (LLDB_ENABLE_DEBUGINFOD) + list(APPEND EXTRA_LIBS ${Debuginfod_LIBRARIES}) +endif() if (WIN32) list(APPEND LLDB_SYSTEM_LIBS psapi) endif () diff --git a/lldb/source/Host/common/DebugInfoD.cpp b/lldb/source/Host/common/DebugInfoD.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Host/common/DebugInfoD.cpp @@ -0,0 +1,120 @@ +//===-- DebugInfoD.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Module.h" +#include "lldb/Host/Config.h" +#include "lldb/Symbol/ObjectFile.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "lldb/Host/DebugInfoD.h" + +#if LLDB_ENABLE_DEBUGINFOD +#include "elfutils/debuginfod.h" +#endif + +namespace lldb_private { + +namespace debuginfod { + +using namespace lldb; +using namespace lldb_private; + +#if !LLDB_ENABLE_DEBUGINFOD +bool isAvailable() { return false; } + +UUID getBuildIDFromModule(const ModuleSP &module) { + llvm_unreachable("debuginfod::getBuildIDFromModule is unavailable"); +}; + +llvm::Error findSource(UUID buildID, const std::string &path, + std::string &cache_path, sys::TimePoint<> &mod_time) { + llvm_unreachable("debuginfod::findSource is unavailable"); +} + +#else // LLDB_ENABLE_DEBUGINFOD + +bool isAvailable() { return true; } + +UUID getBuildIDFromModule(const ModuleSP &module) { + UUID buildID; + + if (!module) + return buildID; + + const FileSpec &moduleFileSpec = module->GetFileSpec(); + ModuleSpecList specList; + size_t nSpecs = + ObjectFile::GetModuleSpecifications(moduleFileSpec, 0, 0, specList); + + for (size_t i = 0; i < nSpecs; i++) { + ModuleSpec spec; + if (!specList.GetModuleSpecAtIndex(i, spec)) + continue; + + const UUID &uuid = spec.GetUUID(); + if (!uuid.IsValid()) + continue; + + buildID = uuid; + break; + } + return buildID; +} + +llvm::Error findSource(UUID buildID, const std::string &path, + std::string &result_path) { + if (!buildID.IsValid()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "invalid build ID: %s", + buildID.GetAsString("").c_str()); + + debuginfod_client *client = debuginfod_begin(); + + if (!client) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "failed to create debuginfod connection handle: %s", strerror(errno)); + + // debuginfod_set_progressfn(client, [](debuginfod_client *client, long a, + // long b) -> int { + // fprintf(stderr, "KWK === a: %ld b : %ld \n", a, b); + // return 0; // continue + // }); + + char *cache_path = nullptr; + int rc = debuginfod_find_source(client, buildID.GetBytes().data(), + buildID.GetBytes().size(), path.c_str(), + &cache_path); + + if (rc < 0) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "debuginfod_find_source query failed: %s", + strerror(-rc)); + + if (cache_path) { + result_path = std::string(cache_path); + free(cache_path); + } + + llvm::Error err = llvm::Error::success(); + if (close(rc) < 0) { + err = llvm::createStringError( + llvm::inconvertibleErrorCode(), + "failed to close result of call to debuginfo_find_source: %s", + strerror(errno)); + } + + debuginfod_end(client); + + return err; +} + +#endif // LLDB_ENABLE_DEBUGINFOD + +} // end of namespace debuginfod +} // namespace lldb_private