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
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to