├── .gitignore ├── .travis.yml ├── BackwardConfig.cmake ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── backward.cpp ├── backward.hpp ├── conanfile.py ├── doc ├── nice.png ├── pretty.png └── rude.png ├── test ├── _test_main.cpp ├── rectrace.cpp ├── select_signals.cpp ├── stacktrace.cpp ├── suicide.cpp ├── test.cpp └── test.hpp └── test_package ├── CMakeLists.txt ├── conanfile.py └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | *.pyc 3 | -------------------------------------------------------------------------------- /.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 test_package --build=outdated 26 | -------------------------------------------------------------------------------- /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 | 32 | set(STACK_DETAILS_AUTO_DETECT TRUE CACHE BOOL 33 | "Auto detect backward's stack details dependencies") 34 | 35 | set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE CACHE BOOL 36 | "Use backtrace from (e)glibc for symbols resolution") 37 | set(STACK_DETAILS_DW FALSE CACHE BOOL 38 | "Use libdw to read debug info") 39 | set(STACK_DETAILS_BFD FALSE CACHE BOOL 40 | "Use libbfd to read debug info") 41 | 42 | set(BACKWARD_TESTS FALSE CACHE BOOL "Enable tests") 43 | 44 | ############################################################################### 45 | # CONFIGS 46 | ############################################################################### 47 | if (${STACK_DETAILS_AUTO_DETECT}) 48 | include(FindPackageHandleStandardArgs) 49 | 50 | # find libdw 51 | find_path(LIBDW_INCLUDE_DIR NAMES "elfutils/libdw.h" "elfutils/libdwfl.h") 52 | find_library(LIBDW_LIBRARY dw) 53 | set(LIBDW_INCLUDE_DIRS ${LIBDW_INCLUDE_DIR} ) 54 | set(LIBDW_LIBRARIES ${LIBDW_LIBRARY} ) 55 | find_package_handle_standard_args(libdw DEFAULT_MSG 56 | LIBDW_LIBRARY LIBDW_INCLUDE_DIR) 57 | mark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARY) 58 | 59 | # find libbfd 60 | find_path(LIBBFD_INCLUDE_DIR NAMES "bfd.h") 61 | find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h") 62 | find_library(LIBBFD_LIBRARY bfd) 63 | find_library(LIBDL_LIBRARY dl) 64 | set(LIBBFD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR}) 65 | set(LIBBFD_LIBRARIES ${LIBBFD_LIBRARY} ${LIBDL_LIBRARY}) 66 | find_package_handle_standard_args(libbfd DEFAULT_MSG 67 | LIBBFD_LIBRARY LIBBFD_INCLUDE_DIR 68 | LIBDL_LIBRARY LIBDL_INCLUDE_DIR) 69 | mark_as_advanced(LIBBFD_INCLUDE_DIR LIBBFD_LIBRARY 70 | LIBDL_INCLUDE_DIR LIBDL_LIBRARY) 71 | 72 | if (LIBDW_FOUND) 73 | LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDW_INCLUDE_DIRS}) 74 | LIST(APPEND BACKWARD_LIBRARIES ${LIBDW_LIBRARIES}) 75 | set(STACK_DETAILS_DW TRUE) 76 | set(STACK_DETAILS_BFD FALSE) 77 | set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) 78 | elseif(LIBBFD_FOUND) 79 | LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIRS}) 80 | LIST(APPEND BACKWARD_LIBRARIES ${LIBBFD_LIBRARIES}) 81 | 82 | # If we attempt to link against static bfd, make sure to link its dependencies, too 83 | get_filename_component(bfd_lib_ext "${LIBBFD_LIBRARY}" EXT) 84 | if (bfd_lib_ext STREQUAL "${CMAKE_STATIC_LIBRARY_SUFFIX}") 85 | list(APPEND BACKWARD_LIBRARIES iberty z) 86 | endif() 87 | 88 | set(STACK_DETAILS_DW FALSE) 89 | set(STACK_DETAILS_BFD TRUE) 90 | set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) 91 | else() 92 | set(STACK_DETAILS_DW FALSE) 93 | set(STACK_DETAILS_BFD FALSE) 94 | set(STACK_DETAILS_BACKTRACE_SYMBOL TRUE) 95 | endif() 96 | else() 97 | if (STACK_DETAILS_DW) 98 | LIST(APPEND BACKWARD_LIBRARIES dw) 99 | endif() 100 | 101 | if (STACK_DETAILS_BFD) 102 | LIST(APPEND BACKWARD_LIBRARIES bfd dl) 103 | endif() 104 | endif() 105 | 106 | macro(map_definitions var_prefix define_prefix) 107 | foreach(def ${ARGN}) 108 | if (${${var_prefix}${def}}) 109 | LIST(APPEND BACKWARD_DEFINITIONS "${define_prefix}${def}=1") 110 | else() 111 | LIST(APPEND BACKWARD_DEFINITIONS "${define_prefix}${def}=0") 112 | endif() 113 | endforeach() 114 | endmacro() 115 | 116 | if (NOT BACKWARD_DEFINITIONS) 117 | map_definitions("STACK_WALKING_" "BACKWARD_HAS_" UNWIND BACKTRACE) 118 | map_definitions("STACK_DETAILS_" "BACKWARD_HAS_" BACKTRACE_SYMBOL DW BFD) 119 | endif() 120 | 121 | foreach(def ${BACKWARD_DEFINITIONS}) 122 | message(STATUS "${def}") 123 | endforeach() 124 | 125 | find_path(BACKWARD_INCLUDE_DIR backward.hpp PATHS ${CMAKE_CURRENT_LIST_DIR}) 126 | 127 | include(FindPackageHandleStandardArgs) 128 | find_package_handle_standard_args(Backward 129 | REQUIRED_VARS 130 | BACKWARD_INCLUDE_DIR 131 | BACKWARD_LIBRARIES 132 | ) 133 | list(APPEND _BACKWARD_INCLUDE_DIRS ${BACKWARD_INCLUDE_DIR}) 134 | 135 | macro(add_backward target) 136 | target_include_directories(${target} PRIVATE ${_BACKWARD_INCLUDE_DIRS}) 137 | set_property(TARGET ${target} APPEND PROPERTY COMPILE_DEFINITIONS ${BACKWARD_DEFINITIONS}) 138 | set_property(TARGET ${target} APPEND PROPERTY LINK_LIBRARIES ${BACKWARD_LIBRARIES}) 139 | endmacro() 140 | set(BACKWARD_INCLUDE_DIRS ${_BACKWARD_INCLUDE_DIRS} CACHE INTERNAL "_BACKWARD_INCLUDE_DIRS") 141 | set(BACKWARD_DEFINITIONS ${BACKWARD_DEFINITIONS} CACHE INTERNAL "BACKWARD_DEFINITIONS") 142 | set(BACKWARD_LIBRARIES ${BACKWARD_LIBRARIES} CACHE INTERNAL "BACKWARD_LIBRARIES") 143 | mark_as_advanced(_BACKWARD_INCLUDE_DIRS BACKWARD_DEFINITIONS BACKWARD_LIBRARIES) 144 | 145 | if (NOT TARGET Backward::Backward) 146 | add_library(Backward::Backward INTERFACE IMPORTED) 147 | set_target_properties(Backward::Backward PROPERTIES 148 | INTERFACE_INCLUDE_DIRECTORIES "${_BACKWARD_INCLUDE_DIRS}" 149 | INTERFACE_LINK_LIBRARIES "${BACKWARD_LIBRARIES}" 150 | INTERFACE_COMPILE_DEFINITIONS "${BACKWARD_DEFINITIONS}" 151 | ) 152 | endif() 153 | -------------------------------------------------------------------------------- /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 2.8.12) 24 | project(backward CXX) 25 | 26 | include(BackwardConfig.cmake) 27 | 28 | set(CMAKE_CXX_STANDARD_REQUIRED True) 29 | set(CMAKE_CXX_STANDARD 11) 30 | 31 | ############################################################################### 32 | # COMPILER FLAGS 33 | ############################################################################### 34 | 35 | if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCXX) 36 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic-errors") 37 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") 38 | endif() 39 | 40 | ############################################################################### 41 | # BACKWARD OBJECT 42 | ############################################################################### 43 | 44 | add_library(backward_object OBJECT backward.cpp) 45 | target_compile_definitions(backward_object PRIVATE ${BACKWARD_DEFINITIONS}) 46 | target_include_directories(backward_object PRIVATE ${BACKWARD_INCLUDE_DIRS}) 47 | set(BACKWARD_ENABLE $ CACHE STRING 48 | "Link with this object to setup backward automatically") 49 | 50 | 51 | ############################################################################### 52 | # BACKWARD LIBRARY (Includes backward.cpp) 53 | ############################################################################### 54 | option(BACKWARD_SHARED "Build dynamic backward-cpp shared lib" OFF) 55 | 56 | if(BACKWARD_SHARED) 57 | set(libtype SHARED) 58 | endif() 59 | add_library(backward ${libtype} backward.cpp) 60 | target_compile_definitions(backward PUBLIC ${BACKWARD_DEFINITIONS}) 61 | target_include_directories(backward PUBLIC ${BACKWARD_INCLUDE_DIRS}) 62 | 63 | ############################################################################### 64 | # TESTS 65 | ############################################################################### 66 | 67 | if(BACKWARD_TESTS) 68 | enable_testing() 69 | 70 | add_library(test_main SHARED test/_test_main.cpp) 71 | 72 | macro(backward_add_test src) 73 | get_filename_component(name ${src} NAME_WE) 74 | set(test_name "test_${name}") 75 | 76 | add_executable(${test_name} ${src} ${ARGN}) 77 | 78 | target_link_libraries(${test_name} PRIVATE Backward::Backward test_main) 79 | 80 | add_test(NAME ${name} COMMAND ${test_name}) 81 | endmacro() 82 | 83 | # Tests without backward.cpp 84 | set(TESTS 85 | test 86 | stacktrace 87 | rectrace 88 | select_signals 89 | ) 90 | 91 | foreach(test ${TESTS}) 92 | backward_add_test(test/${test}.cpp) 93 | endforeach() 94 | 95 | # Tests with backward.cpp 96 | set(TESTS 97 | suicide 98 | ) 99 | 100 | foreach(test ${TESTS}) 101 | backward_add_test(test/${test}.cpp ${BACKWARD_ENABLE}) 102 | endforeach() 103 | endif() 104 | 105 | install( 106 | FILES "backward.hpp" 107 | DESTINATION ${CMAKE_INSTALL_PREFIX}/include 108 | ) 109 | install( 110 | FILES "BackwardConfig.cmake" 111 | DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/backward 112 | ) 113 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Backward-cpp [![badge](https://img.shields.io/badge/conan.io-backward%2F1.3.0-green.svg?logo=data:image/png;base64%2CiVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAA1VBMVEUAAABhlctjlstkl8tlmMtlmMxlmcxmmcxnmsxpnMxpnM1qnc1sn85voM91oM11oc1xotB2oc56pNF6pNJ2ptJ8ptJ8ptN9ptN8p9N5qNJ9p9N9p9R8qtOBqdSAqtOAqtR%2BrNSCrNJ/rdWDrNWCsNWCsNaJs9eLs9iRvNuVvdyVv9yXwd2Zwt6axN6dxt%2Bfx%2BChyeGiyuGjyuCjyuGly%2BGlzOKmzOGozuKoz%2BKqz%2BOq0OOv1OWw1OWw1eWx1eWy1uay1%2Baz1%2Baz1%2Bez2Oe02Oe12ee22ujUGwH3AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgBQkREyOxFIh/AAAAiklEQVQI12NgAAMbOwY4sLZ2NtQ1coVKWNvoc/Eq8XDr2wB5Ig62ekza9vaOqpK2TpoMzOxaFtwqZua2Bm4makIM7OzMAjoaCqYuxooSUqJALjs7o4yVpbowvzSUy87KqSwmxQfnsrPISyFzWeWAXCkpMaBVIC4bmCsOdgiUKwh3JojLgAQ4ZCE0AMm2D29tZwe6AAAAAElFTkSuQmCC)](http://www.conan.io/source/backward/1.3.0/Manu343726/testing) [![Build Status](https://travis-ci.org/bombela/backward-cpp.svg?branch=master)](https://travis-ci.org/bombela/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 | ## Configuration & Dependencies 43 | 44 | ### Integration with CMake 45 | 46 | If you are using CMake and want to use its configuration abilities to save 47 | you the trouble, you can easily integrate Backward, depending on how you obtained 48 | the library. 49 | 50 | #### As a subdirectory: 51 | 52 | In this case you have a subdirectory containing the whole repository of Backward 53 | (eg.: using git-submodules), in this case you can do: 54 | 55 | ``` 56 | add_subdirectory(/path/to/backward-cpp) 57 | 58 | # This will add backward.cpp to your target 59 | add_executable(mytarget mysource.cpp ${BACKWARD_ENABLE}) 60 | 61 | # This will add libraries, definitions and include directories needed by backward 62 | # by setting each property on the target. 63 | add_backward(mytarget) 64 | ``` 65 | 66 | #### Modifying CMAKE_MODULE_PATH 67 | 68 | In this case you can have Backward installed as a subdirectory: 69 | 70 | ``` 71 | list(APPEND CMAKE_MODULE_PATH /path/to/backward-cpp) 72 | find_package(Backward) 73 | 74 | # This will add libraries, definitions and include directories needed by backward 75 | # through an IMPORTED target. 76 | target_link_libraries(mytarget PUBLIC Backward::Backward) 77 | ``` 78 | 79 | Notice that this is equivalent to using the the approach that uses `add_subdirectory()`, 80 | however it uses cmake's [imported target](https://cmake.org/Wiki/CMake/Tutorials/Exporting_and_Importing_Targets) mechanism. 81 | 82 | #### Installation through a regular package manager 83 | 84 | In this case you have obtained Backward through a package manager. 85 | 86 | Packages currently available: 87 | - [conda-forge](https://anaconda.org/conda-forge/backward-cpp) 88 | 89 | ``` 90 | find_package(Backward) 91 | 92 | # This will add libraries, definitions and include directories needed by backward 93 | # through an IMPORTED target. 94 | target_link_libraries(mytarget PUBLIC Backward::Backward) 95 | ``` 96 | 97 | ### Compile with debug info 98 | 99 | You need to compile your project with generation of debug symbols enabled, 100 | usually `-g` with clang++ and g++. 101 | 102 | Note that you can use `-g` with any level of optimization, with modern debug 103 | information encoding like DWARF, it only takes space in the binary (it's not 104 | loaded in memory until your debugger or Backward makes use of it, don't worry), 105 | and it doesn't impact the code generation (at least on GNU/Linux x86\_64 for 106 | what I know). 107 | 108 | If you are missing debug information, the stack trace will lack details about 109 | your sources. 110 | 111 | ### Libraries to read the debug info 112 | 113 | Backward support pretty printed stack traces on GNU/Linux only, it will compile 114 | fine under other platforms but will not do anything. **Pull requests are 115 | welcome :)** 116 | 117 | Also, by default you will get a really basic stack trace, based on the 118 | `backtrace_symbols` API: 119 | 120 | ![default trace](doc/nice.png) 121 | 122 | You will need to install some dependencies to get the ultimate stack trace. Two 123 | libraries are currently supported, the only difference is which one is the 124 | easiest for you to install, so pick your poison: 125 | 126 | #### libbfd from the [GNU/binutils](http://www.gnu.org/software/binutils/) 127 | 128 | apt-get install binutils-dev (or equivalent) 129 | 130 | And do not forget to link with the lib: `g++/clang++ -lbfd ...` 131 | 132 | Then define the following before every inclusion of `backward.hpp` (don't 133 | forget to update `backward.cpp` as well): 134 | 135 | #define BACKWARD_HAS_BFD 1 136 | 137 | #### libdw from the [elfutils](https://fedorahosted.org/elfutils/) 138 | 139 | apt-get install libdw-dev (or equivalent) 140 | 141 | And do not forget to link with the lib and inform Backward to use it: 142 | 143 | #define BACKWARD_HAS_DW 1 144 | 145 | Of course you can simply add the define (`-DBACKWARD_HAS_...=1`) and the 146 | linkage details in your build system and even auto-detect which library is 147 | installed, it's up to you. 148 | 149 | That'ss it, you are all set, you should be getting nice stack traces like the 150 | one at the beginning of this document. 151 | 152 | ## API 153 | 154 | If you don't want to limit yourself to the defaults offered by `backward.cpp`, 155 | and you want to take some random stack traces for whatever reason and pretty 156 | print them the way you love or you decide to send them all to your buddies over 157 | the Internet, you will appreciate the simplicity of Backward's API. 158 | 159 | ### Stacktrace 160 | 161 | The StackTrace class lets you take a "snapshot" of the current stack. 162 | You can use it like this: 163 | 164 | ```c++ 165 | using namespace backward; 166 | StackTrace st; st.load_here(32); 167 | Printer p; p.print(st); 168 | ``` 169 | 170 | The public methods are: 171 | 172 | ```c++ 173 | class StackTrace { public: 174 | // Take a snapshot of the current stack, with at most "trace_cnt_max" 175 | // traces in it. The first trace is the most recent (ie the current 176 | // frame). You can also provide a trace address to load_from() assuming 177 | // the address is a valid stack frame (useful for signal handling traces). 178 | // Both function return size(). 179 | size_t load_here(size_t trace_cnt_max) 180 | size_t load_from(void* address, size_t trace_cnt_max) 181 | 182 | // The number of traces loaded. This can be less than "trace_cnt_max". 183 | size_t size() const 184 | 185 | // A unique id for the thread in which the trace was taken. The value 186 | // 0 means the stack trace comes from the main thread. 187 | size_t thread_id() const 188 | 189 | // Retrieve a trace by index. 0 is the most recent trace, size()-1 is 190 | // the oldest one. 191 | Trace operator[](size_t trace_idx) 192 | }; 193 | ``` 194 | 195 | ### TraceResolver 196 | 197 | The `TraceResolver` does the heavy lifting, and intends to transform a simple 198 | `Trace` from its address into a fully detailed `ResolvedTrace` with the 199 | filename of the source, line numbers, inlined functions and so on. 200 | 201 | You can use it like this: 202 | 203 | ```c++ 204 | using namespace backward; 205 | StackTrace st; st.load_here(32); 206 | 207 | TraceResolver tr; tr.load_stacktrace(st); 208 | for (size_t i = 0; i < st.size(); ++i) { 209 | ResolvedTrace trace = tr.resolve(st[i]); 210 | std::cout << "#" << i 211 | << " " << trace.object_filename 212 | << " " << trace.object_function 213 | << " [" << trace.addr << "]" 214 | << std::endl; 215 | } 216 | ``` 217 | 218 | The public methods are: 219 | 220 | ```c++ 221 | class TraceResolver { public: 222 | // Pre-load whatever is necessary from the stack trace. 223 | template 224 | void load_stacktrace(ST&) 225 | 226 | // Resolve a trace. It takes a ResolvedTrace, because a `Trace` is 227 | // implicitly convertible to it. 228 | ResolvedTrace resolve(ResolvedTrace t) 229 | }; 230 | ``` 231 | 232 | ### SnippetFactory 233 | 234 | The SnippetFactory is a simple helper class to automatically load and cache 235 | source files in order to extract code snippets. 236 | 237 | ```c++ 238 | class SnippetFactory { public: 239 | // A snippet is a list of line numbers and line contents. 240 | typedef std::vector > lines_t; 241 | 242 | // Return a snippet starting at line_start with up to context_size lines. 243 | lines_t get_snippet(const std::string& filename, 244 | size_t line_start, size_t context_size) 245 | 246 | // Return a combined snippet from two different locations and combine them. 247 | // context_size / 2 lines will be extracted from each location. 248 | lines_t get_combined_snippet( 249 | const std::string& filename_a, size_t line_a, 250 | const std::string& filename_b, size_t line_b, 251 | size_t context_size) 252 | 253 | // Tries to return a unified snippet if the two locations from the same 254 | // file are close enough to fit inside one context_size, else returns 255 | // the equivalent of get_combined_snippet(). 256 | lines_t get_coalesced_snippet(const std::string& filename, 257 | size_t line_a, size_t line_b, size_t context_size) 258 | ``` 259 | 260 | ### Printer 261 | 262 | A simpler way to pretty print a stack trace to the terminal. It will 263 | automatically resolve the traces for you: 264 | 265 | ```c++ 266 | using namespace backward; 267 | StackTrace st; st.load_here(32); 268 | Printer p; 269 | p.object = true; 270 | p.color_mode = ColorMode::always; 271 | p.address = true; 272 | p.print(st, stderr); 273 | ``` 274 | 275 | You can set a few options: 276 | 277 | ```c++ 278 | class Printer { public: 279 | // Print a little snippet of code if possible. 280 | bool snippet = true; 281 | 282 | // Colorize the trace 283 | // - ColorMode::automatic: Activate colors if possible. For example, when using a TTY on linux. 284 | // - ColorMode::always: Always use colors. 285 | // - ColorMode::never: Never use colors. 286 | bool color_mode = ColorMode::automatic; 287 | 288 | // Add the addresses of every source location to the trace. 289 | bool address = false; 290 | 291 | // Even if there is a source location, also prints the object 292 | // from where the trace came from. 293 | bool object = false; 294 | 295 | // Resolve and print a stack trace to the given C FILE* object. 296 | // On linux, if the FILE* object is attached to a TTY, 297 | // color will be used if color_mode is set to automatic. 298 | template 299 | FILE* print(StackTrace& st, FILE* fp = stderr); 300 | 301 | // Resolve and print a stack trace to the given std::ostream object. 302 | // Color will only be used if color_mode is set to always. 303 | template 304 | std::ostream& print(ST& st, std::ostream& os); 305 | ``` 306 | 307 | 308 | ### SignalHandling 309 | 310 | A simple helper class that registers for you the most common signals and other 311 | callbacks to segfault, hardware exception, un-handled exception etc. 312 | 313 | `backward.cpp` simply uses it like that: 314 | 315 | ```c++ 316 | backward::SignalHandling sh; 317 | ``` 318 | 319 | Creating the object registers all the different signals and hooks. Destroying 320 | this object doesn't do anything. It exposes only one method: 321 | 322 | ```c++ 323 | bool loaded() const // true if loaded with success 324 | ``` 325 | 326 | ### Trace object 327 | 328 | To keep the memory footprint of a loaded `StackTrace` on the low-side, there a 329 | hierarchy of trace object, from a minimal `Trace `to a `ResolvedTrace`. 330 | 331 | #### Simple trace 332 | 333 | ```c++ 334 | struct Trace { 335 | void* addr; // address of the trace 336 | size_t idx; // its index (0 == most recent) 337 | }; 338 | ``` 339 | 340 | #### Resolved trace 341 | 342 | A `ResolvedTrace` should contains a maximum of details about the location of 343 | the trace in the source code. Note that not all fields might be set. 344 | 345 | ```c++ 346 | struct ResolvedTrace: public Trace { 347 | 348 | struct SourceLoc { 349 | std::string function; 350 | std::string filename; 351 | size_t line; 352 | size_t col; 353 | }; 354 | 355 | // In which binary object this trace is located. 356 | std::string object_filename; 357 | 358 | // The function in the object that contains the trace. This is not the same 359 | // as source.function which can be an function inlined in object_function. 360 | std::string object_function; 361 | 362 | // The source location of this trace. It is possible for filename to be 363 | // empty and for line/col to be invalid (value 0) if this information 364 | // couldn't be deduced, for example if there is no debug information in the 365 | // binary object. 366 | SourceLoc source; 367 | 368 | // An optional list of "inliners". All of these sources locations where 369 | // inlined in the source location of the trace (the attribute right above). 370 | // This is especially useful when you compile with optimizations turned on. 371 | typedef std::vector source_locs_t; 372 | source_locs_t inliners; 373 | }; 374 | ``` 375 | 376 | ## Contact and copyright 377 | 378 | François-Xavier Bourlet 379 | 380 | Copyright 2013-2017 Google Inc. All Rights Reserved. 381 | MIT License. 382 | 383 | ### Disclaimer 384 | 385 | Although this project is owned by Google Inc. this is not a Google supported or 386 | affiliated project. 387 | -------------------------------------------------------------------------------- /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 library 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 | #include "backward.hpp" 27 | 28 | namespace backward { 29 | 30 | backward::SignalHandling sh; 31 | 32 | } // namespace backward 33 | -------------------------------------------------------------------------------- /backward.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * backward.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 | #ifndef H_6B9572DA_A64B_49E6_B234_051480991C89 25 | #define H_6B9572DA_A64B_49E6_B234_051480991C89 26 | 27 | #ifndef __cplusplus 28 | # error "It's not going to compile without a C++ compiler..." 29 | #endif 30 | 31 | #if defined(BACKWARD_CXX11) 32 | #elif defined(BACKWARD_CXX98) 33 | #else 34 | # if __cplusplus >= 201103L 35 | # define BACKWARD_CXX11 36 | # define BACKWARD_ATLEAST_CXX11 37 | # define BACKWARD_ATLEAST_CXX98 38 | # else 39 | # define BACKWARD_CXX98 40 | # define BACKWARD_ATLEAST_CXX98 41 | # endif 42 | #endif 43 | 44 | // You can define one of the following (or leave it to the auto-detection): 45 | // 46 | // #define BACKWARD_SYSTEM_LINUX 47 | // - specialization for linux 48 | // 49 | // #define BACKWARD_SYSTEM_UNKNOWN 50 | // - placebo implementation, does nothing. 51 | // 52 | #if defined(BACKWARD_SYSTEM_LINUX) 53 | #elif defined(BACKWARD_SYSTEM_UNKNOWN) 54 | #else 55 | # if defined(__linux) 56 | # define BACKWARD_SYSTEM_LINUX 57 | # else 58 | # define BACKWARD_SYSTEM_UNKNOWN 59 | # endif 60 | #endif 61 | 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | 77 | #if defined(BACKWARD_SYSTEM_LINUX) 78 | 79 | // On linux, backtrace can back-trace or "walk" the stack using the following 80 | // libraries: 81 | // 82 | // #define BACKWARD_HAS_UNWIND 1 83 | // - unwind comes from libgcc, but I saw an equivalent inside clang itself. 84 | // - with unwind, the stacktrace is as accurate as it can possibly be, since 85 | // this is used by the C++ runtine in gcc/clang for stack unwinding on 86 | // exception. 87 | // - normally libgcc is already linked to your program by default. 88 | // 89 | // #define BACKWARD_HAS_BACKTRACE == 1 90 | // - backtrace seems to be a little bit more portable than libunwind, but on 91 | // linux, it uses unwind anyway, but abstract away a tiny information that is 92 | // sadly really important in order to get perfectly accurate stack traces. 93 | // - backtrace is part of the (e)glib library. 94 | // 95 | // The default is: 96 | // #define BACKWARD_HAS_UNWIND == 1 97 | // 98 | // Note that only one of the define should be set to 1 at a time. 99 | // 100 | # if BACKWARD_HAS_UNWIND == 1 101 | # elif BACKWARD_HAS_BACKTRACE == 1 102 | # else 103 | # undef BACKWARD_HAS_UNWIND 104 | # define BACKWARD_HAS_UNWIND 1 105 | # undef BACKWARD_HAS_BACKTRACE 106 | # define BACKWARD_HAS_BACKTRACE 0 107 | # endif 108 | 109 | // On linux, backward can extract detailed information about a stack trace 110 | // using one of the following libraries: 111 | // 112 | // #define BACKWARD_HAS_DW 1 113 | // - libdw gives you the most juicy details out of your stack traces: 114 | // - object filename 115 | // - function name 116 | // - source filename 117 | // - line and column numbers 118 | // - source code snippet (assuming the file is accessible) 119 | // - variables name and values (if not optimized out) 120 | // - You need to link with the lib "dw": 121 | // - apt-get install libdw-dev 122 | // - g++/clang++ -ldw ... 123 | // 124 | // #define BACKWARD_HAS_BFD 1 125 | // - With libbfd, you get a fair amount of details: 126 | // - object filename 127 | // - function name 128 | // - source filename 129 | // - line numbers 130 | // - source code snippet (assuming the file is accessible) 131 | // - You need to link with the lib "bfd": 132 | // - apt-get install binutils-dev 133 | // - g++/clang++ -lbfd ... 134 | // 135 | // #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 136 | // - backtrace provides minimal details for a stack trace: 137 | // - object filename 138 | // - function name 139 | // - backtrace is part of the (e)glib library. 140 | // 141 | // The default is: 142 | // #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 143 | // 144 | // Note that only one of the define should be set to 1 at a time. 145 | // 146 | # if BACKWARD_HAS_DW == 1 147 | # elif BACKWARD_HAS_BFD == 1 148 | # elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 149 | # else 150 | # undef BACKWARD_HAS_DW 151 | # define BACKWARD_HAS_DW 0 152 | # undef BACKWARD_HAS_BFD 153 | # define BACKWARD_HAS_BFD 0 154 | # undef BACKWARD_HAS_BACKTRACE_SYMBOL 155 | # define BACKWARD_HAS_BACKTRACE_SYMBOL 1 156 | # endif 157 | 158 | 159 | # if BACKWARD_HAS_UNWIND == 1 160 | 161 | # include 162 | // while gcc's unwind.h defines something like that: 163 | // extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); 164 | // extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *); 165 | // 166 | // clang's unwind.h defines something like this: 167 | // uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context); 168 | // 169 | // Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we 170 | // cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr 171 | // anyway. 172 | // 173 | // Luckily we can play on the fact that the guard macros have a different name: 174 | #ifdef __CLANG_UNWIND_H 175 | // In fact, this function still comes from libgcc (on my different linux boxes, 176 | // clang links against libgcc). 177 | # include 178 | extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context*, int*); 179 | #endif 180 | 181 | # endif 182 | 183 | # include 184 | # include 185 | # include 186 | # include 187 | # include 188 | # include 189 | # include 190 | 191 | # if BACKWARD_HAS_BFD == 1 192 | // NOTE: defining PACKAGE{,_VERSION} is required before including 193 | // bfd.h on some platforms, see also: 194 | // https://sourceware.org/bugzilla/show_bug.cgi?id=14243 195 | # ifndef PACKAGE 196 | # define PACKAGE 197 | # endif 198 | # ifndef PACKAGE_VERSION 199 | # define PACKAGE_VERSION 200 | # endif 201 | # include 202 | # ifndef _GNU_SOURCE 203 | # define _GNU_SOURCE 204 | # include 205 | # undef _GNU_SOURCE 206 | # else 207 | # include 208 | # endif 209 | # endif 210 | 211 | # if BACKWARD_HAS_DW == 1 212 | # include 213 | # include 214 | # include 215 | # endif 216 | 217 | # if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) 218 | // then we shall rely on backtrace 219 | # include 220 | # endif 221 | 222 | #endif // defined(BACKWARD_SYSTEM_LINUX) 223 | 224 | #ifdef BACKWARD_ATLEAST_CXX11 225 | # include 226 | # include // for std::swap 227 | namespace backward { 228 | namespace details { 229 | template 230 | struct hashtable { 231 | typedef std::unordered_map type; 232 | }; 233 | using std::move; 234 | } // namespace details 235 | } // namespace backward 236 | #else // NOT BACKWARD_ATLEAST_CXX11 237 | # include 238 | namespace backward { 239 | namespace details { 240 | template 241 | struct hashtable { 242 | typedef std::map type; 243 | }; 244 | template 245 | const T& move(const T& v) { return v; } 246 | template 247 | T& move(T& v) { return v; } 248 | } // namespace details 249 | } // namespace backward 250 | #endif // BACKWARD_ATLEAST_CXX11 251 | 252 | namespace backward { 253 | 254 | namespace system_tag { 255 | struct linux_tag; // seems that I cannot call that "linux" because the name 256 | // is already defined... so I am adding _tag everywhere. 257 | struct unknown_tag; 258 | 259 | #if defined(BACKWARD_SYSTEM_LINUX) 260 | typedef linux_tag current_tag; 261 | #elif defined(BACKWARD_SYSTEM_UNKNOWN) 262 | typedef unknown_tag current_tag; 263 | #else 264 | # error "May I please get my system defines?" 265 | #endif 266 | } // namespace system_tag 267 | 268 | 269 | namespace trace_resolver_tag { 270 | #ifdef BACKWARD_SYSTEM_LINUX 271 | struct libdw; 272 | struct libbfd; 273 | struct backtrace_symbol; 274 | 275 | # if BACKWARD_HAS_DW == 1 276 | typedef libdw current; 277 | # elif BACKWARD_HAS_BFD == 1 278 | typedef libbfd current; 279 | # elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 280 | typedef backtrace_symbol current; 281 | # else 282 | # error "You shall not pass, until you know what you want." 283 | # endif 284 | #endif // BACKWARD_SYSTEM_LINUX 285 | } // namespace trace_resolver_tag 286 | 287 | 288 | namespace details { 289 | 290 | template 291 | struct rm_ptr { typedef T type; }; 292 | 293 | template 294 | struct rm_ptr { typedef T type; }; 295 | 296 | template 297 | struct rm_ptr { typedef const T type; }; 298 | 299 | template 300 | struct deleter { 301 | template 302 | void operator()(U& ptr) const { 303 | (*F)(ptr); 304 | } 305 | }; 306 | 307 | template 308 | struct default_delete { 309 | void operator()(T& ptr) const { 310 | delete ptr; 311 | } 312 | }; 313 | 314 | template > 315 | class handle { 316 | struct dummy; 317 | T _val; 318 | bool _empty; 319 | 320 | #ifdef BACKWARD_ATLEAST_CXX11 321 | handle(const handle&) = delete; 322 | handle& operator=(const handle&) = delete; 323 | #endif 324 | 325 | public: 326 | ~handle() { 327 | if (!_empty) { 328 | Deleter()(_val); 329 | } 330 | } 331 | 332 | explicit handle(): _val(), _empty(true) {} 333 | explicit handle(T val): _val(val), _empty(false) {} 334 | 335 | #ifdef BACKWARD_ATLEAST_CXX11 336 | handle(handle&& from): _empty(true) { 337 | swap(from); 338 | } 339 | handle& operator=(handle&& from) { 340 | swap(from); return *this; 341 | } 342 | #else 343 | explicit handle(const handle& from): _empty(true) { 344 | // some sort of poor man's move semantic. 345 | swap(const_cast(from)); 346 | } 347 | handle& operator=(const handle& from) { 348 | // some sort of poor man's move semantic. 349 | swap(const_cast(from)); return *this; 350 | } 351 | #endif 352 | 353 | void reset(T new_val) { 354 | handle tmp(new_val); 355 | swap(tmp); 356 | } 357 | operator const dummy*() const { 358 | if (_empty) { 359 | return 0; 360 | } 361 | return reinterpret_cast(_val); 362 | } 363 | T get() { 364 | return _val; 365 | } 366 | T release() { 367 | _empty = true; 368 | return _val; 369 | } 370 | void swap(handle& b) { 371 | using std::swap; 372 | swap(b._val, _val); // can throw, we are safe here. 373 | swap(b._empty, _empty); // should not throw: if you cannot swap two 374 | // bools without throwing... It's a lost cause anyway! 375 | } 376 | 377 | T operator->() { return _val; } 378 | const T operator->() const { return _val; } 379 | 380 | typedef typename rm_ptr::type& ref_t; 381 | typedef const typename rm_ptr::type& const_ref_t; 382 | ref_t operator*() { return *_val; } 383 | const_ref_t operator*() const { return *_val; } 384 | ref_t operator[](size_t idx) { return _val[idx]; } 385 | 386 | // Watch out, we've got a badass over here 387 | T* operator&() { 388 | _empty = false; 389 | return &_val; 390 | } 391 | }; 392 | 393 | // Default demangler implementation (do nothing). 394 | template 395 | struct demangler_impl { 396 | static std::string demangle(const char* funcname) { 397 | return funcname; 398 | } 399 | }; 400 | 401 | #ifdef BACKWARD_SYSTEM_LINUX 402 | 403 | template <> 404 | struct demangler_impl { 405 | demangler_impl(): _demangle_buffer_length(0) {} 406 | 407 | std::string demangle(const char* funcname) { 408 | using namespace details; 409 | _demangle_buffer.reset( 410 | abi::__cxa_demangle(funcname, _demangle_buffer.release(), 411 | &_demangle_buffer_length, 0) 412 | ); 413 | if (_demangle_buffer) { 414 | return _demangle_buffer.get(); 415 | } 416 | return funcname; 417 | } 418 | 419 | private: 420 | details::handle _demangle_buffer; 421 | size_t _demangle_buffer_length; 422 | }; 423 | 424 | #endif // BACKWARD_SYSTEM_LINUX 425 | 426 | struct demangler: 427 | public demangler_impl {}; 428 | 429 | } // namespace details 430 | 431 | /*************** A TRACE ***************/ 432 | 433 | struct Trace { 434 | void* addr; 435 | size_t idx; 436 | 437 | Trace(): 438 | addr(0), idx(0) {} 439 | 440 | explicit Trace(void* _addr, size_t _idx): 441 | addr(_addr), idx(_idx) {} 442 | }; 443 | 444 | struct ResolvedTrace: public Trace { 445 | 446 | struct SourceLoc { 447 | std::string function; 448 | std::string filename; 449 | unsigned line; 450 | unsigned col; 451 | 452 | SourceLoc(): line(0), col(0) {} 453 | 454 | bool operator==(const SourceLoc& b) const { 455 | return function == b.function 456 | && filename == b.filename 457 | && line == b.line 458 | && col == b.col; 459 | } 460 | 461 | bool operator!=(const SourceLoc& b) const { 462 | return !(*this == b); 463 | } 464 | }; 465 | 466 | // In which binary object this trace is located. 467 | std::string object_filename; 468 | 469 | // The function in the object that contain the trace. This is not the same 470 | // as source.function which can be an function inlined in object_function. 471 | std::string object_function; 472 | 473 | // The source location of this trace. It is possible for filename to be 474 | // empty and for line/col to be invalid (value 0) if this information 475 | // couldn't be deduced, for example if there is no debug information in the 476 | // binary object. 477 | SourceLoc source; 478 | 479 | // An optionals list of "inliners". All the successive sources location 480 | // from where the source location of the trace (the attribute right above) 481 | // is inlined. It is especially useful when you compiled with optimization. 482 | typedef std::vector source_locs_t; 483 | source_locs_t inliners; 484 | 485 | ResolvedTrace(): 486 | Trace() {} 487 | ResolvedTrace(const Trace& mini_trace): 488 | Trace(mini_trace) {} 489 | }; 490 | 491 | /*************** STACK TRACE ***************/ 492 | 493 | // default implemention. 494 | template 495 | class StackTraceImpl { 496 | public: 497 | size_t size() const { return 0; } 498 | Trace operator[](size_t) { return Trace(); } 499 | size_t load_here(size_t=0) { return 0; } 500 | size_t load_from(void*, size_t=0) { return 0; } 501 | size_t thread_id() const { return 0; } 502 | void skip_n_firsts(size_t) { } 503 | }; 504 | 505 | #ifdef BACKWARD_SYSTEM_LINUX 506 | 507 | class StackTraceLinuxImplBase { 508 | public: 509 | StackTraceLinuxImplBase(): _thread_id(0), _skip(0) {} 510 | 511 | size_t thread_id() const { 512 | return _thread_id; 513 | } 514 | 515 | void skip_n_firsts(size_t n) { _skip = n; } 516 | 517 | protected: 518 | void load_thread_info() { 519 | _thread_id = (size_t)syscall(SYS_gettid); 520 | if (_thread_id == (size_t) getpid()) { 521 | // If the thread is the main one, let's hide that. 522 | // I like to keep little secret sometimes. 523 | _thread_id = 0; 524 | } 525 | } 526 | 527 | size_t skip_n_firsts() const { return _skip; } 528 | 529 | private: 530 | size_t _thread_id; 531 | size_t _skip; 532 | }; 533 | 534 | class StackTraceLinuxImplHolder: public StackTraceLinuxImplBase { 535 | public: 536 | size_t size() const { 537 | return _stacktrace.size() ? _stacktrace.size() - skip_n_firsts() : 0; 538 | } 539 | Trace operator[](size_t idx) { 540 | if (idx >= size()) { 541 | return Trace(); 542 | } 543 | return Trace(_stacktrace[idx + skip_n_firsts()], idx); 544 | } 545 | void** begin() { 546 | if (size()) { 547 | return &_stacktrace[skip_n_firsts()]; 548 | } 549 | return 0; 550 | } 551 | 552 | protected: 553 | std::vector _stacktrace; 554 | }; 555 | 556 | 557 | #if BACKWARD_HAS_UNWIND == 1 558 | 559 | namespace details { 560 | 561 | template 562 | class Unwinder { 563 | public: 564 | size_t operator()(F& f, size_t depth) { 565 | _f = &f; 566 | _index = -1; 567 | _depth = depth; 568 | _Unwind_Backtrace(&this->backtrace_trampoline, this); 569 | return _index; 570 | } 571 | 572 | private: 573 | F* _f; 574 | ssize_t _index; 575 | size_t _depth; 576 | 577 | static _Unwind_Reason_Code backtrace_trampoline( 578 | _Unwind_Context* ctx, void *self) { 579 | return ((Unwinder*)self)->backtrace(ctx); 580 | } 581 | 582 | _Unwind_Reason_Code backtrace(_Unwind_Context* ctx) { 583 | if (_index >= 0 && static_cast(_index) >= _depth) 584 | return _URC_END_OF_STACK; 585 | 586 | int ip_before_instruction = 0; 587 | uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction); 588 | 589 | if (!ip_before_instruction) { 590 | // calculating 0-1 for unsigned, looks like a possible bug to sanitiziers, so let's do it explicitly: 591 | if (ip==0) { 592 | ip = std::numeric_limits::max(); // set it to 0xffff... (as from casting 0-1) 593 | } else { 594 | ip -= 1; // else just normally decrement it (no overflow/underflow will happen) 595 | } 596 | } 597 | 598 | if (_index >= 0) { // ignore first frame. 599 | (*_f)(_index, (void*)ip); 600 | } 601 | _index += 1; 602 | return _URC_NO_REASON; 603 | } 604 | }; 605 | 606 | template 607 | size_t unwind(F f, size_t depth) { 608 | Unwinder unwinder; 609 | return unwinder(f, depth); 610 | } 611 | 612 | } // namespace details 613 | 614 | 615 | template <> 616 | class StackTraceImpl: public StackTraceLinuxImplHolder { 617 | public: 618 | __attribute__ ((noinline)) // TODO use some macro 619 | size_t load_here(size_t depth=32) { 620 | load_thread_info(); 621 | if (depth == 0) { 622 | return 0; 623 | } 624 | _stacktrace.resize(depth); 625 | size_t trace_cnt = details::unwind(callback(*this), depth); 626 | _stacktrace.resize(trace_cnt); 627 | skip_n_firsts(0); 628 | return size(); 629 | } 630 | size_t load_from(void* addr, size_t depth=32) { 631 | load_here(depth + 8); 632 | 633 | for (size_t i = 0; i < _stacktrace.size(); ++i) { 634 | if (_stacktrace[i] == addr) { 635 | skip_n_firsts(i); 636 | break; 637 | } 638 | } 639 | 640 | _stacktrace.resize(std::min(_stacktrace.size(), 641 | skip_n_firsts() + depth)); 642 | return size(); 643 | } 644 | 645 | private: 646 | struct callback { 647 | StackTraceImpl& self; 648 | callback(StackTraceImpl& _self): self(_self) {} 649 | 650 | void operator()(size_t idx, void* addr) { 651 | self._stacktrace[idx] = addr; 652 | } 653 | }; 654 | }; 655 | 656 | 657 | #else // BACKWARD_HAS_UNWIND == 0 658 | 659 | template <> 660 | class StackTraceImpl: public StackTraceLinuxImplHolder { 661 | public: 662 | __attribute__ ((noinline)) // TODO use some macro 663 | size_t load_here(size_t depth=32) { 664 | load_thread_info(); 665 | if (depth == 0) { 666 | return 0; 667 | } 668 | _stacktrace.resize(depth + 1); 669 | size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size()); 670 | _stacktrace.resize(trace_cnt); 671 | skip_n_firsts(1); 672 | return size(); 673 | } 674 | 675 | size_t load_from(void* addr, size_t depth=32) { 676 | load_here(depth + 8); 677 | 678 | for (size_t i = 0; i < _stacktrace.size(); ++i) { 679 | if (_stacktrace[i] == addr) { 680 | skip_n_firsts(i); 681 | _stacktrace[i] = (void*)( (uintptr_t)_stacktrace[i] + 1); 682 | break; 683 | } 684 | } 685 | 686 | _stacktrace.resize(std::min(_stacktrace.size(), 687 | skip_n_firsts() + depth)); 688 | return size(); 689 | } 690 | }; 691 | 692 | #endif // BACKWARD_HAS_UNWIND 693 | #endif // BACKWARD_SYSTEM_LINUX 694 | 695 | class StackTrace: 696 | public StackTraceImpl {}; 697 | 698 | /*************** TRACE RESOLVER ***************/ 699 | 700 | template 701 | class TraceResolverImpl; 702 | 703 | #ifdef BACKWARD_SYSTEM_UNKNOWN 704 | 705 | template <> 706 | class TraceResolverImpl { 707 | public: 708 | template 709 | void load_stacktrace(ST&) {} 710 | ResolvedTrace resolve(ResolvedTrace t) { 711 | return t; 712 | } 713 | }; 714 | 715 | #endif 716 | 717 | #ifdef BACKWARD_SYSTEM_LINUX 718 | 719 | class TraceResolverLinuxImplBase { 720 | protected: 721 | std::string demangle(const char* funcname) { 722 | return _demangler.demangle(funcname); 723 | } 724 | 725 | private: 726 | details::demangler _demangler; 727 | }; 728 | 729 | template 730 | class TraceResolverLinuxImpl; 731 | 732 | #if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 733 | 734 | template <> 735 | class TraceResolverLinuxImpl: 736 | public TraceResolverLinuxImplBase { 737 | public: 738 | template 739 | void load_stacktrace(ST& st) { 740 | using namespace details; 741 | if (st.size() == 0) { 742 | return; 743 | } 744 | _symbols.reset( 745 | backtrace_symbols(st.begin(), (int)st.size()) 746 | ); 747 | } 748 | 749 | ResolvedTrace resolve(ResolvedTrace trace) { 750 | char* filename = _symbols[trace.idx]; 751 | char* funcname = filename; 752 | while (*funcname && *funcname != '(') { 753 | funcname += 1; 754 | } 755 | trace.object_filename.assign(filename, funcname); // ok even if funcname is the ending \0 (then we assign entire string) 756 | 757 | if (*funcname) { // if it's not end of string (e.g. from last frame ip==0) 758 | funcname += 1; 759 | char* funcname_end = funcname; 760 | while (*funcname_end && *funcname_end != ')' && *funcname_end != '+') { 761 | funcname_end += 1; 762 | } 763 | *funcname_end = '\0'; 764 | trace.object_function = this->demangle(funcname); 765 | trace.source.function = trace.object_function; // we cannot do better. 766 | } 767 | return trace; 768 | } 769 | 770 | private: 771 | details::handle _symbols; 772 | }; 773 | 774 | #endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1 775 | 776 | #if BACKWARD_HAS_BFD == 1 777 | 778 | template <> 779 | class TraceResolverLinuxImpl: 780 | public TraceResolverLinuxImplBase { 781 | public: 782 | TraceResolverLinuxImpl(): _bfd_loaded(false) {} 783 | 784 | template 785 | void load_stacktrace(ST&) {} 786 | 787 | ResolvedTrace resolve(ResolvedTrace trace) { 788 | Dl_info symbol_info; 789 | 790 | // trace.addr is a virtual address in memory pointing to some code. 791 | // Let's try to find from which loaded object it comes from. 792 | // The loaded object can be yourself btw. 793 | if (!dladdr(trace.addr, &symbol_info)) { 794 | return trace; // dat broken trace... 795 | } 796 | 797 | // Now we get in symbol_info: 798 | // .dli_fname: 799 | // pathname of the shared object that contains the address. 800 | // .dli_fbase: 801 | // where the object is loaded in memory. 802 | // .dli_sname: 803 | // the name of the nearest symbol to trace.addr, we expect a 804 | // function name. 805 | // .dli_saddr: 806 | // the exact address corresponding to .dli_sname. 807 | 808 | if (symbol_info.dli_sname) { 809 | trace.object_function = demangle(symbol_info.dli_sname); 810 | } 811 | 812 | if (!symbol_info.dli_fname) { 813 | return trace; 814 | } 815 | 816 | trace.object_filename = symbol_info.dli_fname; 817 | bfd_fileobject& fobj = load_object_with_bfd(symbol_info.dli_fname); 818 | if (!fobj.handle) { 819 | return trace; // sad, we couldn't load the object :( 820 | } 821 | 822 | 823 | find_sym_result* details_selected; // to be filled. 824 | 825 | // trace.addr is the next instruction to be executed after returning 826 | // from the nested stack frame. In C++ this usually relate to the next 827 | // statement right after the function call that leaded to a new stack 828 | // frame. This is not usually what you want to see when printing out a 829 | // stacktrace... 830 | find_sym_result details_call_site = find_symbol_details(fobj, 831 | trace.addr, symbol_info.dli_fbase); 832 | details_selected = &details_call_site; 833 | 834 | #if BACKWARD_HAS_UNWIND == 0 835 | // ...this is why we also try to resolve the symbol that is right 836 | // before the return address. If we are lucky enough, we will get the 837 | // line of the function that was called. But if the code is optimized, 838 | // we might get something absolutely not related since the compiler 839 | // can reschedule the return address with inline functions and 840 | // tail-call optimisation (among other things that I don't even know 841 | // or cannot even dream about with my tiny limited brain). 842 | find_sym_result details_adjusted_call_site = find_symbol_details(fobj, 843 | (void*) (uintptr_t(trace.addr) - 1), 844 | symbol_info.dli_fbase); 845 | 846 | // In debug mode, we should always get the right thing(TM). 847 | if (details_call_site.found && details_adjusted_call_site.found) { 848 | // Ok, we assume that details_adjusted_call_site is a better estimation. 849 | details_selected = &details_adjusted_call_site; 850 | trace.addr = (void*) (uintptr_t(trace.addr) - 1); 851 | } 852 | 853 | if (details_selected == &details_call_site && details_call_site.found) { 854 | // we have to re-resolve the symbol in order to reset some 855 | // internal state in BFD... so we can call backtrace_inliners 856 | // thereafter... 857 | details_call_site = find_symbol_details(fobj, trace.addr, 858 | symbol_info.dli_fbase); 859 | } 860 | #endif // BACKWARD_HAS_UNWIND 861 | 862 | if (details_selected->found) { 863 | if (details_selected->filename) { 864 | trace.source.filename = details_selected->filename; 865 | } 866 | trace.source.line = details_selected->line; 867 | 868 | if (details_selected->funcname) { 869 | // this time we get the name of the function where the code is 870 | // located, instead of the function were the address is 871 | // located. In short, if the code was inlined, we get the 872 | // function correspoding to the code. Else we already got in 873 | // trace.function. 874 | trace.source.function = demangle(details_selected->funcname); 875 | 876 | if (!symbol_info.dli_sname) { 877 | // for the case dladdr failed to find the symbol name of 878 | // the function, we might as well try to put something 879 | // here. 880 | trace.object_function = trace.source.function; 881 | } 882 | } 883 | 884 | // Maybe the source of the trace got inlined inside the function 885 | // (trace.source.function). Let's see if we can get all the inlined 886 | // calls along the way up to the initial call site. 887 | trace.inliners = backtrace_inliners(fobj, *details_selected); 888 | 889 | #if 0 890 | if (trace.inliners.size() == 0) { 891 | // Maybe the trace was not inlined... or maybe it was and we 892 | // are lacking the debug information. Let's try to make the 893 | // world better and see if we can get the line number of the 894 | // function (trace.source.function) now. 895 | // 896 | // We will get the location of where the function start (to be 897 | // exact: the first instruction that really start the 898 | // function), not where the name of the function is defined. 899 | // This can be quite far away from the name of the function 900 | // btw. 901 | // 902 | // If the source of the function is the same as the source of 903 | // the trace, we cannot say if the trace was really inlined or 904 | // not. However, if the filename of the source is different 905 | // between the function and the trace... we can declare it as 906 | // an inliner. This is not 100% accurate, but better than 907 | // nothing. 908 | 909 | if (symbol_info.dli_saddr) { 910 | find_sym_result details = find_symbol_details(fobj, 911 | symbol_info.dli_saddr, 912 | symbol_info.dli_fbase); 913 | 914 | if (details.found) { 915 | ResolvedTrace::SourceLoc diy_inliner; 916 | diy_inliner.line = details.line; 917 | if (details.filename) { 918 | diy_inliner.filename = details.filename; 919 | } 920 | if (details.funcname) { 921 | diy_inliner.function = demangle(details.funcname); 922 | } else { 923 | diy_inliner.function = trace.source.function; 924 | } 925 | if (diy_inliner != trace.source) { 926 | trace.inliners.push_back(diy_inliner); 927 | } 928 | } 929 | } 930 | } 931 | #endif 932 | } 933 | 934 | return trace; 935 | } 936 | 937 | private: 938 | bool _bfd_loaded; 939 | 940 | typedef details::handle 942 | > bfd_handle_t; 943 | 944 | typedef details::handle bfd_symtab_t; 945 | 946 | 947 | struct bfd_fileobject { 948 | bfd_handle_t handle; 949 | bfd_vma base_addr; 950 | bfd_symtab_t symtab; 951 | bfd_symtab_t dynamic_symtab; 952 | }; 953 | 954 | typedef details::hashtable::type 955 | fobj_bfd_map_t; 956 | fobj_bfd_map_t _fobj_bfd_map; 957 | 958 | bfd_fileobject& load_object_with_bfd(const std::string& filename_object) { 959 | using namespace details; 960 | 961 | if (!_bfd_loaded) { 962 | using namespace details; 963 | bfd_init(); 964 | _bfd_loaded = true; 965 | } 966 | 967 | fobj_bfd_map_t::iterator it = 968 | _fobj_bfd_map.find(filename_object); 969 | if (it != _fobj_bfd_map.end()) { 970 | return it->second; 971 | } 972 | 973 | // this new object is empty for now. 974 | bfd_fileobject& r = _fobj_bfd_map[filename_object]; 975 | 976 | // we do the work temporary in this one; 977 | bfd_handle_t bfd_handle; 978 | 979 | int fd = open(filename_object.c_str(), O_RDONLY); 980 | bfd_handle.reset( 981 | bfd_fdopenr(filename_object.c_str(), "default", fd) 982 | ); 983 | if (!bfd_handle) { 984 | close(fd); 985 | return r; 986 | } 987 | 988 | if (!bfd_check_format(bfd_handle.get(), bfd_object)) { 989 | return r; // not an object? You lose. 990 | } 991 | 992 | if ((bfd_get_file_flags(bfd_handle.get()) & HAS_SYMS) == 0) { 993 | return r; // that's what happen when you forget to compile in debug. 994 | } 995 | 996 | ssize_t symtab_storage_size = 997 | bfd_get_symtab_upper_bound(bfd_handle.get()); 998 | 999 | ssize_t dyn_symtab_storage_size = 1000 | bfd_get_dynamic_symtab_upper_bound(bfd_handle.get()); 1001 | 1002 | if (symtab_storage_size <= 0 && dyn_symtab_storage_size <= 0) { 1003 | return r; // weird, is the file is corrupted? 1004 | } 1005 | 1006 | bfd_symtab_t symtab, dynamic_symtab; 1007 | ssize_t symcount = 0, dyn_symcount = 0; 1008 | 1009 | if (symtab_storage_size > 0) { 1010 | symtab.reset( 1011 | (bfd_symbol**) malloc(symtab_storage_size) 1012 | ); 1013 | symcount = bfd_canonicalize_symtab( 1014 | bfd_handle.get(), symtab.get() 1015 | ); 1016 | } 1017 | 1018 | if (dyn_symtab_storage_size > 0) { 1019 | dynamic_symtab.reset( 1020 | (bfd_symbol**) malloc(dyn_symtab_storage_size) 1021 | ); 1022 | dyn_symcount = bfd_canonicalize_dynamic_symtab( 1023 | bfd_handle.get(), dynamic_symtab.get() 1024 | ); 1025 | } 1026 | 1027 | 1028 | if (symcount <= 0 && dyn_symcount <= 0) { 1029 | return r; // damned, that's a stripped file that you got there! 1030 | } 1031 | 1032 | r.handle = move(bfd_handle); 1033 | r.symtab = move(symtab); 1034 | r.dynamic_symtab = move(dynamic_symtab); 1035 | return r; 1036 | } 1037 | 1038 | struct find_sym_result { 1039 | bool found; 1040 | const char* filename; 1041 | const char* funcname; 1042 | unsigned int line; 1043 | }; 1044 | 1045 | struct find_sym_context { 1046 | TraceResolverLinuxImpl* self; 1047 | bfd_fileobject* fobj; 1048 | void* addr; 1049 | void* base_addr; 1050 | find_sym_result result; 1051 | }; 1052 | 1053 | find_sym_result find_symbol_details(bfd_fileobject& fobj, void* addr, 1054 | void* base_addr) { 1055 | find_sym_context context; 1056 | context.self = this; 1057 | context.fobj = &fobj; 1058 | context.addr = addr; 1059 | context.base_addr = base_addr; 1060 | context.result.found = false; 1061 | bfd_map_over_sections(fobj.handle.get(), &find_in_section_trampoline, 1062 | (void*)&context); 1063 | return context.result; 1064 | } 1065 | 1066 | static void find_in_section_trampoline(bfd*, asection* section, 1067 | void* data) { 1068 | find_sym_context* context = static_cast(data); 1069 | context->self->find_in_section( 1070 | reinterpret_cast(context->addr), 1071 | reinterpret_cast(context->base_addr), 1072 | *context->fobj, 1073 | section, context->result 1074 | ); 1075 | } 1076 | 1077 | void find_in_section(bfd_vma addr, bfd_vma base_addr, 1078 | bfd_fileobject& fobj, asection* section, find_sym_result& result) 1079 | { 1080 | if (result.found) return; 1081 | 1082 | if ((bfd_get_section_flags(fobj.handle.get(), section) 1083 | & SEC_ALLOC) == 0) 1084 | return; // a debug section is never loaded automatically. 1085 | 1086 | bfd_vma sec_addr = bfd_get_section_vma(fobj.handle.get(), section); 1087 | bfd_size_type size = bfd_get_section_size(section); 1088 | 1089 | // are we in the boundaries of the section? 1090 | if (addr < sec_addr || addr >= sec_addr + size) { 1091 | addr -= base_addr; // oups, a relocated object, lets try again... 1092 | if (addr < sec_addr || addr >= sec_addr + size) { 1093 | return; 1094 | } 1095 | } 1096 | 1097 | if (!result.found && fobj.symtab) { 1098 | result.found = bfd_find_nearest_line(fobj.handle.get(), section, 1099 | fobj.symtab.get(), addr - sec_addr, &result.filename, 1100 | &result.funcname, &result.line); 1101 | } 1102 | 1103 | if (!result.found && fobj.dynamic_symtab) { 1104 | result.found = bfd_find_nearest_line(fobj.handle.get(), section, 1105 | fobj.dynamic_symtab.get(), addr - sec_addr, 1106 | &result.filename, &result.funcname, &result.line); 1107 | } 1108 | 1109 | } 1110 | 1111 | ResolvedTrace::source_locs_t backtrace_inliners(bfd_fileobject& fobj, 1112 | find_sym_result previous_result) { 1113 | // This function can be called ONLY after a SUCCESSFUL call to 1114 | // find_symbol_details. The state is global to the bfd_handle. 1115 | ResolvedTrace::source_locs_t results; 1116 | while (previous_result.found) { 1117 | find_sym_result result; 1118 | result.found = bfd_find_inliner_info(fobj.handle.get(), 1119 | &result.filename, &result.funcname, &result.line); 1120 | 1121 | if (result.found) /* and not ( 1122 | cstrings_eq(previous_result.filename, result.filename) 1123 | and cstrings_eq(previous_result.funcname, result.funcname) 1124 | and result.line == previous_result.line 1125 | )) */ { 1126 | ResolvedTrace::SourceLoc src_loc; 1127 | src_loc.line = result.line; 1128 | if (result.filename) { 1129 | src_loc.filename = result.filename; 1130 | } 1131 | if (result.funcname) { 1132 | src_loc.function = demangle(result.funcname); 1133 | } 1134 | results.push_back(src_loc); 1135 | } 1136 | previous_result = result; 1137 | } 1138 | return results; 1139 | } 1140 | 1141 | bool cstrings_eq(const char* a, const char* b) { 1142 | if (!a || !b) { 1143 | return false; 1144 | } 1145 | return strcmp(a, b) == 0; 1146 | } 1147 | 1148 | }; 1149 | #endif // BACKWARD_HAS_BFD == 1 1150 | 1151 | #if BACKWARD_HAS_DW == 1 1152 | 1153 | template <> 1154 | class TraceResolverLinuxImpl: 1155 | public TraceResolverLinuxImplBase { 1156 | public: 1157 | TraceResolverLinuxImpl(): _dwfl_handle_initialized(false) {} 1158 | 1159 | template 1160 | void load_stacktrace(ST&) {} 1161 | 1162 | ResolvedTrace resolve(ResolvedTrace trace) { 1163 | using namespace details; 1164 | 1165 | Dwarf_Addr trace_addr = (Dwarf_Addr) trace.addr; 1166 | 1167 | if (!_dwfl_handle_initialized) { 1168 | // initialize dwfl... 1169 | _dwfl_cb.reset(new Dwfl_Callbacks); 1170 | _dwfl_cb->find_elf = &dwfl_linux_proc_find_elf; 1171 | _dwfl_cb->find_debuginfo = &dwfl_standard_find_debuginfo; 1172 | _dwfl_cb->debuginfo_path = 0; 1173 | 1174 | _dwfl_handle.reset(dwfl_begin(_dwfl_cb.get())); 1175 | _dwfl_handle_initialized = true; 1176 | 1177 | if (!_dwfl_handle) { 1178 | return trace; 1179 | } 1180 | 1181 | // ...from the current process. 1182 | dwfl_report_begin(_dwfl_handle.get()); 1183 | int r = dwfl_linux_proc_report (_dwfl_handle.get(), getpid()); 1184 | dwfl_report_end(_dwfl_handle.get(), NULL, NULL); 1185 | if (r < 0) { 1186 | return trace; 1187 | } 1188 | } 1189 | 1190 | if (!_dwfl_handle) { 1191 | return trace; 1192 | } 1193 | 1194 | // find the module (binary object) that contains the trace's address. 1195 | // This is not using any debug information, but the addresses ranges of 1196 | // all the currently loaded binary object. 1197 | Dwfl_Module* mod = dwfl_addrmodule(_dwfl_handle.get(), trace_addr); 1198 | if (mod) { 1199 | // now that we found it, lets get the name of it, this will be the 1200 | // full path to the running binary or one of the loaded library. 1201 | const char* module_name = dwfl_module_info (mod, 1202 | 0, 0, 0, 0, 0, 0, 0); 1203 | if (module_name) { 1204 | trace.object_filename = module_name; 1205 | } 1206 | // We also look after the name of the symbol, equal or before this 1207 | // address. This is found by walking the symtab. We should get the 1208 | // symbol corresponding to the function (mangled) containing the 1209 | // address. If the code corresponding to the address was inlined, 1210 | // this is the name of the out-most inliner function. 1211 | const char* sym_name = dwfl_module_addrname(mod, trace_addr); 1212 | if (sym_name) { 1213 | trace.object_function = demangle(sym_name); 1214 | } 1215 | } 1216 | 1217 | // now let's get serious, and find out the source location (file and 1218 | // line number) of the address. 1219 | 1220 | // This function will look in .debug_aranges for the address and map it 1221 | // to the location of the compilation unit DIE in .debug_info and 1222 | // return it. 1223 | Dwarf_Addr mod_bias = 0; 1224 | Dwarf_Die* cudie = dwfl_module_addrdie(mod, trace_addr, &mod_bias); 1225 | 1226 | #if 1 1227 | if (!cudie) { 1228 | // Sadly clang does not generate the section .debug_aranges, thus 1229 | // dwfl_module_addrdie will fail early. Clang doesn't either set 1230 | // the lowpc/highpc/range info for every compilation unit. 1231 | // 1232 | // So in order to save the world: 1233 | // for every compilation unit, we will iterate over every single 1234 | // DIEs. Normally functions should have a lowpc/highpc/range, which 1235 | // we will use to infer the compilation unit. 1236 | 1237 | // note that this is probably badly inefficient. 1238 | while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias))) { 1239 | Dwarf_Die die_mem; 1240 | Dwarf_Die* fundie = find_fundie_by_pc(cudie, 1241 | trace_addr - mod_bias, &die_mem); 1242 | if (fundie) { 1243 | break; 1244 | } 1245 | } 1246 | } 1247 | #endif 1248 | 1249 | //#define BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE 1250 | #ifdef BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE 1251 | if (!cudie) { 1252 | // If it's still not enough, lets dive deeper in the shit, and try 1253 | // to save the world again: for every compilation unit, we will 1254 | // load the corresponding .debug_line section, and see if we can 1255 | // find our address in it. 1256 | 1257 | Dwarf_Addr cfi_bias; 1258 | Dwarf_CFI* cfi_cache = dwfl_module_eh_cfi(mod, &cfi_bias); 1259 | 1260 | Dwarf_Addr bias; 1261 | while ((cudie = dwfl_module_nextcu(mod, cudie, &bias))) { 1262 | if (dwarf_getsrc_die(cudie, trace_addr - bias)) { 1263 | 1264 | // ...but if we get a match, it might be a false positive 1265 | // because our (address - bias) might as well be valid in a 1266 | // different compilation unit. So we throw our last card on 1267 | // the table and lookup for the address into the .eh_frame 1268 | // section. 1269 | 1270 | handle frame; 1271 | dwarf_cfi_addrframe(cfi_cache, trace_addr - cfi_bias, &frame); 1272 | if (frame) { 1273 | break; 1274 | } 1275 | } 1276 | } 1277 | } 1278 | #endif 1279 | 1280 | if (!cudie) { 1281 | return trace; // this time we lost the game :/ 1282 | } 1283 | 1284 | // Now that we have a compilation unit DIE, this function will be able 1285 | // to load the corresponding section in .debug_line (if not already 1286 | // loaded) and hopefully find the source location mapped to our 1287 | // address. 1288 | Dwarf_Line* srcloc = dwarf_getsrc_die(cudie, trace_addr - mod_bias); 1289 | 1290 | if (srcloc) { 1291 | const char* srcfile = dwarf_linesrc(srcloc, 0, 0); 1292 | if (srcfile) { 1293 | trace.source.filename = srcfile; 1294 | } 1295 | int line = 0, col = 0; 1296 | dwarf_lineno(srcloc, &line); 1297 | dwarf_linecol(srcloc, &col); 1298 | trace.source.line = line; 1299 | trace.source.col = col; 1300 | } 1301 | 1302 | deep_first_search_by_pc(cudie, trace_addr - mod_bias, 1303 | inliners_search_cb(trace)); 1304 | if (trace.source.function.size() == 0) { 1305 | // fallback. 1306 | trace.source.function = trace.object_function; 1307 | } 1308 | 1309 | return trace; 1310 | } 1311 | 1312 | private: 1313 | typedef details::handle > 1314 | dwfl_handle_t; 1315 | details::handle > 1316 | _dwfl_cb; 1317 | dwfl_handle_t _dwfl_handle; 1318 | bool _dwfl_handle_initialized; 1319 | 1320 | // defined here because in C++98, template function cannot take locally 1321 | // defined types... grrr. 1322 | struct inliners_search_cb { 1323 | void operator()(Dwarf_Die* die) { 1324 | switch (dwarf_tag(die)) { 1325 | const char* name; 1326 | case DW_TAG_subprogram: 1327 | if ((name = dwarf_diename(die))) { 1328 | trace.source.function = name; 1329 | } 1330 | break; 1331 | 1332 | case DW_TAG_inlined_subroutine: 1333 | ResolvedTrace::SourceLoc sloc; 1334 | Dwarf_Attribute attr_mem; 1335 | 1336 | if ((name = dwarf_diename(die))) { 1337 | sloc.function = name; 1338 | } 1339 | if ((name = die_call_file(die))) { 1340 | sloc.filename = name; 1341 | } 1342 | 1343 | Dwarf_Word line = 0, col = 0; 1344 | dwarf_formudata(dwarf_attr(die, DW_AT_call_line, 1345 | &attr_mem), &line); 1346 | dwarf_formudata(dwarf_attr(die, DW_AT_call_column, 1347 | &attr_mem), &col); 1348 | sloc.line = (unsigned)line; 1349 | sloc.col = (unsigned)col; 1350 | 1351 | trace.inliners.push_back(sloc); 1352 | break; 1353 | }; 1354 | } 1355 | ResolvedTrace& trace; 1356 | inliners_search_cb(ResolvedTrace& t): trace(t) {} 1357 | }; 1358 | 1359 | 1360 | static bool die_has_pc(Dwarf_Die* die, Dwarf_Addr pc) { 1361 | Dwarf_Addr low, high; 1362 | 1363 | // continuous range 1364 | if (dwarf_hasattr(die, DW_AT_low_pc) && 1365 | dwarf_hasattr(die, DW_AT_high_pc)) { 1366 | if (dwarf_lowpc(die, &low) != 0) { 1367 | return false; 1368 | } 1369 | if (dwarf_highpc(die, &high) != 0) { 1370 | Dwarf_Attribute attr_mem; 1371 | Dwarf_Attribute* attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem); 1372 | Dwarf_Word value; 1373 | if (dwarf_formudata(attr, &value) != 0) { 1374 | return false; 1375 | } 1376 | high = low + value; 1377 | } 1378 | return pc >= low && pc < high; 1379 | } 1380 | 1381 | // non-continuous range. 1382 | Dwarf_Addr base; 1383 | ptrdiff_t offset = 0; 1384 | while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) { 1385 | if (pc >= low && pc < high) { 1386 | return true; 1387 | } 1388 | } 1389 | return false; 1390 | } 1391 | 1392 | static Dwarf_Die* find_fundie_by_pc(Dwarf_Die* parent_die, Dwarf_Addr pc, 1393 | Dwarf_Die* result) { 1394 | if (dwarf_child(parent_die, result) != 0) { 1395 | return 0; 1396 | } 1397 | 1398 | Dwarf_Die* die = result; 1399 | do { 1400 | switch (dwarf_tag(die)) { 1401 | case DW_TAG_subprogram: 1402 | case DW_TAG_inlined_subroutine: 1403 | if (die_has_pc(die, pc)) { 1404 | return result; 1405 | } 1406 | }; 1407 | bool declaration = false; 1408 | Dwarf_Attribute attr_mem; 1409 | dwarf_formflag(dwarf_attr(die, DW_AT_declaration, 1410 | &attr_mem), &declaration); 1411 | if (!declaration) { 1412 | // let's be curious and look deeper in the tree, 1413 | // function are not necessarily at the first level, but 1414 | // might be nested inside a namespace, structure etc. 1415 | Dwarf_Die die_mem; 1416 | Dwarf_Die* indie = find_fundie_by_pc(die, pc, &die_mem); 1417 | if (indie) { 1418 | *result = die_mem; 1419 | return result; 1420 | } 1421 | } 1422 | } while (dwarf_siblingof(die, result) == 0); 1423 | return 0; 1424 | } 1425 | 1426 | template 1427 | static bool deep_first_search_by_pc(Dwarf_Die* parent_die, 1428 | Dwarf_Addr pc, CB cb) { 1429 | Dwarf_Die die_mem; 1430 | if (dwarf_child(parent_die, &die_mem) != 0) { 1431 | return false; 1432 | } 1433 | 1434 | bool branch_has_pc = false; 1435 | Dwarf_Die* die = &die_mem; 1436 | do { 1437 | bool declaration = false; 1438 | Dwarf_Attribute attr_mem; 1439 | dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), &declaration); 1440 | if (!declaration) { 1441 | // let's be curious and look deeper in the tree, function are 1442 | // not necessarily at the first level, but might be nested 1443 | // inside a namespace, structure, a function, an inlined 1444 | // function etc. 1445 | branch_has_pc = deep_first_search_by_pc(die, pc, cb); 1446 | } 1447 | if (!branch_has_pc) { 1448 | branch_has_pc = die_has_pc(die, pc); 1449 | } 1450 | if (branch_has_pc) { 1451 | cb(die); 1452 | } 1453 | } while (dwarf_siblingof(die, &die_mem) == 0); 1454 | return branch_has_pc; 1455 | } 1456 | 1457 | static const char* die_call_file(Dwarf_Die *die) { 1458 | Dwarf_Attribute attr_mem; 1459 | Dwarf_Sword file_idx = 0; 1460 | 1461 | dwarf_formsdata(dwarf_attr(die, DW_AT_call_file, &attr_mem), 1462 | &file_idx); 1463 | 1464 | if (file_idx == 0) { 1465 | return 0; 1466 | } 1467 | 1468 | Dwarf_Die die_mem; 1469 | Dwarf_Die* cudie = dwarf_diecu(die, &die_mem, 0, 0); 1470 | if (!cudie) { 1471 | return 0; 1472 | } 1473 | 1474 | Dwarf_Files* files = 0; 1475 | size_t nfiles; 1476 | dwarf_getsrcfiles(cudie, &files, &nfiles); 1477 | if (!files) { 1478 | return 0; 1479 | } 1480 | 1481 | return dwarf_filesrc(files, file_idx, 0, 0); 1482 | } 1483 | 1484 | }; 1485 | #endif // BACKWARD_HAS_DW == 1 1486 | 1487 | template<> 1488 | class TraceResolverImpl: 1489 | public TraceResolverLinuxImpl {}; 1490 | 1491 | #endif // BACKWARD_SYSTEM_LINUX 1492 | 1493 | class TraceResolver: 1494 | public TraceResolverImpl {}; 1495 | 1496 | /*************** CODE SNIPPET ***************/ 1497 | 1498 | class SourceFile { 1499 | public: 1500 | typedef std::vector > lines_t; 1501 | 1502 | SourceFile() {} 1503 | SourceFile(const std::string& path): _file(new std::ifstream(path.c_str())) {} 1504 | bool is_open() const { return _file->is_open(); } 1505 | 1506 | lines_t& get_lines(unsigned line_start, unsigned line_count, lines_t& lines) { 1507 | using namespace std; 1508 | // This function make uses of the dumbest algo ever: 1509 | // 1) seek(0) 1510 | // 2) read lines one by one and discard until line_start 1511 | // 3) read line one by one until line_start + line_count 1512 | // 1513 | // If you are getting snippets many time from the same file, it is 1514 | // somewhat a waste of CPU, feel free to benchmark and propose a 1515 | // better solution ;) 1516 | 1517 | _file->clear(); 1518 | _file->seekg(0); 1519 | string line; 1520 | unsigned line_idx; 1521 | 1522 | for (line_idx = 1; line_idx < line_start; ++line_idx) { 1523 | std::getline(*_file, line); 1524 | if (!*_file) { 1525 | return lines; 1526 | } 1527 | } 1528 | 1529 | // think of it like a lambda in C++98 ;) 1530 | // but look, I will reuse it two times! 1531 | // What a good boy am I. 1532 | struct isspace { 1533 | bool operator()(char c) { 1534 | return std::isspace(c); 1535 | } 1536 | }; 1537 | 1538 | bool started = false; 1539 | for (; line_idx < line_start + line_count; ++line_idx) { 1540 | getline(*_file, line); 1541 | if (!*_file) { 1542 | return lines; 1543 | } 1544 | if (!started) { 1545 | if (std::find_if(line.begin(), line.end(), 1546 | not_isspace()) == line.end()) 1547 | continue; 1548 | started = true; 1549 | } 1550 | lines.push_back(make_pair(line_idx, line)); 1551 | } 1552 | 1553 | lines.erase( 1554 | std::find_if(lines.rbegin(), lines.rend(), 1555 | not_isempty()).base(), lines.end() 1556 | ); 1557 | return lines; 1558 | } 1559 | 1560 | lines_t get_lines(unsigned line_start, unsigned line_count) { 1561 | lines_t lines; 1562 | return get_lines(line_start, line_count, lines); 1563 | } 1564 | 1565 | // there is no find_if_not in C++98, lets do something crappy to 1566 | // workaround. 1567 | struct not_isspace { 1568 | bool operator()(char c) { 1569 | return !std::isspace(c); 1570 | } 1571 | }; 1572 | // and define this one here because C++98 is not happy with local defined 1573 | // struct passed to template functions, fuuuu. 1574 | struct not_isempty { 1575 | bool operator()(const lines_t::value_type& p) { 1576 | return !(std::find_if(p.second.begin(), p.second.end(), 1577 | not_isspace()) == p.second.end()); 1578 | } 1579 | }; 1580 | 1581 | void swap(SourceFile& b) { 1582 | _file.swap(b._file); 1583 | } 1584 | 1585 | #ifdef BACKWARD_ATLEAST_CXX11 1586 | SourceFile(SourceFile&& from): _file(0) { 1587 | swap(from); 1588 | } 1589 | SourceFile& operator=(SourceFile&& from) { 1590 | swap(from); return *this; 1591 | } 1592 | #else 1593 | explicit SourceFile(const SourceFile& from) { 1594 | // some sort of poor man's move semantic. 1595 | swap(const_cast(from)); 1596 | } 1597 | SourceFile& operator=(const SourceFile& from) { 1598 | // some sort of poor man's move semantic. 1599 | swap(const_cast(from)); return *this; 1600 | } 1601 | #endif 1602 | 1603 | private: 1604 | details::handle 1606 | > _file; 1607 | 1608 | #ifdef BACKWARD_ATLEAST_CXX11 1609 | SourceFile(const SourceFile&) = delete; 1610 | SourceFile& operator=(const SourceFile&) = delete; 1611 | #endif 1612 | }; 1613 | 1614 | class SnippetFactory { 1615 | public: 1616 | typedef SourceFile::lines_t lines_t; 1617 | 1618 | lines_t get_snippet(const std::string& filename, 1619 | unsigned line_start, unsigned context_size) { 1620 | 1621 | SourceFile& src_file = get_src_file(filename); 1622 | unsigned start = line_start - context_size / 2; 1623 | return src_file.get_lines(start, context_size); 1624 | } 1625 | 1626 | lines_t get_combined_snippet( 1627 | const std::string& filename_a, unsigned line_a, 1628 | const std::string& filename_b, unsigned line_b, 1629 | unsigned context_size) { 1630 | SourceFile& src_file_a = get_src_file(filename_a); 1631 | SourceFile& src_file_b = get_src_file(filename_b); 1632 | 1633 | lines_t lines = src_file_a.get_lines(line_a - context_size / 4, 1634 | context_size / 2); 1635 | src_file_b.get_lines(line_b - context_size / 4, context_size / 2, 1636 | lines); 1637 | return lines; 1638 | } 1639 | 1640 | lines_t get_coalesced_snippet(const std::string& filename, 1641 | unsigned line_a, unsigned line_b, unsigned context_size) { 1642 | SourceFile& src_file = get_src_file(filename); 1643 | 1644 | using std::min; using std::max; 1645 | unsigned a = min(line_a, line_b); 1646 | unsigned b = max(line_a, line_b); 1647 | 1648 | if ((b - a) < (context_size / 3)) { 1649 | return src_file.get_lines((a + b - context_size + 1) / 2, 1650 | context_size); 1651 | } 1652 | 1653 | lines_t lines = src_file.get_lines(a - context_size / 4, 1654 | context_size / 2); 1655 | src_file.get_lines(b - context_size / 4, context_size / 2, lines); 1656 | return lines; 1657 | } 1658 | 1659 | 1660 | private: 1661 | typedef details::hashtable::type src_files_t; 1662 | src_files_t _src_files; 1663 | 1664 | SourceFile& get_src_file(const std::string& filename) { 1665 | src_files_t::iterator it = _src_files.find(filename); 1666 | if (it != _src_files.end()) { 1667 | return it->second; 1668 | } 1669 | SourceFile& new_src_file = _src_files[filename]; 1670 | new_src_file = SourceFile(filename); 1671 | return new_src_file; 1672 | } 1673 | }; 1674 | 1675 | /*************** PRINTER ***************/ 1676 | 1677 | namespace ColorMode { 1678 | enum type { 1679 | automatic, 1680 | never, 1681 | always 1682 | }; 1683 | } 1684 | 1685 | class cfile_streambuf: public std::streambuf { 1686 | public: 1687 | cfile_streambuf(FILE *_sink): sink(_sink) {} 1688 | int_type underflow() { return traits_type::eof(); } 1689 | int_type overflow(int_type ch) { 1690 | if (traits_type::not_eof(ch) && fwrite(&ch, sizeof ch, 1, sink) == 1) { 1691 | return ch; 1692 | } 1693 | return traits_type::eof(); 1694 | } 1695 | 1696 | std::streamsize xsputn(const char_type* s, std::streamsize count) { 1697 | return fwrite(s, sizeof *s, count, sink); 1698 | } 1699 | 1700 | #ifdef BACKWARD_ATLEAST_CXX11 1701 | public: 1702 | cfile_streambuf(const cfile_streambuf&) = delete; 1703 | cfile_streambuf& operator=(const cfile_streambuf&) = delete; 1704 | #else 1705 | private: 1706 | cfile_streambuf(const cfile_streambuf &); 1707 | cfile_streambuf &operator= (const cfile_streambuf &); 1708 | #endif 1709 | 1710 | private: 1711 | FILE *sink; 1712 | std::vector buffer; 1713 | }; 1714 | 1715 | #ifdef BACKWARD_SYSTEM_LINUX 1716 | 1717 | namespace Color { 1718 | enum type { 1719 | yellow = 33, 1720 | purple = 35, 1721 | reset = 39 1722 | }; 1723 | } // namespace Color 1724 | 1725 | class Colorize { 1726 | public: 1727 | Colorize(std::ostream& os): 1728 | _os(os), _reset(false), _enabled(false) {} 1729 | 1730 | void activate(ColorMode::type mode) { 1731 | _enabled = mode == ColorMode::always; 1732 | } 1733 | 1734 | void activate(ColorMode::type mode, FILE* fp) { 1735 | activate(mode, fileno(fp)); 1736 | } 1737 | 1738 | void set_color(Color::type ccode) { 1739 | if (!_enabled) return; 1740 | 1741 | // I assume that the terminal can handle basic colors. Seriously I 1742 | // don't want to deal with all the termcap shit. 1743 | _os << "\033[" << static_cast(ccode) << "m"; 1744 | _reset = (ccode != Color::reset); 1745 | } 1746 | 1747 | ~Colorize() { 1748 | if (_reset) { 1749 | set_color(Color::reset); 1750 | } 1751 | } 1752 | 1753 | private: 1754 | void activate(ColorMode::type mode, int fd) { 1755 | activate(mode == ColorMode::automatic && isatty(fd) ? ColorMode::always : mode); 1756 | } 1757 | 1758 | std::ostream& _os; 1759 | bool _reset; 1760 | bool _enabled; 1761 | }; 1762 | 1763 | #else // ndef BACKWARD_SYSTEM_LINUX 1764 | 1765 | namespace Color { 1766 | enum type { 1767 | yellow = 0, 1768 | purple = 0, 1769 | reset = 0 1770 | }; 1771 | } // namespace Color 1772 | 1773 | class Colorize { 1774 | public: 1775 | Colorize(std::ostream&) {} 1776 | void activate(ColorMode::type) {} 1777 | void activate(ColorMode::type, FILE*) {} 1778 | void set_color(Color::type) {} 1779 | }; 1780 | 1781 | #endif // BACKWARD_SYSTEM_LINUX 1782 | 1783 | class Printer { 1784 | public: 1785 | 1786 | bool snippet; 1787 | ColorMode::type color_mode; 1788 | bool address; 1789 | bool object; 1790 | int inliner_context_size; 1791 | int trace_context_size; 1792 | 1793 | Printer(): 1794 | snippet(true), 1795 | color_mode(ColorMode::automatic), 1796 | address(false), 1797 | object(false), 1798 | inliner_context_size(5), 1799 | trace_context_size(7) 1800 | {} 1801 | 1802 | template 1803 | FILE* print(ST& st, FILE* fp = stderr) { 1804 | cfile_streambuf obuf(fp); 1805 | std::ostream os(&obuf); 1806 | Colorize colorize(os); 1807 | colorize.activate(color_mode, fp); 1808 | print_stacktrace(st, os, colorize); 1809 | return fp; 1810 | } 1811 | 1812 | template 1813 | std::ostream& print(ST& st, std::ostream& os) { 1814 | Colorize colorize(os); 1815 | colorize.activate(color_mode); 1816 | print_stacktrace(st, os, colorize); 1817 | return os; 1818 | } 1819 | 1820 | template 1821 | FILE* print(IT begin, IT end, FILE* fp = stderr, size_t thread_id = 0) { 1822 | cfile_streambuf obuf(fp); 1823 | std::ostream os(&obuf); 1824 | Colorize colorize(os); 1825 | colorize.activate(color_mode, fp); 1826 | print_stacktrace(begin, end, os, thread_id, colorize); 1827 | return fp; 1828 | } 1829 | 1830 | template 1831 | std::ostream& print(IT begin, IT end, std::ostream& os, size_t thread_id = 0) { 1832 | Colorize colorize(os); 1833 | colorize.activate(color_mode); 1834 | print_stacktrace(begin, end, os, thread_id, colorize); 1835 | return os; 1836 | } 1837 | 1838 | private: 1839 | TraceResolver _resolver; 1840 | SnippetFactory _snippets; 1841 | 1842 | template 1843 | void print_stacktrace(ST& st, std::ostream& os, Colorize& colorize) { 1844 | print_header(os, st.thread_id()); 1845 | _resolver.load_stacktrace(st); 1846 | for (size_t trace_idx = st.size(); trace_idx > 0; --trace_idx) { 1847 | print_trace(os, _resolver.resolve(st[trace_idx-1]), colorize); 1848 | } 1849 | } 1850 | 1851 | template 1852 | void print_stacktrace(IT begin, IT end, std::ostream& os, size_t thread_id, Colorize& colorize) { 1853 | print_header(os, thread_id); 1854 | for (; begin != end; ++begin) { 1855 | print_trace(os, *begin, colorize); 1856 | } 1857 | } 1858 | 1859 | void print_header(std::ostream& os, size_t thread_id) { 1860 | os << "Stack trace (most recent call last)"; 1861 | if (thread_id) { 1862 | os << " in thread " << thread_id; 1863 | } 1864 | os << ":\n"; 1865 | } 1866 | 1867 | void print_trace(std::ostream& os, const ResolvedTrace& trace, 1868 | Colorize& colorize) { 1869 | os << "#" 1870 | << std::left << std::setw(2) << trace.idx 1871 | << std::right; 1872 | bool already_indented = true; 1873 | 1874 | if (!trace.source.filename.size() || object) { 1875 | os << " Object \"" 1876 | << trace.object_filename 1877 | << ", at " 1878 | << trace.addr 1879 | << ", in " 1880 | << trace.object_function 1881 | << "\n"; 1882 | already_indented = false; 1883 | } 1884 | 1885 | for (size_t inliner_idx = trace.inliners.size(); 1886 | inliner_idx > 0; --inliner_idx) { 1887 | if (!already_indented) { 1888 | os << " "; 1889 | } 1890 | const ResolvedTrace::SourceLoc& inliner_loc 1891 | = trace.inliners[inliner_idx-1]; 1892 | print_source_loc(os, " | ", inliner_loc); 1893 | if (snippet) { 1894 | print_snippet(os, " | ", inliner_loc, 1895 | colorize, Color::purple, inliner_context_size); 1896 | } 1897 | already_indented = false; 1898 | } 1899 | 1900 | if (trace.source.filename.size()) { 1901 | if (!already_indented) { 1902 | os << " "; 1903 | } 1904 | print_source_loc(os, " ", trace.source, trace.addr); 1905 | if (snippet) { 1906 | print_snippet(os, " ", trace.source, 1907 | colorize, Color::yellow, trace_context_size); 1908 | } 1909 | } 1910 | } 1911 | 1912 | void print_snippet(std::ostream& os, const char* indent, 1913 | const ResolvedTrace::SourceLoc& source_loc, 1914 | Colorize& colorize, Color::type color_code, 1915 | int context_size) 1916 | { 1917 | using namespace std; 1918 | typedef SnippetFactory::lines_t lines_t; 1919 | 1920 | lines_t lines = _snippets.get_snippet(source_loc.filename, 1921 | source_loc.line, context_size); 1922 | 1923 | for (lines_t::const_iterator it = lines.begin(); 1924 | it != lines.end(); ++it) { 1925 | if (it-> first == source_loc.line) { 1926 | colorize.set_color(color_code); 1927 | os << indent << ">"; 1928 | } else { 1929 | os << indent << " "; 1930 | } 1931 | os << std::setw(4) << it->first 1932 | << ": " 1933 | << it->second 1934 | << "\n"; 1935 | if (it-> first == source_loc.line) { 1936 | colorize.set_color(Color::reset); 1937 | } 1938 | } 1939 | } 1940 | 1941 | void print_source_loc(std::ostream& os, const char* indent, 1942 | const ResolvedTrace::SourceLoc& source_loc, 1943 | void* addr=0) { 1944 | os << indent 1945 | << "Source \"" 1946 | << source_loc.filename 1947 | << "\", line " 1948 | << source_loc.line 1949 | << ", in " 1950 | << source_loc.function; 1951 | 1952 | if (address && addr != 0) { 1953 | os << " [" << addr << "]"; 1954 | } 1955 | os << "\n"; 1956 | } 1957 | }; 1958 | 1959 | /*************** SIGNALS HANDLING ***************/ 1960 | 1961 | #ifdef BACKWARD_SYSTEM_LINUX 1962 | 1963 | 1964 | class SignalHandling { 1965 | public: 1966 | static std::vector make_default_signals() { 1967 | const int posix_signals[] = { 1968 | // Signals for which the default action is "Core". 1969 | SIGABRT, // Abort signal from abort(3) 1970 | SIGBUS, // Bus error (bad memory access) 1971 | SIGFPE, // Floating point exception 1972 | SIGILL, // Illegal Instruction 1973 | SIGIOT, // IOT trap. A synonym for SIGABRT 1974 | SIGQUIT, // Quit from keyboard 1975 | SIGSEGV, // Invalid memory reference 1976 | SIGSYS, // Bad argument to routine (SVr4) 1977 | SIGTRAP, // Trace/breakpoint trap 1978 | SIGXCPU, // CPU time limit exceeded (4.2BSD) 1979 | SIGXFSZ, // File size limit exceeded (4.2BSD) 1980 | }; 1981 | return std::vector(posix_signals, posix_signals + sizeof posix_signals / sizeof posix_signals[0] ); 1982 | } 1983 | 1984 | SignalHandling(const std::vector& posix_signals = make_default_signals()): 1985 | _loaded(false) { 1986 | bool success = true; 1987 | 1988 | const size_t stack_size = 1024 * 1024 * 8; 1989 | _stack_content.reset((char*)malloc(stack_size)); 1990 | if (_stack_content) { 1991 | stack_t ss; 1992 | ss.ss_sp = _stack_content.get(); 1993 | ss.ss_size = stack_size; 1994 | ss.ss_flags = 0; 1995 | if (sigaltstack(&ss, 0) < 0) { 1996 | success = false; 1997 | } 1998 | } else { 1999 | success = false; 2000 | } 2001 | 2002 | for (size_t i = 0; i < posix_signals.size(); ++i) { 2003 | struct sigaction action; 2004 | memset(&action, 0, sizeof action); 2005 | action.sa_flags = (SA_SIGINFO | SA_ONSTACK | SA_NODEFER | 2006 | SA_RESETHAND); 2007 | sigfillset(&action.sa_mask); 2008 | sigdelset(&action.sa_mask, posix_signals[i]); 2009 | action.sa_sigaction = &sig_handler; 2010 | 2011 | int r = sigaction(posix_signals[i], &action, 0); 2012 | if (r < 0) success = false; 2013 | } 2014 | 2015 | _loaded = success; 2016 | } 2017 | 2018 | bool loaded() const { return _loaded; } 2019 | 2020 | private: 2021 | details::handle _stack_content; 2022 | bool _loaded; 2023 | 2024 | #ifdef __GNUC__ 2025 | __attribute__((noreturn)) 2026 | #endif 2027 | static void sig_handler(int, siginfo_t* info, void* _ctx) { 2028 | ucontext_t *uctx = (ucontext_t*) _ctx; 2029 | 2030 | StackTrace st; 2031 | void* error_addr = 0; 2032 | #ifdef REG_RIP // x86_64 2033 | error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); 2034 | #elif defined(REG_EIP) // x86_32 2035 | error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); 2036 | #elif defined(__arm__) 2037 | error_addr = reinterpret_cast(uctx->uc_mcontext.arm_pc); 2038 | #elif defined(__aarch64__) 2039 | error_addr = reinterpret_cast(uctx->uc_mcontext.pc); 2040 | #elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) 2041 | error_addr = reinterpret_cast(uctx->uc_mcontext.regs->nip); 2042 | #else 2043 | # warning ":/ sorry, ain't know no nothing none not of your architecture!" 2044 | #endif 2045 | if (error_addr) { 2046 | st.load_from(error_addr, 32); 2047 | } else { 2048 | st.load_here(32); 2049 | } 2050 | 2051 | Printer printer; 2052 | printer.address = true; 2053 | printer.print(st, stderr); 2054 | 2055 | #if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L 2056 | psiginfo(info, 0); 2057 | #endif 2058 | 2059 | // try to forward the signal. 2060 | raise(info->si_signo); 2061 | 2062 | // terminate the process immediately. 2063 | puts("watf? exit"); 2064 | _exit(EXIT_FAILURE); 2065 | } 2066 | }; 2067 | 2068 | #endif // BACKWARD_SYSTEM_LINUX 2069 | 2070 | #ifdef BACKWARD_SYSTEM_UNKNOWN 2071 | 2072 | class SignalHandling { 2073 | public: 2074 | SignalHandling(const std::vector& = std::vector()) {} 2075 | bool init() { return false; } 2076 | bool loaded() { return false; } 2077 | }; 2078 | 2079 | #endif // BACKWARD_SYSTEM_UNKNOWN 2080 | 2081 | } // namespace backward 2082 | 2083 | #endif /* H_GUARD */ 2084 | -------------------------------------------------------------------------------- /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 cmake_option(self, option, prefix = ''): 32 | return '-D{}{}={}'.format(prefix, option.upper(), getattr(self.options, option)) 33 | 34 | def build(self): 35 | cmake = CMake(self.settings) 36 | 37 | options = '' 38 | options += self.cmake_option('stack_walking_unwind') 39 | options += self.cmake_option('stack_walking_backtrace') 40 | options += self.cmake_option('stack_details_auto_detect') 41 | options += self.cmake_option('stack_details_backtrace_symbol') 42 | options += self.cmake_option('stack_details_dw') 43 | options += self.cmake_option('stack_details_bfd') 44 | options += self.cmake_option('shared', prefix = 'BACKWARD_') 45 | 46 | self.run('cmake {} {} {} -DBACKWARD_TESTS=OFF'.format(self.conanfile_directory, cmake.command_line, options)) 47 | self.run('cmake --build . {}'.format(cmake.build_config)) 48 | 49 | def package(self): 50 | self.copy('backward.hpp', os.path.join('include', 'backward')) 51 | self.copy('*.a', dst='lib') 52 | self.copy('*.so', dst='lib') 53 | self.copy('*.lib', dst='lib') 54 | self.copy('*.dll', dst='lib') 55 | 56 | def package_info(self): 57 | self.cpp_info.libs = ['backward'] 58 | -------------------------------------------------------------------------------- /doc/nice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Manu343726/backward-cpp/HEAD/doc/nice.png -------------------------------------------------------------------------------- /doc/pretty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Manu343726/backward-cpp/HEAD/doc/pretty.png -------------------------------------------------------------------------------- /doc/rude.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Manu343726/backward-cpp/HEAD/doc/rude.png -------------------------------------------------------------------------------- /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 | #include 28 | #include 29 | #include 30 | 31 | test::test_registry_t test::test_registry; 32 | using namespace test; 33 | 34 | bool run_test(TestBase& test) { 35 | printf("-- running test case: %s\n", test.name); 36 | 37 | fflush(stdout); 38 | pid_t child_pid = fork(); 39 | if (child_pid == 0) { 40 | exit(static_cast(test.run())); 41 | } 42 | if (child_pid == -1) { 43 | error(EXIT_FAILURE, 0, "unable to fork"); 44 | } 45 | 46 | int child_status = 0; 47 | waitpid(child_pid, &child_status, 0); 48 | 49 | test::TestStatus status; 50 | 51 | if (WIFEXITED(child_status)) { 52 | int exit_status = WEXITSTATUS(child_status); 53 | if (exit_status & ~test::STATUS_MASK) { 54 | status = test::FAILED; 55 | } else { 56 | status = static_cast(exit_status); 57 | } 58 | } else if (WIFSIGNALED(child_status)) { 59 | const int signum = WTERMSIG(child_status); 60 | printf("!! signal (%d) %s\n", signum, strsignal(signum)); 61 | switch (signum) { 62 | case SIGABRT: 63 | status = test::SIGNAL_ABORT; break; 64 | case SIGSEGV: 65 | case SIGBUS: 66 | status = test::SIGNAL_SEGFAULT; break; 67 | case SIGFPE: 68 | status = test::SIGNAL_DIVZERO; break; 69 | default: 70 | status = test::SIGNAL_UNCAUGHT; 71 | } 72 | } else { 73 | status = test::SUCCESS; 74 | } 75 | 76 | if (test.expected_status == test::FAILED) { 77 | return (status & test::FAILED); 78 | } 79 | 80 | if (test.expected_status == test::SIGNAL_UNCAUGHT) { 81 | return (status & test::SIGNAL_UNCAUGHT); 82 | } 83 | 84 | return status == test.expected_status; 85 | } 86 | 87 | int main(int argc, const char* const argv[]) { 88 | 89 | size_t success_cnt = 0; 90 | size_t total_cnt = 0; 91 | for (test_registry_t::iterator it = test_registry.begin(); 92 | it != test_registry.end(); ++it) { 93 | TestBase& test = **it; 94 | 95 | bool consider_test = (argc <= 1); 96 | for (int i = 1; i < argc; ++i) { 97 | if (strcasecmp(argv[i], test.name) == 0) { 98 | consider_test = true; 99 | break; 100 | } 101 | } 102 | if (not consider_test) { 103 | continue; 104 | } 105 | 106 | total_cnt += 1; 107 | if (run_test(test)) { 108 | printf("-- test case success: %s\n", test.name); 109 | success_cnt += 1; 110 | } else { 111 | printf("** test case FAILED : %s\n", test.name); 112 | } 113 | } 114 | printf("-- tests passing: %lu/%lu", success_cnt, total_cnt); 115 | if (total_cnt) { 116 | printf(" (%lu%%)\n", success_cnt * 100 / total_cnt); 117 | } else { 118 | printf("\n"); 119 | } 120 | return (success_cnt == total_cnt) ? EXIT_SUCCESS : EXIT_FAILURE; 121 | } 122 | -------------------------------------------------------------------------------- /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 26 | #include "test/test.hpp" 27 | 28 | using namespace backward; 29 | 30 | typedef StackTrace stacktrace_t; 31 | 32 | void end_of_our_journey(stacktrace_t& st) { 33 | if (not 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 | __attribute__((noinline)) 54 | static int trampoline(stacktrace_t& st, int level) { 55 | return rec(st, level); 56 | } 57 | }; 58 | }; 59 | 60 | } // namespace titi 61 | 62 | } // namespace toto 63 | 64 | TEST (recursion) { 65 | { // lexical scope. 66 | stacktrace_t st; 67 | const int input = 3; 68 | int r = toto::titi::foo::bar::trampoline(st, input); 69 | 70 | std::cout << "rec(" << input << ") == " << r << std::endl; 71 | 72 | Printer printer; 73 | // printer.address = true; 74 | printer.object = true; 75 | printer.print(st, stdout); 76 | } 77 | } 78 | 79 | int fib(StackTrace& st, int level) { 80 | if (level == 2) { 81 | return 1; 82 | } 83 | if (level <= 1) { 84 | end_of_our_journey(st); 85 | return 0; 86 | } 87 | return fib(st, level - 1) + fib(st, level - 2); 88 | } 89 | 90 | TEST (fibrecursive) { 91 | StackTrace st; 92 | const int input = 6; 93 | int r = fib(st, input); 94 | 95 | std::cout << "fib(" << input << ") == " << r << std::endl; 96 | 97 | Printer printer; 98 | printer.print(st, stdout); 99 | } 100 | -------------------------------------------------------------------------------- /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 27 | #include 28 | #include "test/test.hpp" 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/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 26 | #include "test/test.hpp" 27 | 28 | using namespace backward; 29 | 30 | void collect_trace(StackTrace& st) { 31 | st.load_here(); 32 | } 33 | 34 | TEST (minitrace) { 35 | StackTrace st; 36 | collect_trace(st); 37 | 38 | Printer printer; 39 | printer.print(st, stdout); 40 | } 41 | 42 | void d(StackTrace& st) { 43 | st.load_here(); 44 | } 45 | 46 | void c(StackTrace& st) { 47 | return d(st); 48 | } 49 | 50 | void b(StackTrace& st) { 51 | return c(st); 52 | } 53 | 54 | __attribute__ ((noinline)) 55 | void a(StackTrace& st) { 56 | return b(st); 57 | } 58 | 59 | TEST (smalltrace) { 60 | StackTrace st; 61 | a(st); 62 | 63 | Printer printer; 64 | printer.print(st, stdout); 65 | } 66 | -------------------------------------------------------------------------------- /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 27 | #include 28 | #include "test/test.hpp" 29 | 30 | using namespace backward; 31 | 32 | void badass_function() 33 | { 34 | char* ptr = (char*)42; 35 | *ptr = 42; 36 | } 37 | 38 | TEST_SEGFAULT (invalid_write) 39 | { 40 | badass_function(); 41 | } 42 | 43 | int you_shall_not_pass() 44 | { 45 | char* ptr = (char*)42; 46 | int v = *ptr; 47 | return v; 48 | } 49 | 50 | TEST_SEGFAULT(invalid_read) 51 | { 52 | int v = you_shall_not_pass(); 53 | std::cout << "v=" << v << std::endl; 54 | } 55 | 56 | void abort_abort_I_repeat_abort_abort() 57 | { 58 | std::cout << "Jumping off the boat!" << std::endl; 59 | abort(); 60 | } 61 | 62 | TEST_ABORT (calling_abort) 63 | { 64 | abort_abort_I_repeat_abort_abort(); 65 | } 66 | 67 | // aarch64 does not trap Division by zero 68 | #ifndef __aarch64__ 69 | volatile int zero = 0; 70 | 71 | int divide_by_zero() 72 | { 73 | std::cout << "And the wild black hole appears..." << std::endl; 74 | int v = 42 / zero; 75 | return v; 76 | } 77 | 78 | TEST_DIVZERO (divide_by_zero) 79 | { 80 | int v = divide_by_zero(); 81 | std::cout << "v=" << v << std::endl; 82 | } 83 | #endif 84 | 85 | int bye_bye_stack(int i) { 86 | return bye_bye_stack(i + 1) + bye_bye_stack(i * 2); 87 | } 88 | 89 | TEST_SEGFAULT(stackoverflow) 90 | { 91 | struct rlimit limit; 92 | limit.rlim_max = 8096; 93 | setrlimit(RLIMIT_STACK, &limit); 94 | int r = bye_bye_stack(42); 95 | std::cout << "r=" << r << std::endl; 96 | } 97 | -------------------------------------------------------------------------------- /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 25 | #include 26 | #include 27 | #include "test/test.hpp" 28 | 29 | TEST (empty_test) { } 30 | 31 | TEST_FAIL_ASSERT (fail_assert) { 32 | ASSERT(1 == 2); 33 | } 34 | 35 | TEST_FAIL_ASSERT (fail_assert_ge) { 36 | ASSERT_GE(4, 5); 37 | } 38 | 39 | TEST_UNCAUGHT_EXCEPTION (uncaught_exception) { 40 | throw std::runtime_error("some random runtime error"); 41 | } 42 | 43 | TEST_UNCAUGHT_EXCEPTION (uncaught_exception_int) { 44 | throw 42; 45 | } 46 | 47 | TEST_SEGFAULT (segfault) { 48 | char* a = 0; 49 | char b = a[42]; 50 | std::cout << "result: " << b << std::endl; 51 | } 52 | 53 | TEST_ABORT (abort) { 54 | abort(); 55 | } 56 | 57 | TEST (catch_int) { 58 | ASSERT_THROW({throw 42;}, int); 59 | } 60 | 61 | TEST_FAIL_ASSERT (fail_catch_int) { 62 | ASSERT_THROW({}, int); 63 | } 64 | 65 | TEST_FAIL_ASSERT (fail_no_throw) { 66 | ASSERT_NO_THROW({throw 42;}); 67 | } 68 | 69 | TEST (any_throw) { 70 | ASSERT_ANY_THROW({throw 42;}); 71 | } 72 | 73 | TEST_FAIL_ASSERT (fail_any_throw) { 74 | ASSERT_ANY_THROW({}); 75 | } 76 | -------------------------------------------------------------------------------- /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 (not _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 | extern test_registry_t test_registry; 109 | 110 | TestBase::TestBase(const char* n, TestStatus s): name(n), expected_status(s) { 111 | test_registry.push_back(this); 112 | } 113 | 114 | } // namespace test 115 | 116 | #define _TEST_STATUS(name, status) \ 117 | struct TEST_##name: ::test::TestBase { \ 118 | TEST_##name(): TestBase(#name, status) {} \ 119 | void do_test(); \ 120 | } TEST_##name; \ 121 | void TEST_##name::do_test() 122 | 123 | #define TEST(name) _TEST_STATUS(name, ::test::SUCCESS) 124 | #define TEST_FAIL(name) _TEST_STATUS(name, ::test::FAILED) 125 | #define TEST_FAIL_ASSERT(name) _TEST_STATUS(name, ::test::ASSERT_FAIL) 126 | #define TEST_UNCAUGHT_EXCEPTION(name) _TEST_STATUS(name, ::test::EXCEPTION_UNCAUGHT) 127 | #define TEST_UNCAUGHT_SIGNAL(name) _TEST_STATUS(name, ::test::SIGNAL_UNCAUGHT) 128 | #define TEST_SEGFAULT(name) _TEST_STATUS(name, ::test::SIGNAL_SEGFAULT) 129 | #define TEST_ABORT(name) _TEST_STATUS(name, ::test::SIGNAL_ABORT) 130 | #define TEST_DIVZERO(name) _TEST_STATUS(name, ::test::SIGNAL_DIVZERO) 131 | 132 | #define ASSERT(expr) \ 133 | (expr) ? static_cast(0) \ 134 | : throw ::test::AssertFailedError( \ 135 | __FILE__, __LINE__, #expr) 136 | 137 | #define _ASSERT_BINOP(a, b, cmp) \ 138 | (not (a cmp b)) ? static_cast(0) \ 139 | : throw ::test::AssertFailedError( \ 140 | __FILE__, __LINE__, "because " #a " " #cmp " " #b) 141 | 142 | #define ASSERT_EQ(a, b) _ASSERT_BINOP(a, b, !=) 143 | #define ASSERT_NE(a, b) _ASSERT_BINOP(a, b, ==) 144 | #define ASSERT_LT(a, b) _ASSERT_BINOP(a, b, >=) 145 | #define ASSERT_LE(a, b) _ASSERT_BINOP(a, b, >) 146 | #define ASSERT_GT(a, b) _ASSERT_BINOP(a, b, <=) 147 | #define ASSERT_GE(a, b) _ASSERT_BINOP(a, b, <) 148 | 149 | #define ASSERT_THROW(expr, e_type) \ 150 | do { try { expr } \ 151 | catch (const e_type&) { break; } \ 152 | throw ::test::AssertFailedError( \ 153 | __FILE__, __LINE__, "expected exception " #e_type); \ 154 | } while(0) 155 | 156 | #define ASSERT_ANY_THROW(expr) \ 157 | do { try { expr } \ 158 | catch (...) { break; } \ 159 | throw ::test::AssertFailedError( \ 160 | __FILE__, __LINE__, "expected any exception"); \ 161 | } while(0) 162 | 163 | #define ASSERT_NO_THROW(expr) \ 164 | try { expr } \ 165 | catch (...) { \ 166 | throw ::test::AssertFailedError( \ 167 | __FILE__, __LINE__, "no exception expected"); \ 168 | } 169 | 170 | #endif /* H_GUARD */ 171 | -------------------------------------------------------------------------------- /test_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(backward-package-test) 2 | cmake_minimum_required(VERSION 2.8) 3 | 4 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 5 | include(${CONAN_CMAKE-UTILS_ROOT}/conan.cmake) 6 | conan_basic_setup() 7 | 8 | add_conan_library(backward) 9 | 10 | add_executable(example main.cpp) 11 | target_link_libraries(example PRIVATE backward-conan) 12 | -------------------------------------------------------------------------------- /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 | requires = 'cmake-utils/0.0.0@Manu343726/testing', 'backward/1.3.0@Manu343726/testing' 7 | generators = 'cmake' 8 | 9 | def build(self): 10 | cmake = CMake(self.settings) 11 | self.run('cmake {} {}'.format(self.conanfile_directory, cmake.command_line)) 12 | self.run('cmake --build . {}'.format(cmake.build_config)) 13 | 14 | def test(self): 15 | self.run(os.path.join('.', 'bin', 'example')) 16 | -------------------------------------------------------------------------------- /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 | { 10 | public: 11 | TracedException() : 12 | std::runtime_error(_get_trace()) 13 | {} 14 | 15 | private: 16 | std::string _get_trace() 17 | { 18 | std::ostringstream ss; 19 | 20 | StackTrace stackTrace; 21 | TraceResolver resolver; 22 | stackTrace.load_here(); 23 | resolver.load_stacktrace(stackTrace); 24 | 25 | for(std::size_t i = 0; i < stackTrace.size(); ++i) 26 | { 27 | const ResolvedTrace trace = resolver.resolve(stackTrace[i]); 28 | 29 | ss << "#" << i << " at " << trace.object_function << "\n"; 30 | } 31 | 32 | return ss.str(); 33 | } 34 | }; 35 | 36 | void f(int i) 37 | { 38 | if(i >= 42) 39 | { 40 | throw TracedException(); 41 | } 42 | else 43 | { 44 | std::cout << "i=" << i << "\n"; 45 | f(i + 1); 46 | } 47 | } 48 | 49 | int main() 50 | { 51 | try 52 | { 53 | f(0); 54 | } catch (const TracedException& ex) 55 | { 56 | std::cout << ex.what(); 57 | } 58 | } 59 | 60 | 61 | --------------------------------------------------------------------------------