diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e4666b7..9af9163 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,34 +1,70 @@ -name: build c++20 +name: build on: push: pull_request: jobs: - build-gcc: + build-linux: runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + compiler: [g++-10, clang++-14] + target: [Debug] + std: [11, 20] + unwind: [ + LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE, + LIBCPPTRACE_GET_SYMBOLS_WITH_NOTHING, + ] + symbols: [ + LIBCPPTRACE_UNWIND_WITH_EXECINFO, + LIBCPPTRACE_UNWIND_WITH_NOTHING, + ] + demangle: [ + LIBCPPTRACE_DEMANGLE_WITH_CXXABI, + LIBCPPTRACE_DEMANGLE_WITH_NOTHING, + ] steps: - uses: actions/checkout@v2 - name: dependencies - run: sudo apt install gcc-10 g++-10 + run: sudo apt install gcc-10 g++-10 libgcc-10-dev - name: build run: | mkdir -p build cd build - cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=g++-10 + cmake .. \ + -DCMAKE_BUILD_TYPE=${{matrix.target}} \ + -DCMAKE_CXX_COMPILER=${{matrix.compiler}} \ + -DCMAKE_CXX_STANDARD=${{matrix.std}} \ + -D${{matrix.unwind}}=On \ + -D${{matrix.symbols}}=On \ + -D${{matrix.demangle}}=On \ + -DLIBCPP_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h make - build-clang: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v2 - - name: build - run: | - mkdir -p build - cd build - cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++-14 - make - build-msvc: + build-windows: runs-on: windows-2019 + strategy: + fail-fast: false + matrix: + compiler: [cl, clang++] + target: [Debug] + std: [11, 20] + unwind: [ + # LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE + LIBCPPTRACE_GET_SYMBOLS_WITH_NOTHING, + ] + symbols: [ + LIBCPPTRACE_UNWIND_WITH_WINAPI, + LIBCPPTRACE_UNWIND_WITH_NOTHING, + ] + demangle: [ + # LIBCPPTRACE_DEMANGLE_WITH_CXXABI, + LIBCPPTRACE_DEMANGLE_WITH_NOTHING, + ] + exclude: + - demangle: LIBCPPTRACE_DEMANGLE_WITH_CXXABI + compiler: cl steps: - uses: actions/checkout@v2 - name: Enable Developer Command Prompt @@ -37,5 +73,61 @@ jobs: run: | mkdir -p build cd build - cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=cl + cmake .. ` + -DCMAKE_BUILD_TYPE=Debug ` + -DCMAKE_CXX_COMPILER=${{matrix.compiler}} ` + -DCMAKE_CXX_STANDARD=${{matrix.std}} ` + -D${{matrix.unwind}}=On ` + -D${{matrix.symbols}}=On ` + -D${{matrix.demangle}}=On + msbuild .\libcpptrace.sln + + build-linux-full-or-auto: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + compiler: [g++-10, clang++-14] + target: [Debug] + std: [11, 20] + config: ["-DLIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE=On", ""] + steps: + - uses: actions/checkout@v2 + - name: dependencies + run: sudo apt install gcc-10 g++-10 libgcc-10-dev + - name: build + run: | + mkdir -p build + cd build + cmake .. \ + -DCMAKE_BUILD_TYPE=${{matrix.target}} \ + -DCMAKE_CXX_COMPILER=${{matrix.compiler}} \ + -DCMAKE_CXX_STANDARD=${{matrix.std}} \ + -DLIBCPP_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h \ + ${{matrix.config}} make + build-windows-full-or-auto: + runs-on: windows-2019 + strategy: + fail-fast: false + matrix: + compiler: [cl, clang++] + target: [Debug] + std: [11, 20] + config: [""] + exclude: + - config: -DLIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE=On # TODO + steps: + - uses: actions/checkout@v2 + - name: Enable Developer Command Prompt + uses: ilammy/msvc-dev-cmd@v1.10.0 + - name: build + run: | + mkdir -p build + cd build + cmake .. ` + -DCMAKE_BUILD_TYPE=Debug ` + -DCMAKE_CXX_COMPILER=${{matrix.compiler}} ` + -DCMAKE_CXX_STANDARD=${{matrix.std}} ` + ${{matrix.config}} + msbuild .\libcpptrace.sln diff --git a/CMakeLists.txt b/CMakeLists.txt index 42b0edd..9a7a152 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,6 @@ include(GNUInstallDirs) include(CheckCXXSourceCompiles) file(GLOB_RECURSE sources src/*.cpp) -#message(STATUS "Sources" ${sources}) add_library(cpptrace ${sources} include/cpptrace/cpptrace.hpp) target_include_directories( @@ -48,15 +47,6 @@ target_compile_options( $<$:/W4 /WX /permissive-> ) -function(check_support var source includes libraries definitions) - set(CMAKE_REQUIRED_INCLUDES "${includes}") - list(APPEND CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - set(CMAKE_REQUIRED_LIBRARIES "${libraries}") - set(CMAKE_REQUIRED_DEFINITIONS "${definitions}") - check_cxx_source_compiles("#include \"${source}\"" ${var}) - set(${var} ${${var}} PARENT_SCOPE) -endfunction() - option(LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE "" OFF) option(LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE "" OFF) @@ -72,6 +62,36 @@ option(LIBCPPTRACE_UNWIND_WITH_NOTHING "" OFF) option(LIBCPPTRACE_DEMANGLE_WITH_CXXABI "" OFF) option(LIBCPPTRACE_DEMANGLE_WITH_NOTHING "" OFF) +set(LIBCPP_BACKTRACE_PATH "" CACHE STRING "Path to backtrace.h, if the compiler doesn't already know it. Check /usr/lib/gcc/x86_64-linux-gnu/*/include.") + +if(NOT "${LIBCPP_BACKTRACE_PATH}" STREQUAL "") + # quotes used over <> because of a macro substitution issue where + # + # is expanded to + # + string(CONCAT LIBCPP_BACKTRACE_PATH "\"" ${LIBCPP_BACKTRACE_PATH}) + string(CONCAT LIBCPP_BACKTRACE_PATH ${LIBCPP_BACKTRACE_PATH} "\"") + #message(STATUS ${LIBCPP_BACKTRACE_PATH}) + string(CONCAT LIBCPP_BACKTRACE_PATH_DEFINITION "-DLIBCPP_BACKTRACE_PATH=" ${LIBCPP_BACKTRACE_PATH}) + #message(STATUS ${LIBCPP_BACKTRACE_PATH_DEFINITION}) +else() + set(LIBCPP_BACKTRACE_PATH_DEFINITION "") +endif() + +function(check_support var source includes libraries definitions) + set(CMAKE_REQUIRED_INCLUDES "${includes}") + list(APPEND CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + set(CMAKE_REQUIRED_LIBRARIES "${libraries}") + set(CMAKE_REQUIRED_DEFINITIONS "${definitions}") + string(CONCAT full_source "#include \"${source}\"" ${nonce}) + check_cxx_source_compiles(${full_source} ${var}) + set(${var} ${${var}} PARENT_SCOPE) +endfunction() + +check_support(HAS_EXECINFO has_execinfo.cpp "" "" "") +check_support(HAS_BACKTRACE has_backtrace.cpp "" "backtrace" "${LIBCPP_BACKTRACE_PATH_DEFINITION}") +check_support(HAS_CXXABI has_cxxabi.cpp "" "" "") + # =============================================== Autoconfig full dump =============================================== # If nothing is specified, attempt to use libbacktrace's full dump if( @@ -85,8 +105,6 @@ if( ) ) # Attempt to auto-config - check_support(HAS_BACKTRACE has_backtrace.cpp "" "backtrace" "") - if(HAS_BACKTRACE) set(LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE On) message(STATUS "Cpptrace auto config: Using libbacktrace for the full trace") @@ -105,14 +123,12 @@ if( ) # Attempt to auto-config if(UNIX) - check_support(HAS_EXECINFO has_execinfo.cpp "" "" "") - if(HAS_EXECINFO) set(LIBCPPTRACE_UNWIND_WITH_EXECINFO On) message(STATUS "Cpptrace auto config: Using execinfo.h for unwinding") else() set(LIBCPPTRACE_UNWIND_WITH_NOTHING On) - message(WARNING "Cpptrace auto config: doesn't seem to be supported, stack tracing will not work.") + message(FATAL_ERROR "Cpptrace auto config: doesn't seem to be supported, stack tracing will not work. To compile anyway set LIBCPPTRACE_UNWIND_WITH_NOTHING.") endif() elseif(WIN32) set(LIBCPPTRACE_UNWIND_WITH_WINAPI On) @@ -133,13 +149,11 @@ if( ) # Attempt to auto-config if(UNIX) - check_support(HAS_EXECINFO has_execinfo.cpp "" "" "") - - if(HAS_EXECINFO) + if(HAS_BACKTRACE) set(LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE On) message(STATUS "Cpptrace auto config: Using libbacktrace for symbols") else() - message(WARNING "Cpptrace auto config: No symbol back end could be automatically configured") + message(FATAL_ERROR "Cpptrace auto config: No symbol back end could be automatically configured. To compile anyway set LIBCPPTRACE_GET_SYMBOLS_WITH_NOTHING.") endif() elseif(WIN32) #set(LIBCPPTRACE_UNWIND_WITH_WINAPI On) @@ -157,7 +171,6 @@ if( LIBCPPTRACE_DEMANGLE_WITH_NOTHING ) ) - check_support(HAS_CXXABI has_cxxabi.cpp "" "" "") if(HAS_CXXABI) set(LIBCPPTRACE_DEMANGLE_WITH_CXXABI On) else() @@ -169,19 +182,34 @@ endif() # =============================================== Apply options to build =============================================== +function(check_backtrace_error) + if(NOT HAS_BACKTRACE) + if(NOT "${LIBCPP_BACKTRACE_PATH}" STREQUAL "") + message(WARNING "Cpptrace: LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE specified but libbacktrace doesn't appear installed or configured properly.") + else() + message(WARNING "Cpptrace: LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE specified but libbacktrace doesn't appear installed or configured properly. You may need to specify LIBCPP_BACKTRACE_PATH.") + endif() + endif() +endfunction() + # Full if(LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE) + check_backtrace_error() target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE) target_link_libraries(cpptrace PRIVATE backtrace) endif() # Symbols if(LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE) + check_backtrace_error() target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE) endif() # Unwinding if(LIBCPPTRACE_UNWIND_WITH_EXECINFO) + if(NOT HAS_EXECINFO) + message(WARNING "Cpptrace: LIBCPPTRACE_UNWIND_WITH_EXECINFO specified but execinfo.h doesn't seem to be available.") + endif() target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_UNWIND_WITH_EXECINFO) endif() @@ -191,6 +219,9 @@ endif() # Demangling if(LIBCPPTRACE_DEMANGLE_WITH_CXXABI) + if(NOT HAS_CXXABI) + message(WARNING "Cpptrace: LIBCPPTRACE_DEMANGLE_WITH_CXXABI specified but cxxabi.h doesn't seem to be available.") + endif() target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_DEMANGLE_WITH_CXXABI) endif() @@ -198,6 +229,12 @@ if(LIBCPPTRACE_DEMANGLE_WITH_NOTHING) target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_DEMANGLE_WITH_NOTHING) endif() +if(NOT "${LIBCPP_BACKTRACE_PATH}" STREQUAL "") + target_compile_definitions(cpptrace PUBLIC LIBCPP_BACKTRACE_PATH=${LIBCPP_BACKTRACE_PATH}) +endif() + +# ====================================================================================================================== + target_link_libraries( cpptrace PRIVATE diff --git a/README.md b/README.md index a5f41e2..a487fa1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# libcpptrace +# Cpptrace + +[![build](https://github.com/jeremy-rifkin/cpptrace/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/jeremy-rifkin/cpptrace/actions/workflows/build.yml) Libcpptrace is a lightweight C++ stacktrace library supporting C++11 and greater on Linux, Unix, MacOS, and Windows (including cygwin / mingw environments). The goal: Make traces simple for once. diff --git a/cmake/has_backtrace.cpp b/cmake/has_backtrace.cpp index 69c599a..7696251 100644 --- a/cmake/has_backtrace.cpp +++ b/cmake/has_backtrace.cpp @@ -1,4 +1,8 @@ +#ifdef LIBCPP_BACKTRACE_PATH +#include LIBCPP_BACKTRACE_PATH +#else #include +#endif int main() { backtrace_state* state = backtrace_create_state(nullptr, true, nullptr, nullptr); diff --git a/cmake/has_cxxabi.cpp b/cmake/has_cxxabi.cpp index 01cafc6..9c53baf 100644 --- a/cmake/has_cxxabi.cpp +++ b/cmake/has_cxxabi.cpp @@ -1,6 +1,6 @@ -#include +#include int main() { - void* frames[10]; - int size = backtrace(frames, 10); + int status; + abi::__cxa_demangle("_Z3foov", nullptr, nullptr, &status); } diff --git a/cmake/has_execinfo.cpp b/cmake/has_execinfo.cpp index 69c599a..0ad09ce 100644 --- a/cmake/has_execinfo.cpp +++ b/cmake/has_execinfo.cpp @@ -1,5 +1,6 @@ -#include +#include int main() { - backtrace_state* state = backtrace_create_state(nullptr, true, nullptr, nullptr); + void* frames[10]; + backtrace(frames, 10); } diff --git a/src/demangle/demangle_with_nothing.cpp b/src/demangle/demangle_with_nothing.cpp index 53beda7..6657af4 100644 --- a/src/demangle/demangle_with_nothing.cpp +++ b/src/demangle/demangle_with_nothing.cpp @@ -3,8 +3,6 @@ #include #include "libcpp_demangle.hpp" -#include - #include #include diff --git a/src/full/full_trace_with_libbacktrace.cpp b/src/full/full_trace_with_libbacktrace.cpp index 91c507c..9b463ee 100644 --- a/src/full/full_trace_with_libbacktrace.cpp +++ b/src/full/full_trace_with_libbacktrace.cpp @@ -6,7 +6,11 @@ #include +#ifdef LIBCPP_BACKTRACE_PATH +#include LIBCPP_BACKTRACE_PATH +#else #include +#endif namespace cpptrace { namespace detail { diff --git a/src/symbols/libcpp_symbols.hpp b/src/symbols/libcpp_symbols.hpp index cc9b102..426534c 100644 --- a/src/symbols/libcpp_symbols.hpp +++ b/src/symbols/libcpp_symbols.hpp @@ -1,5 +1,5 @@ -#ifndef LIBCPP_SYMBOLIZE_HPP -#define LIBCPP_SYMBOLIZE_HPP +#ifndef LIBCPP_SYMBOLS_HPP +#define LIBCPP_SYMBOLS_HPP #include @@ -10,7 +10,7 @@ namespace cpptrace { namespace detail { class symbolizer { struct impl; - std::unique_ptr impl; + std::unique_ptr pimpl; public: symbolizer(); ~symbolizer(); diff --git a/src/symbols/symbols_with_libbacktrace.cpp b/src/symbols/symbols_with_libbacktrace.cpp index d97629c..c6b5e90 100644 --- a/src/symbols/symbols_with_libbacktrace.cpp +++ b/src/symbols/symbols_with_libbacktrace.cpp @@ -1,16 +1,33 @@ #ifdef LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE #include -#include "libcpp_symbolize.hpp" +#include "libcpp_symbols.hpp" #include "../platform/libcpp_program_name.hpp" #include #include +#ifdef LIBCPP_BACKTRACE_PATH +#include LIBCPP_BACKTRACE_PATH +#else #include +#endif namespace cpptrace { namespace detail { + int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) { + stacktrace_frame& frame = *static_cast(data); + frame.address = address; + frame.line = line; + frame.filename = file ? file : ""; + frame.symbol = symbol ? symbol : ""; + return 0; + } + + void error_callback(void*, const char*, int) { + // nothing at the moment + } + backtrace_state* get_backtrace_state() { // backtrace_create_state must be called only one time per program static backtrace_state* state = nullptr; @@ -22,26 +39,6 @@ namespace cpptrace { return state; } - int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) { - stacktrace_frame& frame = *static_cast(data); - data.address = address; - data.line = line; - data.filename = file ? file : ""; - data.symbol = symbol ? symbol : ""; - return 0; - } - - void error_callback(void*, const char*, int) { - // nothing at the moment - } - - symbolizer::symbolizer() : impl(std::make_unique()) {} - symbolizer::~symbolizer() = default; - - stacktrace_frame symbolizer::resolve_frame(void* addr) { - impl->resolve_frame(addr); - } - // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions struct symbolizer::impl { stacktrace_frame resolve_frame(void* addr) { @@ -57,6 +54,13 @@ namespace cpptrace { return frame; } }; + + symbolizer::symbolizer() : pimpl{new impl} {} + symbolizer::~symbolizer() = default; + + stacktrace_frame symbolizer::resolve_frame(void* addr) { + return pimpl->resolve_frame(addr); + } } } diff --git a/src/symbols/symbols_with_nothing.cpp b/src/symbols/symbols_with_nothing.cpp index d54e713..0c098f6 100644 --- a/src/symbols/symbols_with_nothing.cpp +++ b/src/symbols/symbols_with_nothing.cpp @@ -1,7 +1,7 @@ #ifdef LIBCPPTRACE_GET_SYMBOLS_WITH_NOTHING #include -#include "libcpp_symbolize.hpp" +#include "libcpp_symbols.hpp" #include "../platform/libcpp_program_name.hpp" #include diff --git a/src/unwind/unwind_with_execinfo.cpp b/src/unwind/unwind_with_execinfo.cpp index 6c50dee..7aa0f32 100644 --- a/src/unwind/unwind_with_execinfo.cpp +++ b/src/unwind/unwind_with_execinfo.cpp @@ -11,7 +11,7 @@ namespace cpptrace { namespace detail { std::vector capture_frames() { std::vector frames(hard_max_frames, nullptr); - int n_frames = backtrace(bt.data(), hard_max_frames); + int n_frames = backtrace(frames.data(), hard_max_frames); frames.resize(n_frames); frames.shrink_to_fit(); return frames; diff --git a/src/unwind/unwind_with_winapi.cpp b/src/unwind/unwind_with_winapi.cpp index 73ff4b7..3ea3a2b 100644 --- a/src/unwind/unwind_with_winapi.cpp +++ b/src/unwind/unwind_with_winapi.cpp @@ -11,11 +11,11 @@ namespace cpptrace { namespace detail { std::vector capture_frames() { // TODO: When does this need to be called? Can it be moved to the symbolizer? - SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS); - HANDLE proc = GetCurrentProcess(); - if(!SymInitialize(proc, NULL, TRUE)) return {}; + //SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS); + //HANDLE proc = GetCurrentProcess(); + //if(!SymInitialize(proc, NULL, TRUE)) return {}; std::vector addrs(hard_max_frames, nullptr); - int frames = CaptureStackBackTrace(n_skip, hard_max_frames, addrs.data(), NULL); + int frames = CaptureStackBackTrace(0, hard_max_frames, addrs.data(), NULL); addrs.resize(frames); addrs.shrink_to_fit(); return addrs;