mirror of
https://github.com/orbbec/OrbbecSDK_ROS2.git
synced 2026-10-12 07:19:50 +08:00
chore: add backward-cpp for crash trace
This commit is contained in:
@@ -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 [](https://conan.io/center/recipes/backward-cpp)
|
||||||
|
============
|
||||||
|
|
||||||
|
Backward is a beautiful stack trace pretty printer for C++.
|
||||||
|
|
||||||
|
If you are bored to see this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Backward will spice it up for you:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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
Executable
+77
@@ -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),
|
||||||
|
|||||||
Reference in New Issue
Block a user