Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@
"POORPROF_DEPS": "vcpkg"
}
},
{
"name": "debug-vcpkg-local",
"displayName": "Debug vcpkg",
"description": "Debug build using deps from local instance of vcpkg",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/debug-vcpkg-local",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_TOOLCHAIN_FILE": "/home/sergey/dev/vcpkg/scripts/buildsystems/vcpkg.cmake",
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
"FORCE_COLORED_OUTPUT": true,
"POORPROF_DEPS": "vcpkg"
}
},
{
"name": "release-system",
"displayName": "Release system",
Expand Down Expand Up @@ -105,6 +119,12 @@
"configurePreset": "debug-system",
"displayName": "Debug system",
"description": "Debug build using deps from system"
},
{
"name": "debug-vcpkg-local",
"configurePreset": "debug-vcpkg-local",
"displayName": "Debug vcpkg with local installation",
"description": "Debug build using deps from vcpkg"
}
]
}
7 changes: 3 additions & 4 deletions cmake/dependencies/system.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ list(APPEND POORPROF_PRIVATE_LIBRARIES fmt::fmt)
find_package(ElfUtils REQUIRED)
list(APPEND POORPROF_PRIVATE_LIBRARIES ${LIBDWARF_LIBRARIES} ${LIBDW_LIBRARIES} ${LIBELF_LIBRARIES})

find_package(LibDebugInfoD REQUIRED)
list(APPEND POORPROF_PRIVATE_LIBRARIES ${LIBDEBUGINFOD_LIBRARIES})

find_package(absl REQUIRED)
list(APPEND POORPROF_PRIVATE_LIBRARIES absl::flat_hash_map)

Expand All @@ -14,7 +17,3 @@ list(APPEND POORPROF_PRIVATE_LIBRARIES spdlog::spdlog)

find_package(re2 REQUIRED)
list(APPEND POORPROF_PRIVATE_LIBRARIES re2::re2)

# set(Boost_USE_STATIC_LIBS ON)
# find_package(Boost REQUIRED COMPONENTS iostreams)
# list(APPEND POORPROF_PRIVATE_LIBRARIES Boost::iostreams)
7 changes: 3 additions & 4 deletions cmake/dependencies/vcpkg.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ list(APPEND POORPROF_PRIVATE_LIBRARIES fmt::fmt)
find_package(ElfUtils REQUIRED)
list(APPEND POORPROF_PRIVATE_LIBRARIES ${LIBDWARF_LIBRARIES} ${LIBDW_LIBRARIES} ${LIBELF_LIBRARIES})

find_package(LibDebugInfoD REQUIRED)
list(APPEND POORPROF_PRIVATE_LIBRARIES ${LIBDEBUGINFOD_LIBRARIES})

find_package(absl REQUIRED)
list(APPEND POORPROF_PRIVATE_LIBRARIES absl::flat_hash_map)

Expand All @@ -14,7 +17,3 @@ list(APPEND POORPROF_PRIVATE_LIBRARIES spdlog::spdlog)

find_package(re2 REQUIRED)
list(APPEND POORPROF_PRIVATE_LIBRARIES re2::re2)

# set(Boost_USE_STATIC_LIBS ON)
# find_package(Boost REQUIRED COMPONENTS iostreams)
# list(APPEND POORPROF_PRIVATE_LIBRARIES Boost::iostreams)
60 changes: 60 additions & 0 deletions cmake/modules/FindLibDebugInfoD.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# - Try to find libdebuginfod
# Once done this will define
#
# LIBDEBUGINFOD_FOUND - system has libdebuginfod
# LIBDEBUGINFOD_INCLUDE_DIRS - the libdebuginfod include directory
# LIBDEBUGINFOD_LIBRARIES - Link these to use libdebuginfod
# LIBDEBUGINFOD_DEFINITIONS - Compiler switches required for using libdebuginfod
#

find_package(PkgConfig QUIET)

if(PKG_CONFIG_FOUND)
set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON)
pkg_check_modules(PC_LIBDEBUGINFOD QUIET libdebuginfod)
endif()

find_path (LIBDEBUGINFOD_INCLUDE_DIR
NAMES
debuginfod.h
HINTS
${PC_LIBDEBUGINFOD_INCLUDE_DIRS}
PATHS
/usr/include
/usr/include/elfutils
/usr/local/include
/usr/local/include/elfutils
/opt/local/include
/opt/local/include/elfutils
/sw/include
/sw/include/elfutils
ENV CPATH)

find_library (LIBDEBUGINFOD_LIBRARY
NAMES
debuginfod
HINTS
${PC_LIBDEBUGINFOD_LIBRARY_DIRS}
PATHS
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib
ENV LIBRARY_PATH
ENV LD_LIBRARY_PATH)

# Transitive deps
find_package(CURL)

include (FindPackageHandleStandardArgs)

find_package_handle_standard_args(LibDebugInfoD DEFAULT_MSG
LIBDEBUGINFOD_LIBRARY
LIBDEBUGINFOD_INCLUDE_DIR
CURL_LIBRARIES)


mark_as_advanced(LIBDEBUGINFOD_LIBRARY LIBDEBUGINFOD_INCLUDE_DIR)

set(LIBDEBUGINFOD_LIBRARIES ${LIBDEBUGINFOD_LIBRARY} ${CURL_LIBRARIES})
set(LIBDEBUGINFOD_INCLUDE_DIRS ${LIBDEBUGINFOD_INCLUDE_DIR} )
70 changes: 63 additions & 7 deletions src/cli/main.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "dw/debuginfod.h"
#include "dw/gdb_index.h"
#include "util/ctrlc.h"
#include "util/defer.h"
#include "util/demangle.h"
#include "util/error.h"
#include "util/iterator.h"
#include "util/literals.h"

Expand Down Expand Up @@ -76,11 +78,11 @@ class DwflError : public std::runtime_error {
};

#define CHECK_DWFL(...) \
if (int err = (__VA_ARGS__); err != 0) { \
if (auto err = (__VA_ARGS__); err != 0) { \
if (err < 0) { \
throw DwflError{}; \
} \
throw std::system_error{err, std::system_category()}; \
throw std::system_error{static_cast<int>(err), std::system_category()}; \
}

class Unwinder {
Expand Down Expand Up @@ -125,7 +127,45 @@ class Unwinder {

using SymbolList = absl::InlinedVector<Symbol, 2>;

public:
static int FindDebugInfo(Dwfl_Module* mod, void** userdata,
const char* modname, Dwarf_Addr base,
const char* file_name, const char* debuglink_name,
GElf_Word debuglink_crc, char** debuginfo_filename)
{
spdlog::info("FindDebugInfo for module {} (file_name: {})", modname, file_name);

// Try to use standard search facility
int fd = dwfl_standard_find_debuginfo(mod, userdata, modname, base, file_name, debuglink_name, debuglink_crc, debuginfo_filename);
if (fd >= 0) {
return fd;
}

// Try to find debuginfo using debuginfod
if (mod == nullptr) {
return -1;
}

GElf_Addr bias;
const unsigned char* buildIdBytes;
int len = dwfl_module_build_id(mod, &buildIdBytes, &bias);
if (len < 0) {
throw DwflError{};
}
std::string buildId(reinterpret_cast<const char*>(buildIdBytes), len);

if (!userdata) {
spdlog::error("Module {} is not registered in the unwinder", modname);
return -1;
}
Unwinder* that = static_cast<Unwinder*>(*userdata);
std::optional<int> res = that->RemoteDebugInfo_.FindDebugInfo(buildId);
if (res) {
return *res;
}

return -1;
}

public:
Unwinder(Options opts)
: Options_{std::move(opts)}
Expand All @@ -135,7 +175,7 @@ class Unwinder {
, DebugInfoPath_{DebugInfo_ ? DebugInfo_->data() : nullptr}
, Callbacks_{
.find_elf = dwfl_linux_proc_find_elf,
.find_debuginfo = dwfl_standard_find_debuginfo,
.find_debuginfo = &FindDebugInfo,
.debuginfo_path = &DebugInfoPath_,
}
{
Expand All @@ -144,6 +184,7 @@ class Unwinder {
}
InitializeDwfl();
FillDwflReport();
RegisterDwflModules();
AttachToProcess();
ValidateAttachedPid();
}
Expand Down Expand Up @@ -667,9 +708,8 @@ class Unwinder {
dwfl_report_begin(Dwfl_);
if (CustomMaps_) {
FILE* maps = fopen(CustomMaps_->data(), "r");
DEFER {
fclose(maps);
};
DEFER { fclose(maps); };

int code = dwfl_linux_proc_maps_report(Dwfl_, maps);
CHECK_DWFL(code);
} else {
Expand All @@ -679,6 +719,20 @@ class Unwinder {
CHECK_DWFL(dwfl_report_end(Dwfl_, nullptr, nullptr));
}

// Fill userdata in each dwfl module.
void RegisterDwflModules() {
ptrdiff_t offset = 0;
do {
offset = dwfl_getmodules(Dwfl_, +[](Dwfl_Module* mod, void**, const char* , Dwarf_Addr, void* arg) -> int {
void** userdata;
dwfl_module_info(mod, &userdata, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
*userdata = arg;
return DWARF_CB_OK;
}, this, offset);
} while (offset > 0);
CHECK_DWFL(offset);
}

void AttachToProcess() {
CHECK_DWFL(dwfl_linux_proc_attach(Dwfl_, Pid_, /*assume_ptrace_stopped=*/false));
}
Expand All @@ -704,6 +758,8 @@ class Unwinder {
absl::flat_hash_map<pid_t, std::string> ThreadNameCache_;
absl::flat_hash_map<size_t, TraceInfo> Traces_;
absl::flat_hash_map<Dwfl_Module*, std::unique_ptr<ObjectFile>> Modules_;

RemoteDebugInfo RemoteDebugInfo_;
};

#undef CHECK_DWFL
Expand Down
4 changes: 3 additions & 1 deletion src/dw/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
set(
SOURCES
debuginfod.cpp
dw.cpp
)

set(
HEADERS
debuginfod.h
dw.h
)

Expand All @@ -16,7 +18,7 @@ set_target_properties(poorprof_dw PROPERTIES
)

target_link_libraries(poorprof_dw
PUBLIC ${LIBDWARF_LIBRARIES}
PUBLIC ${LIBDWARF_LIBRARIES} ${LIBDEBUGINFOD_LIBRARIES}
PUBLIC ${CMAKE_DL_LIBS}
)

Expand Down
59 changes: 59 additions & 0 deletions src/dw/debuginfod.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include "debuginfod.h"

#include "util/assert.h"
#include "util/defer.h"

#include <spdlog/spdlog.h>

#include <elfutils/debuginfod.h>

#ifdef __linux__
#include <dlfcn.h>
#endif


namespace poorprof::dw {

RemoteDebugInfo::RemoteDebugInfo()
{
Client_ = debuginfod_begin();
if (Client_) {
debuginfod_set_progressfn(Client_, +[](debuginfod_client*, long a, long b) {
spdlog::info("Loading {}/{}", a, b);
return 0;
});
spdlog::info("Successfully initialized debuginfod client");
} else {
spdlog::info("Failed to initialize debuginfod client");
}
}

RemoteDebugInfo::~RemoteDebugInfo() {
if (Client_) {
debuginfod_end(std::exchange(Client_, nullptr));
}
}

std::optional<FileDescriptor> RemoteDebugInfo::FindDebugInfo(std::string buildId) {
if (!Client_) {
return std::nullopt;
}

char* path = nullptr;
DEFER {
::free(path);
};

spdlog::info("Loading remote debug info");
const unsigned char* ptr = reinterpret_cast<const unsigned char*>(buildId.data());
int fd = debuginfod_find_debuginfo(Client_, ptr, buildId.size(), &path);
if (fd <= 0) {
spdlog::warn("Failed to find remote debug info: {}", strerror(-fd));
return std::nullopt;
}

spdlog::info("Successfully fetched remote debug info");
return fd;
}

} // namespace poorprof::dw
30 changes: 30 additions & 0 deletions src/dw/debuginfod.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

#include "util/fs.h"
#include "util/noncopyable.h"

#include <absl/container/flat_hash_map.h>

#include <optional>


struct debuginfod_client;

namespace poorprof::dw {

using FileDescriptor = int;

class RemoteDebugInfo : util::NonCopyable {
struct DebugInfoLib;

public:
RemoteDebugInfo();
~RemoteDebugInfo();

std::optional<FileDescriptor> FindDebugInfo(std::string buildId);

private:
debuginfod_client* Client_ = nullptr;
};

} // namespace poorprof::dw
10 changes: 10 additions & 0 deletions src/util/fs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

#include <filesystem>


namespace poorprof {

namespace fs = std::filesystem;

} // namespace poorprof
Loading