Initial commit
This commit is contained in:
commit
b3474b50c3
41
.github/workflows/build.yml
vendored
Normal file
41
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: build c++20
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build-gcc:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: dependencies
|
||||
run: sudo apt install gcc-10 g++-10
|
||||
- name: build
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=g++-10
|
||||
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:
|
||||
runs-on: windows-2019
|
||||
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=cl
|
||||
make
|
||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.vscode
|
||||
build
|
||||
a.out
|
||||
test/build
|
||||
261
CMakeLists.txt
Normal file
261
CMakeLists.txt
Normal file
@ -0,0 +1,261 @@
|
||||
cmake_minimum_required(VERSION 3.8...3.23)
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.12)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
endif()
|
||||
|
||||
project(
|
||||
libcpptrace
|
||||
VERSION 1.0.0
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
|
||||
#set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
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(
|
||||
cpptrace
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cpptrace/cpptrace>
|
||||
)
|
||||
|
||||
target_compile_features(
|
||||
cpptrace
|
||||
PUBLIC
|
||||
cxx_std_11
|
||||
)
|
||||
|
||||
set_target_properties(
|
||||
cpptrace
|
||||
PROPERTIES
|
||||
CXX_STANDARD_REQUIRED TRUE
|
||||
CXX_EXTENSIONS OFF
|
||||
)
|
||||
|
||||
target_compile_options(
|
||||
cpptrace
|
||||
PRIVATE
|
||||
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Werror=return-type -Wshadow>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/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)
|
||||
option(LIBCPPTRACE_GET_SYMBOLS_WITH_LIBDL "" OFF)
|
||||
option(LIBCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE "" OFF)
|
||||
option(LIBCPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF)
|
||||
option(LIBCPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF)
|
||||
|
||||
option(LIBCPPTRACE_UNWIND_WITH_EXECINFO "" OFF)
|
||||
option(LIBCPPTRACE_UNWIND_WITH_WINAPI "" OFF)
|
||||
option(LIBCPPTRACE_UNWIND_WITH_NOTHING "" OFF)
|
||||
|
||||
option(LIBCPPTRACE_DEMANGLE_WITH_CXXABI "" OFF)
|
||||
option(LIBCPPTRACE_DEMANGLE_WITH_NOTHING "" OFF)
|
||||
|
||||
# =============================================== Autoconfig full dump ===============================================
|
||||
# If nothing is specified, attempt to use libbacktrace's full dump
|
||||
if(
|
||||
NOT (
|
||||
LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
|
||||
LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE OR
|
||||
LIBCPPTRACE_GET_SYMBOLS_WITH_NOTHING OR
|
||||
LIBCPPTRACE_UNWIND_WITH_EXECINFO OR
|
||||
LIBCPPTRACE_UNWIND_WITH_WINAPI OR
|
||||
LIBCPPTRACE_UNWIND_WITH_NOTHING
|
||||
)
|
||||
)
|
||||
# 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")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# =============================================== Autoconfig unwinding ===============================================
|
||||
# Unwind back-ends (If not doing LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE)
|
||||
if(
|
||||
NOT (
|
||||
LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
|
||||
LIBCPPTRACE_UNWIND_WITH_EXECINFO OR
|
||||
LIBCPPTRACE_UNWIND_WITH_WINAPI OR
|
||||
LIBCPPTRACE_UNWIND_WITH_NOTHING
|
||||
)
|
||||
)
|
||||
# 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: <execinfo.h> doesn't seem to be supported, stack tracing will not work.")
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(LIBCPPTRACE_UNWIND_WITH_WINAPI On)
|
||||
message(STATUS "Cpptrace auto config: Using winapi for unwinding")
|
||||
endif()
|
||||
else()
|
||||
#message(STATUS "MANUAL CONFIG SPECIFIED")
|
||||
endif()
|
||||
|
||||
# =============================================== Autoconfig symbols ===============================================
|
||||
# Symbol back-ends (If not doing LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE)
|
||||
if(
|
||||
NOT (
|
||||
LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
|
||||
LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE OR
|
||||
LIBCPPTRACE_GET_SYMBOLS_WITH_NOTHING
|
||||
)
|
||||
)
|
||||
# Attempt to auto-config
|
||||
if(UNIX)
|
||||
check_support(HAS_EXECINFO has_execinfo.cpp "" "" "")
|
||||
|
||||
if(HAS_EXECINFO)
|
||||
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")
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
#set(LIBCPPTRACE_UNWIND_WITH_WINAPI On)
|
||||
#message(STATUS "Cpptrace auto config: Using winapi for unwinding")
|
||||
endif()
|
||||
else()
|
||||
#message(STATUS "MANUAL CONFIG SPECIFIED")
|
||||
endif()
|
||||
|
||||
# =============================================== Autoconfig demangling ===============================================
|
||||
# Handle demangle configuration
|
||||
if(
|
||||
NOT (
|
||||
LIBCPPTRACE_DEMANGLE_WITH_CXXABI OR
|
||||
LIBCPPTRACE_DEMANGLE_WITH_NOTHING
|
||||
)
|
||||
)
|
||||
check_support(HAS_CXXABI has_cxxabi.cpp "" "" "")
|
||||
if(HAS_CXXABI)
|
||||
set(LIBCPPTRACE_DEMANGLE_WITH_CXXABI On)
|
||||
else()
|
||||
set(LIBCPPTRACE_DEMANGLE_WITH_NOTHING On)
|
||||
endif()
|
||||
else()
|
||||
#message(STATUS "Manual demangling back-end specified")
|
||||
endif()
|
||||
|
||||
# =============================================== Apply options to build ===============================================
|
||||
|
||||
# Full
|
||||
if(LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE)
|
||||
target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE)
|
||||
target_link_libraries(cpptrace PRIVATE backtrace)
|
||||
endif()
|
||||
|
||||
# Symbols
|
||||
if(LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
|
||||
target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
|
||||
endif()
|
||||
|
||||
# Unwinding
|
||||
if(LIBCPPTRACE_UNWIND_WITH_EXECINFO)
|
||||
target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_UNWIND_WITH_EXECINFO)
|
||||
endif()
|
||||
|
||||
if(LIBCPPTRACE_UNWIND_WITH_WINAPI)
|
||||
target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_UNWIND_WITH_WINAPI)
|
||||
endif()
|
||||
|
||||
# Demangling
|
||||
if(LIBCPPTRACE_DEMANGLE_WITH_CXXABI)
|
||||
target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_DEMANGLE_WITH_CXXABI)
|
||||
endif()
|
||||
|
||||
if(LIBCPPTRACE_DEMANGLE_WITH_NOTHING)
|
||||
target_compile_definitions(cpptrace PUBLIC LIBCPPTRACE_DEMANGLE_WITH_NOTHING)
|
||||
endif()
|
||||
|
||||
target_link_libraries(
|
||||
cpptrace
|
||||
PRIVATE
|
||||
$<$<CXX_COMPILER_ID:MSVC>:dbghelp>
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "")
|
||||
message(FATAL_ERROR "Setting CMAKE_BUILD_TYPE is required")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SKIP_INSTALL_RULES)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
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_LIBDIR}
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
include/cpptrace.hpp
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpptrace/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
|
||||
)
|
||||
endif()
|
||||
18
LICENSE
Normal file
18
LICENSE
Normal file
@ -0,0 +1,18 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2023 Jeremy Rifkin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
74
README.md
Normal file
74
README.md
Normal file
@ -0,0 +1,74 @@
|
||||
# libcpptrace
|
||||
|
||||
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.
|
||||
|
||||
*Some day C++23's `<stacktrace>` will be ubiquitous*
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [libcpptrace](#libcpptrace)
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Docs](#docs)
|
||||
- [Backends](#backends)
|
||||
- [License](#license)
|
||||
|
||||
## Docs
|
||||
|
||||
`cpptrace::print_trace()` can be used to print a stacktrace at the current call site, `cpptrace::generate_trace()` can
|
||||
be used to get raw frame information for custom use.
|
||||
|
||||
**Note:** Debug info (`-g`) is generally required for good trace information. Some back-ends read symbols from dynamic
|
||||
export information which requires `-rdynamic` or manually marking symbols for exporting.
|
||||
|
||||
```cpp
|
||||
namespace cpptrace {
|
||||
struct stacktrace_frame {
|
||||
size_t line;
|
||||
size_t col;
|
||||
std::string filename;
|
||||
std::string symbol;
|
||||
};
|
||||
std::vector<stacktrace_frame> generate_trace();
|
||||
void print_trace();
|
||||
}
|
||||
```
|
||||
|
||||
## Back-ends
|
||||
|
||||
Back-end libraries are required for unwinding the stack and resolving symbol information (name and source location) in
|
||||
order to generate a stacktrace.
|
||||
|
||||
The CMake script attempts to automatically choose a good back-end based on what is available on your system. You can
|
||||
also manually set which back-end you want used.
|
||||
|
||||
**Unwinding**
|
||||
|
||||
| Library | CMake config | Windows | Linux | Info |
|
||||
|---------|--------------|---------|-------|------|
|
||||
| execinfo.h | `LIBCPPTRACE_UNWIND_WITH_EXECINFO` | | ✔️ | Frames are captured with `execinfo.h`'s `backtrace`, part of libc. |
|
||||
| winapi | `LIBCPPTRACE_UNWIND_WITH_WINAPI` | ✔️ | | Frames are captured with `CaptureStackBackTrace`. |
|
||||
| N/A | `LIBCPPTRACE_UNWIND_WITH_NOTHING` | ✔️ | ✔️ | Unwinding is not done, stack traces will be empty. |
|
||||
|
||||
**Symbol resolution**
|
||||
|
||||
| Library | CMake config | Windows | Linux | Info |
|
||||
|---------|--------------|---------|-------|------|
|
||||
| libbacktrace | `LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE` | ❌ | ✔️ | Libbacktrace is already installed on most systems, or available through the compiler directly. |
|
||||
| libdl | `LIBCPPTRACE_GET_SYMBOLS_WITH_LIBDL` | | ✔️ | Libdl uses dynamic export information. Compiling with `-rdynamic` is often needed. |
|
||||
| addr2line | `LIBCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE` | | ✔️ | Symbols are resolved by invoking `addr2line` via `fork()`. |
|
||||
| dbghelp.h | `LIBCPPTRACE_GET_SYMBOLS_WITH_DBGHELP` | ✔️ | ❌ | Dbghelp.h allows access to symbols via debug info. |
|
||||
| N/A | `LIBCPPTRACE_GET_SYMBOLS_WITH_NOTHING` | ✔️ | ✔️ | Don't attempt to resolve symbols. |
|
||||
|
||||
Lastly, C++ symbol demangling is done with `<cxxabi.h>`. Under dbghelp.h this is not needed, the symbols aren't mangled
|
||||
when they are first extracted.
|
||||
|
||||
Libbacktrace can generate a full stack trace itself, both unwinding and resolving symbols, and this can be chosen with
|
||||
`LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE`.
|
||||
|
||||
There are plenty more libraries that can be used for unwinding, parsing debug information, and demangling. In the future
|
||||
more options may be added.
|
||||
|
||||
## License
|
||||
|
||||
The library is under the MIT license.
|
||||
3
cmake/cpptrace-config.cmake.in
Normal file
3
cmake/cpptrace-config.cmake.in
Normal file
@ -0,0 +1,3 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cpptrace_targets.cmake)
|
||||
5
cmake/has_backtrace.cpp
Normal file
5
cmake/has_backtrace.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
#include <backtrace.h>
|
||||
|
||||
int main() {
|
||||
backtrace_state* state = backtrace_create_state(nullptr, true, nullptr, nullptr);
|
||||
}
|
||||
6
cmake/has_cxxabi.cpp
Normal file
6
cmake/has_cxxabi.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include <execinfo.h>
|
||||
|
||||
int main() {
|
||||
void* frames[10];
|
||||
int size = backtrace(frames, 10);
|
||||
}
|
||||
5
cmake/has_execinfo.cpp
Normal file
5
cmake/has_execinfo.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
#include <backtrace.h>
|
||||
|
||||
int main() {
|
||||
backtrace_state* state = backtrace_create_state(nullptr, true, nullptr, nullptr);
|
||||
}
|
||||
20
include/cpptrace/cpptrace.hpp
Normal file
20
include/cpptrace/cpptrace.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef CPPTRACE_HPP
|
||||
#define CPPTRACE_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace cpptrace {
|
||||
struct stacktrace_frame {
|
||||
uintptr_t address;
|
||||
int line;
|
||||
int col;
|
||||
std::string filename;
|
||||
std::string symbol;
|
||||
};
|
||||
std::vector<stacktrace_frame> generate_trace();
|
||||
void print_trace();
|
||||
}
|
||||
|
||||
#endif
|
||||
64
src/cpptrace.cpp
Normal file
64
src/cpptrace.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#ifndef LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE
|
||||
|
||||
#include "symbols/libcpp_symbols.hpp"
|
||||
#include "unwind/libcpp_unwind.hpp"
|
||||
#include "demangle/libcpp_demangle.hpp"
|
||||
|
||||
namespace cpptrace {
|
||||
std::vector<stacktrace_frame> generate_trace() {
|
||||
std::vector<void*> frames = detail::capture_frames();
|
||||
std::vector<stacktrace_frame> trace;
|
||||
detail::symbolizer symbolizer;
|
||||
for(const auto frame : frames) {
|
||||
auto entry = symbolizer.resolve_frame(frame);
|
||||
entry.symbol = detail::demangle(entry.symbol);
|
||||
trace.push_back(entry);
|
||||
}
|
||||
return trace;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// full trace
|
||||
|
||||
#include "full/libcpp_full_trace.hpp"
|
||||
#include "demangle/libcpp_demangle.hpp"
|
||||
|
||||
namespace cpptrace {
|
||||
std::vector<stacktrace_frame> generate_trace() {
|
||||
auto trace = detail::generate_trace();
|
||||
for(auto& entry : trace) {
|
||||
entry.symbol = detail::demangle(entry.symbol);
|
||||
}
|
||||
return trace;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace cpptrace {
|
||||
void print_trace() {
|
||||
std::cerr<<"Stack trace (most recent call first):"<<std::endl;
|
||||
std::size_t i = 0;
|
||||
for(const auto& frame : generate_trace()) {
|
||||
std::cerr
|
||||
<< i++
|
||||
<< " "
|
||||
<< frame.filename
|
||||
<< " at "
|
||||
<< frame.line
|
||||
<< (frame.col > 0 ? ":" + std::to_string(frame.col) : "")
|
||||
<< " "
|
||||
<< frame.symbol
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/demangle/demangle_with_cxxabi.cpp
Normal file
27
src/demangle/demangle_with_cxxabi.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#ifdef LIBCPPTRACE_DEMANGLE_WITH_CXXABI
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
#include "libcpp_demangle.hpp"
|
||||
|
||||
#include <cxxabi.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
std::string demangle(const std::string& name) {
|
||||
int status;
|
||||
char* demangled = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status);
|
||||
if(demangled) {
|
||||
std::string s = demangled;
|
||||
free(demangled);
|
||||
return s;
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
19
src/demangle/demangle_with_nothing.cpp
Normal file
19
src/demangle/demangle_with_nothing.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#ifdef LIBCPPTRACE_DEMANGLE_WITH_NOTHING
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
#include "libcpp_demangle.hpp"
|
||||
|
||||
#include <cxxabi.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
std::string demangle(const std::string& name) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
14
src/demangle/libcpp_demangle.hpp
Normal file
14
src/demangle/libcpp_demangle.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef LIBCPP_DEMANGLE_HPP
|
||||
#define LIBCPP_DEMANGLE_HPP
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
std::string demangle(const std::string&);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
47
src/full/full_trace_with_libbacktrace.cpp
Normal file
47
src/full/full_trace_with_libbacktrace.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#ifdef LIBCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
#include "libcpp_full_trace.hpp"
|
||||
#include "../platform/libcpp_program_name.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <backtrace.h>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) {
|
||||
reinterpret_cast<std::vector<stacktrace_frame>*>(data)->push_back({
|
||||
address,
|
||||
line,
|
||||
-1,
|
||||
file ? file : "",
|
||||
symbol ? symbol : ""
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
void error_callback(void*, const char*, int) {
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
backtrace_state* get_backtrace_state() {
|
||||
// backtrace_create_state must be called only one time per program
|
||||
static backtrace_state* state = nullptr;
|
||||
static bool called = false;
|
||||
if(!called) {
|
||||
state = backtrace_create_state(program_name().c_str(), true, error_callback, nullptr);
|
||||
called = true;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
std::vector<stacktrace_frame> generate_trace() {
|
||||
std::vector<stacktrace_frame> frames;
|
||||
backtrace_full(get_backtrace_state(), 0, full_callback, error_callback, &frames);
|
||||
return frames;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
14
src/full/libcpp_full_trace.hpp
Normal file
14
src/full/libcpp_full_trace.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef LIBCPP_FULL_TRACE_HPP
|
||||
#define LIBCPP_FULL_TRACE_HPP
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
std::vector<stacktrace_frame> generate_trace();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
35
src/platform/libcpp_program_name.hpp
Normal file
35
src/platform/libcpp_program_name.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef LIBCPP_PROGRAM_NAME_HPP
|
||||
#define LIBCPP_PROGRAM_NAME_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
inline std::string program_name() {
|
||||
char buffer[MAX_PATH + 1];
|
||||
int res = GetModuleFileNameA(nullptr, buffer, MAX_PATH);
|
||||
if(res) {
|
||||
return buffer;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
inline std::string program_name() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
22
src/symbols/libcpp_symbols.hpp
Normal file
22
src/symbols/libcpp_symbols.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef LIBCPP_SYMBOLIZE_HPP
|
||||
#define LIBCPP_SYMBOLIZE_HPP
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
class symbolizer {
|
||||
struct impl;
|
||||
std::unique_ptr<impl> impl;
|
||||
public:
|
||||
symbolizer();
|
||||
~symbolizer();
|
||||
stacktrace_frame resolve_frame(void* addr);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
0
src/symbols/symbols_with_addr2line.cpp
Normal file
0
src/symbols/symbols_with_addr2line.cpp
Normal file
0
src/symbols/symbols_with_dbghelp.cpp
Normal file
0
src/symbols/symbols_with_dbghelp.cpp
Normal file
0
src/symbols/symbols_with_dl.cpp
Normal file
0
src/symbols/symbols_with_dl.cpp
Normal file
63
src/symbols/symbols_with_libbacktrace.cpp
Normal file
63
src/symbols/symbols_with_libbacktrace.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
#ifdef LIBCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
#include "libcpp_symbolize.hpp"
|
||||
#include "../platform/libcpp_program_name.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <backtrace.h>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
backtrace_state* get_backtrace_state() {
|
||||
// backtrace_create_state must be called only one time per program
|
||||
static backtrace_state* state = nullptr;
|
||||
static bool called = false;
|
||||
if(!called) {
|
||||
state = backtrace_create_state(program_name().c_str(), true, error_callback, nullptr);
|
||||
called = true;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) {
|
||||
stacktrace_frame& frame = *static_cast<stacktrace_frame*>(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<impl>()) {}
|
||||
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) {
|
||||
stacktrace_frame frame;
|
||||
frame.col = -1;
|
||||
backtrace_pcinfo(
|
||||
get_backtrace_state(),
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
full_callback,
|
||||
error_callback,
|
||||
&frame
|
||||
);
|
||||
return frame;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
28
src/symbols/symbols_with_nothing.cpp
Normal file
28
src/symbols/symbols_with_nothing.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#ifdef LIBCPPTRACE_GET_SYMBOLS_WITH_NOTHING
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
#include "libcpp_symbolize.hpp"
|
||||
#include "../platform/libcpp_program_name.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
symbolizer::symbolizer() = default;
|
||||
symbolizer::~symbolizer() = default;
|
||||
|
||||
stacktrace_frame symbolizer::resolve_frame(void*) {
|
||||
return {
|
||||
0,
|
||||
-1,
|
||||
-1,
|
||||
"",
|
||||
"",
|
||||
};
|
||||
}
|
||||
|
||||
struct symbolizer::impl {};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
19
src/unwind/libcpp_unwind.hpp
Normal file
19
src/unwind/libcpp_unwind.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef LIBCPP_UNWIND_HPP
|
||||
#define LIBCPP_UNWIND_HPP
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
#ifdef LIBCPPTRACE_HARD_MAX_FRAMES
|
||||
constexpr size_t hard_max_frames = LIBCPPTRACE_HARD_MAX_FRAMES;
|
||||
#else
|
||||
constexpr size_t hard_max_frames = 100;
|
||||
#endif
|
||||
std::vector<void*> capture_frames();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
22
src/unwind/unwind_with_execinfo.cpp
Normal file
22
src/unwind/unwind_with_execinfo.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#ifdef LIBCPPTRACE_UNWIND_WITH_EXECINFO
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
#include "libcpp_unwind.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <execinfo.h>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
std::vector<void*> capture_frames() {
|
||||
std::vector<void*> frames(hard_max_frames, nullptr);
|
||||
int n_frames = backtrace(bt.data(), hard_max_frames);
|
||||
frames.resize(n_frames);
|
||||
frames.shrink_to_fit();
|
||||
return frames;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
16
src/unwind/unwind_with_nothing.cpp
Normal file
16
src/unwind/unwind_with_nothing.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#ifdef LIBCPPTRACE_UNWIND_WITH_NOTHING
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
#include "libcpp_unwind.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
std::vector<void*> capture_frames() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
26
src/unwind/unwind_with_winapi.cpp
Normal file
26
src/unwind/unwind_with_winapi.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#ifdef LIBCPPTRACE_UNWIND_WITH_WINAPI
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
#include "libcpp_unwind.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
std::vector<void*> 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 {};
|
||||
std::vector<PVOID> addrs(hard_max_frames, nullptr);
|
||||
int frames = CaptureStackBackTrace(n_skip, hard_max_frames, addrs.data(), NULL);
|
||||
addrs.resize(frames);
|
||||
addrs.shrink_to_fit();
|
||||
return addrs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
16
test/CMakeLists.txt
Normal file
16
test/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.8...3.23)
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.12)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
endif()
|
||||
|
||||
project(
|
||||
test-libcpptrace
|
||||
VERSION 1.0.0
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
add_executable(test test.cpp)
|
||||
target_link_libraries(test PRIVATE cpptrace)
|
||||
target_include_directories(test PRIVATE ../include)
|
||||
target_link_directories(test PRIVATE ../build)
|
||||
30
test/test.cpp
Normal file
30
test/test.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
void trace() {
|
||||
cpptrace::print_trace();
|
||||
}
|
||||
|
||||
void foo(int n) {
|
||||
if(n == 0) {
|
||||
trace();
|
||||
} else {
|
||||
foo(n - 1);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void foo(int x, Args... args) {
|
||||
foo(args...);
|
||||
}
|
||||
|
||||
void function_two(int, float) {
|
||||
foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
}
|
||||
|
||||
void function_one(int) {
|
||||
function_two(0, 0);
|
||||
}
|
||||
|
||||
int main() {
|
||||
function_one(0);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user