CMake improvements (#57)

This commit is contained in:
Jeremy Rifkin 2023-11-06 12:12:07 -05:00 committed by GitHub
parent e14eac364f
commit 98368fb417
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 562 additions and 338 deletions

View File

@ -1,15 +1,24 @@
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.14)
if(${CMAKE_VERSION} VERSION_LESS 3.12)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif()
include(cmake/PreventInSourceBuilds.cmake)
# ---- Initialize Project ----
# Used to support find_package
set(package_name "cpptrace")
project(
cpptrace
VERSION 0.2.1
DESCRIPTION "Simple, portable, and self-contained stacktrace library for C++11 and newer "
HOMEPAGE_URL "https://github.com/jeremy-rifkin/cpptrace"
LANGUAGES C CXX
)
# Don't change include order, OptionVariables checks if project is top level
include(cmake/ProjectIsTopLevel.cmake)
include(cmake/OptionVariables.cmake)
include(GNUInstallDirs)
include(CheckCXXSourceCompiles)
include(CheckCXXCompilerFlag)
@ -22,47 +31,6 @@ if(CMAKE_GENERATOR STREQUAL "Ninja")
endif()
endif()
option(CPPTRACE_STATIC "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_LIBDL "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF)
option(CPPTRACE_UNWIND_WITH_UNWIND "" OFF)
option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF)
option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF)
option(CPPTRACE_UNWIND_WITH_DBGHELP "" OFF)
option(CPPTRACE_UNWIND_WITH_NOTHING "" OFF)
option(CPPTRACE_DEMANGLE_WITH_CXXABI "" OFF)
option(CPPTRACE_DEMANGLE_WITH_WINAPI "" OFF)
option(CPPTRACE_DEMANGLE_WITH_NOTHING "" OFF)
set(CPPTRACE_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.")
set(CPPTRACE_HARD_MAX_FRAMES "" CACHE STRING "Hard limit on unwinding depth. Default is 100.")
set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.")
option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF)
option(CPPTRACE_BUILD_TEST "" OFF)
option(CPPTRACE_BUILD_DEMO "" OFF)
option(CPPTRACE_BUILD_TEST_RDYNAMIC "" OFF)
option(CPPTRACE_USE_SYSTEM_LIBDWARF "" OFF)
option(CPPTRACE_SANITIZER_BUILD "" OFF)
mark_as_advanced(
CPPTRACE_BACKTRACE_PATH
CPPTRACE_ADDR2LINE_PATH
CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH
CPPTRACE_BUILD_TEST
CPPTRACE_BUILD_DEMO
CPPTRACE_BUILD_TEST_RDYNAMIC
CPPTRACE_SANITIZER_BUILD
)
if(CPPTRACE_SANITIZER_BUILD)
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
@ -82,6 +50,7 @@ else()
set(CPPTRACE_BACKTRACE_PATH_DEFINITION "")
endif()
# =============================================== Platform Support ===============================================
function(check_support var source includes libraries definitions)
set(CMAKE_REQUIRED_INCLUDES "${includes}")
list(APPEND CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
@ -195,93 +164,126 @@ endif()
# =============================================== Now define the library ===============================================
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
# Target that we can modify (can't modify ALIAS targets)
# Target name should not be the same as ${PROJECT_NAME}, causes add_subdirectory issues
set(target_name "cpptrace-lib")
add_library(${target_name} ${build_type})
set(
sources
src/cpptrace.cpp
src/demangle/demangle_with_cxxabi.cpp
src/demangle/demangle_with_winapi.cpp
src/demangle/demangle_with_nothing.cpp
src/symbols/symbols_with_addr2line.cpp
src/symbols/symbols_with_dbghelp.cpp
src/symbols/symbols_with_dl.cpp
src/symbols/symbols_with_libbacktrace.cpp
src/symbols/symbols_with_libdwarf.cpp
src/symbols/symbols_with_nothing.cpp
src/symbols/symbols_core.cpp
src/unwind/unwind_with_execinfo.cpp
src/unwind/unwind_with_nothing.cpp
src/unwind/unwind_with_unwind.cpp
src/unwind/unwind_with_winapi.cpp
src/unwind/unwind_with_dbghelp.cpp
# Alias to cause error at configuration time instead of link time if target is missing
add_library(cpptrace::cpptrace ALIAS ${target_name})
# Add /include files to target
# This is solely for IDE benefit, doesn't affect building
target_sources(
${target_name} PRIVATE
include/cpptrace/cpptrace.hpp
)
if(CPPTRACE_STATIC)
add_library(cpptrace STATIC ${sources} include/cpptrace/cpptrace.hpp)
else()
add_library(cpptrace SHARED ${sources} include/cpptrace/cpptrace.hpp)
set_property(TARGET cpptrace PROPERTY POSITION_INDEPENDENT_CODE ON)
endif()
# add /src files to target
target_sources(
${target_name} PRIVATE
# src
src/cpptrace.cpp
src/demangle/demangle_with_cxxabi.cpp
src/demangle/demangle_with_winapi.cpp
src/demangle/demangle_with_nothing.cpp
src/symbols/symbols_with_addr2line.cpp
src/symbols/symbols_with_dbghelp.cpp
src/symbols/symbols_with_dl.cpp
src/symbols/symbols_with_libbacktrace.cpp
src/symbols/symbols_with_libdwarf.cpp
src/symbols/symbols_with_nothing.cpp
src/symbols/symbols_core.cpp
src/unwind/unwind_with_execinfo.cpp
src/unwind/unwind_with_nothing.cpp
src/unwind/unwind_with_unwind.cpp
src/unwind/unwind_with_winapi.cpp
src/unwind/unwind_with_dbghelp.cpp
)
target_include_directories(
cpptrace
${target_name}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cpptrace/>
)
# TODO
target_compile_features(cpptrace PRIVATE cxx_range_for cxx_constexpr cxx_nullptr cxx_static_assert)
# ---- Generate Build Info Headers ----
set_target_properties(
cpptrace
PROPERTIES
CXX_STANDARD_REQUIRED TRUE
CXX_EXTENSIONS OFF
# used in export header generated below
if(NOT CPPTRACE_BUILD_SHARED)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_STATIC_DEFINE)
endif()
# generate header file with export macros prefixed with BASE_NAME
include(GenerateExportHeader)
generate_export_header(
${target_name}
BASE_NAME cpptrace
EXPORT_FILE_NAME include/cpptrace/cpptrace_export.hpp
)
target_compile_options(
cpptrace
PRIVATE
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Werror=return-type -Wundef>
$<$<CXX_COMPILER_ID:GNU>:-Wuseless-cast>
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX /permissive->
# ---- Library Properties ----
# Hide all symbols by default
# Use SameMajorVersion versioning for shared library runtime linker lookup
set_target_properties(
${target_name} PROPERTIES
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN YES
VERSION "${PROJECT_VERSION}"
SOVERSION "${PROJECT_VERSION_MAJOR}"
EXPORT_NAME "cpptrace"
OUTPUT_NAME "cpptrace"
)
# Header files generated by CMake
target_include_directories(
${target_name} SYSTEM PUBLIC
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>"
)
# Header files from /include
target_include_directories(
${target_name} ${warning_guard} PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
)
# Require C++11 support
target_compile_features(
${target_name}
PRIVATE cxx_std_11
)
# =============================================== Apply options to build ===============================================
if(HAS_CXX_EXCEPTION_TYPE)
target_compile_definitions(cpptrace PUBLIC CPPTRACE_HAS_CXX_EXCEPTION_TYPE)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_HAS_CXX_EXCEPTION_TYPE)
endif()
function(check_backtrace_error)
if(NOT HAS_BACKTRACE)
if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "")
message(WARNING "Cpptrace: CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE specified but libbacktrace doesn't appear installed or configured properly.")
else()
message(WARNING "Cpptrace: CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE specified but libbacktrace doesn't appear installed or configured properly. You may need to specify CPPTRACE_BACKTRACE_PATH.")
endif()
endif()
endfunction()
# Symbols
if(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
check_backtrace_error()
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
target_link_libraries(cpptrace PRIVATE backtrace)
if(NOT HAS_BACKTRACE)
if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "")
message(WARNING "Cpptrace: Using libbacktrace for symbols but libbacktrace doesn't appear installed or configured properly.")
else()
message(WARNING "Cpptrace: Using libbacktrace for symbols but libbacktrace doesn't appear installed or configured properly. You may need to specify CPPTRACE_BACKTRACE_PATH.")
endif()
endif()
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
target_link_libraries(${target_name} PRIVATE backtrace dl)
endif()
if(CPPTRACE_GET_SYMBOLS_WITH_LIBDL)
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDL)
target_link_libraries(cpptrace PRIVATE dl)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDL)
target_link_libraries(${target_name} PRIVATE dl)
endif()
if(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
# set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.")
# option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF)
if(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH)
target_compile_definitions(cpptrace PUBLIC CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH)
else()
if("${CPPTRACE_ADDR2LINE_PATH}" STREQUAL "")
if(APPLE)
@ -293,35 +295,46 @@ if(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
set(CPPTRACE_ADDR2LINE_PATH_FINAL "${CPPTRACE_ADDR2LINE_PATH}")
endif()
message(STATUS "Cpptrace: Using ${CPPTRACE_ADDR2LINE_PATH_FINAL} for addr2line path")
target_compile_definitions(cpptrace PUBLIC CPPTRACE_ADDR2LINE_PATH="${CPPTRACE_ADDR2LINE_PATH_FINAL}")
target_compile_definitions(${target_name} PUBLIC CPPTRACE_ADDR2LINE_PATH="${CPPTRACE_ADDR2LINE_PATH_FINAL}")
endif()
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
if(UNIX)
target_link_libraries(cpptrace PRIVATE dl)
target_link_libraries(${target_name} PRIVATE dl)
endif()
endif()
if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
if(CPPTRACE_USE_SYSTEM_LIBDWARF)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
if(CPPTRACE_USE_EXTERNAL_LIBDWARF)
find_package(libdwarf REQUIRED)
target_link_libraries(cpptrace PRIVATE libdwarf)
target_link_libraries(${target_name} PRIVATE libdwarf::dwarf-static)
else()
add_subdirectory(bundled/libdwarf)
target_link_libraries(cpptrace PRIVATE dwarf)
target_link_libraries(${target_name} PRIVATE dwarf)
# set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
# set(PIC_ALWAYS TRUE)
# set(BUILD_DWARFDUMP FALSE)
# include(FetchContent)
# FetchContent_Declare(
# libdwarf
# GIT_REPOSITORY https://github.com/jeremy-rifkin/libdwarf-code.git
# GIT_TAG 4ca6a7bc86b27d56741923f03c15b5aa274efc28 # v0.8.0
# )
# FetchContent_MakeAvailable(libdwarf)
# target_link_libraries(${target_name} PRIVATE dwarf-static)
endif()
if(UNIX)
target_link_libraries(cpptrace PRIVATE dl)
target_link_libraries(${target_name} PRIVATE dl)
endif()
endif()
if(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
target_link_libraries(cpptrace PRIVATE dbghelp)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
target_link_libraries(${target_name} PRIVATE dbghelp)
endif()
if(CPPTRACE_GET_SYMBOLS_WITH_NOTHING)
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_NOTHING)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_NOTHING)
endif()
# Unwinding
@ -329,30 +342,30 @@ if(CPPTRACE_UNWIND_WITH_UNWIND)
if(NOT HAS_UNWIND)
message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_UNWIND specified but libgcc unwind doesn't seem to be available.")
endif()
target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_UNWIND)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_UNWIND)
endif()
if(CPPTRACE_UNWIND_WITH_EXECINFO)
if(NOT HAS_EXECINFO)
message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_EXECINFO specified but execinfo.h doesn't seem to be available.")
endif()
target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_EXECINFO)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_EXECINFO)
endif()
if(CPPTRACE_UNWIND_WITH_WINAPI)
target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_WINAPI)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_WINAPI)
endif()
if(CPPTRACE_UNWIND_WITH_DBGHELP)
if(NOT HAS_STACKWALK)
message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_DBGHELP specified but dbghelp stackwalk64 doesn't seem to be available.")
endif()
target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_DBGHELP)
target_link_libraries(cpptrace PRIVATE dbghelp)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_DBGHELP)
target_link_libraries(${target_name} PRIVATE dbghelp)
endif()
if(CPPTRACE_UNWIND_WITH_NOTHING)
target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_NOTHING)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_NOTHING)
endif()
# Demangling
@ -360,100 +373,38 @@ if(CPPTRACE_DEMANGLE_WITH_CXXABI)
if(NOT HAS_CXXABI)
message(WARNING "Cpptrace: CPPTRACE_DEMANGLE_WITH_CXXABI specified but cxxabi.h doesn't seem to be available.")
endif()
target_compile_definitions(cpptrace PUBLIC CPPTRACE_DEMANGLE_WITH_CXXABI)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_DEMANGLE_WITH_CXXABI)
endif()
if(CPPTRACE_DEMANGLE_WITH_WINAPI)
target_compile_definitions(cpptrace PUBLIC CPPTRACE_DEMANGLE_WITH_WINAPI)
target_link_libraries(cpptrace PRIVATE dbghelp)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_DEMANGLE_WITH_WINAPI)
target_link_libraries(${target_name} PRIVATE dbghelp)
endif()
if(CPPTRACE_DEMANGLE_WITH_NOTHING)
target_compile_definitions(cpptrace PUBLIC CPPTRACE_DEMANGLE_WITH_NOTHING)
target_compile_definitions(${target_name} PUBLIC CPPTRACE_DEMANGLE_WITH_NOTHING)
endif()
if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "")
target_compile_definitions(cpptrace PUBLIC CPPTRACE_BACKTRACE_PATH=${CPPTRACE_BACKTRACE_PATH})
target_compile_definitions(${target_name} PUBLIC CPPTRACE_BACKTRACE_PATH=${CPPTRACE_BACKTRACE_PATH})
endif()
if(NOT "${CPPTRACE_HARD_MAX_FRAMES}" STREQUAL "")
target_compile_definitions(cpptrace PUBLIC CPPTRACE_HARD_MAX_FRAMES=${CPPTRACE_HARD_MAX_FRAMES})
target_compile_definitions(${target_name} PUBLIC CPPTRACE_HARD_MAX_FRAMES=${CPPTRACE_HARD_MAX_FRAMES})
endif()
# ======================================================================================================================
# =============================================== Install ===============================================
if(NOT CMAKE_SKIP_INSTALL_RULES)
include(CMakePackageConfigHelpers)
if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
set(CPPTRACE_STATIC_EXPORT_TARGETS cpptrace dwarf)
else()
set(CPPTRACE_STATIC_EXPORT_TARGETS cpptrace)
endif()
if(CPPTRACE_STATIC)
install(
TARGETS ${CPPTRACE_STATIC_EXPORT_TARGETS}
EXPORT cpptrace_targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
else()
install(
TARGETS cpptrace
EXPORT cpptrace_targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
install(
FILES
include/cpptrace/cpptrace.hpp
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpptrace
)
export(
EXPORT cpptrace_targets
FILE ${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace_targets.cmake
NAMESPACE cpptrace::
)
configure_package_config_file(
cmake/cpptrace-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpptrace
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config-version.cmake
VERSION ${PACKAGE_VERSION}
COMPATIBILITY SameMajorVersion
)
install(
EXPORT cpptrace_targets
FILE cpptrace_targets.cmake
NAMESPACE cpptrace::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpptrace
)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpptrace
)
include(cmake/InstallRules.cmake)
endif()
# =============================================== Demo/test ===============================================
if(CPPTRACE_BUILD_TEST)
add_executable(test test/test.cpp)
target_compile_features(test PRIVATE cxx_range_for cxx_constexpr cxx_nullptr cxx_static_assert)
target_link_libraries(test PRIVATE cpptrace)
target_link_libraries(test PRIVATE ${target_name})
# Clang has been fast to adopt dwarf 5, other tools (e.g. addr2line from binutils) have not
check_cxx_compiler_flag("-gdwarf-4" HAS_DWARF4)
if(HAS_DWARF4)
@ -474,7 +425,7 @@ endif()
if(CPPTRACE_BUILD_DEMO)
add_executable(demo test/demo.cpp)
target_compile_features(demo PRIVATE cxx_range_for cxx_constexpr cxx_nullptr cxx_static_assert)
target_link_libraries(demo PRIVATE cpptrace)
target_link_libraries(demo PRIVATE ${target_name})
# Clang has been fast to adopt dwarf 5, other tools (e.g. addr2line from binutils) have not
check_cxx_compiler_flag("-gdwarf-4" HAS_DWARF4)
if(HAS_DWARF4)

View File

@ -585,7 +585,7 @@ configurable with `CPPTRACE_HARD_MAX_FRAMES`.
| Library | CMake config | Platforms | Info |
| ------------ | ---------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| libdwarf | `CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF` | linux, macos, mingw | Libdwarf is the preferred method for symbol resolution for cpptrace, and it's bundled in this repository for ease of use. |
| libdwarf | `CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF` | linux, macos, mingw | Libdwarf is the preferred method for symbol resolution for cpptrace. Cpptrace will get it via FetchContent or find_package depending on `CPPTRACE_USE_EXTERNAL_LIBDWARF`. |
| dbghelp | `CPPTRACE_GET_SYMBOLS_WITH_DBGHELP` | windows | Dbghelp.h is the preferred method for symbol resolution on windows under msvc/clang and is supported on all windows machines. |
| libbacktrace | `CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE` | linux, macos*, mingw* | Libbacktrace is already installed on most systems or available through the compiler directly. For clang you must specify the absolute path to `backtrace.h` using `CPPTRACE_BACKTRACE_PATH`. |
| addr2line | `CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE` | linux, macos, mingw | Symbols are resolved by invoking `addr2line` (or `atos` on mac) via `fork()` (on linux/unix, and `popen` under mingw). |
@ -634,7 +634,6 @@ Back-ends:
- `CPPTRACE_DEMANGLE_WITH_NOTHING=On/Off`
Back-end configuration:
- `CPPTRACE_STATIC=On/Off`: Create cpptrace as a static library.
- `CPPTRACE_BACKTRACE_PATH=<string>`: Path to libbacktrace backtrace.h, needed when compiling with clang/
- `CPPTRACE_HARD_MAX_FRAMES=<number>`: Some back-ends write to a fixed-size buffer. This is the size of that buffer.
Default is `100`.
@ -643,7 +642,13 @@ Back-end configuration:
injection).
- `CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH=On/Off`: Specifies whether cpptrace should let the system search the PATH
environment variable directories for the binary.
- `CPPTRACE_USE_SYSTEM_LIBDWARF=On/Off`: Use libdwarf resolved via `find_package` rather than the bundled libdwarf.
Other userufl configurations:
- `CPPTRACE_BUILD_SHARED=On/Off`: Override for `BUILD_SHARED_LIBS`.
- `CPPTRACE_INCLUDES_WITH_SYSTEM=On/Off`: Marks cpptrace headers as `SYSTEM` which will hide any warnings that aren't
the fault of your project. Defaults to On.
- `CPPTRACE_INSTALL_CMAKEDIR`: Override for the installation path for the cmake configs.
- `CPPTRACE_USE_EXTERNAL_LIBDWARF=On/Off`: Get libdwarf from `find_package` rather than `FetchContent`.
Testing:
- `CPPTRACE_BUILD_TEST` Build a small test program

View File

@ -430,11 +430,17 @@ set(
libdwarf_private.h
)
set(
libdwarf_export_headers
dwarf.h
libdwarf.h
)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
include(GNUInstallDirs)
add_library(dwarf OBJECT ${libdwarf_sources} ${libdwarf_headers})
add_library(dwarf STATIC ${libdwarf_sources} ${libdwarf_headers})
set_property(TARGET dwarf PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(dwarf PUBLIC LIBDWARF_BUILD)
@ -452,53 +458,53 @@ else()
endif()
# TODO: Something will have to be figured out for dynamic linking, but for now there's nothing to install
# if(NOT CMAKE_SKIP_INSTALL_RULES)
# include(CMakePackageConfigHelpers)
if(NOT CMAKE_SKIP_INSTALL_RULES)
include(CMakePackageConfigHelpers)
# install(
# TARGETS dwarf
# EXPORT libdwarf_targets
# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
# INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
# ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
# RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
# )
install(
TARGETS dwarf
EXPORT libdwarf_targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
# install(
# FILES
# ${libdwarf_headers}
# DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libdwarf
# )
install(
FILES
${libdwarf_export_headers}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libdwarf
)
# export(
# EXPORT libdwarf_targets
# FILE ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf_targets.cmake
# NAMESPACE libdwarf::
# )
export(
EXPORT libdwarf_targets
FILE ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf_targets.cmake
NAMESPACE libdwarf::
)
# configure_package_config_file(
# cmake/libdwarf-config.cmake.in
# ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config.cmake
# INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf
# )
configure_package_config_file(
cmake/libdwarf-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf
)
# write_basic_package_version_file(
# ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config-version.cmake
# VERSION "${VERSION}"
# COMPATIBILITY SameMajorVersion
# )
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config-version.cmake
VERSION "${VERSION}"
COMPATIBILITY SameMajorVersion
)
# install(
# EXPORT libdwarf_targets
# FILE libdwarf_targets.cmake
# NAMESPACE libdwarf::
# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf
# )
install(
EXPORT libdwarf_targets
FILE libdwarf_targets.cmake
NAMESPACE libdwarf::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf
)
# install(
# FILES
# ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config.cmake
# ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config-version.cmake
# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf
# )
# endif()
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf
)
endif()

View File

@ -15,6 +15,9 @@ failed = False
expected_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../test/expected/")
def get_c_compiler_counterpart(compiler: str) -> str:
return compiler.replace("clang++", "clang").replace("g++", "gcc")
MAX_LINE_DIFF = 2
def similarity(name: str, target: List[str]) -> int:
@ -149,6 +152,7 @@ def build(matrix):
"..",
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}",
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
f"-D{matrix['unwind']}=On",
f"-D{matrix['symbols']}=On",
@ -167,6 +171,7 @@ def build(matrix):
"..",
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}",
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
f"-D{matrix['unwind']}=On",
f"-D{matrix['symbols']}=On",
@ -190,6 +195,7 @@ def build_full_or_auto(matrix):
"..",
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}",
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
f"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h",
"-DCPPTRACE_BUILD_TEST=On"
@ -205,6 +211,7 @@ def build_full_or_auto(matrix):
"..",
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}",
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
"-DCPPTRACE_BUILD_TEST=On"
]
@ -309,7 +316,8 @@ def main():
#"CPPTRACE_UNWIND_WITH_NOTHING",
],
"symbols": [
"CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
# Disabled due to libbacktrace bug
# "CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
"CPPTRACE_GET_SYMBOLS_WITH_LIBDL",
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
"CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",

69
cmake/InstallRules.cmake Normal file
View File

@ -0,0 +1,69 @@
include(CMakePackageConfigHelpers)
# copy header files to CMAKE_INSTALL_INCLUDEDIR
# don't include third party header files
install(
DIRECTORY
"${PROJECT_SOURCE_DIR}/include/" # our header files
"${PROJECT_BINARY_DIR}/include/" # generated header files
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT ${package_name}-development
# PATTERN "**/third_party" EXCLUDE # skip third party directory
# PATTERN "**/third_party/**" EXCLUDE # skip third party files
)
# copy target build output artifacts to OS dependent locations
# (Except includes, that just sets a compiler flag with the path)
install(
TARGETS ${target_name}
EXPORT ${package_name}-targets
RUNTIME #
COMPONENT ${package_name}-runtime
LIBRARY #
COMPONENT ${package_name}-runtime
NAMELINK_COMPONENT ${package_name}-development
ARCHIVE #
COMPONENT ${package_name}-development
INCLUDES #
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
# create config file that points to targets file
configure_file(
"${PROJECT_SOURCE_DIR}/cmake/in/cpptrace-config-cmake.in"
"${PROJECT_BINARY_DIR}/cmake/${package_name}-config.cmake"
@ONLY
)
# copy config file for find_package to find
install(
FILES "${PROJECT_BINARY_DIR}/cmake/${package_name}-config.cmake"
DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}"
COMPONENT ${package_name}-development
)
# create version file for consumer to check version in CMake
write_basic_package_version_file(
"${package_name}-config-version.cmake"
COMPATIBILITY SameMajorVersion # a.k.a SemVer
)
# copy version file for find_package to find for version check
install(
FILES "${PROJECT_BINARY_DIR}/${package_name}-config-version.cmake"
DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}"
COMPONENT ${package_name}-development
)
# create targets file included by config file with targets for consumers
install(
EXPORT ${package_name}-targets
NAMESPACE cpptrace::
DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}"
COMPONENT ${package_name}-development
)
# support packaging library
if(PROJECT_IS_TOP_LEVEL)
include(CPack)
endif()

170
cmake/OptionVariables.cmake Normal file
View File

@ -0,0 +1,170 @@
# Included further down to avoid interfering with our cache variables
# include(GNUInstallDirs)
# ---- Options Summary ----
# ---------------------------------------------------------------------------------------------------
# | Option | Availability | Default |
# |=================================|===============|===============================================|
# | BUILD_SHARED_LIBS | Top-Level | OFF |
# | BUILD_TESTING | Top-Level | OFF |
# | CMAKE_INSTALL_INCLUDEDIR | Top-Level | include/${package_name}-${PROJECT_VERSION} |
# |---------------------------------|---------------|-----------------------------------------------|
# | ASSERT_BUILD_SHARED | Always | ${BUILD_SHARED_LIBS} |
# | ASSERT_BUILD_TESTING | Always | ${BUILD_TESTING} AND ${PROJECT_IS_TOP_LEVEL} |
# | ASSERT_INCLUDES_WITH_SYSTEM | Not Top-Level | ON |
# | ASSERT_INSTALL_CMAKEDIR | Always | ${CMAKE_INSTALL_LIBDIR}/cmake/${package_name} |
# | CPPTRACE_USE_EXTERNAL_LIBDWARF | Always | OFF |
# | ... | | |
# ---------------------------------------------------------------------------------------------------
# ---- Build Shared ----
# Sometimes it's useful to be able to single out a dependency to be built as
# static or shared, even if obtained from source
if(PROJECT_IS_TOP_LEVEL)
option(BUILD_SHARED_LIBS "Build shared libs" OFF)
endif()
option(
CPPTRACE_BUILD_SHARED
"Override BUILD_SHARED_LIBS for ${package_name} library"
${BUILD_SHARED_LIBS}
)
mark_as_advanced(CPPTRACE_BUILD_SHARED)
set(build_type STATIC)
if(CPPTRACE_BUILD_SHARED)
set(build_type SHARED)
endif()
# ---- Warning Guard ----
# target_include_directories with SYSTEM modifier will request the compiler to
# omit warnings from the provided paths, if the compiler supports that.
# This is to provide a user experience similar to find_package when
# add_subdirectory or FetchContent is used to consume this project.
set(warning_guard )
if(NOT PROJECT_IS_TOP_LEVEL)
option(
CPPTRACE_INCLUDES_WITH_SYSTEM
"Use SYSTEM modifier for ${package_name}'s includes, disabling warnings"
ON
)
mark_as_advanced(CPPTRACE_INCLUDES_WITH_SYSTEM)
if(CPPTRACE_INCLUDES_WITH_SYSTEM)
set(warning_guard SYSTEM)
endif()
endif()
# ---- Enable Testing ----
# By default tests aren't enabled even with BUILD_TESTING=ON unless the library
# is built as a top level project.
# This is in order to cut down on unnecessary compile times, since it's unlikely
# for users to want to run the tests of their dependencies.
if(PROJECT_IS_TOP_LEVEL)
option(BUILD_TESTING "Build tests" OFF)
endif()
if(PROJECT_IS_TOP_LEVEL AND BUILD_TESTING)
set(build_testing ON)
endif()
option(
CPPTRACE_BUILD_TESTING
"Override BUILD_TESTING for ${package_name} library"
${build_testing}
)
set(build_testing )
mark_as_advanced(CPPTRACE_BUILD_TESTING)
# ---- Install Include Directory ----
# Adds an extra directory to the include path by default, so that when you link
# against the target, you get `<prefix>/include/<package-X.Y.Z>` added to your
# include paths rather than `<prefix>/include`.
# This doesn't affect include paths used by consumers of this project, but helps
# prevent consumers having access to other projects in the same include
# directory (e.g. usr/include).
# The variable type is STRING rather than PATH, because otherwise passing
# -DCMAKE_INSTALL_INCLUDEDIR=include on the command line would expand to an
# absolute path with the base being the current CMake directory, leading to
# unexpected errors.
if(PROJECT_IS_TOP_LEVEL)
set(
CMAKE_INSTALL_INCLUDEDIR "include/${package_name}-${PROJECT_VERSION}"
CACHE STRING ""
)
# marked as advanced in GNUInstallDirs version, so we follow their lead
mark_as_advanced(CMAKE_INSTALL_INCLUDEDIR)
endif()
# do not include earlier or we can't set CMAKE_INSTALL_INCLUDEDIR above
# include required for CMAKE_INSTALL_LIBDIR below
include(GNUInstallDirs)
# ---- Install CMake Directory ----
# This allows package maintainers to freely override the installation path for
# the CMake configs.
# This doesn't affects include paths used by consumers of this project.
# The variable type is STRING rather than PATH, because otherwise passing
# -DCPPTRACE_INSTALL_CMAKEDIR=lib/cmake on the command line would expand to an
# absolute path with the base being the current CMake directory, leading to
# unexpected errors.
set(
CPPTRACE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}"
CACHE STRING "CMake package config location relative to the install prefix"
)
# depends on CMAKE_INSTALL_LIBDIR which is marked as advanced in GNUInstallDirs
mark_as_advanced(CPPTRACE_INSTALL_CMAKEDIR)
# ---- Symbol Options ----
option(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_LIBDL "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF)
option(CPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF)
# ---- Unwinding Options ----
option(CPPTRACE_UNWIND_WITH_UNWIND "" OFF)
option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF)
option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF)
option(CPPTRACE_UNWIND_WITH_DBGHELP "" OFF)
option(CPPTRACE_UNWIND_WITH_NOTHING "" OFF)
# ---- Demangling Options ----
option(CPPTRACE_DEMANGLE_WITH_CXXABI "" OFF)
option(CPPTRACE_DEMANGLE_WITH_WINAPI "" OFF)
option(CPPTRACE_DEMANGLE_WITH_NOTHING "" OFF)
# ---- Back-end configurations ----
set(CPPTRACE_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.")
set(CPPTRACE_HARD_MAX_FRAMES "" CACHE STRING "Hard limit on unwinding depth. Default is 100.")
set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.")
option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF)
# ---- Other configurations ----
if(PROJECT_IS_TOP_LEVEL)
option(CPPTRACE_BUILD_TEST "" OFF)
option(CPPTRACE_BUILD_DEMO "" OFF)
option(CPPTRACE_BUILD_TEST_RDYNAMIC "" OFF)
mark_as_advanced(
CPPTRACE_BUILD_TEST
CPPTRACE_BUILD_DEMO
CPPTRACE_BUILD_TEST_RDYNAMIC
)
endif()
option(CPPTRACE_USE_EXTERNAL_LIBDWARF "" OFF)
option(CPPTRACE_SANITIZER_BUILD "" OFF)
mark_as_advanced(
CPPTRACE_BACKTRACE_PATH
CPPTRACE_ADDR2LINE_PATH
CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH
CPPTRACE_SANITIZER_BUILD
)

View File

@ -0,0 +1,8 @@
# In-source build guard
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(
FATAL_ERROR
"In-source builds are not supported. "
"You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' before rebuilding this project."
)
endif()

View File

@ -0,0 +1,6 @@
# This variable is set by project() in CMake 3.21+
string(
COMPARE EQUAL
"${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}"
PROJECT_IS_TOP_LEVEL
)

View File

@ -1,3 +0,0 @@
@PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/cpptrace_targets.cmake)

9
cmake/has_backtrace.cpp Normal file
View File

@ -0,0 +1,9 @@
#ifdef CPPTRACE_BACKTRACE_PATH
#include CPPTRACE_BACKTRACE_PATH
#else
#include <backtrace.h>
#endif
int main() {
backtrace_state* state = backtrace_create_state(nullptr, true, nullptr, nullptr);
}

View File

@ -0,0 +1,14 @@
# Init @ variables before doing anything else
@PACKAGE_INIT@
# Dependencies
include(CMakeFindDependencyMacro)
find_dependency(libdwarf REQUIRED)
# We cannot modify an existing IMPORT target
if(NOT TARGET assert::assert)
# import targets
include("${CMAKE_CURRENT_LIST_DIR}/@package_name@-targets.cmake")
endif()

View File

@ -7,6 +7,8 @@
#include <string>
#include <vector>
#include "cpptrace/cpptrace_export.hpp"
#if __cplusplus >= 202002L
#ifdef __has_include
#if __has_include(<format>)
@ -16,24 +18,18 @@
#endif
#endif
#if defined(_WIN32) || defined(__CYGWIN__)
#define CPPTRACE_API __declspec(dllexport)
#else
#define CPPTRACE_API
#endif
namespace cpptrace {
struct object_trace;
struct stacktrace;
struct raw_trace {
struct CPPTRACE_EXPORT raw_trace {
std::vector<std::uintptr_t> frames;
CPPTRACE_API static raw_trace current(std::uint_least32_t skip = 0);
CPPTRACE_API static raw_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_API object_trace resolve_object_trace() const;
CPPTRACE_API stacktrace resolve() const;
CPPTRACE_API void clear();
CPPTRACE_API bool empty() const noexcept;
static raw_trace current(std::uint_least32_t skip = 0);
static raw_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth);
object_trace resolve_object_trace() const;
stacktrace resolve() const;
void clear();
bool empty() const noexcept;
using iterator = std::vector<std::uintptr_t>::iterator;
using const_iterator = std::vector<std::uintptr_t>::const_iterator;
@ -45,20 +41,20 @@ namespace cpptrace {
inline const_iterator cend() const noexcept { return frames.cend(); }
};
struct object_frame {
struct CPPTRACE_EXPORT object_frame {
std::string obj_path;
std::string symbol;
std::uintptr_t raw_address = 0;
std::uintptr_t obj_address = 0;
};
struct object_trace {
struct CPPTRACE_EXPORT object_trace {
std::vector<object_frame> frames;
CPPTRACE_API static object_trace current(std::uint_least32_t skip = 0);
CPPTRACE_API static object_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_API stacktrace resolve() const;
CPPTRACE_API void clear();
CPPTRACE_API bool empty() const noexcept;
static object_trace current(std::uint_least32_t skip = 0);
static object_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth);
stacktrace resolve() const;
void clear();
bool empty() const noexcept;
using iterator = std::vector<object_frame>::iterator;
using const_iterator = std::vector<object_frame>::const_iterator;
@ -70,7 +66,7 @@ namespace cpptrace {
inline const_iterator cend() const noexcept { return frames.cend(); }
};
struct stacktrace_frame {
struct CPPTRACE_EXPORT stacktrace_frame {
std::uintptr_t address;
std::uint_least32_t line; // TODO: This should use UINT_LEAST32_MAX as a sentinel
std::uint_least32_t column; // UINT_LEAST32_MAX if not present
@ -89,21 +85,21 @@ namespace cpptrace {
return !operator==(other);
}
CPPTRACE_API std::string to_string() const;
CPPTRACE_API friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame);
std::string to_string() const;
friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame);
};
struct stacktrace {
struct CPPTRACE_EXPORT stacktrace {
std::vector<stacktrace_frame> frames;
CPPTRACE_API static stacktrace current(std::uint_least32_t skip = 0);
CPPTRACE_API static stacktrace current(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_API void print() const;
CPPTRACE_API void print(std::ostream& stream) const;
CPPTRACE_API void print(std::ostream& stream, bool color) const;
CPPTRACE_API void clear();
CPPTRACE_API bool empty() const noexcept;
CPPTRACE_API std::string to_string(bool color = false) const;
CPPTRACE_API friend std::ostream& operator<<(std::ostream& stream, const stacktrace& trace);
static stacktrace current(std::uint_least32_t skip = 0);
static stacktrace current(std::uint_least32_t skip, std::uint_least32_t max_depth);
void print() const;
void print(std::ostream& stream) const;
void print(std::ostream& stream, bool color) const;
void clear();
bool empty() const noexcept;
std::string to_string(bool color = false) const;
friend std::ostream& operator<<(std::ostream& stream, const stacktrace& trace);
using iterator = std::vector<stacktrace_frame>::iterator;
using const_iterator = std::vector<stacktrace_frame>::const_iterator;
@ -114,27 +110,27 @@ namespace cpptrace {
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
inline const_iterator cend() const noexcept { return frames.cend(); }
private:
CPPTRACE_API void print(std::ostream& stream, bool color, bool newline_at_end, const char* header) const;
void print(std::ostream& stream, bool color, bool newline_at_end, const char* header) const;
friend void print_terminate_trace();
};
CPPTRACE_API raw_trace generate_raw_trace(std::uint_least32_t skip = 0);
CPPTRACE_API raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_API object_trace generate_object_trace(std::uint_least32_t skip = 0);
CPPTRACE_API object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_API stacktrace generate_trace(std::uint_least32_t skip = 0);
CPPTRACE_API stacktrace generate_trace(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::uint_least32_t skip = 0);
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_EXPORT object_trace generate_object_trace(std::uint_least32_t skip = 0);
CPPTRACE_EXPORT object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_EXPORT stacktrace generate_trace(std::uint_least32_t skip = 0);
CPPTRACE_EXPORT stacktrace generate_trace(std::uint_least32_t skip, std::uint_least32_t max_depth);
// utilities:
CPPTRACE_API std::string demangle(const std::string& name);
CPPTRACE_API void absorb_trace_exceptions(bool absorb);
CPPTRACE_API bool isatty(int fd);
CPPTRACE_EXPORT std::string demangle(const std::string& name);
CPPTRACE_EXPORT void absorb_trace_exceptions(bool absorb);
CPPTRACE_EXPORT bool isatty(int fd);
CPPTRACE_API extern const int stdin_fileno;
CPPTRACE_API extern const int stderr_fileno;
CPPTRACE_API extern const int stdout_fileno;
CPPTRACE_EXPORT extern const int stdin_fileno;
CPPTRACE_EXPORT extern const int stderr_fileno;
CPPTRACE_EXPORT extern const int stdout_fileno;
CPPTRACE_API void register_terminate_handler();
CPPTRACE_EXPORT void register_terminate_handler();
enum class cache_mode {
// Only minimal lookup tables
@ -146,15 +142,15 @@ namespace cpptrace {
};
namespace experimental {
CPPTRACE_API void set_cache_mode(cache_mode mode);
CPPTRACE_EXPORT void set_cache_mode(cache_mode mode);
}
namespace detail {
CPPTRACE_API bool should_absorb_trace_exceptions();
CPPTRACE_API enum cache_mode get_cache_mode();
CPPTRACE_EXPORT bool should_absorb_trace_exceptions();
CPPTRACE_EXPORT enum cache_mode get_cache_mode();
}
class exception : public std::exception {
class CPPTRACE_EXPORT exception : public std::exception {
mutable raw_trace trace;
mutable stacktrace resolved_trace;
mutable std::string what_string;
@ -177,7 +173,7 @@ namespace cpptrace {
const stacktrace& get_trace() const noexcept;
};
class exception_with_message : public exception {
class CPPTRACE_EXPORT exception_with_message : public exception {
mutable std::string message;
protected:

View File

@ -28,17 +28,16 @@
#define CYAN ESC "36m"
namespace cpptrace {
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
raw_trace raw_trace::current(std::uint_least32_t skip) {
return generate_raw_trace(skip + 1);
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
raw_trace raw_trace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) {
return generate_raw_trace(skip + 1, max_depth);
}
CPPTRACE_API
object_trace raw_trace::resolve_object_trace() const {
try {
return object_trace{detail::get_frames_object_info(frames)};
@ -50,7 +49,6 @@ namespace cpptrace {
}
}
CPPTRACE_API
stacktrace raw_trace::resolve() const {
try {
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
@ -66,27 +64,24 @@ namespace cpptrace {
}
}
CPPTRACE_API
void raw_trace::clear() {
frames.clear();
}
CPPTRACE_API
bool raw_trace::empty() const noexcept {
return frames.empty();
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
object_trace object_trace::current(std::uint_least32_t skip) {
return generate_object_trace(skip + 1);
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
object_trace object_trace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) {
return generate_object_trace(skip + 1, max_depth);
}
CPPTRACE_API
stacktrace object_trace::resolve() const {
try {
return stacktrace{detail::resolve_frames(frames)};
@ -98,23 +93,21 @@ namespace cpptrace {
}
}
CPPTRACE_API
void object_trace::clear() {
frames.clear();
}
CPPTRACE_API
bool object_trace::empty() const noexcept {
return frames.empty();
}
CPPTRACE_API std::string stacktrace_frame::to_string() const {
std::string stacktrace_frame::to_string() const {
std::ostringstream oss;
oss << *this;
return std::move(oss).str();
}
CPPTRACE_API std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame) {
std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame) {
stream
<< std::hex
<< "0x"
@ -138,32 +131,28 @@ namespace cpptrace {
return stream;
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
stacktrace stacktrace::current(std::uint32_t skip) {
return generate_trace(skip + 1);
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
stacktrace stacktrace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) {
return generate_trace(skip + 1, max_depth);
}
CPPTRACE_API
void stacktrace::print() const {
print(std::cerr, true);
}
CPPTRACE_API
void stacktrace::print(std::ostream& stream) const {
print(stream, true);
}
CPPTRACE_API
void stacktrace::print(std::ostream& stream, bool color) const {
print(stream, color, true, nullptr);
}
CPPTRACE_API
void stacktrace::print(std::ostream& stream, bool color, bool newline_at_end, const char* header) const {
if(color) {
detail::enable_virtual_terminal_processing_if_needed();
@ -224,28 +213,25 @@ namespace cpptrace {
}
}
CPPTRACE_API
void stacktrace::clear() {
frames.clear();
}
CPPTRACE_API
bool stacktrace::empty() const noexcept {
return frames.empty();
}
CPPTRACE_API
std::string stacktrace::to_string(bool color) const {
std::ostringstream oss;
print(oss, color, false, nullptr);
return std::move(oss).str();
}
CPPTRACE_API std::ostream& operator<<(std::ostream& stream, const stacktrace& trace) {
std::ostream& operator<<(std::ostream& stream, const stacktrace& trace) {
return stream << trace.to_string();
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
raw_trace generate_raw_trace(std::uint_least32_t skip) {
try {
return raw_trace{detail::capture_frames(skip + 1, UINT_LEAST32_MAX)};
@ -257,7 +243,7 @@ namespace cpptrace {
}
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth) {
try {
return raw_trace{detail::capture_frames(skip + 1, max_depth)};
@ -269,7 +255,7 @@ namespace cpptrace {
}
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
object_trace generate_object_trace(std::uint_least32_t skip) {
try {
return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, UINT_LEAST32_MAX))};
@ -281,7 +267,7 @@ namespace cpptrace {
}
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth) {
try {
return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, max_depth))};
@ -293,12 +279,12 @@ namespace cpptrace {
}
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
stacktrace generate_trace(std::uint_least32_t skip) {
return generate_trace(skip + 1, UINT_LEAST32_MAX);
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
CPPTRACE_FORCE_NO_INLINE
stacktrace generate_trace(std::uint32_t skip, std::uint_least32_t max_depth) {
try {
std::vector<std::uintptr_t> frames = detail::capture_frames(skip + 1, max_depth);
@ -315,19 +301,17 @@ namespace cpptrace {
}
}
CPPTRACE_API
std::string demangle(const std::string& name) {
return detail::demangle(name);
}
CPPTRACE_API
bool isatty(int fd) {
return detail::isatty(fd);
}
CPPTRACE_API extern const int stdin_fileno = detail::fileno(stdin);
CPPTRACE_API extern const int stdout_fileno = detail::fileno(stdout);
CPPTRACE_API extern const int stderr_fileno = detail::fileno(stderr);
extern const int stdin_fileno = detail::fileno(stdin);
extern const int stdout_fileno = detail::fileno(stdout);
extern const int stderr_fileno = detail::fileno(stderr);
CPPTRACE_FORCE_NO_INLINE void print_terminate_trace() {
generate_trace(1).print(
@ -338,7 +322,7 @@ namespace cpptrace {
);
}
[[noreturn]] CPPTRACE_API void terminate_handler() {
[[noreturn]] void terminate_handler() {
try {
auto ptr = std::current_exception();
if(ptr == nullptr) {
@ -371,7 +355,7 @@ namespace cpptrace {
abort();
}
CPPTRACE_API void register_terminate_handler() {
void register_terminate_handler() {
std::set_terminate(terminate_handler);
}
@ -380,22 +364,22 @@ namespace cpptrace {
std::atomic<enum cache_mode> cache_mode(cache_mode::prioritize_speed); // NOSONAR
}
CPPTRACE_API void absorb_trace_exceptions(bool absorb) {
void absorb_trace_exceptions(bool absorb) {
detail::absorb_trace_exceptions = absorb;
}
namespace experimental {
CPPTRACE_API void set_cache_mode(cache_mode mode) {
void set_cache_mode(cache_mode mode) {
detail::cache_mode = mode;
}
}
namespace detail {
CPPTRACE_API bool should_absorb_trace_exceptions() {
bool should_absorb_trace_exceptions() {
return absorb_trace_exceptions;
}
CPPTRACE_API enum cache_mode get_cache_mode() {
enum cache_mode get_cache_mode() {
return cache_mode;
}

View File

@ -5,14 +5,14 @@ project(demo_project VERSION 0.0.1 LANGUAGES CXX)
add_executable(main main.cpp)
add_subdirectory(cpptrace)
target_link_libraries(main cpptrace)
target_link_libraries(main cpptrace::cpptrace)
target_compile_features(main PRIVATE cxx_std_11)
if(WIN32)
add_custom_command(
TARGET main POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:cpptrace>
$<TARGET_FILE:cpptrace::cpptrace>
$<TARGET_FILE_DIR:main>
)
endif()

View File

@ -20,7 +20,7 @@ if(WIN32)
add_custom_command(
TARGET main POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:cpptrace>
$<TARGET_FILE:cpptrace::cpptrace>
$<TARGET_FILE_DIR:main>
)
endif()

View File

@ -35,6 +35,7 @@ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
set(cpptrace_DIR "../../build/foo/lib/cmake/cpptrace")
set(libdwarf_DIR "../../build/foo/lib/cmake/libdwarf")
find_package(cpptrace REQUIRED)
add_executable(speedtest speedtest.cpp)