Initial commit

This commit is contained in:
Jeremy 2023-07-01 16:44:57 -04:00
commit b3474b50c3
No known key found for this signature in database
GPG Key ID: BE03111EB7ED6E2E
29 changed files with 899 additions and 0 deletions

41
.github/workflows/build.yml vendored Normal file
View 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
View File

@ -0,0 +1,4 @@
.vscode
build
a.out
test/build

261
CMakeLists.txt Normal file
View 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
View 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
View 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.

View File

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

5
cmake/has_backtrace.cpp Normal file
View 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
View 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
View File

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

View 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
View 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;
}
}
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

View File

View File

View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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);
}