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 Sep 19, 2024
1 parent a778e3d commit 8f1a61b
Show file tree
Hide file tree
Showing 7 changed files with 153 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 @@ -73,6 +74,7 @@ stacktrace_check(BOOST_STACKTRACE_HAS_WINDBG_CACHED has_windbg_cached.cpp "${CMA

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 @@ -83,6 +85,7 @@ unset(_default_addr2line)
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 @@ -91,6 +94,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 @@ -111,6 +115,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
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_standard_find_debuginfo,
.section_address = nullptr,
.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: 2 additions & 2 deletions test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ using boost::stacktrace::stacktrace;
using boost::stacktrace::frame;


#if (defined(BOOST_GCC) && defined(BOOST_WINDOWS) && !defined(BOOST_STACKTRACE_USE_BACKTRACE) && !defined(BOOST_STACKTRACE_USE_ADDR2LINE)) \
|| defined(BOOST_STACKTRACE_TEST_NO_DEBUG_AT_ALL)
#if (defined(BOOST_GCC) && defined(BOOST_WINDOWS) && !defined(BOOST_STACKTRACE_USE_BACKTRACE) && !defined(BOOST_STACKTRACE_USE_ADDR2LINE) \
&& !defined(BOOST_STACKTRACE_USE_DWFL)) || defined(BOOST_STACKTRACE_TEST_NO_DEBUG_AT_ALL)

# define BOOST_STACKTRACE_TEST_SHOULD_OUTPUT_READABLE_NAMES 0
#else
Expand Down

0 comments on commit 8f1a61b

Please sign in to comment.