├── .gitignore ├── .clang-format ├── doc ├── nice.png ├── rude.png └── pretty.png ├── test_package ├── CMakeLists.txt ├── conanfile.py └── main.cpp ├── .travis.yml ├── LICENSE.txt ├── backward.cpp ├── conanfile.py ├── test ├── stacktrace.cpp ├── select_signals.cpp ├── test.cpp ├── rectrace.cpp ├── suicide.cpp ├── _test_main.cpp └── test.hpp ├── CMakeLists.txt ├── BackwardConfig.cmake └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | *.pyc 3 | *.sw? 4 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | -------------------------------------------------------------------------------- /doc/nice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bombela/backward-cpp/HEAD/doc/nice.png -------------------------------------------------------------------------------- /doc/rude.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bombela/backward-cpp/HEAD/doc/rude.png -------------------------------------------------------------------------------- /doc/pretty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bombela/backward-cpp/HEAD/doc/pretty.png -------------------------------------------------------------------------------- /test_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(backward-package-test) 2 | cmake_minimum_required(VERSION 2.8.12) 3 | 4 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 5 | conan_basic_setup(TARGETS) 6 | 7 | add_executable(example main.cpp) 8 | target_link_libraries(example PRIVATE ${CONAN_TARGETS}) 9 | -------------------------------------------------------------------------------- /test_package/conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake 2 | import os 3 | 4 | class TestBackward(ConanFile): 5 | settings = 'os', 'compiler', 'build_type', 'arch' 6 | generators = 'cmake' 7 | 8 | def build(self): 9 | cmake = CMake(self) 10 | cmake.configure(defs={'CMAKE_VERBOSE_MAKEFILE': 'ON'}) 11 | cmake.build() 12 | 13 | def test(self): 14 | self.run(os.path.join('.', 'bin', 'example')) 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | - clang 5 | 6 | addons: 7 | apt: 8 | packages: 9 | - valgrind 10 | 11 | install: 12 | - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" 13 | - mkdir ${DEPS_DIR} && cd ${DEPS_DIR} 14 | - CMAKE_URL="http://www.cmake.org/files/v3.3/cmake-3.3.2-Linux-x86_64.tar.gz" 15 | - mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake 16 | - export PATH=${DEPS_DIR}/cmake/bin:${PATH} 17 | - pip install --user conan && export PATH=$PATH:$HOME/.local/bin 18 | - cd ${TRAVIS_BUILD_DIR} 19 | - mkdir build && cd build 20 | - cmake .. -DBACKWARD_TESTS=ON 21 | - cmake --build . 22 | 23 | script: 24 | - valgrind ctest .. --verbose 25 | - cd ${TRAVIS_BUILD_DIR} && conan create . Manu343726/testing --build=outdated 26 | -------------------------------------------------------------------------------- /test_package/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace backward; 7 | 8 | class TracedException : public std::runtime_error { 9 | public: 10 | TracedException() : std::runtime_error(_get_trace()) {} 11 | 12 | private: 13 | std::string _get_trace() { 14 | std::ostringstream ss; 15 | 16 | StackTrace stackTrace; 17 | TraceResolver resolver; 18 | stackTrace.load_here(); 19 | resolver.load_stacktrace(stackTrace); 20 | 21 | for (std::size_t i = 0; i < stackTrace.size(); ++i) { 22 | const ResolvedTrace trace = resolver.resolve(stackTrace[i]); 23 | 24 | ss << "#" << i << " at " << trace.object_function << "\n"; 25 | } 26 | 27 | return ss.str(); 28 | } 29 | }; 30 | 31 | void f(int i) { 32 | if (i >= 42) { 33 | throw TracedException(); 34 | } else { 35 | std::cout << "i=" << i << "\n"; 36 | f(i + 1); 37 | } 38 | } 39 | 40 | int main() { 41 | try { 42 | f(0); 43 | } catch (const TracedException &ex) { 44 | std::cout << ex.what(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | The MIT License (MIT) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /backward.cpp: -------------------------------------------------------------------------------- 1 | // Pick your poison. 2 | // 3 | // On GNU/Linux, you have few choices to get the most out of your stack trace. 4 | // 5 | // By default you get: 6 | // - object filename 7 | // - function name 8 | // 9 | // In order to add: 10 | // - source filename 11 | // - line and column numbers 12 | // - source code snippet (assuming the file is accessible) 13 | 14 | // Install one of the following libraries then uncomment one of the macro (or 15 | // better, add the detection of the lib and the macro definition in your build 16 | // system) 17 | 18 | // - apt-get install libdw-dev ... 19 | // - g++/clang++ -ldw ... 20 | // #define BACKWARD_HAS_DW 1 21 | 22 | // - apt-get install binutils-dev ... 23 | // - g++/clang++ -lbfd ... 24 | // #define BACKWARD_HAS_BFD 1 25 | 26 | // - apt-get install libdwarf-dev ... 27 | // - g++/clang++ -ldwarf ... 28 | // #define BACKWARD_HAS_DWARF 1 29 | 30 | // Regardless of the library you choose to read the debug information, 31 | // for potentially more detailed stack traces you can use libunwind 32 | // - apt-get install libunwind-dev 33 | // - g++/clang++ -lunwind 34 | // #define BACKWARD_HAS_LIBUNWIND 1 35 | 36 | #include "backward.hpp" 37 | 38 | namespace backward { 39 | 40 | backward::SignalHandling sh; 41 | 42 | } // namespace backward 43 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake 2 | import os 3 | 4 | class BackwardCpp(ConanFile): 5 | settings = 'os', 'compiler', 'build_type', 'arch' 6 | name = 'backward' 7 | url = 'https://github.com/bombela/backward-cpp' 8 | license = 'MIT' 9 | version = '1.3.0' 10 | options = { 11 | 'stack_walking_unwind': [True, False], 12 | 'stack_walking_backtrace': [True, False], 13 | 'stack_details_auto_detect': [True, False], 14 | 'stack_details_backtrace_symbol': [True, False], 15 | 'stack_details_dw': [True, False], 16 | 'stack_details_bfd': [True, False], 17 | 'shared': [True, False] 18 | } 19 | default_options = ( 20 | 'stack_walking_unwind=True', 21 | 'stack_walking_backtrace=False', 22 | 'stack_details_auto_detect=True', 23 | 'stack_details_backtrace_symbol=False', 24 | 'stack_details_dw=False', 25 | 'stack_details_bfd=False', 26 | 'shared=False' 27 | ) 28 | exports = 'backward.cpp', 'backward.hpp', 'test/*', 'CMakeLists.txt', 'BackwardConfig.cmake' 29 | generators = 'cmake' 30 | 31 | def build(self): 32 | cmake = CMake(self) 33 | 34 | cmake.configure(defs={'BACKWARD_' + name.upper(): value for name, value in self.options.values.as_list()}) 35 | cmake.build() 36 | 37 | def package(self): 38 | self.copy('backward.hpp', os.path.join('include', 'backward')) 39 | self.copy('*.a', dst='lib') 40 | self.copy('*.so', dst='lib') 41 | self.copy('*.lib', dst='lib') 42 | self.copy('*.dll', dst='lib') 43 | 44 | def package_info(self): 45 | self.cpp_info.libs = ['backward'] 46 | -------------------------------------------------------------------------------- /test/stacktrace.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test/stacktrace.cpp 3 | * Copyright 2013 Google Inc. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | #include "backward.hpp" 25 | #include "test/test.hpp" 26 | #include 27 | #include 28 | 29 | using namespace backward; 30 | 31 | void collect_trace(StackTrace &st) { st.load_here(); } 32 | 33 | TEST(minitrace) { 34 | Printer printer; 35 | 36 | StackTrace st; 37 | collect_trace(st); 38 | 39 | printer.print(st, std::cout); 40 | } 41 | 42 | void d(StackTrace &st) { st.load_here(); } 43 | 44 | void c(StackTrace &st) { return d(st); } 45 | 46 | void b(StackTrace &st) { return c(st); } 47 | 48 | NOINLINE void a(StackTrace &st) { return b(st); } 49 | 50 | TEST(smalltrace) { 51 | Printer printer; 52 | 53 | StackTrace st; 54 | a(st); 55 | 56 | printer.print(st, std::cout); 57 | } 58 | -------------------------------------------------------------------------------- /test/select_signals.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test/segfault.cpp 3 | * Copyright 2013 Google Inc. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | #include "backward.hpp" 25 | 26 | #include "test/test.hpp" 27 | #include 28 | #include 29 | 30 | using namespace backward; 31 | 32 | void badass_function() { 33 | char *ptr = (char *)42; 34 | *ptr = 42; 35 | } 36 | 37 | TEST_SEGFAULT(pprint_sigsev) { 38 | std::vector signals; 39 | signals.push_back(SIGSEGV); 40 | SignalHandling sh(signals); 41 | std::cout << std::boolalpha << "sh.loaded() == " << sh.loaded() << std::endl; 42 | badass_function(); 43 | } 44 | 45 | TEST_SEGFAULT(wont_pprint) { 46 | std::vector signals; 47 | signals.push_back(SIGABRT); 48 | SignalHandling sh(signals); 49 | std::cout << std::boolalpha << "sh.loaded() == " << sh.loaded() << std::endl; 50 | badass_function(); 51 | } 52 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test/test.cpp 3 | * Copyright 2013 Google Inc. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | #include "test/test.hpp" 25 | #include 26 | #include 27 | #include 28 | 29 | TEST(empty_test) {} 30 | 31 | TEST_FAIL_ASSERT(fail_assert) { ASSERT(1 == 2); } 32 | 33 | TEST_FAIL_ASSERT(fail_assert_ge) { ASSERT_GE(4, 5); } 34 | 35 | TEST_UNCAUGHT_EXCEPTION(uncaught_exception) { 36 | throw std::runtime_error("some random runtime error"); 37 | } 38 | 39 | TEST_UNCAUGHT_EXCEPTION(uncaught_exception_int) { throw 42; } 40 | 41 | TEST_SEGFAULT(segfault) { 42 | char *a = 0; 43 | char b = a[42]; 44 | std::cout << "result: " << b << std::endl; 45 | } 46 | 47 | TEST_ABORT(abort) { abort(); } 48 | 49 | TEST(catch_int) { 50 | ASSERT_THROW({ throw 42; }, int); 51 | } 52 | 53 | TEST_FAIL_ASSERT(fail_catch_int) { ASSERT_THROW({}, int); } 54 | 55 | TEST_FAIL_ASSERT(fail_no_throw) { 56 | ASSERT_NO_THROW({ throw 42; }); 57 | } 58 | 59 | TEST(any_throw) { 60 | ASSERT_ANY_THROW({ throw 42; }); 61 | } 62 | 63 | TEST_FAIL_ASSERT(fail_any_throw) { ASSERT_ANY_THROW({}); } 64 | -------------------------------------------------------------------------------- /test/rectrace.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test/rectrace.cpp 3 | * Copyright 2013 Google Inc. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | #include "backward.hpp" 25 | #include "test/test.hpp" 26 | #include 27 | 28 | using namespace backward; 29 | 30 | typedef StackTrace stacktrace_t; 31 | 32 | void end_of_our_journey(stacktrace_t &st) { 33 | if (!st.size()) { 34 | st.load_here(); 35 | } 36 | } 37 | 38 | int rec(stacktrace_t &st, int level) { 39 | if (level <= 1) { 40 | end_of_our_journey(st); 41 | return 0; 42 | } 43 | return rec(st, level - 1); 44 | } 45 | 46 | namespace toto { 47 | 48 | namespace titi { 49 | 50 | struct foo { 51 | 52 | union bar { 53 | NOINLINE static int trampoline(stacktrace_t &st, int level) { 54 | return rec(st, level); 55 | } 56 | }; 57 | }; 58 | 59 | } // namespace titi 60 | 61 | } // namespace toto 62 | 63 | TEST(recursion) { 64 | { // lexical scope. 65 | stacktrace_t st; 66 | const int input = 3; 67 | int r = toto::titi::foo::bar::trampoline(st, input); 68 | 69 | std::cout << "rec(" << input << ") == " << r << std::endl; 70 | 71 | Printer printer; 72 | // printer.address = true; 73 | printer.object = true; 74 | printer.print(st, stdout); 75 | } 76 | } 77 | 78 | int fib(StackTrace &st, int level) { 79 | if (level == 2) { 80 | return 1; 81 | } 82 | if (level <= 1) { 83 | end_of_our_journey(st); 84 | return 0; 85 | } 86 | return fib(st, level - 1) + fib(st, level - 2); 87 | } 88 | 89 | TEST(fibrecursive) { 90 | StackTrace st; 91 | const int input = 6; 92 | int r = fib(st, input); 93 | 94 | std::cout << "fib(" << input << ") == " << r << std::endl; 95 | 96 | Printer printer; 97 | printer.print(st, stdout); 98 | } 99 | -------------------------------------------------------------------------------- /test/suicide.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test/suicide.cpp 3 | * Copyright 2013 Google Inc. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | #include "backward.hpp" 25 | 26 | #include "test/test.hpp" 27 | #include 28 | 29 | #ifndef _WIN32 30 | #include 31 | #endif 32 | 33 | using namespace backward; 34 | 35 | void badass_function() { 36 | char *ptr = (char *)42; 37 | *ptr = 42; 38 | } 39 | 40 | TEST_SEGFAULT(invalid_write) { badass_function(); } 41 | 42 | int you_shall_not_pass() { 43 | char *ptr = (char *)42; 44 | int v = *ptr; 45 | return v; 46 | } 47 | 48 | TEST_SEGFAULT(invalid_read) { 49 | int v = you_shall_not_pass(); 50 | std::cout << "v=" << v << std::endl; 51 | } 52 | 53 | void abort_abort_I_repeat_abort_abort() { 54 | std::cout << "Jumping off the boat!" << std::endl; 55 | abort(); 56 | } 57 | 58 | TEST_ABORT(calling_abort) { abort_abort_I_repeat_abort_abort(); } 59 | 60 | // aarch64, mips, PowerPC and RISC-V do not trap Division by zero 61 | #if !defined(__aarch64__) && !defined(__mips__) && !defined (__powerpc__) && !defined (__riscv) 62 | volatile int zero = 0; 63 | 64 | int divide_by_zero() { 65 | std::cout << "And the wild black hole appears..." << std::endl; 66 | int v = 42 / zero; 67 | return v; 68 | } 69 | 70 | TEST_DIVZERO(divide_by_zero) { 71 | int v = divide_by_zero(); 72 | std::cout << "v=" << v << std::endl; 73 | } 74 | #endif 75 | 76 | // Darwin does not allow RLIMIT_STACK to be reduced 77 | #ifndef __APPLE__ 78 | int bye_bye_stack(int i) { return bye_bye_stack(i + 1) + bye_bye_stack(i * 2); } 79 | 80 | TEST_SEGFAULT(stackoverflow) { 81 | #ifndef _WIN32 82 | struct rlimit limit; 83 | limit.rlim_max = 8096; 84 | setrlimit(RLIMIT_STACK, &limit); 85 | #endif 86 | int r = bye_bye_stack(42); 87 | std::cout << "r=" << r << std::endl; 88 | } 89 | #endif 90 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # CMakeLists.txt 3 | # Copyright 2013 Google Inc. All Rights Reserved. 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | cmake_minimum_required(VERSION 3.14) 24 | project(backward CXX) 25 | 26 | # Introduce variables: 27 | # * CMAKE_INSTALL_LIBDIR 28 | # * CMAKE_INSTALL_BINDIR 29 | # * CMAKE_INSTALL_INCLUDEDIR 30 | include(GNUInstallDirs) 31 | 32 | include(BackwardConfig.cmake) 33 | 34 | ############################################################################### 35 | # OPTIONS 36 | ############################################################################### 37 | 38 | option(BACKWARD_SHARED "Build backward as a shared library" OFF) 39 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND NOT DEFINED BACKWARD_TESTS) 40 | # If this is a top level CMake project, we most lixely want the tests 41 | set(BACKWARD_TESTS ON CACHE BOOL "Enable tests") 42 | else() 43 | set(BACKWARD_TESTS OFF CACHE BOOL "Enable tests") 44 | endif() 45 | 46 | ############################################################################### 47 | # COMPILER FLAGS 48 | ############################################################################### 49 | 50 | # check if compiler is nvcc or nvcc_wrapper 51 | set(COMPILER_IS_NVCC false) 52 | get_filename_component(COMPILER_NAME ${CMAKE_CXX_COMPILER} NAME) 53 | if (COMPILER_NAME MATCHES "^nvcc") 54 | set(COMPILER_IS_NVCC true) 55 | endif() 56 | 57 | if (DEFINED ENV{OMPI_CXX} OR DEFINED ENV{MPICH_CXX}) 58 | if ( ($ENV{OMPI_CXX} MATCHES "nvcc") OR ($ENV{MPICH_CXX} MATCHES "nvcc") ) 59 | set(COMPILER_IS_NVCC true) 60 | endif() 61 | endif() 62 | 63 | # set CXX standard 64 | set(CMAKE_CXX_STANDARD_REQUIRED True) 65 | set(CMAKE_CXX_STANDARD 11) 66 | if (${COMPILER_IS_NVCC}) 67 | # GNU CXX extensions are not supported by nvcc 68 | set(CMAKE_CXX_EXTENSIONS OFF) 69 | endif() 70 | 71 | if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCXX) 72 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") 73 | if (NOT ${COMPILER_IS_NVCC}) 74 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic-errors") 75 | endif() 76 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") 77 | endif() 78 | 79 | ############################################################################### 80 | # BACKWARD INTERFACE 81 | ############################################################################### 82 | 83 | add_library(backward_interface INTERFACE) 84 | set_target_properties(backward_interface PROPERTIES EXPORT_NAME Interface) 85 | target_compile_definitions(backward_interface INTERFACE ${BACKWARD_DEFINITIONS}) 86 | target_include_directories(backward_interface INTERFACE ${BACKWARD_INCLUDE_DIRS}) 87 | if(BACKWARD_HAS_EXTERNAL_LIBRARIES) 88 | target_link_libraries(backward_interface INTERFACE ${BACKWARD_LIBRARIES}) 89 | endif() 90 | add_library(Backward::Interface ALIAS backward_interface) 91 | 92 | ############################################################################### 93 | # BACKWARD OBJECT (Includes backward.cpp) 94 | # (Note that this target is not exported, since CMake currently does not allow 95 | # exporting an OBJECT library.) 96 | ############################################################################### 97 | 98 | add_library(backward_object OBJECT backward.cpp) 99 | set_target_properties(backward_object PROPERTIES EXPORT_NAME Object) 100 | target_link_libraries(backward_object PUBLIC Backward::Interface) 101 | add_library(Backward::Object ALIAS backward_object) 102 | 103 | ############################################################################### 104 | # BACKWARD LIBRARY (Includes backward.cpp) 105 | # (Note that the linker will not include unused objects from a static library, 106 | # unless the -Wl,--whole-archive option (or similar) is used.) 107 | ############################################################################### 108 | 109 | set(libtype STATIC) 110 | if(BACKWARD_SHARED) 111 | set(libtype SHARED) 112 | endif() 113 | add_library(backward ${libtype} backward.cpp) 114 | set_target_properties(backward PROPERTIES EXPORT_NAME Backward) 115 | target_link_libraries(backward PUBLIC Backward::Interface) 116 | add_library(Backward::Backward ALIAS backward) 117 | 118 | ############################################################################### 119 | # TESTS 120 | ############################################################################### 121 | 122 | if(BACKWARD_TESTS) 123 | enable_testing() 124 | 125 | add_library(test_main OBJECT test/_test_main.cpp) 126 | 127 | macro(backward_add_test src) 128 | get_filename_component(name ${src} NAME_WE) 129 | set(test_name "test_${name}") 130 | 131 | add_executable(${test_name} ${src} ${ARGN} $) 132 | 133 | target_link_libraries(${test_name} PRIVATE Backward::Interface) 134 | 135 | add_test(NAME ${name} COMMAND ${test_name}) 136 | endmacro() 137 | 138 | # Tests without backward.cpp 139 | set(TESTS 140 | test 141 | stacktrace 142 | rectrace 143 | select_signals 144 | ) 145 | 146 | foreach(test ${TESTS}) 147 | backward_add_test(test/${test}.cpp) 148 | endforeach() 149 | 150 | # Tests with backward.cpp 151 | set(TESTS 152 | suicide 153 | ) 154 | 155 | foreach(test ${TESTS}) 156 | backward_add_test(test/${test}.cpp backward.cpp) 157 | endforeach() 158 | endif() 159 | 160 | install( 161 | FILES "backward.hpp" 162 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 163 | ) 164 | install( 165 | FILES "BackwardConfig.cmake" 166 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 167 | ) 168 | # check if Backward is being used as a top-level project or included as a subproject 169 | if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) 170 | # export the targets (note that exporting backward_object does not make sense) 171 | install(TARGETS backward_interface backward EXPORT BackwardTargets) 172 | # install a CMake file for the exported targets 173 | install(EXPORT BackwardTargets 174 | NAMESPACE Backward:: 175 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") 176 | endif() 177 | -------------------------------------------------------------------------------- /test/_test_main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * _test_main.cpp 3 | * Copyright 2013 Google Inc. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | #include "test.hpp" 25 | #include 26 | #include 27 | 28 | #ifdef _WIN32 29 | #include 30 | #define strcasecmp _stricmp 31 | #else 32 | #include 33 | #include 34 | #endif 35 | 36 | #if defined(__has_include) && __has_include() 37 | #include 38 | #else 39 | #include 40 | 41 | #ifdef _WIN32 42 | char argv0[MAX_PATH]; 43 | inline const char *getprogname() { 44 | return GetModuleFileName(NULL, argv0, sizeof(argv0)) ? argv0 : NULL; 45 | } 46 | #elif !defined(__APPLE__) 47 | // N.B. getprogname() is an Apple/BSD-ism. 48 | // program_invocation_name is a GLIBC-ism, but it's also 49 | // supported by libmusl. 50 | #define getprogname() program_invocation_name 51 | #endif 52 | 53 | void error(int status, int errnum, const char *format, ...) { 54 | fflush(stdout); 55 | fprintf(stderr, "%s: ", getprogname()); 56 | 57 | va_list args; 58 | va_start(args, format); 59 | vfprintf(stderr, format, args); 60 | va_end(args); 61 | 62 | if (errnum != 0) { 63 | fprintf(stderr, ": %s\n", strerror(errnum)); 64 | } else { 65 | fprintf(stderr, "\n"); 66 | } 67 | if (status != 0) { 68 | exit(status); 69 | } 70 | } 71 | #endif 72 | 73 | using namespace test; 74 | 75 | bool run_test(TestBase &test, bool use_child_process = true) { 76 | if (!use_child_process) { 77 | exit(static_cast(test.run())); 78 | } 79 | 80 | printf("-- running test case: %s\n", test.name); 81 | 82 | fflush(stdout); 83 | 84 | test::TestStatus status = test::SUCCESS; 85 | 86 | #ifdef _WIN32 87 | char filename[256]; 88 | GetModuleFileName(NULL, filename, 256); // TODO: check for error 89 | std::string cmd_line = filename; 90 | cmd_line += " --nofork "; 91 | cmd_line += test.name; 92 | 93 | STARTUPINFO si; 94 | PROCESS_INFORMATION pi; 95 | ZeroMemory(&si, sizeof(si)); 96 | si.cb = sizeof(si); 97 | ZeroMemory(&pi, sizeof(pi)); 98 | 99 | if (!CreateProcessA(nullptr, const_cast(cmd_line.c_str()), nullptr, 100 | nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { 101 | printf("unable to create process\n"); 102 | exit(-1); 103 | } 104 | 105 | WaitForSingleObject(pi.hProcess, INFINITE); 106 | 107 | DWORD exit_code; 108 | GetExitCodeProcess(pi.hProcess, &exit_code); 109 | switch (exit_code) { 110 | case 3: 111 | status = test::SIGNAL_ABORT; 112 | break; 113 | case 5: 114 | status = test::EXCEPTION_UNCAUGHT; 115 | break; 116 | case EXCEPTION_ACCESS_VIOLATION: 117 | status = test::SIGNAL_SEGFAULT; 118 | break; 119 | case EXCEPTION_STACK_OVERFLOW: 120 | status = test::SIGNAL_SEGFAULT; 121 | break; 122 | case EXCEPTION_INT_DIVIDE_BY_ZERO: 123 | status = test::SIGNAL_DIVZERO; 124 | break; 125 | } 126 | printf("Exit code: %lu\n", exit_code); 127 | 128 | CloseHandle(pi.hProcess); 129 | CloseHandle(pi.hThread); 130 | 131 | if (test.expected_status == test::ASSERT_FAIL) { 132 | // assert calls abort on windows 133 | return (status & test::SIGNAL_ABORT); 134 | } 135 | 136 | #else 137 | 138 | pid_t child_pid = fork(); 139 | if (child_pid == 0) { 140 | exit(static_cast(test.run())); 141 | } 142 | if (child_pid == -1) { 143 | error(EXIT_FAILURE, 0, "unable to fork"); 144 | } 145 | 146 | int child_status = 0; 147 | waitpid(child_pid, &child_status, 0); 148 | 149 | if (WIFEXITED(child_status)) { 150 | int exit_status = WEXITSTATUS(child_status); 151 | if (exit_status & ~test::STATUS_MASK) { 152 | status = test::FAILED; 153 | } else { 154 | status = static_cast(exit_status); 155 | } 156 | } else if (WIFSIGNALED(child_status)) { 157 | const int signum = WTERMSIG(child_status); 158 | printf("!! signal (%d) %s\n", signum, strsignal(signum)); 159 | switch (signum) { 160 | case SIGABRT: 161 | status = test::SIGNAL_ABORT; 162 | break; 163 | case SIGSEGV: 164 | case SIGBUS: 165 | status = test::SIGNAL_SEGFAULT; 166 | break; 167 | case SIGFPE: 168 | status = test::SIGNAL_DIVZERO; 169 | break; 170 | default: 171 | status = test::SIGNAL_UNCAUGHT; 172 | } 173 | } 174 | 175 | #endif 176 | 177 | if (test.expected_status == test::FAILED) { 178 | return (status & test::FAILED); 179 | } 180 | 181 | if (test.expected_status == test::SIGNAL_UNCAUGHT) { 182 | return (status & test::SIGNAL_UNCAUGHT); 183 | } 184 | 185 | return status == test.expected_status; 186 | } 187 | 188 | int main(int argc, const char *const argv[]) { 189 | 190 | #ifdef _WIN32 191 | _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); 192 | #endif 193 | 194 | if (argc == 3 && strcmp("--nofork", argv[1]) == 0) { 195 | // Windows has no fork, so we simulate it 196 | // we only execute one test, without forking 197 | for (test_registry_t::iterator it = test_registry().begin(); 198 | it != test_registry().end(); ++it) { 199 | TestBase &test = **it; 200 | if (strcasecmp(argv[2], test.name) == 0) { 201 | run_test(test, false); 202 | 203 | return 0; 204 | } 205 | } 206 | return -1; 207 | } 208 | 209 | size_t success_cnt = 0; 210 | size_t total_cnt = 0; 211 | for (test_registry_t::iterator it = test_registry().begin(); 212 | it != test_registry().end(); ++it) { 213 | TestBase &test = **it; 214 | 215 | bool consider_test = (argc <= 1); 216 | for (int i = 1; i < argc; ++i) { 217 | if (strcasecmp(argv[i], test.name) == 0) { 218 | consider_test = true; 219 | break; 220 | } 221 | } 222 | if (!consider_test) { 223 | continue; 224 | } 225 | 226 | total_cnt += 1; 227 | if (run_test(test)) { 228 | printf("-- test case success: %s\n", test.name); 229 | success_cnt += 1; 230 | } else { 231 | printf("** test case FAILED : %s\n", test.name); 232 | } 233 | } 234 | printf("-- tests passing: %zu/%zu", success_cnt, total_cnt); 235 | if (total_cnt) { 236 | printf(" (%zu%%)\n", success_cnt * 100 / total_cnt); 237 | } else { 238 | printf("\n"); 239 | } 240 | return (success_cnt == total_cnt) ? EXIT_SUCCESS : EXIT_FAILURE; 241 | } 242 | -------------------------------------------------------------------------------- /test/test.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test/test.hpp 3 | * Copyright 2013 Google Inc. All Rights Reserved. 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | #pragma once 25 | #ifndef H_54E531F7_9154_454B_BEB9_257408429470 26 | #define H_54E531F7_9154_454B_BEB9_257408429470 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | namespace test { 36 | 37 | struct AssertFailedError : std::exception { 38 | ~AssertFailedError() throw() {} 39 | 40 | AssertFailedError(const char *filename, int _line, const char *_errmsg) 41 | : basename(_basename(filename)), line(_line), errmsg(_errmsg) {} 42 | 43 | const char *what() const throw() { 44 | if (!_what.size()) { 45 | std::ostringstream ss; 46 | ss << "assertion failed (" << basename << ":" << line; 47 | ss << ") " << errmsg; 48 | _what = ss.str(); 49 | } 50 | return _what.c_str(); 51 | } 52 | 53 | const char *basename; 54 | int line; 55 | const char *errmsg; 56 | 57 | mutable std::string _what; 58 | 59 | static const char *_basename(const char *filename) { 60 | const char *basename = filename + strlen(filename); 61 | while (basename != filename && *basename != '/') { 62 | basename -= 1; 63 | } 64 | return basename + 1; 65 | } 66 | }; 67 | 68 | enum TestStatus { 69 | SUCCESS = 0 << 0, 70 | FAILED = 1 << 0, 71 | 72 | ASSERT_FAIL = FAILED | 1 << 1, 73 | EXCEPTION_UNCAUGHT = FAILED | 2 << 1, 74 | SIGNAL_UNCAUGHT = FAILED | 3 << 1, 75 | SIGNAL_SEGFAULT = SIGNAL_UNCAUGHT | 1 << 3, 76 | SIGNAL_ABORT = SIGNAL_UNCAUGHT | 2 << 3, 77 | SIGNAL_DIVZERO = SIGNAL_UNCAUGHT | 2 << 3, 78 | 79 | STATUS_MASK = 0x1F 80 | }; 81 | 82 | struct TestBase { 83 | const char *name; 84 | TestStatus expected_status; 85 | 86 | virtual ~TestBase() {} 87 | TestBase(const char *, TestStatus); 88 | virtual void do_test() = 0; 89 | 90 | TestStatus run() { 91 | try { 92 | do_test(); 93 | return SUCCESS; 94 | } catch (const AssertFailedError &e) { 95 | printf("!! %s\n", e.what()); 96 | return ASSERT_FAIL; 97 | } catch (const std::exception &e) { 98 | printf("!! exception: %s\n", e.what()); 99 | return EXCEPTION_UNCAUGHT; 100 | } catch (...) { 101 | printf("!! unknown exception\n"); 102 | return EXCEPTION_UNCAUGHT; 103 | } 104 | } 105 | }; 106 | 107 | typedef std::vector test_registry_t; 108 | inline test_registry_t &test_registry() { 109 | static test_registry_t reg; 110 | return reg; 111 | } 112 | 113 | inline TestBase::TestBase(const char *n, TestStatus s) 114 | : name(n), expected_status(s) { 115 | test_registry().push_back(this); 116 | } 117 | 118 | } // namespace test 119 | 120 | #define _TEST_STATUS(name, status) \ 121 | struct TEST_##name : ::test::TestBase { \ 122 | TEST_##name() : TestBase(#name, status) {} \ 123 | void do_test(); \ 124 | } TEST_##name; \ 125 | void TEST_##name::do_test() 126 | 127 | #define TEST(name) _TEST_STATUS(name, ::test::SUCCESS) 128 | #define TEST_FAIL(name) _TEST_STATUS(name, ::test::FAILED) 129 | #define TEST_FAIL_ASSERT(name) _TEST_STATUS(name, ::test::ASSERT_FAIL) 130 | #define TEST_UNCAUGHT_EXCEPTION(name) \ 131 | _TEST_STATUS(name, ::test::EXCEPTION_UNCAUGHT) 132 | #define TEST_UNCAUGHT_SIGNAL(name) _TEST_STATUS(name, ::test::SIGNAL_UNCAUGHT) 133 | #define TEST_SEGFAULT(name) _TEST_STATUS(name, ::test::SIGNAL_SEGFAULT) 134 | #define TEST_ABORT(name) _TEST_STATUS(name, ::test::SIGNAL_ABORT) 135 | #define TEST_DIVZERO(name) _TEST_STATUS(name, ::test::SIGNAL_DIVZERO) 136 | 137 | #define ASSERT(expr) \ 138 | (expr) ? static_cast(0) \ 139 | : throw ::test::AssertFailedError(__FILE__, __LINE__, #expr) 140 | 141 | #define _ASSERT_BINOP(a, b, cmp) \ 142 | (!(a cmp b)) ? static_cast(0) \ 143 | : throw ::test::AssertFailedError( \ 144 | __FILE__, __LINE__, "because " #a " " #cmp " " #b) 145 | 146 | #define ASSERT_EQ(a, b) _ASSERT_BINOP(a, b, !=) 147 | #define ASSERT_NE(a, b) _ASSERT_BINOP(a, b, ==) 148 | #define ASSERT_LT(a, b) _ASSERT_BINOP(a, b, >=) 149 | #define ASSERT_LE(a, b) _ASSERT_BINOP(a, b, >) 150 | #define ASSERT_GT(a, b) _ASSERT_BINOP(a, b, <=) 151 | #define ASSERT_GE(a, b) _ASSERT_BINOP(a, b, <) 152 | 153 | #define ASSERT_THROW(expr, e_type) \ 154 | do { \ 155 | try { \ 156 | expr \ 157 | } catch (const e_type &) { \ 158 | break; \ 159 | } \ 160 | throw ::test::AssertFailedError(__FILE__, __LINE__, \ 161 | "expected exception " #e_type); \ 162 | } while (0) 163 | 164 | #define ASSERT_ANY_THROW(expr) \ 165 | do { \ 166 | try { \ 167 | expr \ 168 | } catch (...) { \ 169 | break; \ 170 | } \ 171 | throw ::test::AssertFailedError(__FILE__, __LINE__, \ 172 | "expected any exception"); \ 173 | } while (0) 174 | 175 | #define ASSERT_NO_THROW(expr) \ 176 | try { \ 177 | expr \ 178 | } catch (...) { \ 179 | throw ::test::AssertFailedError(__FILE__, __LINE__, \ 180 | "no exception expected"); \ 181 | } 182 | 183 | #endif /* H_GUARD */ 184 | -------------------------------------------------------------------------------- /BackwardConfig.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # BackwardMacros.cmake 3 | # Copyright 2013 Google Inc. All Rights Reserved. 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | ############################################################################### 24 | # OPTIONS 25 | ############################################################################### 26 | 27 | set(STACK_WALKING_UNWIND TRUE CACHE BOOL 28 | "Use compiler's unwind API") 29 | set(STACK_WALKING_BACKTRACE FALSE CACHE BOOL 30 | "Use backtrace from (e)glibc for stack walking") 31 | set(STACK_WALKING_LIBUNWIND FALSE CACHE BOOL 32 | "Use libunwind for stack walking") 33 | 34 | set(STACK_DETAILS_AUTO_DETECT TRUE CACHE BOOL 35 | "Auto detect backward's stack details dependencies") 36 | 37 | set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE CACHE BOOL 38 | "Use backtrace from (e)glibc for symbols resolution") 39 | set(STACK_DETAILS_DW FALSE CACHE BOOL 40 | "Use libdw to read debug info") 41 | set(STACK_DETAILS_BFD FALSE CACHE BOOL 42 | "Use libbfd to read debug info") 43 | set(STACK_DETAILS_DWARF FALSE CACHE BOOL 44 | "Use libdwarf/libelf to read debug info") 45 | 46 | ############################################################################### 47 | # CONFIGS 48 | ############################################################################### 49 | include(FindPackageHandleStandardArgs) 50 | 51 | if (STACK_WALKING_LIBUNWIND) 52 | # libunwind works on the macOS without having to add special include 53 | # paths or libraries 54 | if (NOT APPLE) 55 | find_path(LIBUNWIND_INCLUDE_DIR NAMES "libunwind.h") 56 | find_library(LIBUNWIND_LIBRARY unwind) 57 | 58 | if (LIBUNWIND_LIBRARY) 59 | include(CheckSymbolExists) 60 | check_symbol_exists(UNW_INIT_SIGNAL_FRAME libunwind.h HAVE_UNW_INIT_SIGNAL_FRAME) 61 | if (NOT HAVE_UNW_INIT_SIGNAL_FRAME) 62 | message(STATUS "libunwind does not support unwinding from signal handler frames") 63 | endif() 64 | endif() 65 | 66 | set(LIBUNWIND_INCLUDE_DIRS ${LIBUNWIND_INCLUDE_DIR}) 67 | set(LIBDWARF_LIBRARIES ${LIBUNWIND_LIBRARY}) 68 | find_package_handle_standard_args(libunwind DEFAULT_MSG 69 | LIBUNWIND_LIBRARY LIBUNWIND_INCLUDE_DIR) 70 | mark_as_advanced(LIBUNWIND_INCLUDE_DIR LIBUNWIND_LIBRARY) 71 | list(APPEND _BACKWARD_LIBRARIES ${LIBUNWIND_LIBRARY}) 72 | endif() 73 | 74 | # Disable other unwinders if libunwind is found 75 | set(STACK_WALKING_UNWIND FALSE) 76 | set(STACK_WALKING_BACKTRACE FALSE) 77 | endif() 78 | 79 | if (${STACK_DETAILS_AUTO_DETECT}) 80 | if(NOT CMAKE_VERSION VERSION_LESS 3.17) 81 | set(_name_mismatched_arg NAME_MISMATCHED) 82 | endif() 83 | # find libdw 84 | find_path(LIBDW_INCLUDE_DIR NAMES "elfutils/libdw.h" "elfutils/libdwfl.h") 85 | find_library(LIBDW_LIBRARY dw) 86 | # in case it's statically linked, look for all the possible dependencies 87 | find_library(LIBELF_LIBRARY elf) 88 | find_library(LIBPTHREAD_LIBRARY pthread) 89 | find_library(LIBZ_LIBRARY z) 90 | find_library(LIBBZ2_LIBRARY bz2) 91 | find_library(LIBLZMA_LIBRARY lzma) 92 | find_library(LIBZSTD_LIBRARY zstd) 93 | set(LIBDW_INCLUDE_DIRS ${LIBDW_INCLUDE_DIR} ) 94 | set(LIBDW_LIBRARIES ${LIBDW_LIBRARY} 95 | $<$:${LIBELF_LIBRARY}> 96 | $<$:${LIBPTHREAD_LIBRARY}> 97 | $<$:${LIBZ_LIBRARY}> 98 | $<$:${LIBBZ2_LIBRARY}> 99 | $<$:${LIBLZMA_LIBRARY}> 100 | $<$:${LIBZSTD_LIBRARY}>) 101 | find_package_handle_standard_args(libdw ${_name_mismatched_arg} 102 | REQUIRED_VARS LIBDW_LIBRARY LIBDW_INCLUDE_DIR) 103 | mark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARY) 104 | 105 | # find libbfd 106 | find_path(LIBBFD_INCLUDE_DIR NAMES "bfd.h") 107 | find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h") 108 | find_library(LIBBFD_LIBRARY bfd) 109 | find_library(LIBDL_LIBRARY dl) 110 | set(LIBBFD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR}) 111 | set(LIBBFD_LIBRARIES ${LIBBFD_LIBRARY} ${LIBDL_LIBRARY}) 112 | find_package_handle_standard_args(libbfd ${_name_mismatched_arg} 113 | REQUIRED_VARS LIBBFD_LIBRARY LIBBFD_INCLUDE_DIR 114 | LIBDL_LIBRARY LIBDL_INCLUDE_DIR) 115 | mark_as_advanced(LIBBFD_INCLUDE_DIR LIBBFD_LIBRARY 116 | LIBDL_INCLUDE_DIR LIBDL_LIBRARY) 117 | 118 | # find libdwarf 119 | find_path(LIBDWARF_INCLUDE_DIR NAMES "libdwarf.h" PATH_SUFFIXES libdwarf) 120 | find_path(LIBELF_INCLUDE_DIR NAMES "libelf.h") 121 | find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h") 122 | find_library(LIBDWARF_LIBRARY dwarf) 123 | find_library(LIBELF_LIBRARY elf) 124 | find_library(LIBDL_LIBRARY dl) 125 | set(LIBDWARF_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIR} ${LIBELF_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR}) 126 | set(LIBDWARF_LIBRARIES ${LIBDWARF_LIBRARY} ${LIBELF_LIBRARY} ${LIBDL_LIBRARY}) 127 | find_package_handle_standard_args(libdwarf ${_name_mismatched_arg} 128 | REQUIRED_VARS LIBDWARF_LIBRARY LIBDWARF_INCLUDE_DIR 129 | LIBELF_LIBRARY LIBELF_INCLUDE_DIR 130 | LIBDL_LIBRARY LIBDL_INCLUDE_DIR) 131 | mark_as_advanced(LIBDWARF_INCLUDE_DIR LIBDWARF_LIBRARY 132 | LIBELF_INCLUDE_DIR LIBELF_LIBRARY 133 | LIBDL_INCLUDE_DIR LIBDL_LIBRARY) 134 | 135 | if (LIBDW_FOUND) 136 | LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDW_INCLUDE_DIRS}) 137 | LIST(APPEND _BACKWARD_LIBRARIES ${LIBDW_LIBRARIES}) 138 | set(STACK_DETAILS_DW TRUE) 139 | set(STACK_DETAILS_BFD FALSE) 140 | set(STACK_DETAILS_DWARF FALSE) 141 | set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) 142 | elseif(LIBBFD_FOUND) 143 | LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIRS}) 144 | LIST(APPEND _BACKWARD_LIBRARIES ${LIBBFD_LIBRARIES}) 145 | 146 | # If we attempt to link against static bfd, make sure to link its dependencies, too 147 | get_filename_component(bfd_lib_ext "${LIBBFD_LIBRARY}" EXT) 148 | if (bfd_lib_ext STREQUAL "${CMAKE_STATIC_LIBRARY_SUFFIX}") 149 | find_library(LIBSFRAME_LIBRARY NAMES sframe) 150 | if (LIBSFRAME_LIBRARY) 151 | list(APPEND _BACKWARD_LIBRARIES ${LIBSFRAME_LIBRARY}) 152 | endif() 153 | 154 | list(APPEND _BACKWARD_LIBRARIES iberty z) 155 | endif() 156 | 157 | set(STACK_DETAILS_DW FALSE) 158 | set(STACK_DETAILS_BFD TRUE) 159 | set(STACK_DETAILS_DWARF FALSE) 160 | set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) 161 | elseif(LIBDWARF_FOUND) 162 | LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIRS}) 163 | LIST(APPEND _BACKWARD_LIBRARIES ${LIBDWARF_LIBRARIES}) 164 | 165 | set(STACK_DETAILS_DW FALSE) 166 | set(STACK_DETAILS_BFD FALSE) 167 | set(STACK_DETAILS_DWARF TRUE) 168 | set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) 169 | else() 170 | set(STACK_DETAILS_DW FALSE) 171 | set(STACK_DETAILS_BFD FALSE) 172 | set(STACK_DETAILS_DWARF FALSE) 173 | set(STACK_DETAILS_BACKTRACE_SYMBOL TRUE) 174 | endif() 175 | else() 176 | if (STACK_DETAILS_DW) 177 | LIST(APPEND _BACKWARD_LIBRARIES dw) 178 | endif() 179 | 180 | if (STACK_DETAILS_BFD) 181 | LIST(APPEND _BACKWARD_LIBRARIES bfd dl) 182 | endif() 183 | 184 | if (STACK_DETAILS_DWARF) 185 | LIST(APPEND _BACKWARD_LIBRARIES dwarf elf) 186 | endif() 187 | endif() 188 | 189 | macro(map_definitions var_prefix define_prefix) 190 | foreach(def ${ARGN}) 191 | if (${${var_prefix}${def}}) 192 | LIST(APPEND _BACKWARD_DEFINITIONS "${define_prefix}${def}=1") 193 | else() 194 | LIST(APPEND _BACKWARD_DEFINITIONS "${define_prefix}${def}=0") 195 | endif() 196 | endforeach() 197 | endmacro() 198 | 199 | if (NOT _BACKWARD_DEFINITIONS) 200 | map_definitions("STACK_WALKING_" "BACKWARD_HAS_" UNWIND LIBUNWIND BACKTRACE) 201 | map_definitions("STACK_DETAILS_" "BACKWARD_HAS_" BACKTRACE_SYMBOL DW BFD DWARF) 202 | endif() 203 | 204 | if(WIN32) 205 | list(APPEND _BACKWARD_LIBRARIES dbghelp psapi) 206 | if(MINGW) 207 | include(CheckCXXCompilerFlag) 208 | check_cxx_compiler_flag(-gcodeview SUPPORT_WINDOWS_DEBUG_INFO) 209 | if(SUPPORT_WINDOWS_DEBUG_INFO) 210 | set(CMAKE_EXE_LINKER_FLAGS "-Wl,--pdb= ") 211 | add_compile_options(-gcodeview) 212 | else() 213 | set(MINGW_MSVCR_LIBRARY "msvcr90$<$:d>" CACHE STRING "Mingw MSVC runtime import library") 214 | list(APPEND _BACKWARD_LIBRARIES ${MINGW_MSVCR_LIBRARY}) 215 | endif() 216 | endif() 217 | endif() 218 | 219 | set(BACKWARD_INCLUDE_DIR 220 | $ 221 | $ 222 | ) 223 | 224 | set(BACKWARD_HAS_EXTERNAL_LIBRARIES FALSE) 225 | set(FIND_PACKAGE_REQUIRED_VARS BACKWARD_INCLUDE_DIR) 226 | if(DEFINED _BACKWARD_LIBRARIES) 227 | set(BACKWARD_HAS_EXTERNAL_LIBRARIES TRUE) 228 | list(APPEND FIND_PACKAGE_REQUIRED_VARS _BACKWARD_LIBRARIES) 229 | endif() 230 | 231 | include(FindPackageHandleStandardArgs) 232 | find_package_handle_standard_args(Backward 233 | REQUIRED_VARS ${FIND_PACKAGE_REQUIRED_VARS} 234 | ) 235 | list(APPEND _BACKWARD_INCLUDE_DIRS ${BACKWARD_INCLUDE_DIR}) 236 | 237 | # add_backward, optional bool argument; if passed and true, backward will be included as a system header 238 | macro(add_backward target) 239 | message(DEPRECATION "The add_backward() macro is deprecated, use target_link_libraries() to link to " 240 | "one of the exported targets: Backward::Interface, Backward::Object, or Backward::Backward." 241 | ) 242 | if ("${ARGN}") 243 | target_include_directories(${target} SYSTEM PRIVATE ${BACKWARD_INCLUDE_DIRS}) 244 | else() 245 | target_include_directories(${target} PRIVATE ${BACKWARD_INCLUDE_DIRS}) 246 | endif() 247 | set_property(TARGET ${target} APPEND PROPERTY COMPILE_DEFINITIONS ${BACKWARD_DEFINITIONS}) 248 | set_property(TARGET ${target} APPEND PROPERTY LINK_LIBRARIES ${BACKWARD_LIBRARIES}) 249 | endmacro() 250 | 251 | set(BACKWARD_INCLUDE_DIRS ${_BACKWARD_INCLUDE_DIRS} CACHE INTERNAL "BACKWARD_INCLUDE_DIRS") 252 | set(BACKWARD_DEFINITIONS ${_BACKWARD_DEFINITIONS} CACHE INTERNAL "BACKWARD_DEFINITIONS") 253 | set(BACKWARD_LIBRARIES ${_BACKWARD_LIBRARIES} CACHE INTERNAL "BACKWARD_LIBRARIES") 254 | mark_as_advanced(BACKWARD_INCLUDE_DIRS BACKWARD_DEFINITIONS BACKWARD_LIBRARIES) 255 | 256 | # Expand each definition in BACKWARD_DEFINITIONS to its own cmake var and export 257 | # to outer scope 258 | foreach(var ${BACKWARD_DEFINITIONS}) 259 | string(REPLACE "=" ";" var_as_list ${var}) 260 | list(GET var_as_list 0 var_name) 261 | list(GET var_as_list 1 var_value) 262 | set(${var_name} ${var_value}) 263 | mark_as_advanced(${var_name}) 264 | endforeach() 265 | 266 | # if this file is used from the install tree by find_package(), include the 267 | # file CMake-generated file where the targets are defined 268 | if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/BackwardTargets.cmake) 269 | include(${CMAKE_CURRENT_LIST_DIR}/BackwardTargets.cmake) 270 | endif() 271 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Backward-cpp [![Conan Center](https://img.shields.io/conan/v/backward-cpp)](https://conan.io/center/recipes/backward-cpp) 2 | ============ 3 | 4 | Backward is a beautiful stack trace pretty printer for C++. 5 | 6 | If you are bored to see this: 7 | 8 | ![default trace](doc/rude.png) 9 | 10 | Backward will spice it up for you: 11 | 12 | ![pretty stackstrace](doc/pretty.png) 13 | 14 | There is not much to say. Of course it will be able to display the code 15 | snippets only if the source files are accessible (else see trace #4 in the 16 | example). 17 | 18 | All "Source" lines and code snippet prefixed by a pipe "|" are frames inline 19 | the next frame. 20 | You can see that for the trace #1 in the example, the function 21 | `you_shall_not_pass()` was inlined in the function `...read2::do_test()` by the 22 | compiler. 23 | 24 | ## Installation 25 | 26 | #### Install backward.hpp 27 | 28 | Backward is a header only library. So installing Backward is easy, simply drop 29 | a copy of `backward.hpp` along with your other source files in your C++ project. 30 | You can also use a git submodule or really any other way that best fits your 31 | environment, as long as you can include `backward.hpp`. 32 | 33 | #### Install backward.cpp 34 | 35 | If you want Backward to automatically print a stack trace on most common fatal 36 | errors (segfault, abort, un-handled exception...), simply add a copy of 37 | `backward.cpp` to your project, and don't forget to tell your build system. 38 | 39 | The code in `backward.cpp` is trivial anyway, you can simply copy what it's 40 | doing at your convenience. 41 | 42 | Note for [folly](https://github.com/facebook/folly) library users: must define `backward::SignalHandling sh;` after `folly::init(&argc, &argv);`. 43 | 44 | ## Configuration & Dependencies 45 | 46 | ### Integration with CMake 47 | 48 | If you are using CMake and want to use its configuration abilities to save you 49 | the trouble, you can easily integrate Backward, depending on how you obtained 50 | the library. 51 | 52 | Notice that all approaches are equivalent in the way Backward is added to a 53 | CMake target, the difference is in how CMake is pointed to the Backward 54 | sources. Backward defines three targets: 55 | 56 | - `Backward::Interface` is an interface target that brings compiler definition 57 | flags, include directory, and external libraries. This is all you need to use 58 | the `backward.hpp` header library. 59 | - `Backward::Object` brings `Backward::Interface` and `backward.cpp` as an 60 | `OBJECT` CMake library. This target cannot be exported, so it is not 61 | available when Backward is used via `find_package`. 62 | - `Backward::Backward` brings `Backward::Interface` and `backward.cpp` as 63 | either `STATIC` or `SHARED` library (depending on the `BACKWARD_SHARED` 64 | option). This target is exported and always available, however note that the 65 | linker will not include unused objects from a static library, unless the 66 | `-Wl,--whole-archive` option (or similar) is used. 67 | 68 | #### With `FetchContent()`: 69 | 70 | If you are using a recent version of CMake, you can integrate `backward` via `FetchContent` like below: 71 | 72 | ```cmake 73 | include(FetchContent) 74 | 75 | # Also requires one of: libbfd (gnu binutils), libdwarf, libdw (elfutils) 76 | FetchContent_Declare(backward 77 | GIT_REPOSITORY https://github.com/bombela/backward-cpp 78 | GIT_TAG master # or a version tag, such as v1.6 79 | SYSTEM # optional, the Backward include directory will be treated as system directory 80 | ) 81 | FetchContent_MakeAvailable(backward) 82 | 83 | # Add Backward to your target (either Backward::Interface, Backward::Object, or Backward::Backward) 84 | target_link_libraries(mytarget PUBLIC Backward::Interface) 85 | ``` 86 | 87 | #### As a subdirectory: 88 | 89 | In this case you have a subdirectory containing the whole repository of Backward 90 | (e.g. using [git-submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules)), 91 | in this case you can do: 92 | 93 | ```cmake 94 | add_subdirectory(/path/to/backward-cpp) 95 | 96 | # Add Backward to your target (either Backward::Interface, Backward::Object, or Backward::Backward) 97 | target_link_libraries(mytarget PUBLIC Backward::Interface) 98 | ``` 99 | 100 | #### Modifying `CMAKE_MODULE_PATH`: 101 | 102 | In this case you can have Backward installed as a subdirectory: 103 | 104 | ``` 105 | list(APPEND CMAKE_MODULE_PATH /path/to/backward-cpp) 106 | find_package(Backward) 107 | 108 | # Add Backward to your target (either Backward::Interface or Backward::Backward) 109 | target_link_libraries(mytarget PUBLIC Backward::Interface) 110 | ``` 111 | 112 | #### Installation through a regular package manager 113 | 114 | In this case you have obtained Backward through a package manager. 115 | 116 | Packages currently available: 117 | - [conda-forge](https://anaconda.org/conda-forge/backward-cpp) 118 | 119 | ``` 120 | find_package(Backward) 121 | 122 | # Add Backward to your target (either Backward::Interface or Backward::Backward) 123 | target_link_libraries(mytarget PUBLIC Backward::Interface) 124 | ``` 125 | 126 | ### Libraries to unwind the stack 127 | 128 | On Linux and macOS, backtrace can back-trace or "walk" the stack using the 129 | following libraries: 130 | 131 | #### unwind 132 | 133 | Unwind comes from libgcc, but there is an equivalent inside clang itself. With 134 | unwind, the stacktrace is as accurate as it can possibly be, since this is 135 | used by the C++ runtine in gcc/clang for stack unwinding on exception. 136 | 137 | Normally libgcc is already linked to your program by default. 138 | 139 | #### libunwind from the [libunwind project](https://github.com/libunwind/libunwind) 140 | 141 | apt-get install binutils-dev (or equivalent) 142 | 143 | Libunwind provides, in some cases, a more accurate stacktrace as it knows 144 | to decode signal handler frames and lets us edit the context registers when 145 | unwinding, allowing stack traces over bad function references. 146 | 147 | For best results make sure you are using libunwind 1.3 or later, which added 148 | `unw_init_local2` and support for handling signal frames. 149 | 150 | CMake will warn you when configuring if your libunwind version doesn't support 151 | signal frames. 152 | 153 | On macOS clang provides a libunwind API compatible library as part of its 154 | environment, so no third party libraries are necessary. 155 | 156 | ### Compile with debug info 157 | 158 | You need to compile your project with generation of debug symbols enabled, 159 | usually `-g` with clang++ and g++. 160 | 161 | Note that you can use `-g` with any level of optimization, with modern debug 162 | information encoding like DWARF, it only takes space in the binary (it's not 163 | loaded in memory until your debugger or Backward makes use of it, don't worry), 164 | and it doesn't impact the code generation (at least on GNU/Linux x86\_64 for 165 | what I know). 166 | 167 | If you are missing debug information, the stack trace will lack details about 168 | your sources. 169 | 170 | ### Libraries to read the debug info 171 | 172 | Backward supports pretty printed stack traces on GNU/Linux, macOS and Windows, 173 | it will compile fine under other platforms but will not do anything. **Pull 174 | requests are welcome :)** 175 | 176 | Also, by default you will get a really basic stack trace, based on the 177 | `backtrace_symbols` API: 178 | 179 | ![default trace](doc/nice.png) 180 | 181 | You will need to install some dependencies to get the ultimate stack trace. 182 | Three libraries are currently supported, the only difference is which one is the 183 | easiest for you to install, so pick your poison: 184 | 185 | #### libbfd from the [GNU/binutils](http://www.gnu.org/software/binutils/) 186 | 187 | apt-get install binutils-dev (or equivalent) 188 | 189 | And do not forget to link with the lib: `g++/clang++ -lbfd -ldl ...` 190 | 191 | This library requires dynamic loading. Which is provided by the library `dl`. 192 | Hence why we also link with `-ldl`. 193 | 194 | Then define the following before every inclusion of `backward.hpp` (don't 195 | forget to update `backward.cpp` as well): 196 | 197 | #define BACKWARD_HAS_BFD 1 198 | 199 | #### libdw from the [elfutils](https://fedorahosted.org/elfutils/) 200 | 201 | apt-get install libdw-dev (or equivalent) 202 | 203 | And do not forget to link with the lib and inform Backward to use it: 204 | 205 | #define BACKWARD_HAS_DW 1 206 | 207 | Of course you can simply add the define (`-DBACKWARD_HAS_...=1`) and the 208 | linkage details in your build system and even auto-detect which library is 209 | installed, it's up to you. 210 | 211 | #### [libdwarf](https://sourceforge.net/projects/libdwarf/) and [libelf](http://www.mr511.de/software/english.html) 212 | 213 | apt-get install libdwarf-dev (or equivalent) 214 | 215 | And do not forget to link with the lib and inform Backward to use it: 216 | 217 | #define BACKWARD_HAS_DWARF 1 218 | 219 | There are several alternative implementations of libdwarf and libelf that 220 | are API compatible so it's possible, although it hasn't been tested, to 221 | replace the ones used when developing backward (in bold, below): 222 | 223 | * **_libelf_** by [Michael "Tired" Riepe](http://www.mr511.de/software/english.html) 224 | * **_libdwarf_** by [David Anderson](https://www.prevanders.net/dwarf.html) 225 | * libelf from [elfutils](https://fedorahosted.org/elfutils/) 226 | * libelf and libdwarf from FreeBSD's [ELF Tool Chain](https://sourceforge.net/p/elftoolchain/wiki/Home/) project 227 | 228 | 229 | Of course you can simply add the define (`-DBACKWARD_HAS_...=1`) and the 230 | linkage details in your build system and even auto-detect which library is 231 | installed, it's up to you. 232 | 233 | That's it, you are all set, you should be getting nice stack traces like the 234 | one at the beginning of this document. 235 | 236 | ## API 237 | 238 | If you don't want to limit yourself to the defaults offered by `backward.cpp`, 239 | and you want to take some random stack traces for whatever reason and pretty 240 | print them the way you love or you decide to send them all to your buddies over 241 | the Internet, you will appreciate the simplicity of Backward's API. 242 | 243 | ### Stacktrace 244 | 245 | The StackTrace class lets you take a "snapshot" of the current stack. 246 | You can use it like this: 247 | 248 | ```c++ 249 | using namespace backward; 250 | StackTrace st; st.load_here(32); 251 | Printer p; p.print(st); 252 | ``` 253 | 254 | The public methods are: 255 | 256 | ```c++ 257 | class StackTrace { public: 258 | // Take a snapshot of the current stack, with at most "trace_cnt_max" 259 | // traces in it. The first trace is the most recent (ie the current 260 | // frame). You can also provide a trace address to load_from() assuming 261 | // the address is a valid stack frame (useful for signal handling traces). 262 | // Both function return size(). 263 | size_t load_here(size_t trace_cnt_max) 264 | size_t load_from(void* address, size_t trace_cnt_max) 265 | 266 | // The number of traces loaded. This can be less than "trace_cnt_max". 267 | size_t size() const 268 | 269 | // A unique id for the thread in which the trace was taken. The value 270 | // 0 means the stack trace comes from the main thread. 271 | size_t thread_id() const 272 | 273 | // Retrieve a trace by index. 0 is the most recent trace, size()-1 is 274 | // the oldest one. 275 | Trace operator[](size_t trace_idx) 276 | }; 277 | ``` 278 | 279 | ### TraceResolver 280 | 281 | The `TraceResolver` does the heavy lifting, and intends to transform a simple 282 | `Trace` from its address into a fully detailed `ResolvedTrace` with the 283 | filename of the source, line numbers, inlined functions and so on. 284 | 285 | You can use it like this: 286 | 287 | ```c++ 288 | using namespace backward; 289 | StackTrace st; st.load_here(32); 290 | 291 | TraceResolver tr; tr.load_stacktrace(st); 292 | for (size_t i = 0; i < st.size(); ++i) { 293 | ResolvedTrace trace = tr.resolve(st[i]); 294 | std::cout << "#" << i 295 | << " " << trace.object_filename 296 | << " " << trace.object_function 297 | << " [" << trace.addr << "]" 298 | << std::endl; 299 | } 300 | ``` 301 | 302 | The public methods are: 303 | 304 | ```c++ 305 | class TraceResolver { public: 306 | // Pre-load whatever is necessary from the stack trace. 307 | template 308 | void load_stacktrace(ST&) 309 | 310 | // Resolve a trace. It takes a ResolvedTrace, because a `Trace` is 311 | // implicitly convertible to it. 312 | ResolvedTrace resolve(ResolvedTrace t) 313 | }; 314 | ``` 315 | 316 | ### SnippetFactory 317 | 318 | The SnippetFactory is a simple helper class to automatically load and cache 319 | source files in order to extract code snippets. 320 | 321 | ```c++ 322 | class SnippetFactory { public: 323 | // A snippet is a list of line numbers and line contents. 324 | typedef std::vector > lines_t; 325 | 326 | // Return a snippet starting at line_start with up to context_size lines. 327 | lines_t get_snippet(const std::string& filename, 328 | size_t line_start, size_t context_size) 329 | 330 | // Return a combined snippet from two different locations and combine them. 331 | // context_size / 2 lines will be extracted from each location. 332 | lines_t get_combined_snippet( 333 | const std::string& filename_a, size_t line_a, 334 | const std::string& filename_b, size_t line_b, 335 | size_t context_size) 336 | 337 | // Tries to return a unified snippet if the two locations from the same 338 | // file are close enough to fit inside one context_size, else returns 339 | // the equivalent of get_combined_snippet(). 340 | lines_t get_coalesced_snippet(const std::string& filename, 341 | size_t line_a, size_t line_b, size_t context_size) 342 | ``` 343 | 344 | ### Printer 345 | 346 | A simpler way to pretty print a stack trace to the terminal. It will 347 | automatically resolve the traces for you: 348 | 349 | ```c++ 350 | using namespace backward; 351 | StackTrace st; st.load_here(32); 352 | Printer p; 353 | p.object = true; 354 | p.color_mode = ColorMode::always; 355 | p.address = true; 356 | p.print(st, stderr); 357 | ``` 358 | 359 | You can set a few options: 360 | 361 | ```c++ 362 | class Printer { public: 363 | // Print a little snippet of code if possible. 364 | bool snippet = true; 365 | 366 | // Colorize the trace 367 | // - ColorMode::automatic: Activate colors if possible. For example, when using a TTY on linux. 368 | // - ColorMode::always: Always use colors. 369 | // - ColorMode::never: Never use colors. 370 | bool color_mode = ColorMode::automatic; 371 | 372 | // Add the addresses of every source location to the trace. 373 | bool address = false; 374 | 375 | // Even if there is a source location, also prints the object 376 | // from where the trace came from. 377 | bool object = false; 378 | 379 | // Resolve and print a stack trace to the given C FILE* object. 380 | // On linux, if the FILE* object is attached to a TTY, 381 | // color will be used if color_mode is set to automatic. 382 | template 383 | FILE* print(StackTrace& st, FILE* fp = stderr); 384 | 385 | // Resolve and print a stack trace to the given std::ostream object. 386 | // Color will only be used if color_mode is set to always. 387 | template 388 | std::ostream& print(ST& st, std::ostream& os); 389 | ``` 390 | 391 | 392 | ### SignalHandling 393 | 394 | A simple helper class that registers for you the most common signals and other 395 | callbacks to segfault, hardware exception, un-handled exception etc. 396 | 397 | `backward.cpp` simply uses it like that: 398 | 399 | ```c++ 400 | backward::SignalHandling sh; 401 | ``` 402 | 403 | Creating the object registers all the different signals and hooks. Destroying 404 | this object doesn't do anything. It exposes only one method: 405 | 406 | ```c++ 407 | bool loaded() const // true if loaded with success 408 | ``` 409 | 410 | **Warning:** The registered signal handlers are not 411 | [signal safe](https://www.man7.org/linux/man-pages/man7/signal-safety.7.html), 412 | mostly because backward-cpp and the DWARF decoding libraries are using `malloc`. 413 | In case a signal is raised while malloc is holding a lock, a deadlock will 414 | occur. 415 | 416 | ### Trace object 417 | 418 | To keep the memory footprint of a loaded `StackTrace` on the low-side, there a 419 | hierarchy of trace object, from a minimal `Trace `to a `ResolvedTrace`. 420 | 421 | #### Simple trace 422 | 423 | ```c++ 424 | struct Trace { 425 | void* addr; // address of the trace 426 | size_t idx; // its index (0 == most recent) 427 | }; 428 | ``` 429 | 430 | #### Resolved trace 431 | 432 | A `ResolvedTrace` should contains a maximum of details about the location of 433 | the trace in the source code. Note that not all fields might be set. 434 | 435 | ```c++ 436 | struct ResolvedTrace: public Trace { 437 | 438 | struct SourceLoc { 439 | std::string function; 440 | std::string filename; 441 | unsigned line; 442 | unsigned col; 443 | }; 444 | 445 | // In which binary object this trace is located. 446 | std::string object_filename; 447 | 448 | // The function in the object that contains the trace. This is not the same 449 | // as source.function which can be an function inlined in object_function. 450 | std::string object_function; 451 | 452 | // The source location of this trace. It is possible for filename to be 453 | // empty and for line/col to be invalid (value 0) if this information 454 | // couldn't be deduced, for example if there is no debug information in the 455 | // binary object. 456 | SourceLoc source; 457 | 458 | // An optional list of "inliners". All of these sources locations where 459 | // inlined in the source location of the trace (the attribute right above). 460 | // This is especially useful when you compile with optimizations turned on. 461 | typedef std::vector source_locs_t; 462 | source_locs_t inliners; 463 | }; 464 | ``` 465 | 466 | ## Contact and copyright 467 | 468 | François-Xavier Bourlet 469 | 470 | Copyright 2013-2017 Google Inc. All Rights Reserved. 471 | MIT License. 472 | 473 | ### Disclaimer 474 | 475 | Although this project is owned by Google Inc. this is not a Google supported or 476 | affiliated project. 477 | --------------------------------------------------------------------------------