From 8f1a61b909dccf2a261501512e8b006e97ac9d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antons=20Je=C4=BCkins?= Date: Thu, 19 Sep 2024 23:44:09 +0200 Subject: [PATCH] Add libdwfl based implementation This commit adds an implementation based on libdwfl from elfutils. Implements #176 --- CMakeLists.txt | 8 ++ build/has_dwfl.cpp | 10 ++ doc/stacktrace.qbk | 1 + .../boost/stacktrace/detail/frame_unwind.ipp | 2 + .../boost/stacktrace/detail/libdwfl_impls.hpp | 116 ++++++++++++++++++ src/dwfl.cpp | 14 +++ test/test.cpp | 4 +- 7 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 build/has_dwfl.cpp create mode 100644 include/boost/stacktrace/detail/libdwfl_impls.hpp create mode 100644 src/dwfl.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c6c4f8a..5edd013 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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") @@ -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}) @@ -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}, " @@ -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") @@ -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) diff --git a/build/has_dwfl.cpp b/build/has_dwfl.cpp new file mode 100644 index 0000000..6d86e8d --- /dev/null +++ b/build/has_dwfl.cpp @@ -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 + +int main() { + Dwfl_Callbacks callbacks{nullptr, nullptr, nullptr, nullptr}; + Dwfl* dwfl_ = dwfl_begin(&callbacks); +} diff --git a/doc/stacktrace.qbk b/doc/stacktrace.qbk index 65914f0..18d6cb3 100644 --- a/doc/stacktrace.qbk +++ b/doc/stacktrace.qbk @@ -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 ``, 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 `` 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=* 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]] ] diff --git a/include/boost/stacktrace/detail/frame_unwind.ipp b/include/boost/stacktrace/detail/frame_unwind.ipp index a985515..7de1cf7 100644 --- a/include/boost/stacktrace/detail/frame_unwind.ipp +++ b/include/boost/stacktrace/detail/frame_unwind.ipp @@ -23,6 +23,8 @@ #ifdef BOOST_STACKTRACE_USE_BACKTRACE # include +#elif defined(BOOST_STACKTRACE_USE_DWFL) +# include #elif defined(BOOST_STACKTRACE_USE_ADDR2LINE) # include #else diff --git a/include/boost/stacktrace/detail/libdwfl_impls.hpp b/include/boost/stacktrace/detail/libdwfl_impls.hpp new file mode 100644 index 0000000..e952bb9 --- /dev/null +++ b/include/boost/stacktrace/detail/libdwfl_impls.hpp @@ -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 + +#include +#include + +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 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(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(addr)); + if (function) { + res = function; + } + } + + bool prepare_source_location(const void* addr) noexcept { + auto [filename, line] = dwfl.source(reinterpret_cast(addr)); + if (!filename) { + return false; + } + + res += " at "; + res += filename; + res += ':'; + res += boost::stacktrace::detail::to_dec_array(line).data(); + + return true; + } +}; + +template class to_string_impl_base; +typedef to_string_impl_base to_string_impl; + +inline std::string name_impl(const void* addr) { + dwfl_handle dwfl; + const char* function = dwfl.function(reinterpret_cast(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(addr_)); + return filename ? std::string{filename} : std::string{}; +} + +std::size_t frame::source_line() const { + detail::dwfl_handle dwfl; + return dwfl.source(reinterpret_cast(addr_)).second; +} + +}} // namespace boost::stacktrace + +#endif // BOOST_STACKTRACE_DETAIL_LIBDWFL_IMPLS_HPP diff --git a/src/dwfl.cpp b/src/dwfl.cpp new file mode 100644 index 0000000..d88745b --- /dev/null +++ b/src/dwfl.cpp @@ -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 +#include diff --git a/test/test.cpp b/test/test.cpp index e9cb5bd..c59a5e9 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -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