chore: add backward-cpp for crash trace

This commit is contained in:
Joe Dong
2024-08-28 13:53:45 +08:00
parent cd3d501d36
commit e1f1e6ee61
26 changed files with 6489 additions and 1 deletions
@@ -0,0 +1,2 @@
---
BasedOnStyle: LLVM
@@ -0,0 +1,3 @@
build*/
*.pyc
*.sw?
@@ -0,0 +1,25 @@
language: cpp
compiler:
- gcc
- clang
addons:
apt:
packages:
- valgrind
install:
- DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
- mkdir ${DEPS_DIR} && cd ${DEPS_DIR}
- CMAKE_URL="http://www.cmake.org/files/v3.3/cmake-3.3.2-Linux-x86_64.tar.gz"
- mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
- export PATH=${DEPS_DIR}/cmake/bin:${PATH}
- pip install --user conan && export PATH=$PATH:$HOME/.local/bin
- cd ${TRAVIS_BUILD_DIR}
- mkdir build && cd build
- cmake .. -DBACKWARD_TESTS=ON
- cmake --build .
script:
- valgrind ctest .. --verbose
- cd ${TRAVIS_BUILD_DIR} && conan create . Manu343726/testing --build=outdated
@@ -0,0 +1,270 @@
#
# BackwardMacros.cmake
# Copyright 2013 Google Inc. All Rights Reserved.
#
# 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.
###############################################################################
# OPTIONS
###############################################################################
set(STACK_WALKING_UNWIND TRUE CACHE BOOL
"Use compiler's unwind API")
set(STACK_WALKING_BACKTRACE FALSE CACHE BOOL
"Use backtrace from (e)glibc for stack walking")
set(STACK_WALKING_LIBUNWIND FALSE CACHE BOOL
"Use libunwind for stack walking")
set(STACK_DETAILS_AUTO_DETECT TRUE CACHE BOOL
"Auto detect backward's stack details dependencies")
set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE CACHE BOOL
"Use backtrace from (e)glibc for symbols resolution")
set(STACK_DETAILS_DW FALSE CACHE BOOL
"Use libdw to read debug info")
set(STACK_DETAILS_BFD FALSE CACHE BOOL
"Use libbfd to read debug info")
set(STACK_DETAILS_DWARF FALSE CACHE BOOL
"Use libdwarf/libelf to read debug info")
###############################################################################
# CONFIGS
###############################################################################
include(FindPackageHandleStandardArgs)
if (STACK_WALKING_LIBUNWIND)
# libunwind works on the macOS without having to add special include
# paths or libraries
if (NOT APPLE)
find_path(LIBUNWIND_INCLUDE_DIR NAMES "libunwind.h")
find_library(LIBUNWIND_LIBRARY unwind)
if (LIBUNWIND_LIBRARY)
include(CheckSymbolExists)
check_symbol_exists(UNW_INIT_SIGNAL_FRAME libunwind.h HAVE_UNW_INIT_SIGNAL_FRAME)
if (NOT HAVE_UNW_INIT_SIGNAL_FRAME)
message(STATUS "libunwind does not support unwinding from signal handler frames")
endif()
endif()
set(LIBUNWIND_INCLUDE_DIRS ${LIBUNWIND_INCLUDE_DIR})
set(LIBDWARF_LIBRARIES ${LIBUNWIND_LIBRARY})
find_package_handle_standard_args(libunwind DEFAULT_MSG
LIBUNWIND_LIBRARY LIBUNWIND_INCLUDE_DIR)
mark_as_advanced(LIBUNWIND_INCLUDE_DIR LIBUNWIND_LIBRARY)
list(APPEND _BACKWARD_LIBRARIES ${LIBUNWIND_LIBRARY})
endif()
# Disable other unwinders if libunwind is found
set(STACK_WALKING_UNWIND FALSE)
set(STACK_WALKING_BACKTRACE FALSE)
endif()
if (${STACK_DETAILS_AUTO_DETECT})
if(NOT CMAKE_VERSION VERSION_LESS 3.17)
set(_name_mismatched_arg NAME_MISMATCHED)
endif()
# find libdw
find_path(LIBDW_INCLUDE_DIR NAMES "elfutils/libdw.h" "elfutils/libdwfl.h")
find_library(LIBDW_LIBRARY dw)
# in case it's statically linked, look for all the possible dependencies
find_library(LIBELF_LIBRARY elf)
find_library(LIBPTHREAD_LIBRARY pthread)
find_library(LIBZ_LIBRARY z)
find_library(LIBBZ2_LIBRARY bz2)
find_library(LIBLZMA_LIBRARY lzma)
find_library(LIBZSTD_LIBRARY zstd)
set(LIBDW_INCLUDE_DIRS ${LIBDW_INCLUDE_DIR} )
set(LIBDW_LIBRARIES ${LIBDW_LIBRARY}
$<$<BOOL:${LIBELF_LIBRARY}>:${LIBELF_LIBRARY}>
$<$<BOOL:${LIBPTHREAD_LIBRARY}>:${LIBPTHREAD_LIBRARY}>
$<$<BOOL:${LIBZ_LIBRARY}>:${LIBZ_LIBRARY}>
$<$<BOOL:${LIBBZ2_LIBRARY}>:${LIBBZ2_LIBRARY}>
$<$<BOOL:${LIBLZMA_LIBRARY}>:${LIBLZMA_LIBRARY}>
$<$<BOOL:${LIBZSTD_LIBRARY}>:${LIBZSTD_LIBRARY}>)
find_package_handle_standard_args(libdw ${_name_mismatched_arg}
REQUIRED_VARS LIBDW_LIBRARY LIBDW_INCLUDE_DIR)
mark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARY)
# find libbfd
find_path(LIBBFD_INCLUDE_DIR NAMES "bfd.h")
find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h")
find_library(LIBBFD_LIBRARY bfd)
find_library(LIBDL_LIBRARY dl)
set(LIBBFD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR})
set(LIBBFD_LIBRARIES ${LIBBFD_LIBRARY} ${LIBDL_LIBRARY})
find_package_handle_standard_args(libbfd ${_name_mismatched_arg}
REQUIRED_VARS LIBBFD_LIBRARY LIBBFD_INCLUDE_DIR
LIBDL_LIBRARY LIBDL_INCLUDE_DIR)
mark_as_advanced(LIBBFD_INCLUDE_DIR LIBBFD_LIBRARY
LIBDL_INCLUDE_DIR LIBDL_LIBRARY)
# find libdwarf
find_path(LIBDWARF_INCLUDE_DIR NAMES "libdwarf.h" PATH_SUFFIXES libdwarf)
find_path(LIBELF_INCLUDE_DIR NAMES "libelf.h")
find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h")
find_library(LIBDWARF_LIBRARY dwarf)
find_library(LIBELF_LIBRARY elf)
find_library(LIBDL_LIBRARY dl)
set(LIBDWARF_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIR} ${LIBELF_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR})
set(LIBDWARF_LIBRARIES ${LIBDWARF_LIBRARY} ${LIBELF_LIBRARY} ${LIBDL_LIBRARY})
find_package_handle_standard_args(libdwarf ${_name_mismatched_arg}
REQUIRED_VARS LIBDWARF_LIBRARY LIBDWARF_INCLUDE_DIR
LIBELF_LIBRARY LIBELF_INCLUDE_DIR
LIBDL_LIBRARY LIBDL_INCLUDE_DIR)
mark_as_advanced(LIBDWARF_INCLUDE_DIR LIBDWARF_LIBRARY
LIBELF_INCLUDE_DIR LIBELF_LIBRARY
LIBDL_INCLUDE_DIR LIBDL_LIBRARY)
if (LIBDW_FOUND)
LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDW_INCLUDE_DIRS})
LIST(APPEND _BACKWARD_LIBRARIES ${LIBDW_LIBRARIES})
set(STACK_DETAILS_DW TRUE)
set(STACK_DETAILS_BFD FALSE)
set(STACK_DETAILS_DWARF FALSE)
set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE)
elseif(LIBBFD_FOUND)
LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIRS})
LIST(APPEND _BACKWARD_LIBRARIES ${LIBBFD_LIBRARIES})
# If we attempt to link against static bfd, make sure to link its dependencies, too
get_filename_component(bfd_lib_ext "${LIBBFD_LIBRARY}" EXT)
if (bfd_lib_ext STREQUAL "${CMAKE_STATIC_LIBRARY_SUFFIX}")
find_library(LIBSFRAME_LIBRARY NAMES sframe)
if (LIBSFRAME_LIBRARY)
list(APPEND _BACKWARD_LIBRARIES ${LIBSFRAME_LIBRARY})
endif()
list(APPEND _BACKWARD_LIBRARIES iberty z)
endif()
set(STACK_DETAILS_DW FALSE)
set(STACK_DETAILS_BFD TRUE)
set(STACK_DETAILS_DWARF FALSE)
set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE)
elseif(LIBDWARF_FOUND)
LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIRS})
LIST(APPEND _BACKWARD_LIBRARIES ${LIBDWARF_LIBRARIES})
set(STACK_DETAILS_DW FALSE)
set(STACK_DETAILS_BFD FALSE)
set(STACK_DETAILS_DWARF TRUE)
set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE)
else()
set(STACK_DETAILS_DW FALSE)
set(STACK_DETAILS_BFD FALSE)
set(STACK_DETAILS_DWARF FALSE)
set(STACK_DETAILS_BACKTRACE_SYMBOL TRUE)
endif()
else()
if (STACK_DETAILS_DW)
LIST(APPEND _BACKWARD_LIBRARIES dw)
endif()
if (STACK_DETAILS_BFD)
LIST(APPEND _BACKWARD_LIBRARIES bfd dl)
endif()
if (STACK_DETAILS_DWARF)
LIST(APPEND _BACKWARD_LIBRARIES dwarf elf)
endif()
endif()
macro(map_definitions var_prefix define_prefix)
foreach(def ${ARGN})
if (${${var_prefix}${def}})
LIST(APPEND _BACKWARD_DEFINITIONS "${define_prefix}${def}=1")
else()
LIST(APPEND _BACKWARD_DEFINITIONS "${define_prefix}${def}=0")
endif()
endforeach()
endmacro()
if (NOT _BACKWARD_DEFINITIONS)
map_definitions("STACK_WALKING_" "BACKWARD_HAS_" UNWIND LIBUNWIND BACKTRACE)
map_definitions("STACK_DETAILS_" "BACKWARD_HAS_" BACKTRACE_SYMBOL DW BFD DWARF)
endif()
if(WIN32)
list(APPEND _BACKWARD_LIBRARIES dbghelp psapi)
if(MINGW)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-gcodeview SUPPORT_WINDOWS_DEBUG_INFO)
if(SUPPORT_WINDOWS_DEBUG_INFO)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--pdb= ")
add_compile_options(-gcodeview)
else()
set(MINGW_MSVCR_LIBRARY "msvcr90$<$<CONFIG:DEBUG>:d>" CACHE STRING "Mingw MSVC runtime import library")
list(APPEND _BACKWARD_LIBRARIES ${MINGW_MSVCR_LIBRARY})
endif()
endif()
endif()
set(BACKWARD_INCLUDE_DIR
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
set(BACKWARD_HAS_EXTERNAL_LIBRARIES FALSE)
set(FIND_PACKAGE_REQUIRED_VARS BACKWARD_INCLUDE_DIR)
if(DEFINED _BACKWARD_LIBRARIES)
set(BACKWARD_HAS_EXTERNAL_LIBRARIES TRUE)
list(APPEND FIND_PACKAGE_REQUIRED_VARS _BACKWARD_LIBRARIES)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Backward
REQUIRED_VARS ${FIND_PACKAGE_REQUIRED_VARS}
)
list(APPEND _BACKWARD_INCLUDE_DIRS ${BACKWARD_INCLUDE_DIR})
# add_backward, optional bool argument; if passed and true, backward will be included as a system header
macro(add_backward target)
message(DEPRECATION "The add_backward() macro is deprecated, use target_link_libraries() to link to "
"one of the exported targets: Backward::Interface, Backward::Object, or Backward::Backward."
)
if ("${ARGN}")
target_include_directories(${target} SYSTEM PRIVATE ${BACKWARD_INCLUDE_DIRS})
else()
target_include_directories(${target} PRIVATE ${BACKWARD_INCLUDE_DIRS})
endif()
set_property(TARGET ${target} APPEND PROPERTY COMPILE_DEFINITIONS ${BACKWARD_DEFINITIONS})
set_property(TARGET ${target} APPEND PROPERTY LINK_LIBRARIES ${BACKWARD_LIBRARIES})
endmacro()
set(BACKWARD_INCLUDE_DIRS ${_BACKWARD_INCLUDE_DIRS} CACHE INTERNAL "BACKWARD_INCLUDE_DIRS")
set(BACKWARD_DEFINITIONS ${_BACKWARD_DEFINITIONS} CACHE INTERNAL "BACKWARD_DEFINITIONS")
set(BACKWARD_LIBRARIES ${_BACKWARD_LIBRARIES} CACHE INTERNAL "BACKWARD_LIBRARIES")
mark_as_advanced(BACKWARD_INCLUDE_DIRS BACKWARD_DEFINITIONS BACKWARD_LIBRARIES)
# Expand each definition in BACKWARD_DEFINITIONS to its own cmake var and export
# to outer scope
foreach(var ${BACKWARD_DEFINITIONS})
string(REPLACE "=" ";" var_as_list ${var})
list(GET var_as_list 0 var_name)
list(GET var_as_list 1 var_value)
set(${var_name} ${var_value})
mark_as_advanced(${var_name})
endforeach()
# if this file is used from the install tree by find_package(), include the
# file CMake-generated file where the targets are defined
if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/BackwardTargets.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/BackwardTargets.cmake)
endif()
@@ -0,0 +1,176 @@
#
# CMakeLists.txt
# Copyright 2013 Google Inc. All Rights Reserved.
#
# 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.
cmake_minimum_required(VERSION 3.14)
project(backward CXX)
# Introduce variables:
# * CMAKE_INSTALL_LIBDIR
# * CMAKE_INSTALL_BINDIR
# * CMAKE_INSTALL_INCLUDEDIR
include(GNUInstallDirs)
include(BackwardConfig.cmake)
###############################################################################
# OPTIONS
###############################################################################
option(BACKWARD_SHARED "Build backward as a shared library" OFF)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND NOT DEFINED BACKWARD_TESTS)
# If this is a top level CMake project, we most lixely want the tests
set(BACKWARD_TESTS ON CACHE BOOL "Enable tests")
else()
set(BACKWARD_TESTS OFF CACHE BOOL "Enable tests")
endif()
###############################################################################
# COMPILER FLAGS
###############################################################################
# check if compiler is nvcc or nvcc_wrapper
set(COMPILER_IS_NVCC false)
get_filename_component(COMPILER_NAME ${CMAKE_CXX_COMPILER} NAME)
if (COMPILER_NAME MATCHES "^nvcc")
set(COMPILER_IS_NVCC true)
endif()
if (DEFINED ENV{OMPI_CXX} OR DEFINED ENV{MPICH_CXX})
if ( ($ENV{OMPI_CXX} MATCHES "nvcc") OR ($ENV{MPICH_CXX} MATCHES "nvcc") )
set(COMPILER_IS_NVCC true)
endif()
endif()
# set CXX standard
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_STANDARD 11)
if (${COMPILER_IS_NVCC})
# GNU CXX extensions are not supported by nvcc
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
if (NOT ${COMPILER_IS_NVCC})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic-errors")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
endif()
###############################################################################
# BACKWARD INTERFACE
###############################################################################
add_library(backward_interface INTERFACE)
set_target_properties(backward_interface PROPERTIES EXPORT_NAME Interface)
target_compile_definitions(backward_interface INTERFACE ${BACKWARD_DEFINITIONS})
target_include_directories(backward_interface INTERFACE ${BACKWARD_INCLUDE_DIRS})
if(BACKWARD_HAS_EXTERNAL_LIBRARIES)
target_link_libraries(backward_interface INTERFACE ${BACKWARD_LIBRARIES})
endif()
add_library(Backward::Interface ALIAS backward_interface)
###############################################################################
# BACKWARD OBJECT (Includes backward.cpp)
# (Note that this target is not exported, since CMake currently does not allow
# exporting an OBJECT library.)
###############################################################################
add_library(backward_object OBJECT backward.cpp)
set_target_properties(backward_object PROPERTIES EXPORT_NAME Object)
target_link_libraries(backward_object PUBLIC Backward::Interface)
add_library(Backward::Object ALIAS backward_object)
###############################################################################
# BACKWARD LIBRARY (Includes backward.cpp)
# (Note that the linker will not include unused objects from a static library,
# unless the -Wl,--whole-archive option (or similar) is used.)
###############################################################################
set(libtype STATIC)
if(BACKWARD_SHARED)
set(libtype SHARED)
endif()
add_library(backward ${libtype} backward.cpp)
set_target_properties(backward PROPERTIES EXPORT_NAME Backward)
target_link_libraries(backward PUBLIC Backward::Interface)
add_library(Backward::Backward ALIAS backward)
###############################################################################
# TESTS
###############################################################################
if(BACKWARD_TESTS)
enable_testing()
add_library(test_main OBJECT test/_test_main.cpp)
macro(backward_add_test src)
get_filename_component(name ${src} NAME_WE)
set(test_name "test_${name}")
add_executable(${test_name} ${src} ${ARGN} $<TARGET_OBJECTS:test_main>)
target_link_libraries(${test_name} PRIVATE Backward::Interface)
add_test(NAME ${name} COMMAND ${test_name})
endmacro()
# Tests without backward.cpp
set(TESTS
test
stacktrace
rectrace
select_signals
)
foreach(test ${TESTS})
backward_add_test(test/${test}.cpp)
endforeach()
# Tests with backward.cpp
set(TESTS
suicide
)
foreach(test ${TESTS})
backward_add_test(test/${test}.cpp backward.cpp)
endforeach()
endif()
install(
FILES "backward.hpp"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(
FILES "BackwardConfig.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
)
# check if Backward is being used as a top-level project or included as a subproject
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
# export the targets (note that exporting backward_object does not make sense)
install(TARGETS backward_interface backward EXPORT BackwardTargets)
# install a CMake file for the exported targets
install(EXPORT BackwardTargets
NAMESPACE Backward::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
endif()
@@ -0,0 +1,21 @@
Copyright 2013 Google Inc. All Rights Reserved.
The MIT License (MIT)
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.
@@ -0,0 +1,476 @@
Backward-cpp [![Conan Center](https://img.shields.io/conan/v/backward-cpp)](https://conan.io/center/recipes/backward-cpp)
============
Backward is a beautiful stack trace pretty printer for C++.
If you are bored to see this:
![default trace](doc/rude.png)
Backward will spice it up for you:
![pretty stackstrace](doc/pretty.png)
There is not much to say. Of course it will be able to display the code
snippets only if the source files are accessible (else see trace #4 in the
example).
All "Source" lines and code snippet prefixed by a pipe "|" are frames inline
the next frame.
You can see that for the trace #1 in the example, the function
`you_shall_not_pass()` was inlined in the function `...read2::do_test()` by the
compiler.
## Installation
#### Install backward.hpp
Backward is a header only library. So installing Backward is easy, simply drop
a copy of `backward.hpp` along with your other source files in your C++ project.
You can also use a git submodule or really any other way that best fits your
environment, as long as you can include `backward.hpp`.
#### Install backward.cpp
If you want Backward to automatically print a stack trace on most common fatal
errors (segfault, abort, un-handled exception...), simply add a copy of
`backward.cpp` to your project, and don't forget to tell your build system.
The code in `backward.cpp` is trivial anyway, you can simply copy what it's
doing at your convenience.
Note for [folly](https://github.com/facebook/folly) library users: must define `backward::SignalHandling sh;` after `folly::init(&argc, &argv);`.
## Configuration & Dependencies
### Integration with CMake
If you are using CMake and want to use its configuration abilities to save you
the trouble, you can easily integrate Backward, depending on how you obtained
the library.
Notice that all approaches are equivalent in the way Backward is added to a
CMake target, the difference is in how CMake is pointed to the Backward
sources. Backward defines three targets:
- `Backward::Interface` is an interface target that brings compiler definition
flags, include directory, and external libraries. This is all you need to use
the `backward.hpp` header library.
- `Backward::Object` brings `Backward::Interface` and `backward.cpp` as an
`OBJECT` CMake library. This target cannot be exported, so it is not
available when Backward is used via `find_package`.
- `Backward::Backward` brings `Backward::Interface` and `backward.cpp` as
either `STATIC` or `SHARED` library (depending on the `BACKWARD_SHARED`
option). This target is exported and always available, however note that the
linker will not include unused objects from a static library, unless the
`-Wl,--whole-archive` option (or similar) is used.
#### With `FetchContent()`:
If you are using a recent version of CMake, you can integrate `backward` via `FetchContent` like below:
```cmake
include(FetchContent)
# Also requires one of: libbfd (gnu binutils), libdwarf, libdw (elfutils)
FetchContent_Declare(backward
GIT_REPOSITORY https://github.com/bombela/backward-cpp
GIT_TAG master # or a version tag, such as v1.6
SYSTEM # optional, the Backward include directory will be treated as system directory
)
FetchContent_MakeAvailable(backward)
# Add Backward to your target (either Backward::Interface, Backward::Object, or Backward::Backward)
target_link_libraries(mytarget PUBLIC Backward::Interface)
```
#### As a subdirectory:
In this case you have a subdirectory containing the whole repository of Backward
(e.g. using [git-submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules)),
in this case you can do:
```cmake
add_subdirectory(/path/to/backward-cpp)
# Add Backward to your target (either Backward::Interface, Backward::Object, or Backward::Backward)
target_link_libraries(mytarget PUBLIC Backward::Interface)
```
#### Modifying `CMAKE_MODULE_PATH`:
In this case you can have Backward installed as a subdirectory:
```
list(APPEND CMAKE_MODULE_PATH /path/to/backward-cpp)
find_package(Backward)
# Add Backward to your target (either Backward::Interface or Backward::Backward)
target_link_libraries(mytarget PUBLIC Backward::Interface)
```
#### Installation through a regular package manager
In this case you have obtained Backward through a package manager.
Packages currently available:
- [conda-forge](https://anaconda.org/conda-forge/backward-cpp)
```
find_package(Backward)
# Add Backward to your target (either Backward::Interface or Backward::Backward)
target_link_libraries(mytarget PUBLIC Backward::Interface)
```
### Libraries to unwind the stack
On Linux and macOS, backtrace can back-trace or "walk" the stack using the
following libraries:
#### unwind
Unwind comes from libgcc, but there is an equivalent inside clang itself. With
unwind, the stacktrace is as accurate as it can possibly be, since this is
used by the C++ runtine in gcc/clang for stack unwinding on exception.
Normally libgcc is already linked to your program by default.
#### libunwind from the [libunwind project](https://github.com/libunwind/libunwind)
apt-get install binutils-dev (or equivalent)
Libunwind provides, in some cases, a more accurate stacktrace as it knows
to decode signal handler frames and lets us edit the context registers when
unwinding, allowing stack traces over bad function references.
For best results make sure you are using libunwind 1.3 or later, which added
`unw_init_local2` and support for handling signal frames.
CMake will warn you when configuring if your libunwind version doesn't support
signal frames.
On macOS clang provides a libunwind API compatible library as part of its
environment, so no third party libraries are necessary.
### Compile with debug info
You need to compile your project with generation of debug symbols enabled,
usually `-g` with clang++ and g++.
Note that you can use `-g` with any level of optimization, with modern debug
information encoding like DWARF, it only takes space in the binary (it's not
loaded in memory until your debugger or Backward makes use of it, don't worry),
and it doesn't impact the code generation (at least on GNU/Linux x86\_64 for
what I know).
If you are missing debug information, the stack trace will lack details about
your sources.
### Libraries to read the debug info
Backward supports pretty printed stack traces on GNU/Linux, macOS and Windows,
it will compile fine under other platforms but will not do anything. **Pull
requests are welcome :)**
Also, by default you will get a really basic stack trace, based on the
`backtrace_symbols` API:
![default trace](doc/nice.png)
You will need to install some dependencies to get the ultimate stack trace.
Three libraries are currently supported, the only difference is which one is the
easiest for you to install, so pick your poison:
#### libbfd from the [GNU/binutils](http://www.gnu.org/software/binutils/)
apt-get install binutils-dev (or equivalent)
And do not forget to link with the lib: `g++/clang++ -lbfd -ldl ...`
This library requires dynamic loading. Which is provided by the library `dl`.
Hence why we also link with `-ldl`.
Then define the following before every inclusion of `backward.hpp` (don't
forget to update `backward.cpp` as well):
#define BACKWARD_HAS_BFD 1
#### libdw from the [elfutils](https://fedorahosted.org/elfutils/)
apt-get install libdw-dev (or equivalent)
And do not forget to link with the lib and inform Backward to use it:
#define BACKWARD_HAS_DW 1
Of course you can simply add the define (`-DBACKWARD_HAS_...=1`) and the
linkage details in your build system and even auto-detect which library is
installed, it's up to you.
#### [libdwarf](https://sourceforge.net/projects/libdwarf/) and [libelf](http://www.mr511.de/software/english.html)
apt-get install libdwarf-dev (or equivalent)
And do not forget to link with the lib and inform Backward to use it:
#define BACKWARD_HAS_DWARF 1
There are several alternative implementations of libdwarf and libelf that
are API compatible so it's possible, although it hasn't been tested, to
replace the ones used when developing backward (in bold, below):
* **_libelf_** by [Michael "Tired" Riepe](http://www.mr511.de/software/english.html)
* **_libdwarf_** by [David Anderson](https://www.prevanders.net/dwarf.html)
* libelf from [elfutils](https://fedorahosted.org/elfutils/)
* libelf and libdwarf from FreeBSD's [ELF Tool Chain](https://sourceforge.net/p/elftoolchain/wiki/Home/) project
Of course you can simply add the define (`-DBACKWARD_HAS_...=1`) and the
linkage details in your build system and even auto-detect which library is
installed, it's up to you.
That's it, you are all set, you should be getting nice stack traces like the
one at the beginning of this document.
## API
If you don't want to limit yourself to the defaults offered by `backward.cpp`,
and you want to take some random stack traces for whatever reason and pretty
print them the way you love or you decide to send them all to your buddies over
the Internet, you will appreciate the simplicity of Backward's API.
### Stacktrace
The StackTrace class lets you take a "snapshot" of the current stack.
You can use it like this:
```c++
using namespace backward;
StackTrace st; st.load_here(32);
Printer p; p.print(st);
```
The public methods are:
```c++
class StackTrace { public:
// Take a snapshot of the current stack, with at most "trace_cnt_max"
// traces in it. The first trace is the most recent (ie the current
// frame). You can also provide a trace address to load_from() assuming
// the address is a valid stack frame (useful for signal handling traces).
// Both function return size().
size_t load_here(size_t trace_cnt_max)
size_t load_from(void* address, size_t trace_cnt_max)
// The number of traces loaded. This can be less than "trace_cnt_max".
size_t size() const
// A unique id for the thread in which the trace was taken. The value
// 0 means the stack trace comes from the main thread.
size_t thread_id() const
// Retrieve a trace by index. 0 is the most recent trace, size()-1 is
// the oldest one.
Trace operator[](size_t trace_idx)
};
```
### TraceResolver
The `TraceResolver` does the heavy lifting, and intends to transform a simple
`Trace` from its address into a fully detailed `ResolvedTrace` with the
filename of the source, line numbers, inlined functions and so on.
You can use it like this:
```c++
using namespace backward;
StackTrace st; st.load_here(32);
TraceResolver tr; tr.load_stacktrace(st);
for (size_t i = 0; i < st.size(); ++i) {
ResolvedTrace trace = tr.resolve(st[i]);
std::cout << "#" << i
<< " " << trace.object_filename
<< " " << trace.object_function
<< " [" << trace.addr << "]"
<< std::endl;
}
```
The public methods are:
```c++
class TraceResolver { public:
// Pre-load whatever is necessary from the stack trace.
template <class ST>
void load_stacktrace(ST&)
// Resolve a trace. It takes a ResolvedTrace, because a `Trace` is
// implicitly convertible to it.
ResolvedTrace resolve(ResolvedTrace t)
};
```
### SnippetFactory
The SnippetFactory is a simple helper class to automatically load and cache
source files in order to extract code snippets.
```c++
class SnippetFactory { public:
// A snippet is a list of line numbers and line contents.
typedef std::vector<std::pair<size_t, std::string> > lines_t;
// Return a snippet starting at line_start with up to context_size lines.
lines_t get_snippet(const std::string& filename,
size_t line_start, size_t context_size)
// Return a combined snippet from two different locations and combine them.
// context_size / 2 lines will be extracted from each location.
lines_t get_combined_snippet(
const std::string& filename_a, size_t line_a,
const std::string& filename_b, size_t line_b,
size_t context_size)
// Tries to return a unified snippet if the two locations from the same
// file are close enough to fit inside one context_size, else returns
// the equivalent of get_combined_snippet().
lines_t get_coalesced_snippet(const std::string& filename,
size_t line_a, size_t line_b, size_t context_size)
```
### Printer
A simpler way to pretty print a stack trace to the terminal. It will
automatically resolve the traces for you:
```c++
using namespace backward;
StackTrace st; st.load_here(32);
Printer p;
p.object = true;
p.color_mode = ColorMode::always;
p.address = true;
p.print(st, stderr);
```
You can set a few options:
```c++
class Printer { public:
// Print a little snippet of code if possible.
bool snippet = true;
// Colorize the trace
// - ColorMode::automatic: Activate colors if possible. For example, when using a TTY on linux.
// - ColorMode::always: Always use colors.
// - ColorMode::never: Never use colors.
bool color_mode = ColorMode::automatic;
// Add the addresses of every source location to the trace.
bool address = false;
// Even if there is a source location, also prints the object
// from where the trace came from.
bool object = false;
// Resolve and print a stack trace to the given C FILE* object.
// On linux, if the FILE* object is attached to a TTY,
// color will be used if color_mode is set to automatic.
template <typename StackTrace>
FILE* print(StackTrace& st, FILE* fp = stderr);
// Resolve and print a stack trace to the given std::ostream object.
// Color will only be used if color_mode is set to always.
template <typename ST>
std::ostream& print(ST& st, std::ostream& os);
```
### SignalHandling
A simple helper class that registers for you the most common signals and other
callbacks to segfault, hardware exception, un-handled exception etc.
`backward.cpp` simply uses it like that:
```c++
backward::SignalHandling sh;
```
Creating the object registers all the different signals and hooks. Destroying
this object doesn't do anything. It exposes only one method:
```c++
bool loaded() const // true if loaded with success
```
**Warning:** The registered signal handlers are not
[signal safe](https://www.man7.org/linux/man-pages/man7/signal-safety.7.html),
mostly because backward-cpp and the DWARF decoding libraries are using `malloc`.
In case a signal is raised while malloc is holding a lock, a deadlock will
occur.
### Trace object
To keep the memory footprint of a loaded `StackTrace` on the low-side, there a
hierarchy of trace object, from a minimal `Trace `to a `ResolvedTrace`.
#### Simple trace
```c++
struct Trace {
void* addr; // address of the trace
size_t idx; // its index (0 == most recent)
};
```
#### Resolved trace
A `ResolvedTrace` should contains a maximum of details about the location of
the trace in the source code. Note that not all fields might be set.
```c++
struct ResolvedTrace: public Trace {
struct SourceLoc {
std::string function;
std::string filename;
unsigned line;
unsigned col;
};
// In which binary object this trace is located.
std::string object_filename;
// The function in the object that contains the trace. This is not the same
// as source.function which can be an function inlined in object_function.
std::string object_function;
// The source location of this trace. It is possible for filename to be
// empty and for line/col to be invalid (value 0) if this information
// couldn't be deduced, for example if there is no debug information in the
// binary object.
SourceLoc source;
// An optional list of "inliners". All of these sources locations where
// inlined in the source location of the trace (the attribute right above).
// This is especially useful when you compile with optimizations turned on.
typedef std::vector<SourceLoc> source_locs_t;
source_locs_t inliners;
};
```
## Contact and copyright
François-Xavier Bourlet <bombela@gmail.com>
Copyright 2013-2017 Google Inc. All Rights Reserved.
MIT License.
### Disclaimer
Although this project is owned by Google Inc. this is not a Google supported or
affiliated project.
@@ -0,0 +1,42 @@
// Pick your poison.
//
// On GNU/Linux, you have few choices to get the most out of your stack trace.
//
// By default you get:
// - object filename
// - function name
//
// In order to add:
// - source filename
// - line and column numbers
// - source code snippet (assuming the file is accessible)
// Install one of the following libraries then uncomment one of the macro (or
// better, add the detection of the lib and the macro definition in your build
// system)
// - apt-get install libdw-dev ...
// - g++/clang++ -ldw ...
// #define BACKWARD_HAS_DW 1
// - apt-get install binutils-dev ...
// - g++/clang++ -lbfd ...
// #define BACKWARD_HAS_BFD 1
// - apt-get install libdwarf-dev ...
// - g++/clang++ -ldwarf ...
// #define BACKWARD_HAS_DWARF 1
// Regardless of the library you choose to read the debug information,
// for potentially more detailed stack traces you can use libunwind
// - apt-get install libunwind-dev
// - g++/clang++ -lunwind
// #define BACKWARD_HAS_LIBUNWIND 1
#include "backward.hpp"
namespace backward {
backward::SignalHandling sh;
} // namespace backward
File diff suppressed because it is too large Load Diff
+77
View File
@@ -0,0 +1,77 @@
#!/bin/bash
COMPILERS_CXX98=`cat<<EOF
gcc-4.4
gcc-4.6
gcc-4.7
gcc-4.8
clang
EOF`
COMPILERS_CXX11=`cat<<EOF
gcc-4.7
gcc-4.8
clang
EOF`
function mkbuild() {
local compiler=$1
local lang=$2
local buildtype=$3
local builddir="$4"
export CC=$compiler
export CXX=`echo $compiler | sed -e 's/clang/clang++/' -e 's/gcc/g++/'`
export CXXFLAGS="-std=$lang"
echo "Creating $builddir"
mkdir $builddir 2>/dev/null
(
cd "$builddir"
cmake -DCMAKE_BUILD_TYPE=$buildtype -DBACKWARD_TESTS=ON ..
)
}
function build() {
local builddir=$1
shift
make -C "$builddir" $@
}
function dotest() {
local builddir=$1
shift
make -C "$builddir" test $@
return 0
}
function do_action() {
local lang=$1
local action=$2
shift 2
for compiler in $COMPILERS; do
local builddir="build_${lang}_${compiler}"
if [[ $action == "cmake" ]]; then
buildtype=$1
mkbuild $compiler $lang "$buildtype" "$builddir"
[[ $? != 0 ]] && exit
elif [[ $action == "make" ]]; then
build "$builddir" $@
[[ $? != 0 ]] && exit
elif [[ $action == "test" ]]; then
dotest "$builddir" $@
[[ $? != 0 ]] && exit
elif [[ $action == "clean" ]]; then
rm -r "$builddir"
else
echo "usage: $0 cmake [debug|release|relwithdbg]|make|test|clean"
exit 255
fi
done
}
COMPILERS=$COMPILERS_CXX98
do_action c++98 $@
COMPILERS=$COMPILERS_CXX11
do_action c++11 $@
@@ -0,0 +1,45 @@
from conans import ConanFile, CMake
import os
class BackwardCpp(ConanFile):
settings = 'os', 'compiler', 'build_type', 'arch'
name = 'backward'
url = 'https://github.com/bombela/backward-cpp'
license = 'MIT'
version = '1.3.0'
options = {
'stack_walking_unwind': [True, False],
'stack_walking_backtrace': [True, False],
'stack_details_auto_detect': [True, False],
'stack_details_backtrace_symbol': [True, False],
'stack_details_dw': [True, False],
'stack_details_bfd': [True, False],
'shared': [True, False]
}
default_options = (
'stack_walking_unwind=True',
'stack_walking_backtrace=False',
'stack_details_auto_detect=True',
'stack_details_backtrace_symbol=False',
'stack_details_dw=False',
'stack_details_bfd=False',
'shared=False'
)
exports = 'backward.cpp', 'backward.hpp', 'test/*', 'CMakeLists.txt', 'BackwardConfig.cmake'
generators = 'cmake'
def build(self):
cmake = CMake(self)
cmake.configure(defs={'BACKWARD_' + name.upper(): value for name, value in self.options.values.as_list()})
cmake.build()
def package(self):
self.copy('backward.hpp', os.path.join('include', 'backward'))
self.copy('*.a', dst='lib')
self.copy('*.so', dst='lib')
self.copy('*.lib', dst='lib')
self.copy('*.dll', dst='lib')
def package_info(self):
self.cpp_info.libs = ['backward']
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

@@ -0,0 +1,241 @@
/*
* _test_main.cpp
* Copyright 2013 Google Inc. All Rights Reserved.
*
* 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.
*/
#include "test.hpp"
#include <cstdio>
#include <cstdlib>
#ifdef _WIN32
#include <windows.h>
#define strcasecmp _stricmp
#else
#include <sys/wait.h>
#include <unistd.h>
#endif
#if defined(__has_include) && __has_include(<error.h>)
#include <error.h>
#else
#include <stdarg.h>
#ifdef _WIN32
char argv0[MAX_PATH];
inline const char *getprogname() {
return GetModuleFileName(NULL, argv0, sizeof(argv0)) ? argv0 : NULL;
}
#elif !defined(__APPLE__)
// N.B. getprogname() is an Apple/BSD-ism.
// program_invocation_name is a GLIBC-ism, but it's also
// supported by libmusl.
#define getprogname() program_invocation_name
#endif
void error(int status, int errnum, const char *format, ...) {
fflush(stdout);
fprintf(stderr, "%s: ", getprogname());
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
if (errnum != 0) {
fprintf(stderr, ": %s\n", strerror(errnum));
} else {
fprintf(stderr, "\n");
}
if (status != 0) {
exit(status);
}
}
#endif
using namespace test;
bool run_test(TestBase &test, bool use_child_process = true) {
if (!use_child_process) {
exit(static_cast<int>(test.run()));
}
printf("-- running test case: %s\n", test.name);
fflush(stdout);
test::TestStatus status = test::SUCCESS;
#ifdef _WIN32
char filename[256];
GetModuleFileName(NULL, filename, 256); // TODO: check for error
std::string cmd_line = filename;
cmd_line += " --nofork ";
cmd_line += test.name;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcessA(nullptr, const_cast<char *>(cmd_line.c_str()), nullptr,
nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) {
printf("unable to create process\n");
exit(-1);
}
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exit_code;
GetExitCodeProcess(pi.hProcess, &exit_code);
switch (exit_code) {
case 3:
status = test::SIGNAL_ABORT;
break;
case 5:
status = test::EXCEPTION_UNCAUGHT;
break;
case EXCEPTION_ACCESS_VIOLATION:
status = test::SIGNAL_SEGFAULT;
break;
case EXCEPTION_STACK_OVERFLOW:
status = test::SIGNAL_SEGFAULT;
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
status = test::SIGNAL_DIVZERO;
break;
}
printf("Exit code: %lu\n", exit_code);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (test.expected_status == test::ASSERT_FAIL) {
// assert calls abort on windows
return (status & test::SIGNAL_ABORT);
}
#else
pid_t child_pid = fork();
if (child_pid == 0) {
exit(static_cast<int>(test.run()));
}
if (child_pid == -1) {
error(EXIT_FAILURE, 0, "unable to fork");
}
int child_status = 0;
waitpid(child_pid, &child_status, 0);
if (WIFEXITED(child_status)) {
int exit_status = WEXITSTATUS(child_status);
if (exit_status & ~test::STATUS_MASK) {
status = test::FAILED;
} else {
status = static_cast<test::TestStatus>(exit_status);
}
} else if (WIFSIGNALED(child_status)) {
const int signum = WTERMSIG(child_status);
printf("!! signal (%d) %s\n", signum, strsignal(signum));
switch (signum) {
case SIGABRT:
status = test::SIGNAL_ABORT;
break;
case SIGSEGV:
case SIGBUS:
status = test::SIGNAL_SEGFAULT;
break;
case SIGFPE:
status = test::SIGNAL_DIVZERO;
break;
default:
status = test::SIGNAL_UNCAUGHT;
}
}
#endif
if (test.expected_status == test::FAILED) {
return (status & test::FAILED);
}
if (test.expected_status == test::SIGNAL_UNCAUGHT) {
return (status & test::SIGNAL_UNCAUGHT);
}
return status == test.expected_status;
}
int main(int argc, const char *const argv[]) {
#ifdef _WIN32
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
if (argc == 3 && strcmp("--nofork", argv[1]) == 0) {
// Windows has no fork, so we simulate it
// we only execute one test, without forking
for (test_registry_t::iterator it = test_registry().begin();
it != test_registry().end(); ++it) {
TestBase &test = **it;
if (strcasecmp(argv[2], test.name) == 0) {
run_test(test, false);
return 0;
}
}
return -1;
}
size_t success_cnt = 0;
size_t total_cnt = 0;
for (test_registry_t::iterator it = test_registry().begin();
it != test_registry().end(); ++it) {
TestBase &test = **it;
bool consider_test = (argc <= 1);
for (int i = 1; i < argc; ++i) {
if (strcasecmp(argv[i], test.name) == 0) {
consider_test = true;
break;
}
}
if (!consider_test) {
continue;
}
total_cnt += 1;
if (run_test(test)) {
printf("-- test case success: %s\n", test.name);
success_cnt += 1;
} else {
printf("** test case FAILED : %s\n", test.name);
}
}
printf("-- tests passing: %zu/%zu", success_cnt, total_cnt);
if (total_cnt) {
printf(" (%zu%%)\n", success_cnt * 100 / total_cnt);
} else {
printf("\n");
}
return (success_cnt == total_cnt) ? EXIT_SUCCESS : EXIT_FAILURE;
}
@@ -0,0 +1,98 @@
/*
* test/rectrace.cpp
* Copyright 2013 Google Inc. All Rights Reserved.
*
* 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.
*/
#include "backward.hpp"
#include "test/test.hpp"
#include <stdio.h>
using namespace backward;
typedef StackTrace stacktrace_t;
void end_of_our_journey(stacktrace_t &st) {
if (!st.size()) {
st.load_here();
}
}
int rec(stacktrace_t &st, int level) {
if (level <= 1) {
end_of_our_journey(st);
return 0;
}
return rec(st, level - 1);
}
namespace toto {
namespace titi {
struct foo {
union bar {
NOINLINE static int trampoline(stacktrace_t &st, int level) {
return rec(st, level);
}
};
};
} // namespace titi
} // namespace toto
TEST(recursion) {
{ // lexical scope.
stacktrace_t st;
const int input = 3;
int r = toto::titi::foo::bar::trampoline(st, input);
std::cout << "rec(" << input << ") == " << r << std::endl;
Printer printer;
// printer.address = true;
printer.object = true;
printer.print(st, stdout);
}
}
int fib(StackTrace &st, int level) {
if (level == 2) {
return 1;
}
if (level <= 1) {
end_of_our_journey(st);
return 0;
}
return fib(st, level - 1) + fib(st, level - 2);
}
TEST(fibrecursive) {
StackTrace st;
const int input = 6;
int r = fib(st, input);
std::cout << "fib(" << input << ") == " << r << std::endl;
Printer printer;
printer.print(st, stdout);
}
@@ -0,0 +1,51 @@
/*
* test/segfault.cpp
* Copyright 2013 Google Inc. All Rights Reserved.
*
* 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.
*/
#include "backward.hpp"
#include "test/test.hpp"
#include <signal.h>
#include <stdio.h>
using namespace backward;
void badass_function() {
char *ptr = (char *)42;
*ptr = 42;
}
TEST_SEGFAULT(pprint_sigsev) {
std::vector<int> signals;
signals.push_back(SIGSEGV);
SignalHandling sh(signals);
std::cout << std::boolalpha << "sh.loaded() == " << sh.loaded() << std::endl;
badass_function();
}
TEST_SEGFAULT(wont_pprint) {
std::vector<int> signals;
signals.push_back(SIGABRT);
SignalHandling sh(signals);
std::cout << std::boolalpha << "sh.loaded() == " << sh.loaded() << std::endl;
badass_function();
}
@@ -0,0 +1,57 @@
/*
* test/stacktrace.cpp
* Copyright 2013 Google Inc. All Rights Reserved.
*
* 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.
*/
#include "backward.hpp"
#include "test/test.hpp"
#include <cstdio>
#include <iostream>
using namespace backward;
void collect_trace(StackTrace &st) { st.load_here(); }
TEST(minitrace) {
Printer printer;
StackTrace st;
collect_trace(st);
printer.print(st, std::cout);
}
void d(StackTrace &st) { st.load_here(); }
void c(StackTrace &st) { return d(st); }
void b(StackTrace &st) { return c(st); }
NOINLINE void a(StackTrace &st) { return b(st); }
TEST(smalltrace) {
Printer printer;
StackTrace st;
a(st);
printer.print(st, std::cout);
}
@@ -0,0 +1,89 @@
/*
* test/suicide.cpp
* Copyright 2013 Google Inc. All Rights Reserved.
*
* 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.
*/
#include "backward.hpp"
#include "test/test.hpp"
#include <cstdio>
#ifndef _WIN32
#include <sys/resource.h>
#endif
using namespace backward;
void badass_function() {
char *ptr = (char *)42;
*ptr = 42;
}
TEST_SEGFAULT(invalid_write) { badass_function(); }
int you_shall_not_pass() {
char *ptr = (char *)42;
int v = *ptr;
return v;
}
TEST_SEGFAULT(invalid_read) {
int v = you_shall_not_pass();
std::cout << "v=" << v << std::endl;
}
void abort_abort_I_repeat_abort_abort() {
std::cout << "Jumping off the boat!" << std::endl;
abort();
}
TEST_ABORT(calling_abort) { abort_abort_I_repeat_abort_abort(); }
// aarch64, mips, PowerPC and RISC-V do not trap Division by zero
#if !defined(__aarch64__) && !defined(__mips__) && !defined (__powerpc__) && !defined (__riscv)
volatile int zero = 0;
int divide_by_zero() {
std::cout << "And the wild black hole appears..." << std::endl;
int v = 42 / zero;
return v;
}
TEST_DIVZERO(divide_by_zero) {
int v = divide_by_zero();
std::cout << "v=" << v << std::endl;
}
#endif
// Darwin does not allow RLIMIT_STACK to be reduced
#ifndef __APPLE__
int bye_bye_stack(int i) { return bye_bye_stack(i + 1) + bye_bye_stack(i * 2); }
TEST_SEGFAULT(stackoverflow) {
#ifndef _WIN32
struct rlimit limit;
limit.rlim_max = 8096;
setrlimit(RLIMIT_STACK, &limit);
#endif
int r = bye_bye_stack(42);
std::cout << "r=" << r << std::endl;
}
#endif
@@ -0,0 +1,63 @@
/*
* test/test.cpp
* Copyright 2013 Google Inc. All Rights Reserved.
*
* 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.
*/
#include "test/test.hpp"
#include <cstdlib>
#include <iostream>
#include <stdexcept>
TEST(empty_test) {}
TEST_FAIL_ASSERT(fail_assert) { ASSERT(1 == 2); }
TEST_FAIL_ASSERT(fail_assert_ge) { ASSERT_GE(4, 5); }
TEST_UNCAUGHT_EXCEPTION(uncaught_exception) {
throw std::runtime_error("some random runtime error");
}
TEST_UNCAUGHT_EXCEPTION(uncaught_exception_int) { throw 42; }
TEST_SEGFAULT(segfault) {
char *a = 0;
char b = a[42];
std::cout << "result: " << b << std::endl;
}
TEST_ABORT(abort) { abort(); }
TEST(catch_int) {
ASSERT_THROW({ throw 42; }, int);
}
TEST_FAIL_ASSERT(fail_catch_int) { ASSERT_THROW({}, int); }
TEST_FAIL_ASSERT(fail_no_throw) {
ASSERT_NO_THROW({ throw 42; });
}
TEST(any_throw) {
ASSERT_ANY_THROW({ throw 42; });
}
TEST_FAIL_ASSERT(fail_any_throw) { ASSERT_ANY_THROW({}); }
@@ -0,0 +1,183 @@
/*
* test/test.hpp
* Copyright 2013 Google Inc. All Rights Reserved.
*
* 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.
*/
#pragma once
#ifndef H_54E531F7_9154_454B_BEB9_257408429470
#define H_54E531F7_9154_454B_BEB9_257408429470
#include <cstdio>
#include <cstring>
#include <exception>
#include <sstream>
#include <string>
#include <vector>
namespace test {
struct AssertFailedError : std::exception {
~AssertFailedError() throw() {}
AssertFailedError(const char *filename, int _line, const char *_errmsg)
: basename(_basename(filename)), line(_line), errmsg(_errmsg) {}
const char *what() const throw() {
if (!_what.size()) {
std::ostringstream ss;
ss << "assertion failed (" << basename << ":" << line;
ss << ") " << errmsg;
_what = ss.str();
}
return _what.c_str();
}
const char *basename;
int line;
const char *errmsg;
mutable std::string _what;
static const char *_basename(const char *filename) {
const char *basename = filename + strlen(filename);
while (basename != filename && *basename != '/') {
basename -= 1;
}
return basename + 1;
}
};
enum TestStatus {
SUCCESS = 0 << 0,
FAILED = 1 << 0,
ASSERT_FAIL = FAILED | 1 << 1,
EXCEPTION_UNCAUGHT = FAILED | 2 << 1,
SIGNAL_UNCAUGHT = FAILED | 3 << 1,
SIGNAL_SEGFAULT = SIGNAL_UNCAUGHT | 1 << 3,
SIGNAL_ABORT = SIGNAL_UNCAUGHT | 2 << 3,
SIGNAL_DIVZERO = SIGNAL_UNCAUGHT | 2 << 3,
STATUS_MASK = 0x1F
};
struct TestBase {
const char *name;
TestStatus expected_status;
virtual ~TestBase() {}
TestBase(const char *, TestStatus);
virtual void do_test() = 0;
TestStatus run() {
try {
do_test();
return SUCCESS;
} catch (const AssertFailedError &e) {
printf("!! %s\n", e.what());
return ASSERT_FAIL;
} catch (const std::exception &e) {
printf("!! exception: %s\n", e.what());
return EXCEPTION_UNCAUGHT;
} catch (...) {
printf("!! unknown exception\n");
return EXCEPTION_UNCAUGHT;
}
}
};
typedef std::vector<TestBase *> test_registry_t;
inline test_registry_t &test_registry() {
static test_registry_t reg;
return reg;
}
inline TestBase::TestBase(const char *n, TestStatus s)
: name(n), expected_status(s) {
test_registry().push_back(this);
}
} // namespace test
#define _TEST_STATUS(name, status) \
struct TEST_##name : ::test::TestBase { \
TEST_##name() : TestBase(#name, status) {} \
void do_test(); \
} TEST_##name; \
void TEST_##name::do_test()
#define TEST(name) _TEST_STATUS(name, ::test::SUCCESS)
#define TEST_FAIL(name) _TEST_STATUS(name, ::test::FAILED)
#define TEST_FAIL_ASSERT(name) _TEST_STATUS(name, ::test::ASSERT_FAIL)
#define TEST_UNCAUGHT_EXCEPTION(name) \
_TEST_STATUS(name, ::test::EXCEPTION_UNCAUGHT)
#define TEST_UNCAUGHT_SIGNAL(name) _TEST_STATUS(name, ::test::SIGNAL_UNCAUGHT)
#define TEST_SEGFAULT(name) _TEST_STATUS(name, ::test::SIGNAL_SEGFAULT)
#define TEST_ABORT(name) _TEST_STATUS(name, ::test::SIGNAL_ABORT)
#define TEST_DIVZERO(name) _TEST_STATUS(name, ::test::SIGNAL_DIVZERO)
#define ASSERT(expr) \
(expr) ? static_cast<void>(0) \
: throw ::test::AssertFailedError(__FILE__, __LINE__, #expr)
#define _ASSERT_BINOP(a, b, cmp) \
(!(a cmp b)) ? static_cast<void>(0) \
: throw ::test::AssertFailedError( \
__FILE__, __LINE__, "because " #a " " #cmp " " #b)
#define ASSERT_EQ(a, b) _ASSERT_BINOP(a, b, !=)
#define ASSERT_NE(a, b) _ASSERT_BINOP(a, b, ==)
#define ASSERT_LT(a, b) _ASSERT_BINOP(a, b, >=)
#define ASSERT_LE(a, b) _ASSERT_BINOP(a, b, >)
#define ASSERT_GT(a, b) _ASSERT_BINOP(a, b, <=)
#define ASSERT_GE(a, b) _ASSERT_BINOP(a, b, <)
#define ASSERT_THROW(expr, e_type) \
do { \
try { \
expr \
} catch (const e_type &) { \
break; \
} \
throw ::test::AssertFailedError(__FILE__, __LINE__, \
"expected exception " #e_type); \
} while (0)
#define ASSERT_ANY_THROW(expr) \
do { \
try { \
expr \
} catch (...) { \
break; \
} \
throw ::test::AssertFailedError(__FILE__, __LINE__, \
"expected any exception"); \
} while (0)
#define ASSERT_NO_THROW(expr) \
try { \
expr \
} catch (...) { \
throw ::test::AssertFailedError(__FILE__, __LINE__, \
"no exception expected"); \
}
#endif /* H_GUARD */
@@ -0,0 +1,8 @@
project(backward-package-test)
cmake_minimum_required(VERSION 2.8.12)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup(TARGETS)
add_executable(example main.cpp)
target_link_libraries(example PRIVATE ${CONAN_TARGETS})
@@ -0,0 +1,14 @@
from conans import ConanFile, CMake
import os
class TestBackward(ConanFile):
settings = 'os', 'compiler', 'build_type', 'arch'
generators = 'cmake'
def build(self):
cmake = CMake(self)
cmake.configure(defs={'CMAKE_VERBOSE_MAKEFILE': 'ON'})
cmake.build()
def test(self):
self.run(os.path.join('.', 'bin', 'example'))
@@ -0,0 +1,46 @@
#include <backward/backward.hpp>
#include <iostream>
#include <sstream>
#include <stdexcept>
using namespace backward;
class TracedException : public std::runtime_error {
public:
TracedException() : std::runtime_error(_get_trace()) {}
private:
std::string _get_trace() {
std::ostringstream ss;
StackTrace stackTrace;
TraceResolver resolver;
stackTrace.load_here();
resolver.load_stacktrace(stackTrace);
for (std::size_t i = 0; i < stackTrace.size(); ++i) {
const ResolvedTrace trace = resolver.resolve(stackTrace[i]);
ss << "#" << i << " at " << trace.object_function << "\n";
}
return ss.str();
}
};
void f(int i) {
if (i >= 42) {
throw TracedException();
} else {
std::cout << "i=" << i << "\n";
f(i + 1);
}
}
int main() {
try {
f(0);
} catch (const TracedException &ex) {
std::cout << ex.what();
}
}
@@ -27,6 +27,8 @@
#include <pthread.h> #include <pthread.h>
#include <std_srvs/srv/empty.hpp> #include <std_srvs/srv/empty.hpp>
#include <backward-cpp/backward.hpp>
namespace orbbec_camera { namespace orbbec_camera {
class OBCameraNodeDriver : public rclcpp::Node { class OBCameraNodeDriver : public rclcpp::Node {
@@ -69,7 +71,7 @@ class OBCameraNodeDriver : public rclcpp::Node {
std::shared_ptr<std_srvs::srv::Empty::Response> response); std::shared_ptr<std_srvs::srv::Empty::Response> response);
private: private:
const rclcpp::NodeOptions node_options_; const rclcpp::NodeOptions node_options_;
std::string config_path_; std::string config_path_;
std::unique_ptr<ob::Context> ctx_ = nullptr; std::unique_ptr<ob::Context> ctx_ = nullptr;
rclcpp::Logger logger_; rclcpp::Logger logger_;
@@ -104,5 +106,6 @@ class OBCameraNodeDriver : public rclcpp::Node {
bool enable_sync_host_time_ = true; bool enable_sync_host_time_ = true;
rclcpp::Service<std_srvs::srv::Empty>::SharedPtr reboot_device_srv_ = nullptr; rclcpp::Service<std_srvs::srv::Empty>::SharedPtr reboot_device_srv_ = nullptr;
std::chrono::time_point<std::chrono::system_clock> start_time_; std::chrono::time_point<std::chrono::system_clock> start_time_;
static backward::SignalHandling sh; // for stack trace
}; };
} // namespace orbbec_camera } // namespace orbbec_camera
@@ -25,6 +25,8 @@
#include <unistd.h> #include <unistd.h>
namespace orbbec_camera { namespace orbbec_camera {
backward::SignalHandling OBCameraNodeDriver::sh;
OBCameraNodeDriver::OBCameraNodeDriver(const rclcpp::NodeOptions &node_options) OBCameraNodeDriver::OBCameraNodeDriver(const rclcpp::NodeOptions &node_options)
: Node("orbbec_camera_node", "/", node_options), : Node("orbbec_camera_node", "/", node_options),
node_options_(node_options), node_options_(node_options),