kwk updated this revision to Diff 252510.
kwk marked 10 inline comments as done.
kwk added a comment.
- Add newline to end of FindDebuginfod.cmake
- Describe empty string returned from debuginfod::findSource()
- Don't treat build IDs of len <= 8 as an error but simply as not found
- move inexpensive debuginfod::isAvailable() check to beginning of if-stmt
- Simplify line number check in test file to avoid adjusting the line number
every time the test changes
- Add newline to source-list.cpp test file
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D75750/new/
https://reviews.llvm.org/D75750
Files:
lldb/cmake/modules/FindDebuginfod.cmake
lldb/cmake/modules/LLDBConfig.cmake
lldb/include/lldb/Host/Config.h.cmake
lldb/include/lldb/Host/DebugInfoD.h
lldb/source/Core/SourceManager.cpp
lldb/source/Host/CMakeLists.txt
lldb/source/Host/common/DebugInfoD.cpp
lldb/test/CMakeLists.txt
lldb/test/Shell/SymbolFile/DWARF/source-list.cpp
lldb/test/Shell/lit.cfg.py
lldb/test/Shell/lit.site.cfg.py.in
Index: lldb/test/Shell/lit.site.cfg.py.in
===================================================================
--- lldb/test/Shell/lit.site.cfg.py.in
+++ lldb/test/Shell/lit.site.cfg.py.in
@@ -16,6 +16,7 @@
config.python_executable = "@PYTHON_EXECUTABLE@"
config.have_zlib = @LLVM_ENABLE_ZLIB@
config.lldb_enable_lzma = @LLDB_ENABLE_LZMA@
+config.lldb_enable_debuginfod = @LLDB_ENABLE_DEBUGINFOD@
config.host_triple = "@LLVM_HOST_TRIPLE@"
config.lldb_bitness = 64 if @LLDB_IS_64_BITS@ else 32
config.lldb_enable_python = @LLDB_ENABLE_PYTHON@
Index: lldb/test/Shell/lit.cfg.py
===================================================================
--- lldb/test/Shell/lit.cfg.py
+++ lldb/test/Shell/lit.cfg.py
@@ -117,6 +117,9 @@
if config.lldb_enable_lzma:
config.available_features.add('lzma')
+if config.lldb_enable_debuginfod:
+ config.available_features.add('debuginfod')
+
if find_executable('xz') != None:
config.available_features.add('xz')
Index: lldb/test/Shell/SymbolFile/DWARF/source-list.cpp
===================================================================
--- /dev/null
+++ lldb/test/Shell/SymbolFile/DWARF/source-list.cpp
@@ -0,0 +1,135 @@
+// clang-format off
+// REQUIRES: debuginfod
+// UNSUPPORTED: darwin, windows
+
+// Test that we can display the source of functions using debuginfod when the
+// original source file is no longer present.
+//
+// The debuginfod client requires a buildid in the binary, so we compile one in.
+// We can create the directory structure on disc that the client expects on a
+// webserver that serves source files. Then we fire up a python based http
+// server in the root of that mock directory, set the DEBUGINFOD_URLS
+// environment variable and let LLDB do the rest.
+//
+// Go here to find more about debuginfod:
+// https://sourceware.org/elfutils/Debuginfod.html
+
+
+// We copy this file because we want to compile and later move it away
+
+// RUN: mkdir -p %t.buildroot
+// RUN: cp %s %t.buildroot/test.cpp
+
+// We use the prefix map in order to overwrite all DW_AT_decl_file paths
+// in the DWARF. We cd into the directory before compiling to get
+// DW_AT_comp_dir pickup %t.buildroot as well so it will be replaced by
+// /my/new/path.
+
+// RUN: cd %t.buildroot
+// RUN: %clang_host \
+// RUN: -g \
+// RUN: -Wl,--build-id="0xaaaaaaaaaabbbbbbbbbbccccccccccdddddddddd" \
+// RUN: -fdebug-prefix-map=%t.buildroot=/my/new/path \
+// RUN: -o %t \
+// RUN: %t.buildroot/test.cpp
+
+
+// We move the original source file to a directory that looks like a debuginfod
+// URL part.
+
+// RUN: rm -rf %t.mock
+// RUN: mkdir -p %t.mock/buildid/aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd/source/my/new/path
+// RUN: mv %t.buildroot/test.cpp %t.mock/buildid/aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd/source/my/new/path
+
+
+// Adjust where debuginfod stores cache files:
+
+// RUN: rm -rf %t.debuginfod_cache_path
+// RUN: mkdir -p %t.debuginfod_cache_path
+// RUN: export DEBUGINFOD_CACHE_PATH=%t.debuginfod_cache_path
+
+
+// Start HTTP file server on port picked by OS and wait until it is ready
+// The server will be closed on exit of the test.
+
+// RUN: rm -f "%t.server.log"
+// RUN: timeout 5 python3 -u -m http.server 0 --directory %t.mock --bind "localhost" &> %t.server.log & export PID=$!
+// RUN: trap 'kill $PID;' EXIT INT
+
+
+// Extract HTTP address from the first line of the server log
+// (e.g. "Serving HTTP on 127.0.0.1 port 40587 (http://127.0.0.1:40587/) ..")
+
+// RUN: echo -n "Waiting for server to be ready"
+// RUN: SERVER_ADDRESS=""
+// RUN: while [ -z "$SERVER_ADDRESS" ]; do \
+// RUN: echo -n "."; \
+// RUN: sleep 0.01; \
+// RUN: SERVER_ADDRESS=$(head -n1 %t.server.log | grep "http://.\+/\+" -o); \
+// RUN: done
+// RUN: echo "DONE"
+
+
+//-- TEST 1 -- No debuginfod awareness ----------------------------------------
+
+
+// RUN: DEBUGINFOD_URLS="" \
+// RUN: %lldb -f %t -o 'source list -n main' | FileCheck --dump-input=fail %s --check-prefix=TEST-1
+
+// TEST-1: (lldb) source list -n main
+// TEST-1: File: /my/new/path/test.cpp
+// TEST-1-EMPTY:
+
+
+//-- TEST 2 -- debuginfod URL pointing in wrong place --------------------------
+
+
+// RUN: DEBUGINFOD_URLS="http://example.com/debuginfod" \
+// RUN: %lldb -f %t -o 'source list -n main' | FileCheck --dump-input=fail %s --check-prefix=TEST-2
+
+// TEST-2: (lldb) source list -n main
+// TEST-2: File: /my/new/path/test.cpp
+// TEST-2-EMPTY:
+
+
+//-- TEST 3 -- debuginfod URL pointing corectly --------------------------------
+
+
+// RUN: DEBUGINFOD_URLS="$SERVER_ADDRESS" \
+// RUN: %lldb -f %t -o 'source list -n main' | FileCheck --dump-input=fail %s --check-prefix=TEST-3
+
+// TEST-3: (lldb) source list -n main
+// TEST-3: File: /my/new/path/test.cpp
+// TEST-3: {{[0-9]+}}
+// TEST-3-NEXT: {{[0-9]+}} // Some context lines before
+// TEST-3-NEXT: {{[0-9]+}} // the function.
+// TEST-3-NEXT: {{[0-9]+}}
+// TEST-3-NEXT: {{[0-9]+}}
+// TEST-3-NEXT: {{[0-9]+}} int main(int argc, char **argv) {
+// TEST-3-NEXT: {{[0-9]+}} // Here are some comments.
+// TEST-3-NEXT: {{[0-9]+}} // That we should print when listing source.
+// TEST-3-NEXT: {{[0-9]+}} return 0;
+// TEST-3-NEXT: {{[0-9]+}} }
+// TEST-3-NEXT: {{[0-9]+}}
+// TEST-3-NEXT: {{[0-9]+}} // Some context lines after
+// TEST-3-NEXT: {{[0-9]+}} // the function.
+// TEST-3-EMPTY:
+
+// Validate that the server received the request from debuginfod client.
+
+// RUN: cat %t.server.log | FileCheck --dump-input=fail %s --check-prefix=VALIDATION
+// VALIDATION: 127.0.0.1 - - [{{.*}}] "GET /buildid/aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd/source/my/new/path/test.cpp HTTP/1.1" 200 -
+
+
+// Some context lines before
+// the function.
+
+
+int main(int argc, char **argv) {
+ // Here are some comments.
+ // That we should print when listing source.
+ return 0;
+}
+
+// Some context lines after
+// the function.
Index: lldb/test/CMakeLists.txt
===================================================================
--- lldb/test/CMakeLists.txt
+++ lldb/test/CMakeLists.txt
@@ -151,6 +151,7 @@
LLDB_ENABLE_PYTHON
LLDB_ENABLE_LUA
LLDB_ENABLE_LZMA
+ LLDB_ENABLE_DEBUGINFOD
LLVM_ENABLE_ZLIB
LLVM_ENABLE_SHARED_LIBS
LLDB_IS_64_BITS)
Index: lldb/source/Host/common/DebugInfoD.cpp
===================================================================
--- /dev/null
+++ lldb/source/Host/common/DebugInfoD.cpp
@@ -0,0 +1,98 @@
+//===-- 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/Host/DebugInfoD.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Errno.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; }
+
+llvm::Error findSource(const UUID &buildID, const std::string &path,
+ std::string &cache_path) {
+ llvm_unreachable("debuginfod::findSource is unavailable");
+}
+
+#else // LLDB_ENABLE_DEBUGINFOD
+
+bool isAvailable() { return true; }
+
+
+
+llvm::Expected<std::string> findSource(const UUID &buildID,
+ const std::string &path) {
+ std::string result_path;
+ // This check works around UUIDs that are not build-ids. For example, callers
+ // of this function use Module::GetUUID() which calls ObjectFileELF::GetUUID()
+ // (in case of the ELF binary format) which has two fallback strategies for
+ // returning a UUID when no UUID for the object was found in the section
+ // header. Those UUIDs have a length of 8 or 4 bytes, which is why we exclude
+ // these lengths.
+ if (!buildID.IsValid() || buildID.GetBytes().size() <= 8)
+ return result_path;
+
+ debuginfod_client *client = debuginfod_begin();
+
+ if (!client)
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "failed to create debuginfod connection handle: %s", strerror(errno));
+
+ char *cache_path = nullptr;
+ int rc = debuginfod_find_source(client, buildID.GetBytes().data(),
+ buildID.GetBytes().size(), path.c_str(),
+ &cache_path);
+
+ debuginfod_end(client);
+
+ if (cache_path) {
+ result_path = std::string(cache_path);
+ free(cache_path);
+ }
+
+ if (rc < 0) {
+ if (rc == -ENOSYS) // No DEBUGINFO_URLS were specified
+ return result_path;
+ else if (rc == -ENOENT) // No such file or directory, aka build-id not
+ // available on servers.
+ return result_path;
+ else
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "debuginfod_find_source query failed (CODE=%d): %s", -rc,
+ llvm::sys::StrError(-rc).c_str());
+ }
+
+ if (close(rc) < 0) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "failed to close result of call to debuginfo_find_source: %s",
+ llvm::sys::StrError(errno).c_str());
+ }
+
+ return result_path;
+}
+
+#endif // LLDB_ENABLE_DEBUGINFOD
+
+} // end of namespace debuginfod
+} // namespace lldb_private
Index: lldb/source/Host/CMakeLists.txt
===================================================================
--- lldb/source/Host/CMakeLists.txt
+++ lldb/source/Host/CMakeLists.txt
@@ -16,6 +16,7 @@
common/HostThread.cpp
common/LockFileBase.cpp
common/LZMA.cpp
+ common/DebugInfoD.cpp
common/MainLoop.cpp
common/MonitoringProcessLauncher.cpp
common/NativeProcessProtocol.cpp
@@ -147,6 +148,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 ()
Index: lldb/source/Core/SourceManager.cpp
===================================================================
--- lldb/source/Core/SourceManager.cpp
+++ lldb/source/Core/SourceManager.cpp
@@ -15,6 +15,7 @@
#include "lldb/Core/Highlighter.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleList.h"
+#include "lldb/Host/DebugInfoD.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/Function.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,6 @@
bool got_multiple = false;
if (num_matches != 0) {
if (num_matches > 1) {
- SymbolContext sc;
CompileUnit *test_cu = nullptr;
for (unsigned i = 0; i < num_matches; i++) {
@@ -432,11 +434,11 @@
}
}
if (!got_multiple) {
- 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 +454,24 @@
m_mod_time = FileSystem::Instance().GetModificationTime(m_file_spec);
}
}
+
+ // Try finding the file using elfutils' debuginfod
+ if (debuginfod::isAvailable() &&
+ !FileSystem::Instance().Exists(m_file_spec) && sc.module_sp) {
+ llvm::Expected<std::string> cache_path = debuginfod::findSource(
+ sc.module_sp->GetUUID(), file_spec.GetCString());
+ if (!cache_path) {
+ sc.module_sp->ReportWarning(
+ "An error occurred while finding the "
+ "source file %s using debuginfod for build ID %s: %s",
+ file_spec.GetCString(),
+ sc.module_sp->GetUUID().GetAsString("").c_str(),
+ llvm::toString(cache_path.takeError()).c_str());
+ } else if (!cache_path->empty()) {
+ m_file_spec = FileSpec(*cache_path);
+ m_mod_time = FileSystem::Instance().GetModificationTime(*cache_path);
+ }
+ }
}
}
Index: lldb/include/lldb/Host/DebugInfoD.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Host/DebugInfoD.h
@@ -0,0 +1,36 @@
+//===-- 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"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Error.h"
+
+namespace lldb_private {
+
+namespace debuginfod {
+
+// Returns \c true if debuginfod support was compiled-in; otherwise \c false is
+// returned.
+bool isAvailable();
+
+// Asks all servers in environment variable \c DEBUGINFOD_URLS for the \a path
+// of an artifact with a given \a buildID and returns the path to a locally
+// cached version of the file. If there was an error, we return that instead.
+// If the file wasn't found on the server or if the UUID is shorter than or
+// equal to 8 bytes, the returned string is empty.
+llvm::Expected<std::string> findSource(const UUID &buildID,
+ const std::string &path);
+
+} // End of namespace debuginfod
+
+} // End of namespace lldb_private
+
+#endif // LLDB_HOST_DEBUGINFOD_H
Index: lldb/include/lldb/Host/Config.h.cmake
===================================================================
--- lldb/include/lldb/Host/Config.h.cmake
+++ 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
Index: lldb/cmake/modules/LLDBConfig.cmake
===================================================================
--- lldb/cmake/modules/LLDBConfig.cmake
+++ 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})
Index: lldb/cmake/modules/FindDebuginfod.cmake
===================================================================
--- /dev/null
+++ 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()
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits