Skip to content

Commit

Permalink
Add libdwfl based implementation
Browse files Browse the repository at this point in the history
This commit adds an implementation based on libdwfl from
elfutils.

Implements #176
  • Loading branch information
kanje committed Nov 24, 2024
1 parent 22982db commit 5f0e423
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 2 deletions.
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function(stacktrace_check var source incs libs defs)
endfunction()

stacktrace_check(BOOST_STACKTRACE_HAS_BACKTRACE has_backtrace.cpp "" "backtrace" "")
stacktrace_check(BOOST_STACKTRACE_HAS_DWFL has_dwfl.cpp "" "dw" "")

set(_default_addr2line ON)
if(WIN32 AND NOT CMAKE_CXX_PLATFORM_ID MATCHES "Cygwin")
Expand All @@ -78,6 +79,7 @@ endif()

option(BOOST_STACKTRACE_ENABLE_NOOP "Boost.Stacktrace: build boost_stacktrace_noop" ON)
option(BOOST_STACKTRACE_ENABLE_BACKTRACE "Boost.Stacktrace: build boost_stacktrace_backtrace" ${BOOST_STACKTRACE_HAS_BACKTRACE})
option(BOOST_STACKTRACE_ENABLE_DWFL "Boost.Stacktrace: build boost_stacktrace_dwfl" ${BOOST_STACKTRACE_HAS_DWFL})
option(BOOST_STACKTRACE_ENABLE_ADDR2LINE "Boost.Stacktrace: build boost_stacktrace_addr2line" ${_default_addr2line})
option(BOOST_STACKTRACE_ENABLE_BASIC "Boost.Stacktrace: build boost_stacktrace_basic" ON)
option(BOOST_STACKTRACE_ENABLE_WINDBG "Boost.Stacktrace: build boost_stacktrace_windbg" ${BOOST_STACKTRACE_HAS_WINDBG})
Expand All @@ -90,6 +92,7 @@ unset(_default_from_exception)
message(STATUS "Boost.Stacktrace: "
"noop ${BOOST_STACKTRACE_ENABLE_NOOP}, "
"backtrace ${BOOST_STACKTRACE_ENABLE_BACKTRACE}, "
"dwfl ${BOOST_STACKTRACE_ENABLE_DWFL}, "
"addr2line ${BOOST_STACKTRACE_ENABLE_ADDR2LINE}, "
"basic ${BOOST_STACKTRACE_ENABLE_BASIC}, "
"windbg ${BOOST_STACKTRACE_ENABLE_WINDBG}, "
Expand All @@ -99,6 +102,7 @@ message(STATUS "Boost.Stacktrace: "

stacktrace_add_library(noop ${BOOST_STACKTRACE_ENABLE_NOOP} "" "")
stacktrace_add_library(backtrace ${BOOST_STACKTRACE_ENABLE_BACKTRACE} "backtrace;${CMAKE_DL_LIBS}" "")
stacktrace_add_library(dwfl ${BOOST_STACKTRACE_ENABLE_DWFL} "dw;${CMAKE_DL_LIBS}" "")
stacktrace_add_library(addr2line ${BOOST_STACKTRACE_ENABLE_ADDR2LINE} "${CMAKE_DL_LIBS}" "")
stacktrace_add_library(basic ${BOOST_STACKTRACE_ENABLE_BASIC} "${CMAKE_DL_LIBS}" "")
stacktrace_add_library(windbg ${BOOST_STACKTRACE_ENABLE_WINDBG} "dbgeng;ole32" "_GNU_SOURCE=1")
Expand All @@ -119,6 +123,10 @@ elseif(BOOST_STACKTRACE_ENABLE_BACKTRACE)

target_link_libraries(boost_stacktrace INTERFACE Boost::stacktrace_backtrace)

elseif(BOOST_STACKTRACE_ENABLE_DWFL)

target_link_libraries(boost_stacktrace INTERFACE Boost::stacktrace_dwfl)

elseif(BOOST_STACKTRACE_ENABLE_ADDR2LINE)

target_link_libraries(boost_stacktrace INTERFACE Boost::stacktrace_addr2line)
Expand Down
3 changes: 3 additions & 0 deletions build.jam
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ project /boost/stacktrace
explicit
[ alias boost_stacktrace_addr2line : build//boost_stacktrace_addr2line ]
[ alias boost_stacktrace_backtrace : build//boost_stacktrace_backtrace ]
[ alias boost_stacktrace_dwfl : build//boost_stacktrace_dwfl ]
[ alias boost_stacktrace_basic : build//boost_stacktrace_basic ]
[ alias boost_stacktrace_from_exception : build//boost_stacktrace_from_exception ]
[ alias boost_stacktrace_noop : build//boost_stacktrace_noop ]
Expand All @@ -31,6 +32,7 @@ explicit
[ alias all :
boost_stacktrace_addr2line
boost_stacktrace_backtrace
boost_stacktrace_dwfl
boost_stacktrace_basic
boost_stacktrace_from_exception
boost_stacktrace_noop
Expand All @@ -44,6 +46,7 @@ call-if : boost-library stacktrace
: install
boost_stacktrace_addr2line
boost_stacktrace_backtrace
boost_stacktrace_dwfl
boost_stacktrace_basic
boost_stacktrace_from_exception
boost_stacktrace_noop
Expand Down
19 changes: 19 additions & 0 deletions build/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ lib dl ;
lib gcc_s ;
lib Dbgeng ;
lib ole32 ;
lib dw ;

local LIBBACKTRACE_PATH = [ modules.peek : LIBBACKTRACE_PATH ] ;
lib backtrace
Expand All @@ -54,6 +55,9 @@ rule mp-run-simple ( sources + : args * : input-files * : requirements * : targe
mp-run-simple has_backtrace.cpp : : : <library>backtrace : libbacktrace ;
explicit libbacktrace ;

mp-run-simple has_dwfl.cpp : : : <library>dw : libdw ;
explicit libdw ;

mp-run-simple has_addr2line.cpp : : : : addr2line ;
explicit addr2line ;

Expand Down Expand Up @@ -90,6 +94,21 @@ lib boost_stacktrace_backtrace
<define>BOOST_STACKTRACE_NO_LIB=1
;

lib boost_stacktrace_dwfl
: # sources
../src/dwfl.cpp
: # requirements
<warnings>all
<target-os>linux:<library>dl
<library>dw
<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
[ check-target-builds libdw : : <build>no ]
: # default build
: # usage-requirements
#<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
<define>BOOST_STACKTRACE_NO_LIB=1
;

lib boost_stacktrace_addr2line
: # sources
../src/addr2line.cpp
Expand Down
10 changes: 10 additions & 0 deletions build/has_dwfl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <elfutils/libdwfl.h>

int main() {
Dwfl_Callbacks callbacks{nullptr, nullptr, nullptr, nullptr};
Dwfl* dwfl_ = dwfl_begin(&callbacks);
}
1 change: 1 addition & 0 deletions doc/stacktrace.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ In header only mode library could be tuned by macro. If one of the link macro fr
[[*BOOST_STACKTRACE_USE_BACKTRACE*] [*boost_stacktrace_backtrace*] [Requires linking with *libdl* on POSIX and *libbacktrace* libraries[footnote Some *libbacktrace* packages SEGFAULT if there's a concurrent work with the same `backtrace_state` instance. To avoid that issue the Boost.Stacktrace library uses `thread_local` states, unfortunately this may consume a lot of memory if you often create and destroy execution threads in your application. Define *BOOST_STACKTRACE_BACKTRACE_FORCE_STATIC* to force single instance, but make sure that [@https://github.com/boostorg/stacktrace/blob/develop/test/thread_safety_checking.cpp thread_safety_checking.cpp] works well in your setup. ]. *libbacktrace* is probably already installed in your system[footnote If you are using Clang with libstdc++ you could get into troubles of including `<backtrace.h>`, because on some platforms Clang does not search for headers in the GCC's include paths and any attempt to add GCC's include path leads to linker errors. To explicitly specify a path to the `<backtrace.h>` header you could define the *BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE* to a full path to the header. For example on Ubuntu Xenial use the command line option *-DBOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE=</usr/lib/gcc/x86_64-linux-gnu/5/include/backtrace.h>* while building with Clang. ], or built into your compiler.

Otherwise (if you are a *MinGW*/*MinGW-w64* user for example) it can be downloaded [@https://github.com/ianlancetaylor/libbacktrace from here] or [@https://github.com/gcc-mirror/gcc/tree/master/libbacktrace from here]. ] [Any compiler on POSIX, or MinGW, or MinGW-w64] [yes] [yes]]
[[*BOOST_STACKTRACE_USE_DWFL*] [*boost_stacktrace_dwfl*] [Use *libdwfl* from *elfutils*.] [POSIX] [yes] [yes]]
[[*BOOST_STACKTRACE_USE_ADDR2LINE*] [*boost_stacktrace_addr2line*] [Use *addr2line* program to retrieve stacktrace. Requires linking with *libdl* library and `::fork` system call. Macro *BOOST_STACKTRACE_ADDR2LINE_LOCATION* must be defined to the absolute path to the addr2line executable if it is not located in /usr/bin/addr2line. ] [Any compiler on POSIX] [yes] [yes]]
[[*BOOST_STACKTRACE_USE_NOOP*] [*boost_stacktrace_noop*] [Use this if you wish to disable backtracing. `stacktrace::size()` with that macro always returns 0. ] [All] [no] [no]]
]
Expand Down
2 changes: 2 additions & 0 deletions include/boost/stacktrace/detail/frame_unwind.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

#ifdef BOOST_STACKTRACE_USE_BACKTRACE
# include <boost/stacktrace/detail/libbacktrace_impls.hpp>
#elif defined(BOOST_STACKTRACE_USE_DWFL)
# include <boost/stacktrace/detail/libdwfl_impls.hpp>
#elif defined(BOOST_STACKTRACE_USE_ADDR2LINE)
# include <boost/stacktrace/detail/addr2line_impls.hpp>
#else
Expand Down
116 changes: 116 additions & 0 deletions include/boost/stacktrace/detail/libdwfl_impls.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_STACKTRACE_DETAIL_LIBDWFL_IMPLS_HPP
#define BOOST_STACKTRACE_DETAIL_LIBDWFL_IMPLS_HPP

#include <elfutils/libdwfl.h>

#include <boost/stacktrace/detail/to_dec_array.hpp>
#include <boost/stacktrace/frame.hpp>

namespace boost { namespace stacktrace { namespace detail {

class dwfl_handle {
public:
dwfl_handle() noexcept
: dwfl_(dwfl_begin(&callbacks_))
{
if (dwfl_) {
dwfl_linux_proc_report(dwfl_, getpid());
dwfl_report_end(dwfl_, nullptr, nullptr);
}
}

~dwfl_handle() {
if (dwfl_) {
dwfl_end(dwfl_);
}
}

const char* function(Dwarf_Addr addr) const noexcept {
if (!dwfl_ || !addr) {
return nullptr;
}

Dwfl_Module* dwfl_module = dwfl_addrmodule (dwfl_, addr);
return dwfl_module ? dwfl_module_addrname(dwfl_module, addr) : nullptr;
}

std::pair<const char*, std::size_t> source(Dwarf_Addr addr) const noexcept {
if (!dwfl_ || !addr) {
return {nullptr, 0};
}

Dwfl_Line* dwfl_line = dwfl_getsrc(dwfl_, addr);
if (!dwfl_line) {
return {nullptr, 0};
}

int line{0};
const char* filename = dwfl_lineinfo(dwfl_line, nullptr, &line, nullptr, nullptr, nullptr);
return {filename, static_cast<std::size_t>(line)};
}

private:
Dwfl_Callbacks callbacks_{
.find_elf = dwfl_linux_proc_find_elf,
.find_debuginfo = dwfl_build_id_find_debuginfo,
.section_address = dwfl_offline_section_address,
.debuginfo_path = nullptr,
};
Dwfl* dwfl_;
};

struct to_string_using_dwfl {
std::string res;
dwfl_handle dwfl;

void prepare_function_name(const void* addr) noexcept {
const char* function = dwfl.function(reinterpret_cast<Dwarf_Addr>(addr));
if (function) {
res = function;
}
}

bool prepare_source_location(const void* addr) noexcept {
auto [filename, line] = dwfl.source(reinterpret_cast<Dwarf_Addr>(addr));
if (!filename) {
return false;
}

res += " at ";
res += filename;
res += ':';
res += boost::stacktrace::detail::to_dec_array(line).data();

return true;
}
};

template <class Base> class to_string_impl_base;
typedef to_string_impl_base<to_string_using_dwfl> to_string_impl;

inline std::string name_impl(const void* addr) {
dwfl_handle dwfl;
const char* function = dwfl.function(reinterpret_cast<Dwarf_Addr>(addr));
return function ? std::string{function} : std::string{};
}

} // namespace detail

std::string frame::source_file() const {
detail::dwfl_handle dwfl;
auto [filename, _] = dwfl.source(reinterpret_cast<Dwarf_Addr>(addr_));
return filename ? std::string{filename} : std::string{};
}

std::size_t frame::source_line() const {
detail::dwfl_handle dwfl;
return dwfl.source(reinterpret_cast<Dwarf_Addr>(addr_)).second;
}

}} // namespace boost::stacktrace

#endif // BOOST_STACKTRACE_DETAIL_LIBDWFL_IMPLS_HPP
14 changes: 14 additions & 0 deletions src/dwfl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#define BOOST_STACKTRACE_INTERNAL_BUILD_LIBS
#define BOOST_STACKTRACE_USE_DWFL
#define BOOST_STACKTRACE_LINK

#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif

#include <boost/stacktrace/detail/frame_unwind.ipp>
#include <boost/stacktrace/safe_dump_to.hpp>
4 changes: 4 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ boost_test(TYPE run SOURCES test.cpp test_impl.cpp LINK_LIBRARIES Boost::stacktr
boost_test(TYPE run SOURCES test_noop.cpp test_impl.cpp LINK_LIBRARIES Boost::stacktrace_noop Boost::core)

boost_test(TYPE run SOURCES test_trivial.cpp LINK_LIBRARIES Boost::stacktrace Boost::core)

if(BOOST_STACKTRACE_ENABLE_DWFL)
boost_test(TYPE run NAME test_dwfl SOURCES test.cpp test_impl.cpp COMPILE_DEFINITIONS BOOST_STACKTRACE_USE_DWFL LINK_LIBRARIES Boost::stacktrace_dwfl Boost::core)
endif()
Loading

0 comments on commit 5f0e423

Please sign in to comment.