├── .gitignore ├── .travis.yml ├── CHANGELOG.rst ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── backward_rosConfig.cmake.in ├── builds.sh ├── cmake ├── BackwardConfig.cmake └── BackwardConfigAment.cmake ├── conanfile.py ├── doc ├── nice.png ├── pretty.png └── rude.png ├── include └── backward_ros │ └── backward.hpp ├── package.xml ├── src └── backward.cpp ├── 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 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package backward_ros 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 1.0.7 (2025-01-30) 6 | ------------------ 7 | * Merge pull request #22 from Tobias-Fischer/patch-5 8 | Add dylib suffix on MacOS 9 | * Merge branch 'foxy-devel' into patch-5 10 | * Install missing package.xml for the ros debian packages (#24) 11 | Co-authored-by: Talha Gulbudak 12 | * Make backward_rosConfig.cmake file relocatable (#23) 13 | * Add dylib suffix on MacOS 14 | * Contributors: Jordan Palacios, Sai Kishor Kothakota, Silvio Traversaro, Tobias Fischer, talhagulbudak 15 | 16 | 1.0.6 (2024-11-07) 17 | ------------------ 18 | * Update backward_rosConfig.cmake.in 19 | adding both OS paths 20 | * Update backward_rosConfig.cmake.in to windows location 21 | * Set cxx standard to 14 22 | Foxy targets = C++14 23 | https://docs.ros.org/en/foxy/The-ROS2-Project/Contributing/Code-Style-Language-Versions.html 24 | * Merge branch 'remove/linters' into 'foxy-devel' 25 | Remove linter tests for backward_ros 26 | See merge request qa/backward_ros!8 27 | * Merge branch 'fix/ament_cmake_config' into 'remove/linters' 28 | Include BackwardConfigAment.cmake in backward_ros cmake config 29 | See merge request qa/backward_ros!10 30 | * Include the full path to BackwardConfigAment.cmake 31 | * include the contents of BackwardConfigAment.cmake 32 | * Remove linter tests for backward_ros 33 | * Merge branch 'fix/link_library_always' into 'foxy-devel' 34 | Set backward_ros library to be linked always 35 | See merge request qa/backward_ros!7 36 | * Set backward_ros library to be linked always 37 | * Contributors: Gilmar Correia, Noel Jimenez, Sai Kishor Kothakota, mosfet80 38 | 39 | 1.0.5 (2023-12-29) 40 | ------------------ 41 | * Merge pull request #14 from christophfroehlich/patch-1 42 | Fix missing cmake config install rule 43 | * Readd old configuration 44 | Co-authored-by: Sai Kishor Kothakota 45 | * Update CMakeLists.txt 46 | * Contributors: Christoph Fröhlich, Sai Kishor Kothakota 47 | 48 | 1.0.4 (2023-12-04) 49 | ------------------ 50 | * Merge branch 'cmake' into 'foxy-devel' 51 | Implement it as a pure CMake library 52 | See merge request qa/backward_ros!4 53 | * Remove INCLUDE_DIRS from package.xml 54 | * Implement it as a pure CMake library 55 | * Contributors: Adria Roig 56 | 57 | 1.0.3 (2023-11-14) 58 | ------------------ 59 | * Add website tag 60 | * Contributors: Noel Jimenez 61 | 62 | 1.0.2 (2022-03-31) 63 | ------------------ 64 | * Merge pull request #11 from vatanaksoytezer/vatan/fix_fphsa 65 | Fix name mismatch warnings 66 | * Fix name mismatch warnings 67 | * Contributors: Jordan Palacios, Vatan Aksoy Tezer 68 | 69 | 1.0.1 (2021-07-14) 70 | ------------------ 71 | * Add missing ament_cmake dependency 72 | * Contributors: Victor Lopez 73 | 74 | 1.0.0 (2021-07-13) 75 | ------------------ 76 | * Update backward.hpp from https://github.com/bombela/backward-cpp 77 | * Update README 78 | * Update package.xml 79 | * Adapt package to ROS2 80 | * Contributors: Victor Lopez 81 | 82 | 0.1.7 (2019-01-11) 83 | ------------------ 84 | * Merge pull request #3 from veimox/bugfix/fix-tests-headers 85 | modified heders to be included with current cmake configuration 86 | * modified heders to be included with current cmake configuration 87 | * Contributors: Jorge Rodriguez, Victor Lopez 88 | 89 | 0.1.6 (2018-05-02) 90 | ------------------ 91 | * Replace #warning with #pragma message 92 | * Update backward to the latest version 93 | * Fx wrong LIBRARIES variable concatenation 94 | * Contributors: Victor Lopez 95 | 96 | 0.1.5 (2018-04-27) 97 | ------------------ 98 | * Fixed version for finding libraries for catkin_make 99 | * Resolve relative paths generated by catkin_make 100 | * Merge branch 'fix_typos' into 'master' 101 | Fix typos in README.md 102 | See merge request qa/backward_ros!1 103 | * Fix typos in README.md 104 | * Change Readme.md so we put backward_ros specific information. 105 | * Force backward lib to link with backward targets 106 | * Contributors: Jordan Palacios, Victor, Victor Lopez 107 | 108 | 0.1.4 (2017-05-31) 109 | ------------------ 110 | * Rosify backward cpp, add automatic sighandle and rosconsole print 111 | * Update doc about color mode & std::ostream. 112 | * Fix typo in build link 113 | * Merge pull request #62 from bryant1410/master 114 | Fix broken headings in Markdown files 115 | * Add color mode. 116 | This is a breaking change. 117 | The printer now offers a color_mode setting: automatic, always, never. 118 | When given a FILE* stream on linux, the automatic mode will retrieve 119 | file descriptor behind it and call isatty(). 120 | * Merge branch 'print-to-streams' of https://github.com/ogdf/backward-cpp into ogdf-print-to-streams 121 | * Fix broken Markdown headings 122 | * Merge pull request #60 from ogdf/g++7-warnings 123 | Fix g++ 7.0.1 warnings 124 | * enable tests that somebody thought it was a good idea to permanently disable. 125 | * Only catch signals with a default action of "Core" 126 | Close #59 127 | * Fix g++- 7.0.1 -Wshadow warnings in test lib 128 | * Fix -Wshadow warnings from g++ 7.0.1 129 | * Fix -Wimplicit-fallthrough warning from g++ 7.0.1 130 | The fallthrough (missing break after a case in a switch) is 131 | not necessary. We can just move the whole default code move 132 | down after the switch. 133 | * Merge pull request #58 from krf/fix-warning 134 | Fix -Wmissing-noreturn warning from Clang 135 | * Fix -Wmissing-noreturn warning from Clang 136 | * Merge pull request #56 from akreuzkamp/master 137 | Operator names are not supported by MSVC out of the box. Using them breaks code that needs to build with MSVC and/or (thus) uses "-fno-operator-names". As a header-only library should pursue maximal 138 | portability, this PR replaces the single usage of operator names with the more portable operator syntax. 139 | * Use `&&` instead of `and`. 140 | Operator names are not supported by MSVC out of the box. Using them 141 | breaks code that needs to build with MSVC and/or (thus) uses 142 | "-fno-operator-names". As a header-only library should pursue maximal 143 | portability, this commit replaces the single usage of operator names 144 | with the more portable operator syntax. 145 | * Merge pull request #57 from bombela/issue-55 146 | Fix conanfile recipe 147 | * [#55] Conan options are represented as attributes instead of map entries 148 | * Revert "[#55] omit cmake options in conanfile build step" 149 | This reverts commit 06fb80378505d5792c8ce8dcadacdabb9ae45ce7. 150 | * [#55] omit cmake options in conanfile build step 151 | * conan recipe url field points to official repository 152 | * Conan badge pointing to 1.3.0 release 153 | * conan recipe pointing to 1.3.0 release 154 | * Update travis ci badge to point to official builds 155 | * conan.io package 156 | * Merge pull request #49 from ruipires/master 157 | adds support for ppc architecture 158 | * Run tests on Travis CI 159 | * adds support for ppc architecture 160 | * Merge pull request #45 from edisongustavo/master 161 | Add support to find_package(Backward) 162 | * Add detailed instructions on how to use cmake to integrate Backward 163 | * Prevent that the cmake variable BACKWARD_INCLUDE_DIRS be infinitely appended on successive cmake runs 164 | * Don't use find_package() in CMakeLists.txt since it does not make sense 165 | * Merge pull request #44 from ogdf/make-skip-public 166 | Make StackTraceImpl*::skip_n_firsts() setter public 167 | * Merge pull request #43 from ogdf/make-context-sizes-configurable 168 | Printer: Make context sizes configurable 169 | * Add install() 170 | * Add support to find_package(Backward) 171 | * Make StackTraceImpl*::skip_n_firsts() setter public 172 | When the stack trace is used directly (and not by a signal), 173 | one may want to hide some of the first stack items because 174 | they will always be the same calls. 175 | * Printer: Make context sizes configurable 176 | * Let Colorizer reset on initialization 177 | * Make Printer::print methods available for streams 178 | * Merge pull request #40 from Jiboo/master 179 | Don't append definitions if already cached 180 | * Don't append definitions if already cached 181 | * Merge pull request #39 from merlinthered/master 182 | Some CMake-related fixes 183 | * Export BACKWARD\_[...] CMake variables as cache variables 184 | If we do not export the variables set in BackwardMacros.cmake as cache variables, they will not be visible wherever add_backward() is called, and the macro will do nothing. 185 | Also, fix typo `BACKWARD_INCLUDE_DIR` -> `BACKWARD_INCLUDE_DIRS` 186 | * Readme fixes/clarifications for CMake 187 | Fixed name of BACKWARD_ENABLE variable 188 | Changed "myproject" to "mytarget" to avoid confusion 189 | * Merge pull request #34 from milianw/fix_bfd_compile_on_archlinux 190 | Define PACKAGE and PACKAGE_VERSION before including bfd.h 191 | * Merge pull request #35 from vvjcarter/master 192 | Add current List directory so that BackwardMacros.cmake correctly inc… 193 | * Add current List directory so that BackwardMacros.cmake correctly includes the directory 194 | * Define PACKAGE and PACKAGE_VERSION before including bfd.h 195 | On ArchLinux at least the bfd.h header errors out early if 196 | PACKAGE or PACKAGE_VERSION have not been set. This patch 197 | makes backward.hpp compile on this platform for me. 198 | * Merge pull request #33 from Manu343726/master 199 | Add ARM support 200 | * Add ARM support 201 | * Merge pull request #30 from akreuzkamp/master 202 | Split off cmake macros and dependency detection to BackwardMacros.cmake 203 | * Merge pull request #31 from akreuzkamp/fixQtCompatibility 204 | Fix compatibility to Qt applications 205 | * Rename variable "signals" to "unix_signals". 206 | This change is needed for compatibility to Qt applications. 207 | Qt defines a macro "signals" that expands to nothing and is used by 208 | Qt's meta object compiler (which is kind of a C++ pre-processor) to 209 | add signal-slot feature (http://doc.qt.io/qt-5/signalsandslots.html). 210 | This will cause compilation of backward.hpp to fail, when used from a 211 | Qt application, because the variable "signals" will be expanded to "" 212 | by the cpp pre processor. 213 | * Split off cmake macros and dependency detection. 214 | This commit seperates some of the CMake code into a 215 | BackwardMacros.cmake file. Including the CMakeLists.txt with 216 | add_subdirectory doesn't work, because the variables it defines won't 217 | be available from the including CMakeLists. With a .cmake file, they 218 | will. 219 | This change is backward-compatible, because CMakeLists.txt includes 220 | the BackwardMacros.cmake as well. 221 | * Merge pull request #27 from gbitzes/master 222 | Fix typos in README code examples 223 | * Fix typos in README code examples 224 | * Try to get the CMakeLists.txt working and somewhat flexible. 225 | * Merge pull request #23 from edisongustavo/master 226 | Compile in Visual Studio 2010 227 | * Replace usages of alternative usages of boolean operators with more more standard ones since they don't compile in Visual Studio 2010. 228 | Replacements are: 229 | - 'and' => '&&' 230 | - 'or' => '||' 231 | - 'not' => '!' 232 | Also added some missing includes since MSVC was complaining of missing 233 | symbols. 234 | * Update README.md 235 | Remove any references to TraceWithLocals since its not even part of the source anymore. 236 | * Merge pull request #22 from edisongustavo/master 237 | Compilers with GLIBC < 2.10 don't have the psiginfo() function 238 | * Compilers with GLIBC < 2.10 don't have the psiginfo() function 239 | * Merge pull request #20 from hesiod/master 240 | Documentation 241 | * Revert 0660344 242 | Turns out I was wrong, we do need that command. 243 | * Mark most cache values as advanced 244 | To avoid confusion, hide some configuration value from the normal user. 245 | * Fix CMake style 246 | I erred concerning CMake variable naming conventions. 247 | * Make feature detection values internal 248 | Previously, the "feature detection options" (STACK_DETAILS\_* and STACK_WALKING\_*) had to be manually set by an user, but now, we (attempt) to detect libraries based on find_library, thus we don't need those to be cache values anymore. Libraries in non-default paths can be manually added in the corresponding _PATH cache value and for testing purposes, users can also disable libraries by supplying an empty string. 249 | * Fix message type 250 | Whoops, there is no INFO message type. Make it a WARNING instead. 251 | * Remove unneeded CMake command 252 | Definitions are already included with add_backward. 253 | * Describe CMake integration 254 | * Merge pull request #19 from hesiod/patch-3 255 | Fix typo 256 | * Fix typo 257 | Should be backward_DEFINITIONS instead of BACKWARD_DEFINITIONS. 258 | * Merge pull request #18 from hesiod/patch-2 259 | Remove BACKWARD_ENABLE_ONLY_IN_DEBUG 260 | * Remove BACKWARD_ENABLE_ONLY_IN_DEBUG 261 | Doesn't work (yet) because CMake lacks generator expressions when listing source files. 262 | * Merge pull request #15 from hesiod/fix-clang-warning 263 | Fix compilation warning under clang 264 | * Merge pull request #16 from hesiod/build-system 265 | Build system 266 | * Require CMake 2.8.8 267 | Object libraries were introduced in CMake 2.8.8. 268 | Signed-off-by: Tobias Markus 269 | * Fix compilation warning under clang 270 | Clang complains about adding const to a reference type having no effect. 271 | Fix this by adding and using a const_ref_t. 272 | * Improve CMake build system, allow easy integration 273 | Make backward easier to use for CMake users, enabling easy feature detection and integration. 274 | We now detect presence of libdw, libbfd and libunwind directly in the script. The macro add_backward adds needed libraries, definitions and the include directory to a target. The pseudo-library enable_backward enables automatic backward processing for common failures to a target. 275 | If BACKWARD_ENABLE_ONLY_IN_DEBUG is set to true, automatic backward processing is only added when the build type is Debug or RelWithDebInfo. 276 | To integrate backward in an existing CMake project: 277 | add_subdirectory(/path/to/backward-cpp) 278 | add_executable(mytarget example.cpp ${backward_ENABLE}) 279 | add_backward(mytarget) 280 | BACKWARD_DEFINITIONS has been renamed to backward_DEFINITIONS to have a naming convention for public variables similiar to the find modules. 281 | * Remove unneeded include directive 282 | There is no "include" directory in backward-cpp and hence no need to include it. 283 | * Add option to disable test compilation 284 | * Merge pull request #14 from hesiod/patch-1 285 | Include current source directory 286 | * Include current source directory 287 | When including the project from a parent directory (add_subdirectory), backward.hpp is not found by the tests because we include CMAKE_SOURCE_DIR, which is the parent's source directory. Include CMAKE_CURRENT_SOURCE_DIR instead. 288 | * Merge pull request #13 from fabceolin/master 289 | Fixing buffer overflow on signals array 290 | * Fixing buffer overflow on signals array 291 | * stackoverflow test 292 | * Tests refactored onto less files. 293 | * Tests are all passing now. 294 | * Better test checking 295 | - fork every test 296 | - capture signals 297 | - more assertion tools 298 | - test can be expected to fail 299 | * Adding BACKWARD_HAS\_* config in cmake for tests 300 | * A more flexible and factorized stack Printer. 301 | * Add defines for "ATLEAST_CXX11" 302 | * A system specializable demangler implementation. 303 | * Remove unused local var support (for now) 304 | * Remove noisy comments. 305 | * Little typo fix 306 | * Add .gitignore 307 | * Some improvement to builds.sh 308 | * Merge pull request #11 from Kobolog/master 309 | Fixed an unused parameter warning. 310 | * Proper way to mark a variable unused. 311 | Instead of explicitly void-ing a variable, just make it anonymous. 312 | * Fix unused parameter warnings. 313 | In SignalHandling constructor, the empty signal vector is never used, 314 | which triggers unused parameter warnings at least on Clang 3.2. 315 | * Merge pull request #10 from Kobolog/master 316 | Unsigned line counters. 317 | * Resolve issue #6: unsigned line counters. 318 | Change all the line counter variable types from size_t to unsigned, so 319 | that we can printf() them as '%u' in a portable way, effectively fixing 320 | build errors on ancient i386 machines. 321 | * Merge pull request #9 from bastih/master 322 | Remove debug printf 323 | * Remove debug printf from SignalHandling() 324 | * Merge pull request #7 from bastih/master 325 | Allow for selection of signals thanks @bastih 326 | * Add default parameter to BACKWARD_SYSTEN_UNKNOWN signal handler 327 | * Improve as per bombela's suggestions, add test 328 | * Merge branch 'buildfix' 329 | * Remove non-existing testcase from CMakeLists 330 | * Make signals configurable 331 | This allows for selecting the appropriate signals 332 | and avoids conflicts with other tools that specific 333 | signals i.e. profilers 334 | * Merge pull request #5 from Kobolog/master 335 | Fixed using the placebo implementation on MacOS 336 | * fixed a typo in the stacktrace_tag, which is fortunately unused as of now 337 | * added an empty body to Colorize::init() method on non-Linux systems 338 | * Use a dedicated stack for signals handler 339 | * few modification for pedantic compliance with C++98 and C++11 340 | * a little shell script to manipulate many builds 341 | * Merge branch 'master' of github.com:bombela/backward-cpp 342 | * Attribute Copyright to Google Inc. 343 | * A little hack to declare _Unwind_GetIPInfo with clang. 344 | fixes #2 345 | * Attribute Copyright to Google Inc. 346 | * Merge pull request #1 from remram44/fix-readme 347 | Corrections to the README file 348 | * Corrections to the README file 349 | * Update README.md 350 | * Some typo fixes. 351 | * Initial import. 352 | * Contributors: Andrey Sibiryov, Anton Kreuzkamp, Edison Gustavo Muenz, Fabrício Ceolin, François-Xavier Bourlet, Georgios Bitzes, Jean-Bapiste Lepesme, Kevin Funk, Manu343726, Marc Strämke, Milian Wolff, Remi Rampin, Rui Pires, Santiago Castro, Stephan Beyer, Tobias Markus, Victor Lopez, bastih, hesiod, merlinthered 353 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # CMakeLists.txt 3 | # Copyright 2013 Google Inc. All Rights Reserved. 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | cmake_minimum_required(VERSION 3.5) 24 | project(backward_ros) 25 | 26 | include(cmake/BackwardConfig.cmake) 27 | 28 | set(CMAKE_CXX_STANDARD_REQUIRED True) 29 | set(CMAKE_CXX_STANDARD 14) 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 | include_directories(include) 44 | 45 | add_library(backward_object OBJECT src/backward.cpp) 46 | target_compile_definitions(backward_object PRIVATE ${BACKWARD_DEFINITIONS}) 47 | target_include_directories(backward_object PRIVATE ${BACKWARD_INCLUDE_DIRS}) 48 | set(BACKWARD_ENABLE $ CACHE STRING 49 | "Link with this object to setup backward automatically") 50 | 51 | 52 | ############################################################################### 53 | # BACKWARD LIBRARY (Includes backward.cpp) 54 | ############################################################################### 55 | option(BACKWARD_SHARED "Build dynamic backward-cpp shared lib" OFF) 56 | 57 | if(BACKWARD_SHARED) 58 | set(libtype SHARED) 59 | endif() 60 | add_library(backward SHARED ${libtype} src/backward.cpp) 61 | add_backward(backward) 62 | target_compile_definitions(backward PUBLIC ${BACKWARD_DEFINITIONS}) 63 | target_include_directories(backward PUBLIC ${BACKWARD_INCLUDE_DIRS}) 64 | 65 | ############################################################################### 66 | # TESTS 67 | ############################################################################### 68 | 69 | if(BACKWARD_TESTS) 70 | enable_testing() 71 | 72 | add_library(test_main SHARED test/_test_main.cpp) 73 | 74 | macro(backward_add_test src) 75 | get_filename_component(name ${src} NAME_WE) 76 | set(test_name "test_${name}") 77 | 78 | add_executable(${test_name} ${src} ${ARGN}) 79 | 80 | target_link_libraries(${test_name} PRIVATE Backward::Backward test_main) 81 | 82 | add_test(NAME ${name} COMMAND ${test_name}) 83 | endmacro() 84 | 85 | # Tests without backward.cpp 86 | set(TESTS 87 | test 88 | stacktrace 89 | rectrace 90 | select_signals 91 | ) 92 | 93 | foreach(test ${TESTS}) 94 | backward_add_test(test/${test}.cpp) 95 | endforeach() 96 | 97 | # Tests with backward.cpp 98 | set(TESTS 99 | suicide 100 | ) 101 | 102 | foreach(test ${TESTS}) 103 | backward_add_test(test/${test}.cpp ${BACKWARD_ENABLE}) 104 | endforeach() 105 | endif() 106 | 107 | install(TARGETS backward 108 | LIBRARY DESTINATION lib 109 | ARCHIVE DESTINATION lib 110 | RUNTIME DESTINATION bin 111 | ) 112 | 113 | install(DIRECTORY include/ 114 | DESTINATION include) 115 | 116 | install(DIRECTORY cmake 117 | DESTINATION share/${PROJECT_NAME} 118 | ) 119 | 120 | install(FILES package.xml 121 | DESTINATION share/${PROJECT_NAME} 122 | ) 123 | 124 | include(CMakePackageConfigHelpers) 125 | set(BACKWARD_ROS_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "backward_ros install prefix") 126 | configure_package_config_file( 127 | "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in" 128 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 129 | INSTALL_DESTINATION lib/cmake/${PROJECT_NAME} 130 | PATH_VARS BACKWARD_ROS_INSTALL_PREFIX 131 | ) 132 | 133 | 134 | configure_file(cmake/BackwardConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/BackwardConfig.cmake @ONLY) 135 | 136 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/BackwardConfig.cmake DESTINATION share/${PROJECT_NAME}/cmake) 137 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake DESTINATION share/${PROJECT_NAME}/cmake) 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /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 ROS 2 | ============ 3 | 4 | For info about the backward-cpp project check https://github.com/bombela/backward-cpp 5 | 6 | This wrapper should make it very easy to integrate backward_cpp into your ROS2 packages. 7 | 8 | ## Integration of backward_ros 9 | 10 | Add backward\_ros to your package.xml `backward_ros` 11 | 12 | Add backward\_ros to your CMakeLists.txt `find_package(backward_ros REQUIRED)` 13 | 14 | You're done, it should automatically add a library to your executables, when they crash, they should print a nice stacktrace like this: 15 | ![pretty stackstrace](doc/pretty.png) 16 | 17 | 18 | 19 | To get line numbers and more details, you need to build with debug information enabled (CMAKE_BUILD_TYPE = Debug or RelWithDebInfo) -------------------------------------------------------------------------------- /backward_rosConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | set_and_check(backward_ros_INCLUDE_DIRS "@PACKAGE_BACKWARD_ROS_INSTALL_PREFIX@/include") 3 | if(WIN32) 4 | set_and_check(backward_ros_LIBRARIES "@PACKAGE_BACKWARD_ROS_INSTALL_PREFIX@/lib/backward.lib") 5 | elseif(APPLE) 6 | set_and_check(backward_ros_LIBRARIES "@PACKAGE_BACKWARD_ROS_INSTALL_PREFIX@/lib/libbackward.dylib") 7 | else() 8 | set_and_check(backward_ros_LIBRARIES "@PACKAGE_BACKWARD_ROS_INSTALL_PREFIX@/lib/libbackward.so") 9 | endif() 10 | check_required_components(backward_ros) 11 | include(@PACKAGE_BACKWARD_ROS_INSTALL_PREFIX@/share/@PROJECT_NAME@/cmake/BackwardConfigAment.cmake) 12 | -------------------------------------------------------------------------------- /builds.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | COMPILERS_CXX98=`cat</dev/null 28 | ( 29 | cd "$builddir" 30 | cmake -DCMAKE_BUILD_TYPE=$buildtype -DBACKWARD_TESTS=ON .. 31 | ) 32 | } 33 | 34 | function build() { 35 | local builddir=$1 36 | shift 37 | make -C "$builddir" $@ 38 | } 39 | 40 | function dotest() { 41 | local builddir=$1 42 | shift 43 | make -C "$builddir" test $@ 44 | return 0 45 | } 46 | 47 | function do_action() { 48 | local lang=$1 49 | local action=$2 50 | shift 2 51 | 52 | for compiler in $COMPILERS; do 53 | local builddir="build_${lang}_${compiler}" 54 | 55 | if [[ $action == "cmake" ]]; then 56 | buildtype=$1 57 | mkbuild $compiler $lang "$buildtype" "$builddir" 58 | [[ $? != 0 ]] && exit 59 | elif [[ $action == "make" ]]; then 60 | build "$builddir" $@ 61 | [[ $? != 0 ]] && exit 62 | elif [[ $action == "test" ]]; then 63 | dotest "$builddir" $@ 64 | [[ $? != 0 ]] && exit 65 | elif [[ $action == "clean" ]]; then 66 | rm -r "$builddir" 67 | else 68 | echo "usage: $0 cmake [debug|release|relwithdbg]|make|test|clean" 69 | exit 255 70 | fi 71 | done 72 | } 73 | 74 | COMPILERS=$COMPILERS_CXX98 75 | do_action c++98 $@ 76 | COMPILERS=$COMPILERS_CXX11 77 | do_action c++11 $@ 78 | -------------------------------------------------------------------------------- /cmake/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 | set(FPHSA_NAME_MISMATCHED TRUE) 56 | find_package_handle_standard_args(libdw DEFAULT_MSG 57 | LIBDW_LIBRARY LIBDW_INCLUDE_DIR) 58 | mark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARY) 59 | 60 | # find libbfd 61 | find_path(LIBBFD_INCLUDE_DIR NAMES "bfd.h") 62 | find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h") 63 | find_library(LIBBFD_LIBRARY bfd) 64 | find_library(LIBDL_LIBRARY dl) 65 | set(LIBBFD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR}) 66 | set(LIBBFD_LIBRARIES ${LIBBFD_LIBRARY} ${LIBDL_LIBRARY}) 67 | find_package_handle_standard_args(libbfd DEFAULT_MSG 68 | LIBBFD_LIBRARY LIBBFD_INCLUDE_DIR 69 | LIBDL_LIBRARY LIBDL_INCLUDE_DIR) 70 | mark_as_advanced(LIBBFD_INCLUDE_DIR LIBBFD_LIBRARY 71 | LIBDL_INCLUDE_DIR LIBDL_LIBRARY) 72 | 73 | if (LIBDW_FOUND) 74 | LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDW_INCLUDE_DIRS}) 75 | LIST(APPEND BACKWARD_LIBRARIES ${LIBDW_LIBRARIES}) 76 | set(STACK_DETAILS_DW TRUE) 77 | set(STACK_DETAILS_BFD FALSE) 78 | set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) 79 | elseif(LIBBFD_FOUND) 80 | LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIRS}) 81 | LIST(APPEND BACKWARD_LIBRARIES ${LIBBFD_LIBRARIES}) 82 | 83 | # If we attempt to link against static bfd, make sure to link its dependencies, too 84 | get_filename_component(bfd_lib_ext "${LIBBFD_LIBRARY}" EXT) 85 | if (bfd_lib_ext STREQUAL "${CMAKE_STATIC_LIBRARY_SUFFIX}") 86 | list(APPEND BACKWARD_LIBRARIES iberty z) 87 | endif() 88 | 89 | set(STACK_DETAILS_DW FALSE) 90 | set(STACK_DETAILS_BFD TRUE) 91 | set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) 92 | else() 93 | set(STACK_DETAILS_DW FALSE) 94 | set(STACK_DETAILS_BFD FALSE) 95 | set(STACK_DETAILS_BACKTRACE_SYMBOL TRUE) 96 | endif() 97 | else() 98 | if (STACK_DETAILS_DW) 99 | LIST(APPEND BACKWARD_LIBRARIES dw) 100 | endif() 101 | 102 | if (STACK_DETAILS_BFD) 103 | LIST(APPEND BACKWARD_LIBRARIES bfd dl) 104 | endif() 105 | endif() 106 | 107 | macro(map_definitions var_prefix define_prefix) 108 | foreach(def ${ARGN}) 109 | if (${${var_prefix}${def}}) 110 | LIST(APPEND BACKWARD_DEFINITIONS "${define_prefix}${def}=1") 111 | else() 112 | LIST(APPEND BACKWARD_DEFINITIONS "${define_prefix}${def}=0") 113 | endif() 114 | endforeach() 115 | endmacro() 116 | 117 | if (NOT BACKWARD_DEFINITIONS) 118 | map_definitions("STACK_WALKING_" "BACKWARD_HAS_" UNWIND BACKTRACE) 119 | map_definitions("STACK_DETAILS_" "BACKWARD_HAS_" BACKTRACE_SYMBOL DW BFD) 120 | endif() 121 | 122 | foreach(def ${BACKWARD_DEFINITIONS}) 123 | message(STATUS "${def}") 124 | endforeach() 125 | 126 | set(BACKWARD_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}") 127 | 128 | include(FindPackageHandleStandardArgs) 129 | find_package_handle_standard_args(Backward 130 | REQUIRED_VARS 131 | BACKWARD_INCLUDE_DIR 132 | BACKWARD_LIBRARIES 133 | ) 134 | list(APPEND _BACKWARD_INCLUDE_DIRS ${BACKWARD_INCLUDE_DIR}) 135 | 136 | macro(add_backward target) 137 | target_include_directories(${target} PRIVATE ${BACKWARD_INCLUDE_DIRS}) 138 | set_property(TARGET ${target} APPEND PROPERTY COMPILE_DEFINITIONS ${BACKWARD_DEFINITIONS}) 139 | set_property(TARGET ${target} APPEND PROPERTY LINK_LIBRARIES ${BACKWARD_LIBRARIES}) 140 | endmacro() 141 | set(BACKWARD_INCLUDE_DIRS ${_BACKWARD_INCLUDE_DIRS} CACHE INTERNAL "_BACKWARD_INCLUDE_DIRS") 142 | set(BACKWARD_DEFINITIONS ${BACKWARD_DEFINITIONS} CACHE INTERNAL "BACKWARD_DEFINITIONS") 143 | set(BACKWARD_LIBRARIES ${BACKWARD_LIBRARIES} CACHE INTERNAL "BACKWARD_LIBRARIES") 144 | mark_as_advanced(_BACKWARD_INCLUDE_DIRS BACKWARD_DEFINITIONS BACKWARD_LIBRARIES) 145 | 146 | if (NOT TARGET Backward::Backward) 147 | add_library(Backward::Backward INTERFACE IMPORTED) 148 | set_target_properties(Backward::Backward PROPERTIES 149 | INTERFACE_INCLUDE_DIRECTORIES "${_BACKWARD_INCLUDE_DIRS}" 150 | INTERFACE_LINK_LIBRARIES "${BACKWARD_LIBRARIES}" 151 | INTERFACE_COMPILE_DEFINITIONS "${BACKWARD_DEFINITIONS}" 152 | ) 153 | endif() 154 | -------------------------------------------------------------------------------- /cmake/BackwardConfigAment.cmake: -------------------------------------------------------------------------------- 1 | if (LIBDW_FOUND) 2 | add_definitions(-DBACKWARD_HAS_DW=1) 3 | set(backward_ros_forced_LIBRARIES "${backward_ros_LIBRARIES};${LIBDW_LIBRARIES}") 4 | elseif(LIBBFD_FOUND) 5 | add_definitions(-DBACKWARD_HAS_BFD=1) 6 | set(backward_ros_forced_LIBRARIES "${backward_ros_LIBRARIES};${LIBBFD_LIBRARIES}") 7 | else() 8 | set(backward_ros_forced_LIBRARIES "${backward_ros_LIBRARIES}") 9 | endif() 10 | #Hack to find absolute path to libraries, won't work if library is not compiled yet 11 | foreach(lib ${backward_ros_forced_LIBRARIES}) 12 | if(NOT EXISTS ${lib}) 13 | message("${lib} doesn't exist, trying to find it in ${backward_ros_PREFIX}") 14 | find_library(backward_ros_lib_path 15 | NAMES ${lib} 16 | PATHS ${backward_ros_PREFIX}) 17 | if(NOT ${backward_ros_lib_path}) 18 | message("${lib} not found") 19 | else() 20 | message("${backward_ros_lib_path} found") 21 | set(backward_ros_full_path_LIBRARIES "${backward_ros_full_path_LIBRARIES} ${backward_ros_lib_path}") 22 | endif() 23 | else() 24 | set(backward_ros_full_path_LIBRARIES "${backward_ros_full_path_LIBRARIES} ${lib}") 25 | endif() 26 | endforeach() 27 | SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed ${backward_ros_full_path_LIBRARIES} -Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}") 28 | 29 | -------------------------------------------------------------------------------- /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/pal-robotics/backward_ros/7d1942aa8e5a597dccf67bc41624db9dccd03e40/doc/nice.png -------------------------------------------------------------------------------- /doc/pretty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pal-robotics/backward_ros/7d1942aa8e5a597dccf67bc41624db9dccd03e40/doc/pretty.png -------------------------------------------------------------------------------- /doc/rude.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pal-robotics/backward_ros/7d1942aa8e5a597dccf67bc41624db9dccd03e40/doc/rude.png -------------------------------------------------------------------------------- /include/backward_ros/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 || (defined(_MSC_VER) && _MSC_VER >= 1800) 35 | #define BACKWARD_CXX11 36 | #define BACKWARD_ATLEAST_CXX11 37 | #define BACKWARD_ATLEAST_CXX98 38 | #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) 39 | #define BACKWARD_ATLEAST_CXX17 40 | #endif 41 | #else 42 | #define BACKWARD_CXX98 43 | #define BACKWARD_ATLEAST_CXX98 44 | #endif 45 | #endif 46 | 47 | // You can define one of the following (or leave it to the auto-detection): 48 | // 49 | // #define BACKWARD_SYSTEM_LINUX 50 | // - specialization for linux 51 | // 52 | // #define BACKWARD_SYSTEM_DARWIN 53 | // - specialization for Mac OS X 10.5 and later. 54 | // 55 | // #define BACKWARD_SYSTEM_WINDOWS 56 | // - specialization for Windows (Clang 9 and MSVC2017) 57 | // 58 | // #define BACKWARD_SYSTEM_UNKNOWN 59 | // - placebo implementation, does nothing. 60 | // 61 | #if defined(BACKWARD_SYSTEM_LINUX) 62 | #elif defined(BACKWARD_SYSTEM_DARWIN) 63 | #elif defined(BACKWARD_SYSTEM_UNKNOWN) 64 | #elif defined(BACKWARD_SYSTEM_WINDOWS) 65 | #else 66 | #if defined(__linux) || defined(__linux__) 67 | #define BACKWARD_SYSTEM_LINUX 68 | #elif defined(__APPLE__) 69 | #define BACKWARD_SYSTEM_DARWIN 70 | #elif defined(_WIN32) 71 | #define BACKWARD_SYSTEM_WINDOWS 72 | #else 73 | #define BACKWARD_SYSTEM_UNKNOWN 74 | #endif 75 | #endif 76 | 77 | #define NOINLINE __attribute__((noinline)) 78 | 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | #include 95 | 96 | #if defined(BACKWARD_SYSTEM_LINUX) 97 | 98 | // On linux, backtrace can back-trace or "walk" the stack using the following 99 | // libraries: 100 | // 101 | // #define BACKWARD_HAS_UNWIND 1 102 | // - unwind comes from libgcc, but I saw an equivalent inside clang itself. 103 | // - with unwind, the stacktrace is as accurate as it can possibly be, since 104 | // this is used by the C++ runtine in gcc/clang for stack unwinding on 105 | // exception. 106 | // - normally libgcc is already linked to your program by default. 107 | // 108 | // #define BACKWARD_HAS_LIBUNWIND 1 109 | // - libunwind provides, in some cases, a more accurate stacktrace as it knows 110 | // to decode signal handler frames and lets us edit the context registers when 111 | // unwinding, allowing stack traces over bad function references. 112 | // 113 | // #define BACKWARD_HAS_BACKTRACE == 1 114 | // - backtrace seems to be a little bit more portable than libunwind, but on 115 | // linux, it uses unwind anyway, but abstract away a tiny information that is 116 | // sadly really important in order to get perfectly accurate stack traces. 117 | // - backtrace is part of the (e)glib library. 118 | // 119 | // The default is: 120 | // #define BACKWARD_HAS_UNWIND == 1 121 | // 122 | // Note that only one of the define should be set to 1 at a time. 123 | // 124 | #if BACKWARD_HAS_UNWIND == 1 125 | #elif BACKWARD_HAS_LIBUNWIND == 1 126 | #elif BACKWARD_HAS_BACKTRACE == 1 127 | #else 128 | #undef BACKWARD_HAS_UNWIND 129 | #define BACKWARD_HAS_UNWIND 1 130 | #undef BACKWARD_HAS_LIBUNWIND 131 | #define BACKWARD_HAS_LIBUNWIND 0 132 | #undef BACKWARD_HAS_BACKTRACE 133 | #define BACKWARD_HAS_BACKTRACE 0 134 | #endif 135 | 136 | // On linux, backward can extract detailed information about a stack trace 137 | // using one of the following libraries: 138 | // 139 | // #define BACKWARD_HAS_DW 1 140 | // - libdw gives you the most juicy details out of your stack traces: 141 | // - object filename 142 | // - function name 143 | // - source filename 144 | // - line and column numbers 145 | // - source code snippet (assuming the file is accessible) 146 | // - variables name and values (if not optimized out) 147 | // - You need to link with the lib "dw": 148 | // - apt-get install libdw-dev 149 | // - g++/clang++ -ldw ... 150 | // 151 | // #define BACKWARD_HAS_BFD 1 152 | // - With libbfd, you get a fair amount of details: 153 | // - object filename 154 | // - function name 155 | // - source filename 156 | // - line numbers 157 | // - source code snippet (assuming the file is accessible) 158 | // - You need to link with the lib "bfd": 159 | // - apt-get install binutils-dev 160 | // - g++/clang++ -lbfd ... 161 | // 162 | // #define BACKWARD_HAS_DWARF 1 163 | // - libdwarf gives you the most juicy details out of your stack traces: 164 | // - object filename 165 | // - function name 166 | // - source filename 167 | // - line and column numbers 168 | // - source code snippet (assuming the file is accessible) 169 | // - variables name and values (if not optimized out) 170 | // - You need to link with the lib "dwarf": 171 | // - apt-get install libdwarf-dev 172 | // - g++/clang++ -ldwarf ... 173 | // 174 | // #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 175 | // - backtrace provides minimal details for a stack trace: 176 | // - object filename 177 | // - function name 178 | // - backtrace is part of the (e)glib library. 179 | // 180 | // The default is: 181 | // #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 182 | // 183 | // Note that only one of the define should be set to 1 at a time. 184 | // 185 | #if BACKWARD_HAS_DW == 1 186 | #elif BACKWARD_HAS_BFD == 1 187 | #elif BACKWARD_HAS_DWARF == 1 188 | #elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 189 | #else 190 | #undef BACKWARD_HAS_DW 191 | #define BACKWARD_HAS_DW 0 192 | #undef BACKWARD_HAS_BFD 193 | #define BACKWARD_HAS_BFD 0 194 | #undef BACKWARD_HAS_DWARF 195 | #define BACKWARD_HAS_DWARF 0 196 | #undef BACKWARD_HAS_BACKTRACE_SYMBOL 197 | #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 198 | #endif 199 | 200 | #include 201 | #include 202 | #ifdef __ANDROID__ 203 | // Old Android API levels define _Unwind_Ptr in both link.h and 204 | // unwind.h Rename the one in link.h as we are not going to be using 205 | // it 206 | #define _Unwind_Ptr _Unwind_Ptr_Custom 207 | #include 208 | #undef _Unwind_Ptr 209 | #else 210 | #include 211 | #endif 212 | #include 213 | #include 214 | #include 215 | #include 216 | 217 | #if BACKWARD_HAS_BFD == 1 218 | // NOTE: defining PACKAGE{,_VERSION} is required before including 219 | // bfd.h on some platforms, see also: 220 | // https://sourceware.org/bugzilla/show_bug.cgi?id=14243 221 | #ifndef PACKAGE 222 | #define PACKAGE 223 | #endif 224 | #ifndef PACKAGE_VERSION 225 | #define PACKAGE_VERSION 226 | #endif 227 | #include 228 | #ifndef _GNU_SOURCE 229 | #define _GNU_SOURCE 230 | #include 231 | #undef _GNU_SOURCE 232 | #else 233 | #include 234 | #endif 235 | #endif 236 | 237 | #if BACKWARD_HAS_DW == 1 238 | #include 239 | #include 240 | #include 241 | #endif 242 | 243 | #if BACKWARD_HAS_DWARF == 1 244 | #include 245 | #include 246 | #include 247 | #include 248 | #include 249 | #ifndef _GNU_SOURCE 250 | #define _GNU_SOURCE 251 | #include 252 | #undef _GNU_SOURCE 253 | #else 254 | #include 255 | #endif 256 | #endif 257 | 258 | #if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) 259 | // then we shall rely on backtrace 260 | #include 261 | #endif 262 | 263 | #endif // defined(BACKWARD_SYSTEM_LINUX) 264 | 265 | #if defined(BACKWARD_SYSTEM_DARWIN) 266 | // On Darwin, backtrace can back-trace or "walk" the stack using the following 267 | // libraries: 268 | // 269 | // #define BACKWARD_HAS_UNWIND 1 270 | // - unwind comes from libgcc, but I saw an equivalent inside clang itself. 271 | // - with unwind, the stacktrace is as accurate as it can possibly be, since 272 | // this is used by the C++ runtine in gcc/clang for stack unwinding on 273 | // exception. 274 | // - normally libgcc is already linked to your program by default. 275 | // 276 | // #define BACKWARD_HAS_LIBUNWIND 1 277 | // - libunwind comes from clang, which implements an API compatible version. 278 | // - libunwind provides, in some cases, a more accurate stacktrace as it knows 279 | // to decode signal handler frames and lets us edit the context registers when 280 | // unwinding, allowing stack traces over bad function references. 281 | // 282 | // #define BACKWARD_HAS_BACKTRACE == 1 283 | // - backtrace is available by default, though it does not produce as much 284 | // information as another library might. 285 | // 286 | // The default is: 287 | // #define BACKWARD_HAS_UNWIND == 1 288 | // 289 | // Note that only one of the define should be set to 1 at a time. 290 | // 291 | #if BACKWARD_HAS_UNWIND == 1 292 | #elif BACKWARD_HAS_BACKTRACE == 1 293 | #elif BACKWARD_HAS_LIBUNWIND == 1 294 | #else 295 | #undef BACKWARD_HAS_UNWIND 296 | #define BACKWARD_HAS_UNWIND 1 297 | #undef BACKWARD_HAS_BACKTRACE 298 | #define BACKWARD_HAS_BACKTRACE 0 299 | #undef BACKWARD_HAS_LIBUNWIND 300 | #define BACKWARD_HAS_LIBUNWIND 0 301 | #endif 302 | 303 | // On Darwin, backward can extract detailed information about a stack trace 304 | // using one of the following libraries: 305 | // 306 | // #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 307 | // - backtrace provides minimal details for a stack trace: 308 | // - object filename 309 | // - function name 310 | // 311 | // The default is: 312 | // #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 313 | // 314 | #if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 315 | #else 316 | #undef BACKWARD_HAS_BACKTRACE_SYMBOL 317 | #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 318 | #endif 319 | 320 | #include 321 | #include 322 | #include 323 | #include 324 | #include 325 | #include 326 | 327 | #if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) 328 | #include 329 | #endif 330 | #endif // defined(BACKWARD_SYSTEM_DARWIN) 331 | 332 | #if defined(BACKWARD_SYSTEM_WINDOWS) 333 | 334 | #include 335 | #include 336 | #include 337 | 338 | #include 339 | typedef SSIZE_T ssize_t; 340 | 341 | #define NOMINMAX 342 | #include 343 | #include 344 | 345 | #include 346 | #include 347 | 348 | #ifndef __clang__ 349 | #undef NOINLINE 350 | #define NOINLINE __declspec(noinline) 351 | #endif 352 | 353 | #pragma comment(lib, "psapi.lib") 354 | #pragma comment(lib, "dbghelp.lib") 355 | 356 | // Comment / packing is from stackoverflow: 357 | // https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 358 | // Some versions of imagehlp.dll lack the proper packing directives themselves 359 | // so we need to do it. 360 | #pragma pack(push, before_imagehlp, 8) 361 | #include 362 | #pragma pack(pop, before_imagehlp) 363 | 364 | // TODO maybe these should be undefined somewhere else? 365 | #undef BACKWARD_HAS_UNWIND 366 | #undef BACKWARD_HAS_BACKTRACE 367 | #if BACKWARD_HAS_PDB_SYMBOL == 1 368 | #else 369 | #undef BACKWARD_HAS_PDB_SYMBOL 370 | #define BACKWARD_HAS_PDB_SYMBOL 1 371 | #endif 372 | 373 | #endif 374 | 375 | #if BACKWARD_HAS_UNWIND == 1 376 | 377 | #include 378 | // while gcc's unwind.h defines something like that: 379 | // extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); 380 | // extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *); 381 | // 382 | // clang's unwind.h defines something like this: 383 | // uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context); 384 | // 385 | // Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we 386 | // cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr 387 | // anyway. 388 | // 389 | // Luckily we can play on the fact that the guard macros have a different name: 390 | #ifdef __CLANG_UNWIND_H 391 | // In fact, this function still comes from libgcc (on my different linux boxes, 392 | // clang links against libgcc). 393 | #include 394 | extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context *, int *); 395 | #endif 396 | 397 | #endif // BACKWARD_HAS_UNWIND == 1 398 | 399 | #if BACKWARD_HAS_LIBUNWIND == 1 400 | #define UNW_LOCAL_ONLY 401 | #include 402 | #endif // BACKWARD_HAS_LIBUNWIND == 1 403 | 404 | #ifdef BACKWARD_ATLEAST_CXX11 405 | #include 406 | #include // for std::swap 407 | namespace backward { 408 | namespace details { 409 | template struct hashtable { 410 | typedef std::unordered_map type; 411 | }; 412 | using std::move; 413 | } // namespace details 414 | } // namespace backward 415 | #else // NOT BACKWARD_ATLEAST_CXX11 416 | #define nullptr NULL 417 | #define override 418 | #include 419 | namespace backward { 420 | namespace details { 421 | template struct hashtable { 422 | typedef std::map type; 423 | }; 424 | template const T &move(const T &v) { return v; } 425 | template T &move(T &v) { return v; } 426 | } // namespace details 427 | } // namespace backward 428 | #endif // BACKWARD_ATLEAST_CXX11 429 | 430 | namespace backward { 431 | namespace details { 432 | #if defined(BACKWARD_SYSTEM_WINDOWS) 433 | const char kBackwardPathDelimiter[] = ";"; 434 | #else 435 | const char kBackwardPathDelimiter[] = ":"; 436 | #endif 437 | } // namespace details 438 | } // namespace backward 439 | 440 | namespace backward { 441 | 442 | namespace system_tag { 443 | struct linux_tag; // seems that I cannot call that "linux" because the name 444 | // is already defined... so I am adding _tag everywhere. 445 | struct darwin_tag; 446 | struct windows_tag; 447 | struct unknown_tag; 448 | 449 | #if defined(BACKWARD_SYSTEM_LINUX) 450 | typedef linux_tag current_tag; 451 | #elif defined(BACKWARD_SYSTEM_DARWIN) 452 | typedef darwin_tag current_tag; 453 | #elif defined(BACKWARD_SYSTEM_WINDOWS) 454 | typedef windows_tag current_tag; 455 | #elif defined(BACKWARD_SYSTEM_UNKNOWN) 456 | typedef unknown_tag current_tag; 457 | #else 458 | #error "May I please get my system defines?" 459 | #endif 460 | } // namespace system_tag 461 | 462 | namespace trace_resolver_tag { 463 | #if defined(BACKWARD_SYSTEM_LINUX) 464 | struct libdw; 465 | struct libbfd; 466 | struct libdwarf; 467 | struct backtrace_symbol; 468 | 469 | #if BACKWARD_HAS_DW == 1 470 | typedef libdw current; 471 | #elif BACKWARD_HAS_BFD == 1 472 | typedef libbfd current; 473 | #elif BACKWARD_HAS_DWARF == 1 474 | typedef libdwarf current; 475 | #elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 476 | typedef backtrace_symbol current; 477 | #else 478 | #error "You shall not pass, until you know what you want." 479 | #endif 480 | #elif defined(BACKWARD_SYSTEM_DARWIN) 481 | struct backtrace_symbol; 482 | 483 | #if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 484 | typedef backtrace_symbol current; 485 | #else 486 | #error "You shall not pass, until you know what you want." 487 | #endif 488 | #elif defined(BACKWARD_SYSTEM_WINDOWS) 489 | struct pdb_symbol; 490 | #if BACKWARD_HAS_PDB_SYMBOL == 1 491 | typedef pdb_symbol current; 492 | #else 493 | #error "You shall not pass, until you know what you want." 494 | #endif 495 | #endif 496 | } // namespace trace_resolver_tag 497 | 498 | namespace details { 499 | 500 | template struct rm_ptr { typedef T type; }; 501 | 502 | template struct rm_ptr { typedef T type; }; 503 | 504 | template struct rm_ptr { typedef const T type; }; 505 | 506 | template struct deleter { 507 | template void operator()(U &ptr) const { (*F)(ptr); } 508 | }; 509 | 510 | template struct default_delete { 511 | void operator()(T &ptr) const { delete ptr; } 512 | }; 513 | 514 | template > 515 | class handle { 516 | struct dummy; 517 | T _val; 518 | bool _empty; 519 | 520 | #ifdef BACKWARD_ATLEAST_CXX11 521 | handle(const handle &) = delete; 522 | handle &operator=(const handle &) = delete; 523 | #endif 524 | 525 | public: 526 | ~handle() { 527 | if (!_empty) { 528 | Deleter()(_val); 529 | } 530 | } 531 | 532 | explicit handle() : _val(), _empty(true) {} 533 | explicit handle(T val) : _val(val), _empty(false) { 534 | if (!_val) 535 | _empty = true; 536 | } 537 | 538 | #ifdef BACKWARD_ATLEAST_CXX11 539 | handle(handle &&from) : _empty(true) { swap(from); } 540 | handle &operator=(handle &&from) { 541 | swap(from); 542 | return *this; 543 | } 544 | #else 545 | explicit handle(const handle &from) : _empty(true) { 546 | // some sort of poor man's move semantic. 547 | swap(const_cast(from)); 548 | } 549 | handle &operator=(const handle &from) { 550 | // some sort of poor man's move semantic. 551 | swap(const_cast(from)); 552 | return *this; 553 | } 554 | #endif 555 | 556 | void reset(T new_val) { 557 | handle tmp(new_val); 558 | swap(tmp); 559 | } 560 | 561 | void update(T new_val) { 562 | _val = new_val; 563 | _empty = !static_cast(new_val); 564 | } 565 | 566 | operator const dummy *() const { 567 | if (_empty) { 568 | return nullptr; 569 | } 570 | return reinterpret_cast(_val); 571 | } 572 | T get() { return _val; } 573 | T release() { 574 | _empty = true; 575 | return _val; 576 | } 577 | void swap(handle &b) { 578 | using std::swap; 579 | swap(b._val, _val); // can throw, we are safe here. 580 | swap(b._empty, _empty); // should not throw: if you cannot swap two 581 | // bools without throwing... It's a lost cause anyway! 582 | } 583 | 584 | T &operator->() { return _val; } 585 | const T &operator->() const { return _val; } 586 | 587 | typedef typename rm_ptr::type &ref_t; 588 | typedef const typename rm_ptr::type &const_ref_t; 589 | ref_t operator*() { return *_val; } 590 | const_ref_t operator*() const { return *_val; } 591 | ref_t operator[](size_t idx) { return _val[idx]; } 592 | 593 | // Watch out, we've got a badass over here 594 | T *operator&() { 595 | _empty = false; 596 | return &_val; 597 | } 598 | }; 599 | 600 | // Default demangler implementation (do nothing). 601 | template struct demangler_impl { 602 | static std::string demangle(const char *funcname) { return funcname; } 603 | }; 604 | 605 | #if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) 606 | 607 | template <> struct demangler_impl { 608 | demangler_impl() : _demangle_buffer_length(0) {} 609 | 610 | std::string demangle(const char *funcname) { 611 | using namespace details; 612 | char *result = abi::__cxa_demangle(funcname, _demangle_buffer.get(), 613 | &_demangle_buffer_length, nullptr); 614 | if (result) { 615 | _demangle_buffer.update(result); 616 | return result; 617 | } 618 | return funcname; 619 | } 620 | 621 | private: 622 | details::handle _demangle_buffer; 623 | size_t _demangle_buffer_length; 624 | }; 625 | 626 | #endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN 627 | 628 | struct demangler : public demangler_impl {}; 629 | 630 | // Split a string on the platform's PATH delimiter. Example: if delimiter 631 | // is ":" then: 632 | // "" --> [] 633 | // ":" --> ["",""] 634 | // "::" --> ["","",""] 635 | // "/a/b/c" --> ["/a/b/c"] 636 | // "/a/b/c:/d/e/f" --> ["/a/b/c","/d/e/f"] 637 | // etc. 638 | inline std::vector split_source_prefixes(const std::string &s) { 639 | std::vector out; 640 | size_t last = 0; 641 | size_t next = 0; 642 | size_t delimiter_size = sizeof(kBackwardPathDelimiter) - 1; 643 | while ((next = s.find(kBackwardPathDelimiter, last)) != std::string::npos) { 644 | out.push_back(s.substr(last, next - last)); 645 | last = next + delimiter_size; 646 | } 647 | if (last <= s.length()) { 648 | out.push_back(s.substr(last)); 649 | } 650 | return out; 651 | } 652 | 653 | } // namespace details 654 | 655 | /*************** A TRACE ***************/ 656 | 657 | struct Trace { 658 | void *addr; 659 | size_t idx; 660 | 661 | Trace() : addr(nullptr), idx(0) {} 662 | 663 | explicit Trace(void *_addr, size_t _idx) : addr(_addr), idx(_idx) {} 664 | }; 665 | 666 | struct ResolvedTrace : public Trace { 667 | 668 | struct SourceLoc { 669 | std::string function; 670 | std::string filename; 671 | unsigned line; 672 | unsigned col; 673 | 674 | SourceLoc() : line(0), col(0) {} 675 | 676 | bool operator==(const SourceLoc &b) const { 677 | return function == b.function && filename == b.filename && 678 | line == b.line && col == b.col; 679 | } 680 | 681 | bool operator!=(const SourceLoc &b) const { return !(*this == b); } 682 | }; 683 | 684 | // In which binary object this trace is located. 685 | std::string object_filename; 686 | 687 | // The function in the object that contain the trace. This is not the same 688 | // as source.function which can be an function inlined in object_function. 689 | std::string object_function; 690 | 691 | // The source location of this trace. It is possible for filename to be 692 | // empty and for line/col to be invalid (value 0) if this information 693 | // couldn't be deduced, for example if there is no debug information in the 694 | // binary object. 695 | SourceLoc source; 696 | 697 | // An optionals list of "inliners". All the successive sources location 698 | // from where the source location of the trace (the attribute right above) 699 | // is inlined. It is especially useful when you compiled with optimization. 700 | typedef std::vector source_locs_t; 701 | source_locs_t inliners; 702 | 703 | ResolvedTrace() : Trace() {} 704 | ResolvedTrace(const Trace &mini_trace) : Trace(mini_trace) {} 705 | }; 706 | 707 | /*************** STACK TRACE ***************/ 708 | 709 | // default implemention. 710 | template class StackTraceImpl { 711 | public: 712 | size_t size() const { return 0; } 713 | Trace operator[](size_t) const { return Trace(); } 714 | size_t load_here(size_t = 0) { return 0; } 715 | size_t load_from(void *, size_t = 0, void * = nullptr, void * = nullptr) { 716 | return 0; 717 | } 718 | size_t thread_id() const { return 0; } 719 | void skip_n_firsts(size_t) {} 720 | }; 721 | 722 | class StackTraceImplBase { 723 | public: 724 | StackTraceImplBase() 725 | : _thread_id(0), _skip(0), _context(nullptr), _error_addr(nullptr) {} 726 | 727 | size_t thread_id() const { return _thread_id; } 728 | 729 | void skip_n_firsts(size_t n) { _skip = n; } 730 | 731 | protected: 732 | void load_thread_info() { 733 | #ifdef BACKWARD_SYSTEM_LINUX 734 | #ifndef __ANDROID__ 735 | _thread_id = static_cast(syscall(SYS_gettid)); 736 | #else 737 | _thread_id = static_cast(gettid()); 738 | #endif 739 | if (_thread_id == static_cast(getpid())) { 740 | // If the thread is the main one, let's hide that. 741 | // I like to keep little secret sometimes. 742 | _thread_id = 0; 743 | } 744 | #elif defined(BACKWARD_SYSTEM_DARWIN) 745 | _thread_id = reinterpret_cast(pthread_self()); 746 | if (pthread_main_np() == 1) { 747 | // If the thread is the main one, let's hide that. 748 | _thread_id = 0; 749 | } 750 | #endif 751 | } 752 | 753 | void set_context(void *context) { _context = context; } 754 | void *context() const { return _context; } 755 | 756 | void set_error_addr(void *error_addr) { _error_addr = error_addr; } 757 | void *error_addr() const { return _error_addr; } 758 | 759 | size_t skip_n_firsts() const { return _skip; } 760 | 761 | private: 762 | size_t _thread_id; 763 | size_t _skip; 764 | void *_context; 765 | void *_error_addr; 766 | }; 767 | 768 | class StackTraceImplHolder : public StackTraceImplBase { 769 | public: 770 | size_t size() const { 771 | return (_stacktrace.size() >= skip_n_firsts()) 772 | ? _stacktrace.size() - skip_n_firsts() 773 | : 0; 774 | } 775 | Trace operator[](size_t idx) const { 776 | if (idx >= size()) { 777 | return Trace(); 778 | } 779 | return Trace(_stacktrace[idx + skip_n_firsts()], idx); 780 | } 781 | void *const *begin() const { 782 | if (size()) { 783 | return &_stacktrace[skip_n_firsts()]; 784 | } 785 | return nullptr; 786 | } 787 | 788 | protected: 789 | std::vector _stacktrace; 790 | }; 791 | 792 | #if BACKWARD_HAS_UNWIND == 1 793 | 794 | namespace details { 795 | 796 | template class Unwinder { 797 | public: 798 | size_t operator()(F &f, size_t depth) { 799 | _f = &f; 800 | _index = -1; 801 | _depth = depth; 802 | _Unwind_Backtrace(&this->backtrace_trampoline, this); 803 | return static_cast(_index); 804 | } 805 | 806 | private: 807 | F *_f; 808 | ssize_t _index; 809 | size_t _depth; 810 | 811 | static _Unwind_Reason_Code backtrace_trampoline(_Unwind_Context *ctx, 812 | void *self) { 813 | return (static_cast(self))->backtrace(ctx); 814 | } 815 | 816 | _Unwind_Reason_Code backtrace(_Unwind_Context *ctx) { 817 | if (_index >= 0 && static_cast(_index) >= _depth) 818 | return _URC_END_OF_STACK; 819 | 820 | int ip_before_instruction = 0; 821 | uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction); 822 | 823 | if (!ip_before_instruction) { 824 | // calculating 0-1 for unsigned, looks like a possible bug to sanitiziers, 825 | // so let's do it explicitly: 826 | if (ip == 0) { 827 | ip = std::numeric_limits::max(); // set it to 0xffff... (as 828 | // from casting 0-1) 829 | } else { 830 | ip -= 1; // else just normally decrement it (no overflow/underflow will 831 | // happen) 832 | } 833 | } 834 | 835 | if (_index >= 0) { // ignore first frame. 836 | (*_f)(static_cast(_index), reinterpret_cast(ip)); 837 | } 838 | _index += 1; 839 | return _URC_NO_REASON; 840 | } 841 | }; 842 | 843 | template size_t unwind(F f, size_t depth) { 844 | Unwinder unwinder; 845 | return unwinder(f, depth); 846 | } 847 | 848 | } // namespace details 849 | 850 | template <> 851 | class StackTraceImpl : public StackTraceImplHolder { 852 | public: 853 | NOINLINE 854 | size_t load_here(size_t depth = 32, void *context = nullptr, 855 | void *error_addr = nullptr) { 856 | load_thread_info(); 857 | set_context(context); 858 | set_error_addr(error_addr); 859 | if (depth == 0) { 860 | return 0; 861 | } 862 | _stacktrace.resize(depth); 863 | size_t trace_cnt = details::unwind(callback(*this), depth); 864 | _stacktrace.resize(trace_cnt); 865 | skip_n_firsts(0); 866 | return size(); 867 | } 868 | size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, 869 | void *error_addr = nullptr) { 870 | load_here(depth + 8, context, error_addr); 871 | 872 | for (size_t i = 0; i < _stacktrace.size(); ++i) { 873 | if (_stacktrace[i] == addr) { 874 | skip_n_firsts(i); 875 | break; 876 | } 877 | } 878 | 879 | _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); 880 | return size(); 881 | } 882 | 883 | private: 884 | struct callback { 885 | StackTraceImpl &self; 886 | callback(StackTraceImpl &_self) : self(_self) {} 887 | 888 | void operator()(size_t idx, void *addr) { self._stacktrace[idx] = addr; } 889 | }; 890 | }; 891 | 892 | #elif BACKWARD_HAS_LIBUNWIND == 1 893 | 894 | template <> 895 | class StackTraceImpl : public StackTraceImplHolder { 896 | public: 897 | __attribute__((noinline)) size_t load_here(size_t depth = 32, 898 | void *_context = nullptr, 899 | void *_error_addr = nullptr) { 900 | set_context(_context); 901 | set_error_addr(_error_addr); 902 | load_thread_info(); 903 | if (depth == 0) { 904 | return 0; 905 | } 906 | _stacktrace.resize(depth + 1); 907 | 908 | int result = 0; 909 | 910 | unw_context_t ctx; 911 | size_t index = 0; 912 | 913 | // Add the tail call. If the Instruction Pointer is the crash address it 914 | // means we got a bad function pointer dereference, so we "unwind" the 915 | // bad pointer manually by using the return address pointed to by the 916 | // Stack Pointer as the Instruction Pointer and letting libunwind do 917 | // the rest 918 | 919 | if (context()) { 920 | ucontext_t *uctx = reinterpret_cast(context()); 921 | #ifdef REG_RIP // x86_64 922 | if (uctx->uc_mcontext.gregs[REG_RIP] == 923 | reinterpret_cast(error_addr())) { 924 | uctx->uc_mcontext.gregs[REG_RIP] = 925 | *reinterpret_cast(uctx->uc_mcontext.gregs[REG_RSP]); 926 | } 927 | _stacktrace[index] = 928 | reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); 929 | ++index; 930 | ctx = *reinterpret_cast(uctx); 931 | #elif defined(REG_EIP) // x86_32 932 | if (uctx->uc_mcontext.gregs[REG_EIP] == 933 | reinterpret_cast(error_addr())) { 934 | uctx->uc_mcontext.gregs[REG_EIP] = 935 | *reinterpret_cast(uctx->uc_mcontext.gregs[REG_ESP]); 936 | } 937 | _stacktrace[index] = 938 | reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); 939 | ++index; 940 | ctx = *reinterpret_cast(uctx); 941 | #elif defined(__arm__) 942 | // libunwind uses its own context type for ARM unwinding. 943 | // Copy the registers from the signal handler's context so we can 944 | // unwind 945 | unw_getcontext(&ctx); 946 | ctx.regs[UNW_ARM_R0] = uctx->uc_mcontext.arm_r0; 947 | ctx.regs[UNW_ARM_R1] = uctx->uc_mcontext.arm_r1; 948 | ctx.regs[UNW_ARM_R2] = uctx->uc_mcontext.arm_r2; 949 | ctx.regs[UNW_ARM_R3] = uctx->uc_mcontext.arm_r3; 950 | ctx.regs[UNW_ARM_R4] = uctx->uc_mcontext.arm_r4; 951 | ctx.regs[UNW_ARM_R5] = uctx->uc_mcontext.arm_r5; 952 | ctx.regs[UNW_ARM_R6] = uctx->uc_mcontext.arm_r6; 953 | ctx.regs[UNW_ARM_R7] = uctx->uc_mcontext.arm_r7; 954 | ctx.regs[UNW_ARM_R8] = uctx->uc_mcontext.arm_r8; 955 | ctx.regs[UNW_ARM_R9] = uctx->uc_mcontext.arm_r9; 956 | ctx.regs[UNW_ARM_R10] = uctx->uc_mcontext.arm_r10; 957 | ctx.regs[UNW_ARM_R11] = uctx->uc_mcontext.arm_fp; 958 | ctx.regs[UNW_ARM_R12] = uctx->uc_mcontext.arm_ip; 959 | ctx.regs[UNW_ARM_R13] = uctx->uc_mcontext.arm_sp; 960 | ctx.regs[UNW_ARM_R14] = uctx->uc_mcontext.arm_lr; 961 | ctx.regs[UNW_ARM_R15] = uctx->uc_mcontext.arm_pc; 962 | 963 | // If we have crashed in the PC use the LR instead, as this was 964 | // a bad function dereference 965 | if (reinterpret_cast(error_addr()) == 966 | uctx->uc_mcontext.arm_pc) { 967 | ctx.regs[UNW_ARM_R15] = 968 | uctx->uc_mcontext.arm_lr - sizeof(unsigned long); 969 | } 970 | _stacktrace[index] = reinterpret_cast(ctx.regs[UNW_ARM_R15]); 971 | ++index; 972 | #elif defined(__APPLE__) && defined(__x86_64__) 973 | unw_getcontext(&ctx); 974 | // OS X's implementation of libunwind uses its own context object 975 | // so we need to convert the passed context to libunwind's format 976 | // (information about the data layout taken from unw_getcontext.s 977 | // in Apple's libunwind source 978 | ctx.data[0] = uctx->uc_mcontext->__ss.__rax; 979 | ctx.data[1] = uctx->uc_mcontext->__ss.__rbx; 980 | ctx.data[2] = uctx->uc_mcontext->__ss.__rcx; 981 | ctx.data[3] = uctx->uc_mcontext->__ss.__rdx; 982 | ctx.data[4] = uctx->uc_mcontext->__ss.__rdi; 983 | ctx.data[5] = uctx->uc_mcontext->__ss.__rsi; 984 | ctx.data[6] = uctx->uc_mcontext->__ss.__rbp; 985 | ctx.data[7] = uctx->uc_mcontext->__ss.__rsp; 986 | ctx.data[8] = uctx->uc_mcontext->__ss.__r8; 987 | ctx.data[9] = uctx->uc_mcontext->__ss.__r9; 988 | ctx.data[10] = uctx->uc_mcontext->__ss.__r10; 989 | ctx.data[11] = uctx->uc_mcontext->__ss.__r11; 990 | ctx.data[12] = uctx->uc_mcontext->__ss.__r12; 991 | ctx.data[13] = uctx->uc_mcontext->__ss.__r13; 992 | ctx.data[14] = uctx->uc_mcontext->__ss.__r14; 993 | ctx.data[15] = uctx->uc_mcontext->__ss.__r15; 994 | ctx.data[16] = uctx->uc_mcontext->__ss.__rip; 995 | 996 | // If the IP is the same as the crash address we have a bad function 997 | // dereference The caller's address is pointed to by %rsp, so we 998 | // dereference that value and set it to be the next frame's IP. 999 | if (uctx->uc_mcontext->__ss.__rip == 1000 | reinterpret_cast<__uint64_t>(error_addr())) { 1001 | ctx.data[16] = 1002 | *reinterpret_cast<__uint64_t *>(uctx->uc_mcontext->__ss.__rsp); 1003 | } 1004 | _stacktrace[index] = reinterpret_cast(ctx.data[16]); 1005 | ++index; 1006 | #elif defined(__APPLE__) 1007 | unw_getcontext(&ctx) 1008 | // TODO: Convert the ucontext_t to libunwind's unw_context_t like 1009 | // we do in 64 bits 1010 | if (ctx.uc_mcontext->__ss.__eip == 1011 | reinterpret_cast(error_addr())) { 1012 | ctx.uc_mcontext->__ss.__eip = ctx.uc_mcontext->__ss.__esp; 1013 | } 1014 | _stacktrace[index] = 1015 | reinterpret_cast(ctx.uc_mcontext->__ss.__eip); 1016 | ++index; 1017 | #endif 1018 | } 1019 | 1020 | unw_cursor_t cursor; 1021 | if (context()) { 1022 | #if defined(UNW_INIT_SIGNAL_FRAME) 1023 | result = unw_init_local2(&cursor, &ctx, UNW_INIT_SIGNAL_FRAME); 1024 | #else 1025 | result = unw_init_local(&cursor, &ctx); 1026 | #endif 1027 | } else { 1028 | unw_getcontext(&ctx); 1029 | ; 1030 | result = unw_init_local(&cursor, &ctx); 1031 | } 1032 | 1033 | if (result != 0) 1034 | return 1; 1035 | 1036 | unw_word_t ip = 0; 1037 | 1038 | while (index <= depth && unw_step(&cursor) > 0) { 1039 | result = unw_get_reg(&cursor, UNW_REG_IP, &ip); 1040 | if (result == 0) { 1041 | _stacktrace[index] = reinterpret_cast(--ip); 1042 | ++index; 1043 | } 1044 | } 1045 | --index; 1046 | 1047 | _stacktrace.resize(index + 1); 1048 | skip_n_firsts(0); 1049 | return size(); 1050 | } 1051 | 1052 | size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, 1053 | void *error_addr = nullptr) { 1054 | load_here(depth + 8, context, error_addr); 1055 | 1056 | for (size_t i = 0; i < _stacktrace.size(); ++i) { 1057 | if (_stacktrace[i] == addr) { 1058 | skip_n_firsts(i); 1059 | _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i]); 1060 | break; 1061 | } 1062 | } 1063 | 1064 | _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); 1065 | return size(); 1066 | } 1067 | }; 1068 | 1069 | #elif defined(BACKWARD_HAS_BACKTRACE) 1070 | 1071 | template <> 1072 | class StackTraceImpl : public StackTraceImplHolder { 1073 | public: 1074 | NOINLINE 1075 | size_t load_here(size_t depth = 32, void *context = nullptr, 1076 | void *error_addr = nullptr) { 1077 | set_context(context); 1078 | set_error_addr(error_addr); 1079 | load_thread_info(); 1080 | if (depth == 0) { 1081 | return 0; 1082 | } 1083 | _stacktrace.resize(depth + 1); 1084 | size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size()); 1085 | _stacktrace.resize(trace_cnt); 1086 | skip_n_firsts(1); 1087 | return size(); 1088 | } 1089 | 1090 | size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, 1091 | void *error_addr = nullptr) { 1092 | load_here(depth + 8, context, error_addr); 1093 | 1094 | for (size_t i = 0; i < _stacktrace.size(); ++i) { 1095 | if (_stacktrace[i] == addr) { 1096 | skip_n_firsts(i); 1097 | _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i] + 1); 1098 | break; 1099 | } 1100 | } 1101 | 1102 | _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); 1103 | return size(); 1104 | } 1105 | }; 1106 | 1107 | #elif defined(BACKWARD_SYSTEM_WINDOWS) 1108 | 1109 | template <> 1110 | class StackTraceImpl : public StackTraceImplHolder { 1111 | public: 1112 | // We have to load the machine type from the image info 1113 | // So we first initialize the resolver, and it tells us this info 1114 | void set_machine_type(DWORD machine_type) { machine_type_ = machine_type; } 1115 | void set_context(CONTEXT *ctx) { ctx_ = ctx; } 1116 | void set_thread_handle(HANDLE handle) { thd_ = handle; } 1117 | 1118 | NOINLINE 1119 | size_t load_here(size_t depth = 32, void *context = nullptr, 1120 | void *error_addr = nullptr) { 1121 | set_context(static_cast(context)); 1122 | set_error_addr(error_addr); 1123 | CONTEXT localCtx; // used when no context is provided 1124 | 1125 | if (depth == 0) { 1126 | return 0; 1127 | } 1128 | 1129 | if (!ctx_) { 1130 | ctx_ = &localCtx; 1131 | RtlCaptureContext(ctx_); 1132 | } 1133 | 1134 | if (!thd_) { 1135 | thd_ = GetCurrentThread(); 1136 | } 1137 | 1138 | HANDLE process = GetCurrentProcess(); 1139 | 1140 | STACKFRAME64 s; 1141 | memset(&s, 0, sizeof(STACKFRAME64)); 1142 | 1143 | // TODO: 32 bit context capture 1144 | s.AddrStack.Mode = AddrModeFlat; 1145 | s.AddrFrame.Mode = AddrModeFlat; 1146 | s.AddrPC.Mode = AddrModeFlat; 1147 | #ifdef _M_X64 1148 | s.AddrPC.Offset = ctx_->Rip; 1149 | s.AddrStack.Offset = ctx_->Rsp; 1150 | s.AddrFrame.Offset = ctx_->Rbp; 1151 | #else 1152 | s.AddrPC.Offset = ctx_->Eip; 1153 | s.AddrStack.Offset = ctx_->Esp; 1154 | s.AddrFrame.Offset = ctx_->Ebp; 1155 | #endif 1156 | 1157 | if (!machine_type_) { 1158 | #ifdef _M_X64 1159 | machine_type_ = IMAGE_FILE_MACHINE_AMD64; 1160 | #else 1161 | machine_type_ = IMAGE_FILE_MACHINE_I386; 1162 | #endif 1163 | } 1164 | 1165 | for (;;) { 1166 | // NOTE: this only works if PDBs are already loaded! 1167 | SetLastError(0); 1168 | if (!StackWalk64(machine_type_, process, thd_, &s, ctx_, NULL, 1169 | SymFunctionTableAccess64, SymGetModuleBase64, NULL)) 1170 | break; 1171 | 1172 | if (s.AddrReturn.Offset == 0) 1173 | break; 1174 | 1175 | _stacktrace.push_back(reinterpret_cast(s.AddrPC.Offset)); 1176 | 1177 | if (size() >= depth) 1178 | break; 1179 | } 1180 | 1181 | return size(); 1182 | } 1183 | 1184 | size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, 1185 | void *error_addr = nullptr) { 1186 | load_here(depth + 8, context, error_addr); 1187 | 1188 | for (size_t i = 0; i < _stacktrace.size(); ++i) { 1189 | if (_stacktrace[i] == addr) { 1190 | skip_n_firsts(i); 1191 | break; 1192 | } 1193 | } 1194 | 1195 | _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); 1196 | return size(); 1197 | } 1198 | 1199 | private: 1200 | DWORD machine_type_ = 0; 1201 | HANDLE thd_ = 0; 1202 | CONTEXT *ctx_ = nullptr; 1203 | }; 1204 | 1205 | #endif 1206 | 1207 | class StackTrace : public StackTraceImpl {}; 1208 | 1209 | /*************** TRACE RESOLVER ***************/ 1210 | 1211 | class TraceResolverImplBase { 1212 | public: 1213 | virtual ~TraceResolverImplBase() {} 1214 | 1215 | virtual void load_addresses(void *const*addresses, int address_count) { 1216 | (void)addresses; 1217 | (void)address_count; 1218 | } 1219 | 1220 | template void load_stacktrace(ST &st) { 1221 | load_addresses(st.begin(), (int)st.size()); 1222 | } 1223 | 1224 | virtual ResolvedTrace resolve(ResolvedTrace t) { return t; } 1225 | 1226 | protected: 1227 | std::string demangle(const char *funcname) { 1228 | return _demangler.demangle(funcname); 1229 | } 1230 | 1231 | private: 1232 | details::demangler _demangler; 1233 | }; 1234 | 1235 | template class TraceResolverImpl; 1236 | 1237 | #ifdef BACKWARD_SYSTEM_UNKNOWN 1238 | 1239 | template <> class TraceResolverImpl 1240 | : public TraceResolverImplBase {}; 1241 | 1242 | #endif 1243 | 1244 | #ifdef BACKWARD_SYSTEM_LINUX 1245 | 1246 | class TraceResolverLinuxBase : public TraceResolverImplBase { 1247 | public: 1248 | TraceResolverLinuxBase() 1249 | : argv0_(get_argv0()), exec_path_(read_symlink("/proc/self/exe")) {} 1250 | std::string resolve_exec_path(Dl_info &symbol_info) const { 1251 | // mutates symbol_info.dli_fname to be filename to open and returns filename 1252 | // to display 1253 | if (symbol_info.dli_fname == argv0_) { 1254 | // dladdr returns argv[0] in dli_fname for symbols contained in 1255 | // the main executable, which is not a valid path if the 1256 | // executable was found by a search of the PATH environment 1257 | // variable; In that case, we actually open /proc/self/exe, which 1258 | // is always the actual executable (even if it was deleted/replaced!) 1259 | // but display the path that /proc/self/exe links to. 1260 | // However, this right away reduces probability of successful symbol 1261 | // resolution, because libbfd may try to find *.debug files in the 1262 | // same dir, in case symbols are stripped. As a result, it may try 1263 | // to find a file /proc/self/.debug, which obviously does 1264 | // not exist. /proc/self/exe is a last resort. First load attempt 1265 | // should go for the original executable file path. 1266 | symbol_info.dli_fname = "/proc/self/exe"; 1267 | return exec_path_; 1268 | } else { 1269 | return symbol_info.dli_fname; 1270 | } 1271 | } 1272 | 1273 | private: 1274 | std::string argv0_; 1275 | std::string exec_path_; 1276 | 1277 | static std::string get_argv0() { 1278 | std::string argv0; 1279 | std::ifstream ifs("/proc/self/cmdline"); 1280 | std::getline(ifs, argv0, '\0'); 1281 | return argv0; 1282 | } 1283 | 1284 | static std::string read_symlink(std::string const &symlink_path) { 1285 | std::string path; 1286 | path.resize(100); 1287 | 1288 | while (true) { 1289 | ssize_t len = 1290 | ::readlink(symlink_path.c_str(), &*path.begin(), path.size()); 1291 | if (len < 0) { 1292 | return ""; 1293 | } 1294 | if (static_cast(len) == path.size()) { 1295 | path.resize(path.size() * 2); 1296 | } else { 1297 | path.resize(static_cast(len)); 1298 | break; 1299 | } 1300 | } 1301 | 1302 | return path; 1303 | } 1304 | }; 1305 | 1306 | template class TraceResolverLinuxImpl; 1307 | 1308 | #if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 1309 | 1310 | template <> 1311 | class TraceResolverLinuxImpl 1312 | : public TraceResolverLinuxBase { 1313 | public: 1314 | void load_addresses(void *const*addresses, int address_count) override { 1315 | if (address_count == 0) { 1316 | return; 1317 | } 1318 | _symbols.reset(backtrace_symbols(addresses, address_count)); 1319 | } 1320 | 1321 | ResolvedTrace resolve(ResolvedTrace trace) override { 1322 | char *filename = _symbols[trace.idx]; 1323 | char *funcname = filename; 1324 | while (*funcname && *funcname != '(') { 1325 | funcname += 1; 1326 | } 1327 | trace.object_filename.assign(filename, 1328 | funcname); // ok even if funcname is the ending 1329 | // \0 (then we assign entire string) 1330 | 1331 | if (*funcname) { // if it's not end of string (e.g. from last frame ip==0) 1332 | funcname += 1; 1333 | char *funcname_end = funcname; 1334 | while (*funcname_end && *funcname_end != ')' && *funcname_end != '+') { 1335 | funcname_end += 1; 1336 | } 1337 | *funcname_end = '\0'; 1338 | trace.object_function = this->demangle(funcname); 1339 | trace.source.function = trace.object_function; // we cannot do better. 1340 | } 1341 | return trace; 1342 | } 1343 | 1344 | private: 1345 | details::handle _symbols; 1346 | }; 1347 | 1348 | #endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1 1349 | 1350 | #if BACKWARD_HAS_BFD == 1 1351 | 1352 | template <> 1353 | class TraceResolverLinuxImpl 1354 | : public TraceResolverLinuxBase { 1355 | public: 1356 | TraceResolverLinuxImpl() : _bfd_loaded(false) {} 1357 | 1358 | ResolvedTrace resolve(ResolvedTrace trace) override { 1359 | Dl_info symbol_info; 1360 | 1361 | // trace.addr is a virtual address in memory pointing to some code. 1362 | // Let's try to find from which loaded object it comes from. 1363 | // The loaded object can be yourself btw. 1364 | if (!dladdr(trace.addr, &symbol_info)) { 1365 | return trace; // dat broken trace... 1366 | } 1367 | 1368 | // Now we get in symbol_info: 1369 | // .dli_fname: 1370 | // pathname of the shared object that contains the address. 1371 | // .dli_fbase: 1372 | // where the object is loaded in memory. 1373 | // .dli_sname: 1374 | // the name of the nearest symbol to trace.addr, we expect a 1375 | // function name. 1376 | // .dli_saddr: 1377 | // the exact address corresponding to .dli_sname. 1378 | 1379 | if (symbol_info.dli_sname) { 1380 | trace.object_function = demangle(symbol_info.dli_sname); 1381 | } 1382 | 1383 | if (!symbol_info.dli_fname) { 1384 | return trace; 1385 | } 1386 | 1387 | trace.object_filename = resolve_exec_path(symbol_info); 1388 | bfd_fileobject *fobj; 1389 | // Before rushing to resolution need to ensure the executable 1390 | // file still can be used. For that compare inode numbers of 1391 | // what is stored by the executable's file path, and in the 1392 | // dli_fname, which not necessarily equals to the executable. 1393 | // It can be a shared library, or /proc/self/exe, and in the 1394 | // latter case has drawbacks. See the exec path resolution for 1395 | // details. In short - the dli object should be used only as 1396 | // the last resort. 1397 | // If inode numbers are equal, it is known dli_fname and the 1398 | // executable file are the same. This is guaranteed by Linux, 1399 | // because if the executable file is changed/deleted, it will 1400 | // be done in a new inode. The old file will be preserved in 1401 | // /proc/self/exe, and may even have inode 0. The latter can 1402 | // happen if the inode was actually reused, and the file was 1403 | // kept only in the main memory. 1404 | // 1405 | struct stat obj_stat; 1406 | struct stat dli_stat; 1407 | if (stat(trace.object_filename.c_str(), &obj_stat) == 0 && 1408 | stat(symbol_info.dli_fname, &dli_stat) == 0 && 1409 | obj_stat.st_ino == dli_stat.st_ino) { 1410 | // The executable file, and the shared object containing the 1411 | // address are the same file. Safe to use the original path. 1412 | // this is preferable. Libbfd will search for stripped debug 1413 | // symbols in the same directory. 1414 | fobj = load_object_with_bfd(trace.object_filename); 1415 | } else{ 1416 | // The original object file was *deleted*! The only hope is 1417 | // that the debug symbols are either inside the shared 1418 | // object file, or are in the same directory, and this is 1419 | // not /proc/self/exe. 1420 | fobj = nullptr; 1421 | } 1422 | if (fobj == nullptr || !fobj->handle) { 1423 | fobj = load_object_with_bfd(symbol_info.dli_fname); 1424 | if (!fobj->handle) { 1425 | return trace; 1426 | } 1427 | } 1428 | 1429 | find_sym_result *details_selected; // to be filled. 1430 | 1431 | // trace.addr is the next instruction to be executed after returning 1432 | // from the nested stack frame. In C++ this usually relate to the next 1433 | // statement right after the function call that leaded to a new stack 1434 | // frame. This is not usually what you want to see when printing out a 1435 | // stacktrace... 1436 | find_sym_result details_call_site = 1437 | find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); 1438 | details_selected = &details_call_site; 1439 | 1440 | #if BACKWARD_HAS_UNWIND == 0 1441 | // ...this is why we also try to resolve the symbol that is right 1442 | // before the return address. If we are lucky enough, we will get the 1443 | // line of the function that was called. But if the code is optimized, 1444 | // we might get something absolutely not related since the compiler 1445 | // can reschedule the return address with inline functions and 1446 | // tail-call optimisation (among other things that I don't even know 1447 | // or cannot even dream about with my tiny limited brain). 1448 | find_sym_result details_adjusted_call_site = find_symbol_details( 1449 | fobj, (void *)(uintptr_t(trace.addr) - 1), symbol_info.dli_fbase); 1450 | 1451 | // In debug mode, we should always get the right thing(TM). 1452 | if (details_call_site.found && details_adjusted_call_site.found) { 1453 | // Ok, we assume that details_adjusted_call_site is a better estimation. 1454 | details_selected = &details_adjusted_call_site; 1455 | trace.addr = (void *)(uintptr_t(trace.addr) - 1); 1456 | } 1457 | 1458 | if (details_selected == &details_call_site && details_call_site.found) { 1459 | // we have to re-resolve the symbol in order to reset some 1460 | // internal state in BFD... so we can call backtrace_inliners 1461 | // thereafter... 1462 | details_call_site = 1463 | find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); 1464 | } 1465 | #endif // BACKWARD_HAS_UNWIND 1466 | 1467 | if (details_selected->found) { 1468 | if (details_selected->filename) { 1469 | trace.source.filename = details_selected->filename; 1470 | } 1471 | trace.source.line = details_selected->line; 1472 | 1473 | if (details_selected->funcname) { 1474 | // this time we get the name of the function where the code is 1475 | // located, instead of the function were the address is 1476 | // located. In short, if the code was inlined, we get the 1477 | // function correspoding to the code. Else we already got in 1478 | // trace.function. 1479 | trace.source.function = demangle(details_selected->funcname); 1480 | 1481 | if (!symbol_info.dli_sname) { 1482 | // for the case dladdr failed to find the symbol name of 1483 | // the function, we might as well try to put something 1484 | // here. 1485 | trace.object_function = trace.source.function; 1486 | } 1487 | } 1488 | 1489 | // Maybe the source of the trace got inlined inside the function 1490 | // (trace.source.function). Let's see if we can get all the inlined 1491 | // calls along the way up to the initial call site. 1492 | trace.inliners = backtrace_inliners(fobj, *details_selected); 1493 | 1494 | #if 0 1495 | if (trace.inliners.size() == 0) { 1496 | // Maybe the trace was not inlined... or maybe it was and we 1497 | // are lacking the debug information. Let's try to make the 1498 | // world better and see if we can get the line number of the 1499 | // function (trace.source.function) now. 1500 | // 1501 | // We will get the location of where the function start (to be 1502 | // exact: the first instruction that really start the 1503 | // function), not where the name of the function is defined. 1504 | // This can be quite far away from the name of the function 1505 | // btw. 1506 | // 1507 | // If the source of the function is the same as the source of 1508 | // the trace, we cannot say if the trace was really inlined or 1509 | // not. However, if the filename of the source is different 1510 | // between the function and the trace... we can declare it as 1511 | // an inliner. This is not 100% accurate, but better than 1512 | // nothing. 1513 | 1514 | if (symbol_info.dli_saddr) { 1515 | find_sym_result details = find_symbol_details(fobj, 1516 | symbol_info.dli_saddr, 1517 | symbol_info.dli_fbase); 1518 | 1519 | if (details.found) { 1520 | ResolvedTrace::SourceLoc diy_inliner; 1521 | diy_inliner.line = details.line; 1522 | if (details.filename) { 1523 | diy_inliner.filename = details.filename; 1524 | } 1525 | if (details.funcname) { 1526 | diy_inliner.function = demangle(details.funcname); 1527 | } else { 1528 | diy_inliner.function = trace.source.function; 1529 | } 1530 | if (diy_inliner != trace.source) { 1531 | trace.inliners.push_back(diy_inliner); 1532 | } 1533 | } 1534 | } 1535 | } 1536 | #endif 1537 | } 1538 | 1539 | return trace; 1540 | } 1541 | 1542 | private: 1543 | bool _bfd_loaded; 1544 | 1545 | typedef details::handle> 1547 | bfd_handle_t; 1548 | 1549 | typedef details::handle bfd_symtab_t; 1550 | 1551 | struct bfd_fileobject { 1552 | bfd_handle_t handle; 1553 | bfd_vma base_addr; 1554 | bfd_symtab_t symtab; 1555 | bfd_symtab_t dynamic_symtab; 1556 | }; 1557 | 1558 | typedef details::hashtable::type fobj_bfd_map_t; 1559 | fobj_bfd_map_t _fobj_bfd_map; 1560 | 1561 | bfd_fileobject *load_object_with_bfd(const std::string &filename_object) { 1562 | using namespace details; 1563 | 1564 | if (!_bfd_loaded) { 1565 | using namespace details; 1566 | bfd_init(); 1567 | _bfd_loaded = true; 1568 | } 1569 | 1570 | fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object); 1571 | if (it != _fobj_bfd_map.end()) { 1572 | return &it->second; 1573 | } 1574 | 1575 | // this new object is empty for now. 1576 | bfd_fileobject *r = &_fobj_bfd_map[filename_object]; 1577 | 1578 | // we do the work temporary in this one; 1579 | bfd_handle_t bfd_handle; 1580 | 1581 | int fd = open(filename_object.c_str(), O_RDONLY); 1582 | bfd_handle.reset(bfd_fdopenr(filename_object.c_str(), "default", fd)); 1583 | if (!bfd_handle) { 1584 | close(fd); 1585 | return r; 1586 | } 1587 | 1588 | if (!bfd_check_format(bfd_handle.get(), bfd_object)) { 1589 | return r; // not an object? You lose. 1590 | } 1591 | 1592 | if ((bfd_get_file_flags(bfd_handle.get()) & HAS_SYMS) == 0) { 1593 | return r; // that's what happen when you forget to compile in debug. 1594 | } 1595 | 1596 | ssize_t symtab_storage_size = bfd_get_symtab_upper_bound(bfd_handle.get()); 1597 | 1598 | ssize_t dyn_symtab_storage_size = 1599 | bfd_get_dynamic_symtab_upper_bound(bfd_handle.get()); 1600 | 1601 | if (symtab_storage_size <= 0 && dyn_symtab_storage_size <= 0) { 1602 | return r; // weird, is the file is corrupted? 1603 | } 1604 | 1605 | bfd_symtab_t symtab, dynamic_symtab; 1606 | ssize_t symcount = 0, dyn_symcount = 0; 1607 | 1608 | if (symtab_storage_size > 0) { 1609 | symtab.reset(static_cast( 1610 | malloc(static_cast(symtab_storage_size)))); 1611 | symcount = bfd_canonicalize_symtab(bfd_handle.get(), symtab.get()); 1612 | } 1613 | 1614 | if (dyn_symtab_storage_size > 0) { 1615 | dynamic_symtab.reset(static_cast( 1616 | malloc(static_cast(dyn_symtab_storage_size)))); 1617 | dyn_symcount = bfd_canonicalize_dynamic_symtab(bfd_handle.get(), 1618 | dynamic_symtab.get()); 1619 | } 1620 | 1621 | if (symcount <= 0 && dyn_symcount <= 0) { 1622 | return r; // damned, that's a stripped file that you got there! 1623 | } 1624 | 1625 | r->handle = move(bfd_handle); 1626 | r->symtab = move(symtab); 1627 | r->dynamic_symtab = move(dynamic_symtab); 1628 | return r; 1629 | } 1630 | 1631 | struct find_sym_result { 1632 | bool found; 1633 | const char *filename; 1634 | const char *funcname; 1635 | unsigned int line; 1636 | }; 1637 | 1638 | struct find_sym_context { 1639 | TraceResolverLinuxImpl *self; 1640 | bfd_fileobject *fobj; 1641 | void *addr; 1642 | void *base_addr; 1643 | find_sym_result result; 1644 | }; 1645 | 1646 | find_sym_result find_symbol_details(bfd_fileobject *fobj, void *addr, 1647 | void *base_addr) { 1648 | find_sym_context context; 1649 | context.self = this; 1650 | context.fobj = fobj; 1651 | context.addr = addr; 1652 | context.base_addr = base_addr; 1653 | context.result.found = false; 1654 | bfd_map_over_sections(fobj->handle.get(), &find_in_section_trampoline, 1655 | static_cast(&context)); 1656 | return context.result; 1657 | } 1658 | 1659 | static void find_in_section_trampoline(bfd *, asection *section, void *data) { 1660 | find_sym_context *context = static_cast(data); 1661 | context->self->find_in_section( 1662 | reinterpret_cast(context->addr), 1663 | reinterpret_cast(context->base_addr), context->fobj, section, 1664 | context->result); 1665 | } 1666 | 1667 | void find_in_section(bfd_vma addr, bfd_vma base_addr, bfd_fileobject *fobj, 1668 | asection *section, find_sym_result &result) { 1669 | if (result.found) 1670 | return; 1671 | 1672 | #ifdef bfd_get_section_flags 1673 | if ((bfd_get_section_flags(fobj->handle.get(), section) & SEC_ALLOC) == 0) 1674 | #else 1675 | if ((bfd_section_flags(section) & SEC_ALLOC) == 0) 1676 | #endif 1677 | return; // a debug section is never loaded automatically. 1678 | 1679 | #ifdef bfd_get_section_vma 1680 | bfd_vma sec_addr = bfd_get_section_vma(fobj->handle.get(), section); 1681 | #else 1682 | bfd_vma sec_addr = bfd_section_vma(section); 1683 | #endif 1684 | #ifdef bfd_get_section_size 1685 | bfd_size_type size = bfd_get_section_size(section); 1686 | #else 1687 | bfd_size_type size = bfd_section_size(section); 1688 | #endif 1689 | 1690 | // are we in the boundaries of the section? 1691 | if (addr < sec_addr || addr >= sec_addr + size) { 1692 | addr -= base_addr; // oups, a relocated object, lets try again... 1693 | if (addr < sec_addr || addr >= sec_addr + size) { 1694 | return; 1695 | } 1696 | } 1697 | 1698 | #if defined(__clang__) 1699 | #pragma clang diagnostic push 1700 | #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" 1701 | #endif 1702 | if (!result.found && fobj->symtab) { 1703 | result.found = bfd_find_nearest_line( 1704 | fobj->handle.get(), section, fobj->symtab.get(), addr - sec_addr, 1705 | &result.filename, &result.funcname, &result.line); 1706 | } 1707 | 1708 | if (!result.found && fobj->dynamic_symtab) { 1709 | result.found = bfd_find_nearest_line( 1710 | fobj->handle.get(), section, fobj->dynamic_symtab.get(), 1711 | addr - sec_addr, &result.filename, &result.funcname, &result.line); 1712 | } 1713 | #if defined(__clang__) 1714 | #pragma clang diagnostic pop 1715 | #endif 1716 | } 1717 | 1718 | ResolvedTrace::source_locs_t 1719 | backtrace_inliners(bfd_fileobject *fobj, find_sym_result previous_result) { 1720 | // This function can be called ONLY after a SUCCESSFUL call to 1721 | // find_symbol_details. The state is global to the bfd_handle. 1722 | ResolvedTrace::source_locs_t results; 1723 | while (previous_result.found) { 1724 | find_sym_result result; 1725 | result.found = bfd_find_inliner_info(fobj->handle.get(), &result.filename, 1726 | &result.funcname, &result.line); 1727 | 1728 | if (result 1729 | .found) /* and not ( 1730 | cstrings_eq(previous_result.filename, 1731 | result.filename) and 1732 | cstrings_eq(previous_result.funcname, result.funcname) 1733 | and result.line == previous_result.line 1734 | )) */ 1735 | { 1736 | ResolvedTrace::SourceLoc src_loc; 1737 | src_loc.line = result.line; 1738 | if (result.filename) { 1739 | src_loc.filename = result.filename; 1740 | } 1741 | if (result.funcname) { 1742 | src_loc.function = demangle(result.funcname); 1743 | } 1744 | results.push_back(src_loc); 1745 | } 1746 | previous_result = result; 1747 | } 1748 | return results; 1749 | } 1750 | 1751 | bool cstrings_eq(const char *a, const char *b) { 1752 | if (!a || !b) { 1753 | return false; 1754 | } 1755 | return strcmp(a, b) == 0; 1756 | } 1757 | }; 1758 | #endif // BACKWARD_HAS_BFD == 1 1759 | 1760 | #if BACKWARD_HAS_DW == 1 1761 | 1762 | template <> 1763 | class TraceResolverLinuxImpl 1764 | : public TraceResolverLinuxBase { 1765 | public: 1766 | TraceResolverLinuxImpl() : _dwfl_handle_initialized(false) {} 1767 | 1768 | ResolvedTrace resolve(ResolvedTrace trace) override { 1769 | using namespace details; 1770 | 1771 | Dwarf_Addr trace_addr = (Dwarf_Addr)trace.addr; 1772 | 1773 | if (!_dwfl_handle_initialized) { 1774 | // initialize dwfl... 1775 | _dwfl_cb.reset(new Dwfl_Callbacks); 1776 | _dwfl_cb->find_elf = &dwfl_linux_proc_find_elf; 1777 | _dwfl_cb->find_debuginfo = &dwfl_standard_find_debuginfo; 1778 | _dwfl_cb->debuginfo_path = 0; 1779 | 1780 | _dwfl_handle.reset(dwfl_begin(_dwfl_cb.get())); 1781 | _dwfl_handle_initialized = true; 1782 | 1783 | if (!_dwfl_handle) { 1784 | return trace; 1785 | } 1786 | 1787 | // ...from the current process. 1788 | dwfl_report_begin(_dwfl_handle.get()); 1789 | int r = dwfl_linux_proc_report(_dwfl_handle.get(), getpid()); 1790 | dwfl_report_end(_dwfl_handle.get(), NULL, NULL); 1791 | if (r < 0) { 1792 | return trace; 1793 | } 1794 | } 1795 | 1796 | if (!_dwfl_handle) { 1797 | return trace; 1798 | } 1799 | 1800 | // find the module (binary object) that contains the trace's address. 1801 | // This is not using any debug information, but the addresses ranges of 1802 | // all the currently loaded binary object. 1803 | Dwfl_Module *mod = dwfl_addrmodule(_dwfl_handle.get(), trace_addr); 1804 | if (mod) { 1805 | // now that we found it, lets get the name of it, this will be the 1806 | // full path to the running binary or one of the loaded library. 1807 | const char *module_name = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0); 1808 | if (module_name) { 1809 | trace.object_filename = module_name; 1810 | } 1811 | // We also look after the name of the symbol, equal or before this 1812 | // address. This is found by walking the symtab. We should get the 1813 | // symbol corresponding to the function (mangled) containing the 1814 | // address. If the code corresponding to the address was inlined, 1815 | // this is the name of the out-most inliner function. 1816 | const char *sym_name = dwfl_module_addrname(mod, trace_addr); 1817 | if (sym_name) { 1818 | trace.object_function = demangle(sym_name); 1819 | } 1820 | } 1821 | 1822 | // now let's get serious, and find out the source location (file and 1823 | // line number) of the address. 1824 | 1825 | // This function will look in .debug_aranges for the address and map it 1826 | // to the location of the compilation unit DIE in .debug_info and 1827 | // return it. 1828 | Dwarf_Addr mod_bias = 0; 1829 | Dwarf_Die *cudie = dwfl_module_addrdie(mod, trace_addr, &mod_bias); 1830 | 1831 | #if 1 1832 | if (!cudie) { 1833 | // Sadly clang does not generate the section .debug_aranges, thus 1834 | // dwfl_module_addrdie will fail early. Clang doesn't either set 1835 | // the lowpc/highpc/range info for every compilation unit. 1836 | // 1837 | // So in order to save the world: 1838 | // for every compilation unit, we will iterate over every single 1839 | // DIEs. Normally functions should have a lowpc/highpc/range, which 1840 | // we will use to infer the compilation unit. 1841 | 1842 | // note that this is probably badly inefficient. 1843 | while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias))) { 1844 | Dwarf_Die die_mem; 1845 | Dwarf_Die *fundie = 1846 | find_fundie_by_pc(cudie, trace_addr - mod_bias, &die_mem); 1847 | if (fundie) { 1848 | break; 1849 | } 1850 | } 1851 | } 1852 | #endif 1853 | 1854 | //#define BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE 1855 | #ifdef BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE 1856 | if (!cudie) { 1857 | // If it's still not enough, lets dive deeper in the shit, and try 1858 | // to save the world again: for every compilation unit, we will 1859 | // load the corresponding .debug_line section, and see if we can 1860 | // find our address in it. 1861 | 1862 | Dwarf_Addr cfi_bias; 1863 | Dwarf_CFI *cfi_cache = dwfl_module_eh_cfi(mod, &cfi_bias); 1864 | 1865 | Dwarf_Addr bias; 1866 | while ((cudie = dwfl_module_nextcu(mod, cudie, &bias))) { 1867 | if (dwarf_getsrc_die(cudie, trace_addr - bias)) { 1868 | 1869 | // ...but if we get a match, it might be a false positive 1870 | // because our (address - bias) might as well be valid in a 1871 | // different compilation unit. So we throw our last card on 1872 | // the table and lookup for the address into the .eh_frame 1873 | // section. 1874 | 1875 | handle frame; 1876 | dwarf_cfi_addrframe(cfi_cache, trace_addr - cfi_bias, &frame); 1877 | if (frame) { 1878 | break; 1879 | } 1880 | } 1881 | } 1882 | } 1883 | #endif 1884 | 1885 | if (!cudie) { 1886 | return trace; // this time we lost the game :/ 1887 | } 1888 | 1889 | // Now that we have a compilation unit DIE, this function will be able 1890 | // to load the corresponding section in .debug_line (if not already 1891 | // loaded) and hopefully find the source location mapped to our 1892 | // address. 1893 | Dwarf_Line *srcloc = dwarf_getsrc_die(cudie, trace_addr - mod_bias); 1894 | 1895 | if (srcloc) { 1896 | const char *srcfile = dwarf_linesrc(srcloc, 0, 0); 1897 | if (srcfile) { 1898 | trace.source.filename = srcfile; 1899 | } 1900 | int line = 0, col = 0; 1901 | dwarf_lineno(srcloc, &line); 1902 | dwarf_linecol(srcloc, &col); 1903 | trace.source.line = line; 1904 | trace.source.col = col; 1905 | } 1906 | 1907 | deep_first_search_by_pc(cudie, trace_addr - mod_bias, 1908 | inliners_search_cb(trace)); 1909 | if (trace.source.function.size() == 0) { 1910 | // fallback. 1911 | trace.source.function = trace.object_function; 1912 | } 1913 | 1914 | return trace; 1915 | } 1916 | 1917 | private: 1918 | typedef details::handle> 1919 | dwfl_handle_t; 1920 | details::handle> 1921 | _dwfl_cb; 1922 | dwfl_handle_t _dwfl_handle; 1923 | bool _dwfl_handle_initialized; 1924 | 1925 | // defined here because in C++98, template function cannot take locally 1926 | // defined types... grrr. 1927 | struct inliners_search_cb { 1928 | void operator()(Dwarf_Die *die) { 1929 | switch (dwarf_tag(die)) { 1930 | const char *name; 1931 | case DW_TAG_subprogram: 1932 | if ((name = dwarf_diename(die))) { 1933 | trace.source.function = name; 1934 | } 1935 | break; 1936 | 1937 | case DW_TAG_inlined_subroutine: 1938 | ResolvedTrace::SourceLoc sloc; 1939 | Dwarf_Attribute attr_mem; 1940 | 1941 | if ((name = dwarf_diename(die))) { 1942 | sloc.function = name; 1943 | } 1944 | if ((name = die_call_file(die))) { 1945 | sloc.filename = name; 1946 | } 1947 | 1948 | Dwarf_Word line = 0, col = 0; 1949 | dwarf_formudata(dwarf_attr(die, DW_AT_call_line, &attr_mem), &line); 1950 | dwarf_formudata(dwarf_attr(die, DW_AT_call_column, &attr_mem), &col); 1951 | sloc.line = (unsigned)line; 1952 | sloc.col = (unsigned)col; 1953 | 1954 | trace.inliners.push_back(sloc); 1955 | break; 1956 | }; 1957 | } 1958 | ResolvedTrace &trace; 1959 | inliners_search_cb(ResolvedTrace &t) : trace(t) {} 1960 | }; 1961 | 1962 | static bool die_has_pc(Dwarf_Die *die, Dwarf_Addr pc) { 1963 | Dwarf_Addr low, high; 1964 | 1965 | // continuous range 1966 | if (dwarf_hasattr(die, DW_AT_low_pc) && dwarf_hasattr(die, DW_AT_high_pc)) { 1967 | if (dwarf_lowpc(die, &low) != 0) { 1968 | return false; 1969 | } 1970 | if (dwarf_highpc(die, &high) != 0) { 1971 | Dwarf_Attribute attr_mem; 1972 | Dwarf_Attribute *attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem); 1973 | Dwarf_Word value; 1974 | if (dwarf_formudata(attr, &value) != 0) { 1975 | return false; 1976 | } 1977 | high = low + value; 1978 | } 1979 | return pc >= low && pc < high; 1980 | } 1981 | 1982 | // non-continuous range. 1983 | Dwarf_Addr base; 1984 | ptrdiff_t offset = 0; 1985 | while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) { 1986 | if (pc >= low && pc < high) { 1987 | return true; 1988 | } 1989 | } 1990 | return false; 1991 | } 1992 | 1993 | static Dwarf_Die *find_fundie_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, 1994 | Dwarf_Die *result) { 1995 | if (dwarf_child(parent_die, result) != 0) { 1996 | return 0; 1997 | } 1998 | 1999 | Dwarf_Die *die = result; 2000 | do { 2001 | switch (dwarf_tag(die)) { 2002 | case DW_TAG_subprogram: 2003 | case DW_TAG_inlined_subroutine: 2004 | if (die_has_pc(die, pc)) { 2005 | return result; 2006 | } 2007 | }; 2008 | bool declaration = false; 2009 | Dwarf_Attribute attr_mem; 2010 | dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), 2011 | &declaration); 2012 | if (!declaration) { 2013 | // let's be curious and look deeper in the tree, 2014 | // function are not necessarily at the first level, but 2015 | // might be nested inside a namespace, structure etc. 2016 | Dwarf_Die die_mem; 2017 | Dwarf_Die *indie = find_fundie_by_pc(die, pc, &die_mem); 2018 | if (indie) { 2019 | *result = die_mem; 2020 | return result; 2021 | } 2022 | } 2023 | } while (dwarf_siblingof(die, result) == 0); 2024 | return 0; 2025 | } 2026 | 2027 | template 2028 | static bool deep_first_search_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, 2029 | CB cb) { 2030 | Dwarf_Die die_mem; 2031 | if (dwarf_child(parent_die, &die_mem) != 0) { 2032 | return false; 2033 | } 2034 | 2035 | bool branch_has_pc = false; 2036 | Dwarf_Die *die = &die_mem; 2037 | do { 2038 | bool declaration = false; 2039 | Dwarf_Attribute attr_mem; 2040 | dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), 2041 | &declaration); 2042 | if (!declaration) { 2043 | // let's be curious and look deeper in the tree, function are 2044 | // not necessarily at the first level, but might be nested 2045 | // inside a namespace, structure, a function, an inlined 2046 | // function etc. 2047 | branch_has_pc = deep_first_search_by_pc(die, pc, cb); 2048 | } 2049 | if (!branch_has_pc) { 2050 | branch_has_pc = die_has_pc(die, pc); 2051 | } 2052 | if (branch_has_pc) { 2053 | cb(die); 2054 | } 2055 | } while (dwarf_siblingof(die, &die_mem) == 0); 2056 | return branch_has_pc; 2057 | } 2058 | 2059 | static const char *die_call_file(Dwarf_Die *die) { 2060 | Dwarf_Attribute attr_mem; 2061 | Dwarf_Word file_idx = 0; 2062 | 2063 | dwarf_formudata(dwarf_attr(die, DW_AT_call_file, &attr_mem), &file_idx); 2064 | 2065 | if (file_idx == 0) { 2066 | return 0; 2067 | } 2068 | 2069 | Dwarf_Die die_mem; 2070 | Dwarf_Die *cudie = dwarf_diecu(die, &die_mem, 0, 0); 2071 | if (!cudie) { 2072 | return 0; 2073 | } 2074 | 2075 | Dwarf_Files *files = 0; 2076 | size_t nfiles; 2077 | dwarf_getsrcfiles(cudie, &files, &nfiles); 2078 | if (!files) { 2079 | return 0; 2080 | } 2081 | 2082 | return dwarf_filesrc(files, file_idx, 0, 0); 2083 | } 2084 | }; 2085 | #endif // BACKWARD_HAS_DW == 1 2086 | 2087 | #if BACKWARD_HAS_DWARF == 1 2088 | 2089 | template <> 2090 | class TraceResolverLinuxImpl 2091 | : public TraceResolverLinuxBase { 2092 | public: 2093 | TraceResolverLinuxImpl() : _dwarf_loaded(false) {} 2094 | 2095 | ResolvedTrace resolve(ResolvedTrace trace) override { 2096 | // trace.addr is a virtual address in memory pointing to some code. 2097 | // Let's try to find from which loaded object it comes from. 2098 | // The loaded object can be yourself btw. 2099 | 2100 | Dl_info symbol_info; 2101 | int dladdr_result = 0; 2102 | #if defined(__GLIBC__) 2103 | link_map *link_map; 2104 | // We request the link map so we can get information about offsets 2105 | dladdr_result = 2106 | dladdr1(trace.addr, &symbol_info, reinterpret_cast(&link_map), 2107 | RTLD_DL_LINKMAP); 2108 | #else 2109 | // Android doesn't have dladdr1. Don't use the linker map. 2110 | dladdr_result = dladdr(trace.addr, &symbol_info); 2111 | #endif 2112 | if (!dladdr_result) { 2113 | return trace; // dat broken trace... 2114 | } 2115 | 2116 | // Now we get in symbol_info: 2117 | // .dli_fname: 2118 | // pathname of the shared object that contains the address. 2119 | // .dli_fbase: 2120 | // where the object is loaded in memory. 2121 | // .dli_sname: 2122 | // the name of the nearest symbol to trace.addr, we expect a 2123 | // function name. 2124 | // .dli_saddr: 2125 | // the exact address corresponding to .dli_sname. 2126 | // 2127 | // And in link_map: 2128 | // .l_addr: 2129 | // difference between the address in the ELF file and the address 2130 | // in memory 2131 | // l_name: 2132 | // absolute pathname where the object was found 2133 | 2134 | if (symbol_info.dli_sname) { 2135 | trace.object_function = demangle(symbol_info.dli_sname); 2136 | } 2137 | 2138 | if (!symbol_info.dli_fname) { 2139 | return trace; 2140 | } 2141 | 2142 | trace.object_filename = resolve_exec_path(symbol_info); 2143 | dwarf_fileobject &fobj = load_object_with_dwarf(symbol_info.dli_fname); 2144 | if (!fobj.dwarf_handle) { 2145 | return trace; // sad, we couldn't load the object :( 2146 | } 2147 | 2148 | #if defined(__GLIBC__) 2149 | // Convert the address to a module relative one by looking at 2150 | // the module's loading address in the link map 2151 | Dwarf_Addr address = reinterpret_cast(trace.addr) - 2152 | reinterpret_cast(link_map->l_addr); 2153 | #else 2154 | Dwarf_Addr address = reinterpret_cast(trace.addr); 2155 | #endif 2156 | 2157 | if (trace.object_function.empty()) { 2158 | symbol_cache_t::iterator it = fobj.symbol_cache.lower_bound(address); 2159 | 2160 | if (it != fobj.symbol_cache.end()) { 2161 | if (it->first != address) { 2162 | if (it != fobj.symbol_cache.begin()) { 2163 | --it; 2164 | } 2165 | } 2166 | trace.object_function = demangle(it->second.c_str()); 2167 | } 2168 | } 2169 | 2170 | // Get the Compilation Unit DIE for the address 2171 | Dwarf_Die die = find_die(fobj, address); 2172 | 2173 | if (!die) { 2174 | return trace; // this time we lost the game :/ 2175 | } 2176 | 2177 | // libdwarf doesn't give us direct access to its objects, it always 2178 | // allocates a copy for the caller. We keep that copy alive in a cache 2179 | // and we deallocate it later when it's no longer required. 2180 | die_cache_entry &die_object = get_die_cache(fobj, die); 2181 | if (die_object.isEmpty()) 2182 | return trace; // We have no line section for this DIE 2183 | 2184 | die_linemap_t::iterator it = die_object.line_section.lower_bound(address); 2185 | 2186 | if (it != die_object.line_section.end()) { 2187 | if (it->first != address) { 2188 | if (it == die_object.line_section.begin()) { 2189 | // If we are on the first item of the line section 2190 | // but the address does not match it means that 2191 | // the address is below the range of the DIE. Give up. 2192 | return trace; 2193 | } else { 2194 | --it; 2195 | } 2196 | } 2197 | } else { 2198 | return trace; // We didn't find the address. 2199 | } 2200 | 2201 | // Get the Dwarf_Line that the address points to and call libdwarf 2202 | // to get source file, line and column info. 2203 | Dwarf_Line line = die_object.line_buffer[it->second]; 2204 | Dwarf_Error error = DW_DLE_NE; 2205 | 2206 | char *filename; 2207 | if (dwarf_linesrc(line, &filename, &error) == DW_DLV_OK) { 2208 | trace.source.filename = std::string(filename); 2209 | dwarf_dealloc(fobj.dwarf_handle.get(), filename, DW_DLA_STRING); 2210 | } 2211 | 2212 | Dwarf_Unsigned number = 0; 2213 | if (dwarf_lineno(line, &number, &error) == DW_DLV_OK) { 2214 | trace.source.line = number; 2215 | } else { 2216 | trace.source.line = 0; 2217 | } 2218 | 2219 | if (dwarf_lineoff_b(line, &number, &error) == DW_DLV_OK) { 2220 | trace.source.col = number; 2221 | } else { 2222 | trace.source.col = 0; 2223 | } 2224 | 2225 | std::vector namespace_stack; 2226 | deep_first_search_by_pc(fobj, die, address, namespace_stack, 2227 | inliners_search_cb(trace, fobj, die)); 2228 | 2229 | dwarf_dealloc(fobj.dwarf_handle.get(), die, DW_DLA_DIE); 2230 | 2231 | return trace; 2232 | } 2233 | 2234 | public: 2235 | static int close_dwarf(Dwarf_Debug dwarf) { 2236 | return dwarf_finish(dwarf, NULL); 2237 | } 2238 | 2239 | private: 2240 | bool _dwarf_loaded; 2241 | 2242 | typedef details::handle> 2243 | dwarf_file_t; 2244 | 2245 | typedef details::handle> 2246 | dwarf_elf_t; 2247 | 2248 | typedef details::handle> 2250 | dwarf_handle_t; 2251 | 2252 | typedef std::map die_linemap_t; 2253 | 2254 | typedef std::map die_specmap_t; 2255 | 2256 | struct die_cache_entry { 2257 | die_specmap_t spec_section; 2258 | die_linemap_t line_section; 2259 | Dwarf_Line *line_buffer; 2260 | Dwarf_Signed line_count; 2261 | Dwarf_Line_Context line_context; 2262 | 2263 | inline bool isEmpty() { 2264 | return line_buffer == NULL || line_count == 0 || line_context == NULL || 2265 | line_section.empty(); 2266 | } 2267 | 2268 | die_cache_entry() : line_buffer(0), line_count(0), line_context(0) {} 2269 | 2270 | ~die_cache_entry() { 2271 | if (line_context) { 2272 | dwarf_srclines_dealloc_b(line_context); 2273 | } 2274 | } 2275 | }; 2276 | 2277 | typedef std::map die_cache_t; 2278 | 2279 | typedef std::map symbol_cache_t; 2280 | 2281 | struct dwarf_fileobject { 2282 | dwarf_file_t file_handle; 2283 | dwarf_elf_t elf_handle; 2284 | dwarf_handle_t dwarf_handle; 2285 | symbol_cache_t symbol_cache; 2286 | 2287 | // Die cache 2288 | die_cache_t die_cache; 2289 | die_cache_entry *current_cu; 2290 | }; 2291 | 2292 | typedef details::hashtable::type 2293 | fobj_dwarf_map_t; 2294 | fobj_dwarf_map_t _fobj_dwarf_map; 2295 | 2296 | static bool cstrings_eq(const char *a, const char *b) { 2297 | if (!a || !b) { 2298 | return false; 2299 | } 2300 | return strcmp(a, b) == 0; 2301 | } 2302 | 2303 | dwarf_fileobject &load_object_with_dwarf(const std::string &filename_object) { 2304 | 2305 | if (!_dwarf_loaded) { 2306 | // Set the ELF library operating version 2307 | // If that fails there's nothing we can do 2308 | _dwarf_loaded = elf_version(EV_CURRENT) != EV_NONE; 2309 | } 2310 | 2311 | fobj_dwarf_map_t::iterator it = _fobj_dwarf_map.find(filename_object); 2312 | if (it != _fobj_dwarf_map.end()) { 2313 | return it->second; 2314 | } 2315 | 2316 | // this new object is empty for now 2317 | dwarf_fileobject &r = _fobj_dwarf_map[filename_object]; 2318 | 2319 | dwarf_file_t file_handle; 2320 | file_handle.reset(open(filename_object.c_str(), O_RDONLY)); 2321 | if (file_handle.get() < 0) { 2322 | return r; 2323 | } 2324 | 2325 | // Try to get an ELF handle. We need to read the ELF sections 2326 | // because we want to see if there is a .gnu_debuglink section 2327 | // that points to a split debug file 2328 | dwarf_elf_t elf_handle; 2329 | elf_handle.reset(elf_begin(file_handle.get(), ELF_C_READ, NULL)); 2330 | if (!elf_handle) { 2331 | return r; 2332 | } 2333 | 2334 | const char *e_ident = elf_getident(elf_handle.get(), 0); 2335 | if (!e_ident) { 2336 | return r; 2337 | } 2338 | 2339 | // Get the number of sections 2340 | // We use the new APIs as elf_getshnum is deprecated 2341 | size_t shdrnum = 0; 2342 | if (elf_getshdrnum(elf_handle.get(), &shdrnum) == -1) { 2343 | return r; 2344 | } 2345 | 2346 | // Get the index to the string section 2347 | size_t shdrstrndx = 0; 2348 | if (elf_getshdrstrndx(elf_handle.get(), &shdrstrndx) == -1) { 2349 | return r; 2350 | } 2351 | 2352 | std::string debuglink; 2353 | // Iterate through the ELF sections to try to get a gnu_debuglink 2354 | // note and also to cache the symbol table. 2355 | // We go the preprocessor way to avoid having to create templated 2356 | // classes or using gelf (which might throw a compiler error if 64 bit 2357 | // is not supported 2358 | #define ELF_GET_DATA(ARCH) \ 2359 | Elf_Scn *elf_section = 0; \ 2360 | Elf_Data *elf_data = 0; \ 2361 | Elf##ARCH##_Shdr *section_header = 0; \ 2362 | Elf_Scn *symbol_section = 0; \ 2363 | size_t symbol_count = 0; \ 2364 | size_t symbol_strings = 0; \ 2365 | Elf##ARCH##_Sym *symbol = 0; \ 2366 | const char *section_name = 0; \ 2367 | \ 2368 | while ((elf_section = elf_nextscn(elf_handle.get(), elf_section)) != NULL) { \ 2369 | section_header = elf##ARCH##_getshdr(elf_section); \ 2370 | if (section_header == NULL) { \ 2371 | return r; \ 2372 | } \ 2373 | \ 2374 | if ((section_name = elf_strptr(elf_handle.get(), shdrstrndx, \ 2375 | section_header->sh_name)) == NULL) { \ 2376 | return r; \ 2377 | } \ 2378 | \ 2379 | if (cstrings_eq(section_name, ".gnu_debuglink")) { \ 2380 | elf_data = elf_getdata(elf_section, NULL); \ 2381 | if (elf_data && elf_data->d_size > 0) { \ 2382 | debuglink = \ 2383 | std::string(reinterpret_cast(elf_data->d_buf)); \ 2384 | } \ 2385 | } \ 2386 | \ 2387 | switch (section_header->sh_type) { \ 2388 | case SHT_SYMTAB: \ 2389 | symbol_section = elf_section; \ 2390 | symbol_count = section_header->sh_size / section_header->sh_entsize; \ 2391 | symbol_strings = section_header->sh_link; \ 2392 | break; \ 2393 | \ 2394 | /* We use .dynsyms as a last resort, we prefer .symtab */ \ 2395 | case SHT_DYNSYM: \ 2396 | if (!symbol_section) { \ 2397 | symbol_section = elf_section; \ 2398 | symbol_count = section_header->sh_size / section_header->sh_entsize; \ 2399 | symbol_strings = section_header->sh_link; \ 2400 | } \ 2401 | break; \ 2402 | } \ 2403 | } \ 2404 | \ 2405 | if (symbol_section && symbol_count && symbol_strings) { \ 2406 | elf_data = elf_getdata(symbol_section, NULL); \ 2407 | symbol = reinterpret_cast(elf_data->d_buf); \ 2408 | for (size_t i = 0; i < symbol_count; ++i) { \ 2409 | int type = ELF##ARCH##_ST_TYPE(symbol->st_info); \ 2410 | if (type == STT_FUNC && symbol->st_value > 0) { \ 2411 | r.symbol_cache[symbol->st_value] = std::string( \ 2412 | elf_strptr(elf_handle.get(), symbol_strings, symbol->st_name)); \ 2413 | } \ 2414 | ++symbol; \ 2415 | } \ 2416 | } 2417 | 2418 | if (e_ident[EI_CLASS] == ELFCLASS32) { 2419 | ELF_GET_DATA(32) 2420 | } else if (e_ident[EI_CLASS] == ELFCLASS64) { 2421 | // libelf might have been built without 64 bit support 2422 | #if __LIBELF64 2423 | ELF_GET_DATA(64) 2424 | #endif 2425 | } 2426 | 2427 | if (!debuglink.empty()) { 2428 | // We have a debuglink section! Open an elf instance on that 2429 | // file instead. If we can't open the file, then return 2430 | // the elf handle we had already opened. 2431 | dwarf_file_t debuglink_file; 2432 | debuglink_file.reset(open(debuglink.c_str(), O_RDONLY)); 2433 | if (debuglink_file.get() > 0) { 2434 | dwarf_elf_t debuglink_elf; 2435 | debuglink_elf.reset(elf_begin(debuglink_file.get(), ELF_C_READ, NULL)); 2436 | 2437 | // If we have a valid elf handle, return the new elf handle 2438 | // and file handle and discard the original ones 2439 | if (debuglink_elf) { 2440 | elf_handle = move(debuglink_elf); 2441 | file_handle = move(debuglink_file); 2442 | } 2443 | } 2444 | } 2445 | 2446 | // Ok, we have a valid ELF handle, let's try to get debug symbols 2447 | Dwarf_Debug dwarf_debug; 2448 | Dwarf_Error error = DW_DLE_NE; 2449 | dwarf_handle_t dwarf_handle; 2450 | 2451 | int dwarf_result = dwarf_elf_init(elf_handle.get(), DW_DLC_READ, NULL, NULL, 2452 | &dwarf_debug, &error); 2453 | 2454 | // We don't do any special handling for DW_DLV_NO_ENTRY specially. 2455 | // If we get an error, or the file doesn't have debug information 2456 | // we just return. 2457 | if (dwarf_result != DW_DLV_OK) { 2458 | return r; 2459 | } 2460 | 2461 | dwarf_handle.reset(dwarf_debug); 2462 | 2463 | r.file_handle = move(file_handle); 2464 | r.elf_handle = move(elf_handle); 2465 | r.dwarf_handle = move(dwarf_handle); 2466 | 2467 | return r; 2468 | } 2469 | 2470 | die_cache_entry &get_die_cache(dwarf_fileobject &fobj, Dwarf_Die die) { 2471 | Dwarf_Error error = DW_DLE_NE; 2472 | 2473 | // Get the die offset, we use it as the cache key 2474 | Dwarf_Off die_offset; 2475 | if (dwarf_dieoffset(die, &die_offset, &error) != DW_DLV_OK) { 2476 | die_offset = 0; 2477 | } 2478 | 2479 | die_cache_t::iterator it = fobj.die_cache.find(die_offset); 2480 | 2481 | if (it != fobj.die_cache.end()) { 2482 | fobj.current_cu = &it->second; 2483 | return it->second; 2484 | } 2485 | 2486 | die_cache_entry &de = fobj.die_cache[die_offset]; 2487 | fobj.current_cu = &de; 2488 | 2489 | Dwarf_Addr line_addr; 2490 | Dwarf_Small table_count; 2491 | 2492 | // The addresses in the line section are not fully sorted (they might 2493 | // be sorted by block of code belonging to the same file), which makes 2494 | // it necessary to do so before searching is possible. 2495 | // 2496 | // As libdwarf allocates a copy of everything, let's get the contents 2497 | // of the line section and keep it around. We also create a map of 2498 | // program counter to line table indices so we can search by address 2499 | // and get the line buffer index. 2500 | // 2501 | // To make things more difficult, the same address can span more than 2502 | // one line, so we need to keep the index pointing to the first line 2503 | // by using insert instead of the map's [ operator. 2504 | 2505 | // Get the line context for the DIE 2506 | if (dwarf_srclines_b(die, 0, &table_count, &de.line_context, &error) == 2507 | DW_DLV_OK) { 2508 | // Get the source lines for this line context, to be deallocated 2509 | // later 2510 | if (dwarf_srclines_from_linecontext(de.line_context, &de.line_buffer, 2511 | &de.line_count, 2512 | &error) == DW_DLV_OK) { 2513 | 2514 | // Add all the addresses to our map 2515 | for (int i = 0; i < de.line_count; i++) { 2516 | if (dwarf_lineaddr(de.line_buffer[i], &line_addr, &error) != 2517 | DW_DLV_OK) { 2518 | line_addr = 0; 2519 | } 2520 | de.line_section.insert(std::pair(line_addr, i)); 2521 | } 2522 | } 2523 | } 2524 | 2525 | // For each CU, cache the function DIEs that contain the 2526 | // DW_AT_specification attribute. When building with -g3 the function 2527 | // DIEs are separated in declaration and specification, with the 2528 | // declaration containing only the name and parameters and the 2529 | // specification the low/high pc and other compiler attributes. 2530 | // 2531 | // We cache those specifications so we don't skip over the declarations, 2532 | // because they have no pc, and we can do namespace resolution for 2533 | // DWARF function names. 2534 | Dwarf_Debug dwarf = fobj.dwarf_handle.get(); 2535 | Dwarf_Die current_die = 0; 2536 | if (dwarf_child(die, ¤t_die, &error) == DW_DLV_OK) { 2537 | for (;;) { 2538 | Dwarf_Die sibling_die = 0; 2539 | 2540 | Dwarf_Half tag_value; 2541 | dwarf_tag(current_die, &tag_value, &error); 2542 | 2543 | if (tag_value == DW_TAG_subprogram || 2544 | tag_value == DW_TAG_inlined_subroutine) { 2545 | 2546 | Dwarf_Bool has_attr = 0; 2547 | if (dwarf_hasattr(current_die, DW_AT_specification, &has_attr, 2548 | &error) == DW_DLV_OK) { 2549 | if (has_attr) { 2550 | Dwarf_Attribute attr_mem; 2551 | if (dwarf_attr(current_die, DW_AT_specification, &attr_mem, 2552 | &error) == DW_DLV_OK) { 2553 | Dwarf_Off spec_offset = 0; 2554 | if (dwarf_formref(attr_mem, &spec_offset, &error) == 2555 | DW_DLV_OK) { 2556 | Dwarf_Off spec_die_offset; 2557 | if (dwarf_dieoffset(current_die, &spec_die_offset, &error) == 2558 | DW_DLV_OK) { 2559 | de.spec_section[spec_offset] = spec_die_offset; 2560 | } 2561 | } 2562 | } 2563 | dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); 2564 | } 2565 | } 2566 | } 2567 | 2568 | int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); 2569 | if (result == DW_DLV_ERROR) { 2570 | break; 2571 | } else if (result == DW_DLV_NO_ENTRY) { 2572 | break; 2573 | } 2574 | 2575 | if (current_die != die) { 2576 | dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); 2577 | current_die = 0; 2578 | } 2579 | 2580 | current_die = sibling_die; 2581 | } 2582 | } 2583 | return de; 2584 | } 2585 | 2586 | static Dwarf_Die get_referenced_die(Dwarf_Debug dwarf, Dwarf_Die die, 2587 | Dwarf_Half attr, bool global) { 2588 | Dwarf_Error error = DW_DLE_NE; 2589 | Dwarf_Attribute attr_mem; 2590 | 2591 | Dwarf_Die found_die = NULL; 2592 | if (dwarf_attr(die, attr, &attr_mem, &error) == DW_DLV_OK) { 2593 | Dwarf_Off offset; 2594 | int result = 0; 2595 | if (global) { 2596 | result = dwarf_global_formref(attr_mem, &offset, &error); 2597 | } else { 2598 | result = dwarf_formref(attr_mem, &offset, &error); 2599 | } 2600 | 2601 | if (result == DW_DLV_OK) { 2602 | if (dwarf_offdie(dwarf, offset, &found_die, &error) != DW_DLV_OK) { 2603 | found_die = NULL; 2604 | } 2605 | } 2606 | dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); 2607 | } 2608 | return found_die; 2609 | } 2610 | 2611 | static std::string get_referenced_die_name(Dwarf_Debug dwarf, Dwarf_Die die, 2612 | Dwarf_Half attr, bool global) { 2613 | Dwarf_Error error = DW_DLE_NE; 2614 | std::string value; 2615 | 2616 | Dwarf_Die found_die = get_referenced_die(dwarf, die, attr, global); 2617 | 2618 | if (found_die) { 2619 | char *name; 2620 | if (dwarf_diename(found_die, &name, &error) == DW_DLV_OK) { 2621 | if (name) { 2622 | value = std::string(name); 2623 | } 2624 | dwarf_dealloc(dwarf, name, DW_DLA_STRING); 2625 | } 2626 | dwarf_dealloc(dwarf, found_die, DW_DLA_DIE); 2627 | } 2628 | 2629 | return value; 2630 | } 2631 | 2632 | // Returns a spec DIE linked to the passed one. The caller should 2633 | // deallocate the DIE 2634 | static Dwarf_Die get_spec_die(dwarf_fileobject &fobj, Dwarf_Die die) { 2635 | Dwarf_Debug dwarf = fobj.dwarf_handle.get(); 2636 | Dwarf_Error error = DW_DLE_NE; 2637 | Dwarf_Off die_offset; 2638 | if (fobj.current_cu && 2639 | dwarf_die_CU_offset(die, &die_offset, &error) == DW_DLV_OK) { 2640 | die_specmap_t::iterator it = 2641 | fobj.current_cu->spec_section.find(die_offset); 2642 | 2643 | // If we have a DIE that completes the current one, check if 2644 | // that one has the pc we are looking for 2645 | if (it != fobj.current_cu->spec_section.end()) { 2646 | Dwarf_Die spec_die = 0; 2647 | if (dwarf_offdie(dwarf, it->second, &spec_die, &error) == DW_DLV_OK) { 2648 | return spec_die; 2649 | } 2650 | } 2651 | } 2652 | 2653 | // Maybe we have an abstract origin DIE with the function information? 2654 | return get_referenced_die(fobj.dwarf_handle.get(), die, 2655 | DW_AT_abstract_origin, true); 2656 | } 2657 | 2658 | static bool die_has_pc(dwarf_fileobject &fobj, Dwarf_Die die, Dwarf_Addr pc) { 2659 | Dwarf_Addr low_pc = 0, high_pc = 0; 2660 | Dwarf_Half high_pc_form = 0; 2661 | Dwarf_Form_Class return_class; 2662 | Dwarf_Error error = DW_DLE_NE; 2663 | Dwarf_Debug dwarf = fobj.dwarf_handle.get(); 2664 | bool has_lowpc = false; 2665 | bool has_highpc = false; 2666 | bool has_ranges = false; 2667 | 2668 | if (dwarf_lowpc(die, &low_pc, &error) == DW_DLV_OK) { 2669 | // If we have a low_pc check if there is a high pc. 2670 | // If we don't have a high pc this might mean we have a base 2671 | // address for the ranges list or just an address. 2672 | has_lowpc = true; 2673 | 2674 | if (dwarf_highpc_b(die, &high_pc, &high_pc_form, &return_class, &error) == 2675 | DW_DLV_OK) { 2676 | // We do have a high pc. In DWARF 4+ this is an offset from the 2677 | // low pc, but in earlier versions it's an absolute address. 2678 | 2679 | has_highpc = true; 2680 | // In DWARF 2/3 this would be a DW_FORM_CLASS_ADDRESS 2681 | if (return_class == DW_FORM_CLASS_CONSTANT) { 2682 | high_pc = low_pc + high_pc; 2683 | } 2684 | 2685 | // We have low and high pc, check if our address 2686 | // is in that range 2687 | return pc >= low_pc && pc < high_pc; 2688 | } 2689 | } else { 2690 | // Reset the low_pc, in case dwarf_lowpc failing set it to some 2691 | // undefined value. 2692 | low_pc = 0; 2693 | } 2694 | 2695 | // Check if DW_AT_ranges is present and search for the PC in the 2696 | // returned ranges list. We always add the low_pc, as it not set it will 2697 | // be 0, in case we had a DW_AT_low_pc and DW_AT_ranges pair 2698 | bool result = false; 2699 | 2700 | Dwarf_Attribute attr; 2701 | if (dwarf_attr(die, DW_AT_ranges, &attr, &error) == DW_DLV_OK) { 2702 | 2703 | Dwarf_Off offset; 2704 | if (dwarf_global_formref(attr, &offset, &error) == DW_DLV_OK) { 2705 | Dwarf_Ranges *ranges; 2706 | Dwarf_Signed ranges_count = 0; 2707 | Dwarf_Unsigned byte_count = 0; 2708 | 2709 | if (dwarf_get_ranges_a(dwarf, offset, die, &ranges, &ranges_count, 2710 | &byte_count, &error) == DW_DLV_OK) { 2711 | has_ranges = ranges_count != 0; 2712 | for (int i = 0; i < ranges_count; i++) { 2713 | if (ranges[i].dwr_addr1 != 0 && 2714 | pc >= ranges[i].dwr_addr1 + low_pc && 2715 | pc < ranges[i].dwr_addr2 + low_pc) { 2716 | result = true; 2717 | break; 2718 | } 2719 | } 2720 | dwarf_ranges_dealloc(dwarf, ranges, ranges_count); 2721 | } 2722 | } 2723 | } 2724 | 2725 | // Last attempt. We might have a single address set as low_pc. 2726 | if (!result && low_pc != 0 && pc == low_pc) { 2727 | result = true; 2728 | } 2729 | 2730 | // If we don't have lowpc, highpc and ranges maybe this DIE is a 2731 | // declaration that relies on a DW_AT_specification DIE that happens 2732 | // later. Use the specification cache we filled when we loaded this CU. 2733 | if (!result && (!has_lowpc && !has_highpc && !has_ranges)) { 2734 | Dwarf_Die spec_die = get_spec_die(fobj, die); 2735 | if (spec_die) { 2736 | result = die_has_pc(fobj, spec_die, pc); 2737 | dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); 2738 | } 2739 | } 2740 | 2741 | return result; 2742 | } 2743 | 2744 | static void get_type(Dwarf_Debug dwarf, Dwarf_Die die, std::string &type) { 2745 | Dwarf_Error error = DW_DLE_NE; 2746 | 2747 | Dwarf_Die child = 0; 2748 | if (dwarf_child(die, &child, &error) == DW_DLV_OK) { 2749 | get_type(dwarf, child, type); 2750 | } 2751 | 2752 | if (child) { 2753 | type.insert(0, "::"); 2754 | dwarf_dealloc(dwarf, child, DW_DLA_DIE); 2755 | } 2756 | 2757 | char *name; 2758 | if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { 2759 | type.insert(0, std::string(name)); 2760 | dwarf_dealloc(dwarf, name, DW_DLA_STRING); 2761 | } else { 2762 | type.insert(0, ""); 2763 | } 2764 | } 2765 | 2766 | static std::string get_type_by_signature(Dwarf_Debug dwarf, Dwarf_Die die) { 2767 | Dwarf_Error error = DW_DLE_NE; 2768 | 2769 | Dwarf_Sig8 signature; 2770 | Dwarf_Bool has_attr = 0; 2771 | if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == DW_DLV_OK) { 2772 | if (has_attr) { 2773 | Dwarf_Attribute attr_mem; 2774 | if (dwarf_attr(die, DW_AT_signature, &attr_mem, &error) == DW_DLV_OK) { 2775 | if (dwarf_formsig8(attr_mem, &signature, &error) != DW_DLV_OK) { 2776 | return std::string(""); 2777 | } 2778 | } 2779 | dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); 2780 | } 2781 | } 2782 | 2783 | Dwarf_Unsigned next_cu_header; 2784 | Dwarf_Sig8 tu_signature; 2785 | std::string result; 2786 | bool found = false; 2787 | 2788 | while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, &tu_signature, 0, 2789 | &next_cu_header, 0, &error) == DW_DLV_OK) { 2790 | 2791 | if (strncmp(signature.signature, tu_signature.signature, 8) == 0) { 2792 | Dwarf_Die type_cu_die = 0; 2793 | if (dwarf_siblingof_b(dwarf, 0, 0, &type_cu_die, &error) == DW_DLV_OK) { 2794 | Dwarf_Die child_die = 0; 2795 | if (dwarf_child(type_cu_die, &child_die, &error) == DW_DLV_OK) { 2796 | get_type(dwarf, child_die, result); 2797 | found = !result.empty(); 2798 | dwarf_dealloc(dwarf, child_die, DW_DLA_DIE); 2799 | } 2800 | dwarf_dealloc(dwarf, type_cu_die, DW_DLA_DIE); 2801 | } 2802 | } 2803 | } 2804 | 2805 | if (found) { 2806 | while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2807 | &next_cu_header, 0, &error) == DW_DLV_OK) { 2808 | // Reset the cu header state. Unfortunately, libdwarf's 2809 | // next_cu_header API keeps its own iterator per Dwarf_Debug 2810 | // that can't be reset. We need to keep fetching elements until 2811 | // the end. 2812 | } 2813 | } else { 2814 | // If we couldn't resolve the type just print out the signature 2815 | std::ostringstream string_stream; 2816 | string_stream << "<0x" << std::hex << std::setfill('0'); 2817 | for (int i = 0; i < 8; ++i) { 2818 | string_stream << std::setw(2) << std::hex 2819 | << (int)(unsigned char)(signature.signature[i]); 2820 | } 2821 | string_stream << ">"; 2822 | result = string_stream.str(); 2823 | } 2824 | return result; 2825 | } 2826 | 2827 | struct type_context_t { 2828 | bool is_const; 2829 | bool is_typedef; 2830 | bool has_type; 2831 | bool has_name; 2832 | std::string text; 2833 | 2834 | type_context_t() 2835 | : is_const(false), is_typedef(false), has_type(false), has_name(false) { 2836 | } 2837 | }; 2838 | 2839 | // Types are resolved from right to left: we get the variable name first 2840 | // and then all specifiers (like const or pointer) in a chain of DW_AT_type 2841 | // DIEs. Call this function recursively until we get a complete type 2842 | // string. 2843 | static void set_parameter_string(dwarf_fileobject &fobj, Dwarf_Die die, 2844 | type_context_t &context) { 2845 | char *name; 2846 | Dwarf_Error error = DW_DLE_NE; 2847 | 2848 | // typedefs contain also the base type, so we skip it and only 2849 | // print the typedef name 2850 | if (!context.is_typedef) { 2851 | if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { 2852 | if (!context.text.empty()) { 2853 | context.text.insert(0, " "); 2854 | } 2855 | context.text.insert(0, std::string(name)); 2856 | dwarf_dealloc(fobj.dwarf_handle.get(), name, DW_DLA_STRING); 2857 | } 2858 | } else { 2859 | context.is_typedef = false; 2860 | context.has_type = true; 2861 | if (context.is_const) { 2862 | context.text.insert(0, "const "); 2863 | context.is_const = false; 2864 | } 2865 | } 2866 | 2867 | bool next_type_is_const = false; 2868 | bool is_keyword = true; 2869 | 2870 | Dwarf_Half tag = 0; 2871 | Dwarf_Bool has_attr = 0; 2872 | if (dwarf_tag(die, &tag, &error) == DW_DLV_OK) { 2873 | switch (tag) { 2874 | case DW_TAG_structure_type: 2875 | case DW_TAG_union_type: 2876 | case DW_TAG_class_type: 2877 | case DW_TAG_enumeration_type: 2878 | context.has_type = true; 2879 | if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == 2880 | DW_DLV_OK) { 2881 | // If we have a signature it means the type is defined 2882 | // in .debug_types, so we need to load the DIE pointed 2883 | // at by the signature and resolve it 2884 | if (has_attr) { 2885 | std::string type = 2886 | get_type_by_signature(fobj.dwarf_handle.get(), die); 2887 | if (context.is_const) 2888 | type.insert(0, "const "); 2889 | 2890 | if (!context.text.empty()) 2891 | context.text.insert(0, " "); 2892 | context.text.insert(0, type); 2893 | } 2894 | 2895 | // Treat enums like typedefs, and skip printing its 2896 | // base type 2897 | context.is_typedef = (tag == DW_TAG_enumeration_type); 2898 | } 2899 | break; 2900 | case DW_TAG_const_type: 2901 | next_type_is_const = true; 2902 | break; 2903 | case DW_TAG_pointer_type: 2904 | context.text.insert(0, "*"); 2905 | break; 2906 | case DW_TAG_reference_type: 2907 | context.text.insert(0, "&"); 2908 | break; 2909 | case DW_TAG_restrict_type: 2910 | context.text.insert(0, "restrict "); 2911 | break; 2912 | case DW_TAG_rvalue_reference_type: 2913 | context.text.insert(0, "&&"); 2914 | break; 2915 | case DW_TAG_volatile_type: 2916 | context.text.insert(0, "volatile "); 2917 | break; 2918 | case DW_TAG_typedef: 2919 | // Propagate the const-ness to the next type 2920 | // as typedefs are linked to its base type 2921 | next_type_is_const = context.is_const; 2922 | context.is_typedef = true; 2923 | context.has_type = true; 2924 | break; 2925 | case DW_TAG_base_type: 2926 | context.has_type = true; 2927 | break; 2928 | case DW_TAG_formal_parameter: 2929 | context.has_name = true; 2930 | break; 2931 | default: 2932 | is_keyword = false; 2933 | break; 2934 | } 2935 | } 2936 | 2937 | if (!is_keyword && context.is_const) { 2938 | context.text.insert(0, "const "); 2939 | } 2940 | 2941 | context.is_const = next_type_is_const; 2942 | 2943 | Dwarf_Die ref = 2944 | get_referenced_die(fobj.dwarf_handle.get(), die, DW_AT_type, true); 2945 | if (ref) { 2946 | set_parameter_string(fobj, ref, context); 2947 | dwarf_dealloc(fobj.dwarf_handle.get(), ref, DW_DLA_DIE); 2948 | } 2949 | 2950 | if (!context.has_type && context.has_name) { 2951 | context.text.insert(0, "void "); 2952 | context.has_type = true; 2953 | } 2954 | } 2955 | 2956 | // Resolve the function return type and parameters 2957 | static void set_function_parameters(std::string &function_name, 2958 | std::vector &ns, 2959 | dwarf_fileobject &fobj, Dwarf_Die die) { 2960 | Dwarf_Debug dwarf = fobj.dwarf_handle.get(); 2961 | Dwarf_Error error = DW_DLE_NE; 2962 | Dwarf_Die current_die = 0; 2963 | std::string parameters; 2964 | bool has_spec = true; 2965 | // Check if we have a spec DIE. If we do we use it as it contains 2966 | // more information, like parameter names. 2967 | Dwarf_Die spec_die = get_spec_die(fobj, die); 2968 | if (!spec_die) { 2969 | has_spec = false; 2970 | spec_die = die; 2971 | } 2972 | 2973 | std::vector::const_iterator it = ns.begin(); 2974 | std::string ns_name; 2975 | for (it = ns.begin(); it < ns.end(); ++it) { 2976 | ns_name.append(*it).append("::"); 2977 | } 2978 | 2979 | if (!ns_name.empty()) { 2980 | function_name.insert(0, ns_name); 2981 | } 2982 | 2983 | // See if we have a function return type. It can be either on the 2984 | // current die or in its spec one (usually true for inlined functions) 2985 | std::string return_type = 2986 | get_referenced_die_name(dwarf, die, DW_AT_type, true); 2987 | if (return_type.empty()) { 2988 | return_type = get_referenced_die_name(dwarf, spec_die, DW_AT_type, true); 2989 | } 2990 | if (!return_type.empty()) { 2991 | return_type.append(" "); 2992 | function_name.insert(0, return_type); 2993 | } 2994 | 2995 | if (dwarf_child(spec_die, ¤t_die, &error) == DW_DLV_OK) { 2996 | for (;;) { 2997 | Dwarf_Die sibling_die = 0; 2998 | 2999 | Dwarf_Half tag_value; 3000 | dwarf_tag(current_die, &tag_value, &error); 3001 | 3002 | if (tag_value == DW_TAG_formal_parameter) { 3003 | // Ignore artificial (ie, compiler generated) parameters 3004 | bool is_artificial = false; 3005 | Dwarf_Attribute attr_mem; 3006 | if (dwarf_attr(current_die, DW_AT_artificial, &attr_mem, &error) == 3007 | DW_DLV_OK) { 3008 | Dwarf_Bool flag = 0; 3009 | if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { 3010 | is_artificial = flag != 0; 3011 | } 3012 | dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); 3013 | } 3014 | 3015 | if (!is_artificial) { 3016 | type_context_t context; 3017 | set_parameter_string(fobj, current_die, context); 3018 | 3019 | if (parameters.empty()) { 3020 | parameters.append("("); 3021 | } else { 3022 | parameters.append(", "); 3023 | } 3024 | parameters.append(context.text); 3025 | } 3026 | } 3027 | 3028 | int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); 3029 | if (result == DW_DLV_ERROR) { 3030 | break; 3031 | } else if (result == DW_DLV_NO_ENTRY) { 3032 | break; 3033 | } 3034 | 3035 | if (current_die != die) { 3036 | dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); 3037 | current_die = 0; 3038 | } 3039 | 3040 | current_die = sibling_die; 3041 | } 3042 | } 3043 | if (parameters.empty()) 3044 | parameters = "("; 3045 | parameters.append(")"); 3046 | 3047 | // If we got a spec DIE we need to deallocate it 3048 | if (has_spec) 3049 | dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); 3050 | 3051 | function_name.append(parameters); 3052 | } 3053 | 3054 | // defined here because in C++98, template function cannot take locally 3055 | // defined types... grrr. 3056 | struct inliners_search_cb { 3057 | void operator()(Dwarf_Die die, std::vector &ns) { 3058 | Dwarf_Error error = DW_DLE_NE; 3059 | Dwarf_Half tag_value; 3060 | Dwarf_Attribute attr_mem; 3061 | Dwarf_Debug dwarf = fobj.dwarf_handle.get(); 3062 | 3063 | dwarf_tag(die, &tag_value, &error); 3064 | 3065 | switch (tag_value) { 3066 | char *name; 3067 | case DW_TAG_subprogram: 3068 | if (!trace.source.function.empty()) 3069 | break; 3070 | if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { 3071 | trace.source.function = std::string(name); 3072 | dwarf_dealloc(dwarf, name, DW_DLA_STRING); 3073 | } else { 3074 | // We don't have a function name in this DIE. 3075 | // Check if there is a referenced non-defining 3076 | // declaration. 3077 | trace.source.function = 3078 | get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); 3079 | if (trace.source.function.empty()) { 3080 | trace.source.function = 3081 | get_referenced_die_name(dwarf, die, DW_AT_specification, true); 3082 | } 3083 | } 3084 | 3085 | // Append the function parameters, if available 3086 | set_function_parameters(trace.source.function, ns, fobj, die); 3087 | 3088 | // If the object function name is empty, it's possible that 3089 | // there is no dynamic symbol table (maybe the executable 3090 | // was stripped or not built with -rdynamic). See if we have 3091 | // a DWARF linkage name to use instead. We try both 3092 | // linkage_name and MIPS_linkage_name because the MIPS tag 3093 | // was the unofficial one until it was adopted in DWARF4. 3094 | // Old gcc versions generate MIPS_linkage_name 3095 | if (trace.object_function.empty()) { 3096 | details::demangler demangler; 3097 | 3098 | if (dwarf_attr(die, DW_AT_linkage_name, &attr_mem, &error) != 3099 | DW_DLV_OK) { 3100 | if (dwarf_attr(die, DW_AT_MIPS_linkage_name, &attr_mem, &error) != 3101 | DW_DLV_OK) { 3102 | break; 3103 | } 3104 | } 3105 | 3106 | char *linkage; 3107 | if (dwarf_formstring(attr_mem, &linkage, &error) == DW_DLV_OK) { 3108 | trace.object_function = demangler.demangle(linkage); 3109 | dwarf_dealloc(dwarf, linkage, DW_DLA_STRING); 3110 | } 3111 | dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); 3112 | } 3113 | break; 3114 | 3115 | case DW_TAG_inlined_subroutine: 3116 | ResolvedTrace::SourceLoc sloc; 3117 | 3118 | if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { 3119 | sloc.function = std::string(name); 3120 | dwarf_dealloc(dwarf, name, DW_DLA_STRING); 3121 | } else { 3122 | // We don't have a name for this inlined DIE, it could 3123 | // be that there is an abstract origin instead. 3124 | // Get the DW_AT_abstract_origin value, which is a 3125 | // reference to the source DIE and try to get its name 3126 | sloc.function = 3127 | get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); 3128 | } 3129 | 3130 | set_function_parameters(sloc.function, ns, fobj, die); 3131 | 3132 | std::string file = die_call_file(dwarf, die, cu_die); 3133 | if (!file.empty()) 3134 | sloc.filename = file; 3135 | 3136 | Dwarf_Unsigned number = 0; 3137 | if (dwarf_attr(die, DW_AT_call_line, &attr_mem, &error) == DW_DLV_OK) { 3138 | if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { 3139 | sloc.line = number; 3140 | } 3141 | dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); 3142 | } 3143 | 3144 | if (dwarf_attr(die, DW_AT_call_column, &attr_mem, &error) == 3145 | DW_DLV_OK) { 3146 | if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { 3147 | sloc.col = number; 3148 | } 3149 | dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); 3150 | } 3151 | 3152 | trace.inliners.push_back(sloc); 3153 | break; 3154 | }; 3155 | } 3156 | ResolvedTrace &trace; 3157 | dwarf_fileobject &fobj; 3158 | Dwarf_Die cu_die; 3159 | inliners_search_cb(ResolvedTrace &t, dwarf_fileobject &f, Dwarf_Die c) 3160 | : trace(t), fobj(f), cu_die(c) {} 3161 | }; 3162 | 3163 | static Dwarf_Die find_fundie_by_pc(dwarf_fileobject &fobj, 3164 | Dwarf_Die parent_die, Dwarf_Addr pc, 3165 | Dwarf_Die result) { 3166 | Dwarf_Die current_die = 0; 3167 | Dwarf_Error error = DW_DLE_NE; 3168 | Dwarf_Debug dwarf = fobj.dwarf_handle.get(); 3169 | 3170 | if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { 3171 | return NULL; 3172 | } 3173 | 3174 | for (;;) { 3175 | Dwarf_Die sibling_die = 0; 3176 | Dwarf_Half tag_value; 3177 | dwarf_tag(current_die, &tag_value, &error); 3178 | 3179 | switch (tag_value) { 3180 | case DW_TAG_subprogram: 3181 | case DW_TAG_inlined_subroutine: 3182 | if (die_has_pc(fobj, current_die, pc)) { 3183 | return current_die; 3184 | } 3185 | }; 3186 | bool declaration = false; 3187 | Dwarf_Attribute attr_mem; 3188 | if (dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == 3189 | DW_DLV_OK) { 3190 | Dwarf_Bool flag = 0; 3191 | if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { 3192 | declaration = flag != 0; 3193 | } 3194 | dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); 3195 | } 3196 | 3197 | if (!declaration) { 3198 | // let's be curious and look deeper in the tree, functions are 3199 | // not necessarily at the first level, but might be nested 3200 | // inside a namespace, structure, a function, an inlined 3201 | // function etc. 3202 | Dwarf_Die die_mem = 0; 3203 | Dwarf_Die indie = find_fundie_by_pc(fobj, current_die, pc, die_mem); 3204 | if (indie) { 3205 | result = die_mem; 3206 | return result; 3207 | } 3208 | } 3209 | 3210 | int res = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); 3211 | if (res == DW_DLV_ERROR) { 3212 | return NULL; 3213 | } else if (res == DW_DLV_NO_ENTRY) { 3214 | break; 3215 | } 3216 | 3217 | if (current_die != parent_die) { 3218 | dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); 3219 | current_die = 0; 3220 | } 3221 | 3222 | current_die = sibling_die; 3223 | } 3224 | return NULL; 3225 | } 3226 | 3227 | template 3228 | static bool deep_first_search_by_pc(dwarf_fileobject &fobj, 3229 | Dwarf_Die parent_die, Dwarf_Addr pc, 3230 | std::vector &ns, CB cb) { 3231 | Dwarf_Die current_die = 0; 3232 | Dwarf_Debug dwarf = fobj.dwarf_handle.get(); 3233 | Dwarf_Error error = DW_DLE_NE; 3234 | 3235 | if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { 3236 | return false; 3237 | } 3238 | 3239 | bool branch_has_pc = false; 3240 | bool has_namespace = false; 3241 | for (;;) { 3242 | Dwarf_Die sibling_die = 0; 3243 | 3244 | Dwarf_Half tag; 3245 | if (dwarf_tag(current_die, &tag, &error) == DW_DLV_OK) { 3246 | if (tag == DW_TAG_namespace || tag == DW_TAG_class_type) { 3247 | char *ns_name = NULL; 3248 | if (dwarf_diename(current_die, &ns_name, &error) == DW_DLV_OK) { 3249 | if (ns_name) { 3250 | ns.push_back(std::string(ns_name)); 3251 | } else { 3252 | ns.push_back(""); 3253 | } 3254 | dwarf_dealloc(dwarf, ns_name, DW_DLA_STRING); 3255 | } else { 3256 | ns.push_back(""); 3257 | } 3258 | has_namespace = true; 3259 | } 3260 | } 3261 | 3262 | bool declaration = false; 3263 | Dwarf_Attribute attr_mem; 3264 | if (tag != DW_TAG_class_type && 3265 | dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == 3266 | DW_DLV_OK) { 3267 | Dwarf_Bool flag = 0; 3268 | if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { 3269 | declaration = flag != 0; 3270 | } 3271 | dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); 3272 | } 3273 | 3274 | if (!declaration) { 3275 | // let's be curious and look deeper in the tree, function are 3276 | // not necessarily at the first level, but might be nested 3277 | // inside a namespace, structure, a function, an inlined 3278 | // function etc. 3279 | branch_has_pc = deep_first_search_by_pc(fobj, current_die, pc, ns, cb); 3280 | } 3281 | 3282 | if (!branch_has_pc) { 3283 | branch_has_pc = die_has_pc(fobj, current_die, pc); 3284 | } 3285 | 3286 | if (branch_has_pc) { 3287 | cb(current_die, ns); 3288 | } 3289 | 3290 | int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); 3291 | if (result == DW_DLV_ERROR) { 3292 | return false; 3293 | } else if (result == DW_DLV_NO_ENTRY) { 3294 | break; 3295 | } 3296 | 3297 | if (current_die != parent_die) { 3298 | dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); 3299 | current_die = 0; 3300 | } 3301 | 3302 | if (has_namespace) { 3303 | has_namespace = false; 3304 | ns.pop_back(); 3305 | } 3306 | current_die = sibling_die; 3307 | } 3308 | 3309 | if (has_namespace) { 3310 | ns.pop_back(); 3311 | } 3312 | return branch_has_pc; 3313 | } 3314 | 3315 | static std::string die_call_file(Dwarf_Debug dwarf, Dwarf_Die die, 3316 | Dwarf_Die cu_die) { 3317 | Dwarf_Attribute attr_mem; 3318 | Dwarf_Error error = DW_DLE_NE; 3319 | Dwarf_Unsigned file_index; 3320 | 3321 | std::string file; 3322 | 3323 | if (dwarf_attr(die, DW_AT_call_file, &attr_mem, &error) == DW_DLV_OK) { 3324 | if (dwarf_formudata(attr_mem, &file_index, &error) != DW_DLV_OK) { 3325 | file_index = 0; 3326 | } 3327 | dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); 3328 | 3329 | if (file_index == 0) { 3330 | return file; 3331 | } 3332 | 3333 | char **srcfiles = 0; 3334 | Dwarf_Signed file_count = 0; 3335 | if (dwarf_srcfiles(cu_die, &srcfiles, &file_count, &error) == DW_DLV_OK) { 3336 | if (file_count > 0 && file_index <= static_cast(file_count)) { 3337 | file = std::string(srcfiles[file_index - 1]); 3338 | } 3339 | 3340 | // Deallocate all strings! 3341 | for (int i = 0; i < file_count; ++i) { 3342 | dwarf_dealloc(dwarf, srcfiles[i], DW_DLA_STRING); 3343 | } 3344 | dwarf_dealloc(dwarf, srcfiles, DW_DLA_LIST); 3345 | } 3346 | } 3347 | return file; 3348 | } 3349 | 3350 | Dwarf_Die find_die(dwarf_fileobject &fobj, Dwarf_Addr addr) { 3351 | // Let's get to work! First see if we have a debug_aranges section so 3352 | // we can speed up the search 3353 | 3354 | Dwarf_Debug dwarf = fobj.dwarf_handle.get(); 3355 | Dwarf_Error error = DW_DLE_NE; 3356 | Dwarf_Arange *aranges; 3357 | Dwarf_Signed arange_count; 3358 | 3359 | Dwarf_Die returnDie; 3360 | bool found = false; 3361 | if (dwarf_get_aranges(dwarf, &aranges, &arange_count, &error) != 3362 | DW_DLV_OK) { 3363 | aranges = NULL; 3364 | } 3365 | 3366 | if (aranges) { 3367 | // We have aranges. Get the one where our address is. 3368 | Dwarf_Arange arange; 3369 | if (dwarf_get_arange(aranges, arange_count, addr, &arange, &error) == 3370 | DW_DLV_OK) { 3371 | 3372 | // We found our address. Get the compilation-unit DIE offset 3373 | // represented by the given address range. 3374 | Dwarf_Off cu_die_offset; 3375 | if (dwarf_get_cu_die_offset(arange, &cu_die_offset, &error) == 3376 | DW_DLV_OK) { 3377 | // Get the DIE at the offset returned by the aranges search. 3378 | // We set is_info to 1 to specify that the offset is from 3379 | // the .debug_info section (and not .debug_types) 3380 | int dwarf_result = 3381 | dwarf_offdie_b(dwarf, cu_die_offset, 1, &returnDie, &error); 3382 | 3383 | found = dwarf_result == DW_DLV_OK; 3384 | } 3385 | dwarf_dealloc(dwarf, arange, DW_DLA_ARANGE); 3386 | } 3387 | } 3388 | 3389 | if (found) 3390 | return returnDie; // The caller is responsible for freeing the die 3391 | 3392 | // The search for aranges failed. Try to find our address by scanning 3393 | // all compilation units. 3394 | Dwarf_Unsigned next_cu_header; 3395 | Dwarf_Half tag = 0; 3396 | returnDie = 0; 3397 | 3398 | while (!found && 3399 | dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3400 | &next_cu_header, 0, &error) == DW_DLV_OK) { 3401 | 3402 | if (returnDie) 3403 | dwarf_dealloc(dwarf, returnDie, DW_DLA_DIE); 3404 | 3405 | if (dwarf_siblingof(dwarf, 0, &returnDie, &error) == DW_DLV_OK) { 3406 | if ((dwarf_tag(returnDie, &tag, &error) == DW_DLV_OK) && 3407 | tag == DW_TAG_compile_unit) { 3408 | if (die_has_pc(fobj, returnDie, addr)) { 3409 | found = true; 3410 | } 3411 | } 3412 | } 3413 | } 3414 | 3415 | if (found) { 3416 | while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3417 | &next_cu_header, 0, &error) == DW_DLV_OK) { 3418 | // Reset the cu header state. Libdwarf's next_cu_header API 3419 | // keeps its own iterator per Dwarf_Debug that can't be reset. 3420 | // We need to keep fetching elements until the end. 3421 | } 3422 | } 3423 | 3424 | if (found) 3425 | return returnDie; 3426 | 3427 | // We couldn't find any compilation units with ranges or a high/low pc. 3428 | // Try again by looking at all DIEs in all compilation units. 3429 | Dwarf_Die cudie; 3430 | while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3431 | &next_cu_header, 0, &error) == DW_DLV_OK) { 3432 | if (dwarf_siblingof(dwarf, 0, &cudie, &error) == DW_DLV_OK) { 3433 | Dwarf_Die die_mem = 0; 3434 | Dwarf_Die resultDie = find_fundie_by_pc(fobj, cudie, addr, die_mem); 3435 | 3436 | if (resultDie) { 3437 | found = true; 3438 | break; 3439 | } 3440 | } 3441 | } 3442 | 3443 | if (found) { 3444 | while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3445 | &next_cu_header, 0, &error) == DW_DLV_OK) { 3446 | // Reset the cu header state. Libdwarf's next_cu_header API 3447 | // keeps its own iterator per Dwarf_Debug that can't be reset. 3448 | // We need to keep fetching elements until the end. 3449 | } 3450 | } 3451 | 3452 | if (found) 3453 | return cudie; 3454 | 3455 | // We failed. 3456 | return NULL; 3457 | } 3458 | }; 3459 | #endif // BACKWARD_HAS_DWARF == 1 3460 | 3461 | template <> 3462 | class TraceResolverImpl 3463 | : public TraceResolverLinuxImpl {}; 3464 | 3465 | #endif // BACKWARD_SYSTEM_LINUX 3466 | 3467 | #ifdef BACKWARD_SYSTEM_DARWIN 3468 | 3469 | template class TraceResolverDarwinImpl; 3470 | 3471 | template <> 3472 | class TraceResolverDarwinImpl 3473 | : public TraceResolverImplBase { 3474 | public: 3475 | void load_addresses(void *const*addresses, int address_count) override { 3476 | if (address_count == 0) { 3477 | return; 3478 | } 3479 | _symbols.reset(backtrace_symbols(addresses, address_count)); 3480 | } 3481 | 3482 | ResolvedTrace resolve(ResolvedTrace trace) override { 3483 | // parse: 3484 | // + 3485 | char *filename = _symbols[trace.idx]; 3486 | 3487 | // skip " " 3488 | while (*filename && *filename != ' ') 3489 | filename++; 3490 | while (*filename == ' ') 3491 | filename++; 3492 | 3493 | // find start of from end ( may contain a space) 3494 | char *p = filename + strlen(filename) - 1; 3495 | // skip to start of " + " 3496 | while (p > filename && *p != ' ') 3497 | p--; 3498 | while (p > filename && *p == ' ') 3499 | p--; 3500 | while (p > filename && *p != ' ') 3501 | p--; 3502 | while (p > filename && *p == ' ') 3503 | p--; 3504 | char *funcname_end = p + 1; 3505 | 3506 | // skip to start of "" 3507 | while (p > filename && *p != ' ') 3508 | p--; 3509 | char *funcname = p + 1; 3510 | 3511 | // skip to start of " " 3512 | while (p > filename && *p == ' ') 3513 | p--; 3514 | while (p > filename && *p != ' ') 3515 | p--; 3516 | while (p > filename && *p == ' ') 3517 | p--; 3518 | 3519 | // skip "", handling the case where it contains a 3520 | char *filename_end = p + 1; 3521 | if (p == filename) { 3522 | // something went wrong, give up 3523 | filename_end = filename + strlen(filename); 3524 | funcname = filename_end; 3525 | } 3526 | trace.object_filename.assign( 3527 | filename, filename_end); // ok even if filename_end is the ending \0 3528 | // (then we assign entire string) 3529 | 3530 | if (*funcname) { // if it's not end of string 3531 | *funcname_end = '\0'; 3532 | 3533 | trace.object_function = this->demangle(funcname); 3534 | trace.object_function += " "; 3535 | trace.object_function += (funcname_end + 1); 3536 | trace.source.function = trace.object_function; // we cannot do better. 3537 | } 3538 | return trace; 3539 | } 3540 | 3541 | private: 3542 | details::handle _symbols; 3543 | }; 3544 | 3545 | template <> 3546 | class TraceResolverImpl 3547 | : public TraceResolverDarwinImpl {}; 3548 | 3549 | #endif // BACKWARD_SYSTEM_DARWIN 3550 | 3551 | #ifdef BACKWARD_SYSTEM_WINDOWS 3552 | 3553 | // Load all symbol info 3554 | // Based on: 3555 | // https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 3556 | 3557 | struct module_data { 3558 | std::string image_name; 3559 | std::string module_name; 3560 | void *base_address; 3561 | DWORD load_size; 3562 | }; 3563 | 3564 | class get_mod_info { 3565 | HANDLE process; 3566 | static const int buffer_length = 4096; 3567 | 3568 | public: 3569 | get_mod_info(HANDLE h) : process(h) {} 3570 | 3571 | module_data operator()(HMODULE module) { 3572 | module_data ret; 3573 | char temp[buffer_length]; 3574 | MODULEINFO mi; 3575 | 3576 | GetModuleInformation(process, module, &mi, sizeof(mi)); 3577 | ret.base_address = mi.lpBaseOfDll; 3578 | ret.load_size = mi.SizeOfImage; 3579 | 3580 | GetModuleFileNameExA(process, module, temp, sizeof(temp)); 3581 | ret.image_name = temp; 3582 | GetModuleBaseNameA(process, module, temp, sizeof(temp)); 3583 | ret.module_name = temp; 3584 | std::vector img(ret.image_name.begin(), ret.image_name.end()); 3585 | std::vector mod(ret.module_name.begin(), ret.module_name.end()); 3586 | SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, 3587 | ret.load_size); 3588 | return ret; 3589 | } 3590 | }; 3591 | 3592 | template <> class TraceResolverImpl 3593 | : public TraceResolverImplBase { 3594 | public: 3595 | TraceResolverImpl() { 3596 | 3597 | HANDLE process = GetCurrentProcess(); 3598 | 3599 | std::vector modules; 3600 | DWORD cbNeeded; 3601 | std::vector module_handles(1); 3602 | SymInitialize(process, NULL, false); 3603 | DWORD symOptions = SymGetOptions(); 3604 | symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME; 3605 | SymSetOptions(symOptions); 3606 | EnumProcessModules(process, &module_handles[0], 3607 | module_handles.size() * sizeof(HMODULE), &cbNeeded); 3608 | module_handles.resize(cbNeeded / sizeof(HMODULE)); 3609 | EnumProcessModules(process, &module_handles[0], 3610 | module_handles.size() * sizeof(HMODULE), &cbNeeded); 3611 | std::transform(module_handles.begin(), module_handles.end(), 3612 | std::back_inserter(modules), get_mod_info(process)); 3613 | void *base = modules[0].base_address; 3614 | IMAGE_NT_HEADERS *h = ImageNtHeader(base); 3615 | image_type = h->FileHeader.Machine; 3616 | } 3617 | 3618 | static const int max_sym_len = 255; 3619 | struct symbol_t { 3620 | SYMBOL_INFO sym; 3621 | char buffer[max_sym_len]; 3622 | } sym; 3623 | 3624 | DWORD64 displacement; 3625 | 3626 | ResolvedTrace resolve(ResolvedTrace t) override { 3627 | HANDLE process = GetCurrentProcess(); 3628 | 3629 | char name[256]; 3630 | 3631 | memset(&sym, 0, sizeof(sym)); 3632 | sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO); 3633 | sym.sym.MaxNameLen = max_sym_len; 3634 | 3635 | if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) { 3636 | // TODO: error handling everywhere 3637 | char* lpMsgBuf; 3638 | DWORD dw = GetLastError(); 3639 | 3640 | FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | 3641 | FORMAT_MESSAGE_FROM_SYSTEM | 3642 | FORMAT_MESSAGE_IGNORE_INSERTS, 3643 | NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 3644 | (char*)&lpMsgBuf, 0, NULL); 3645 | 3646 | printf(lpMsgBuf); 3647 | 3648 | // abort(); 3649 | } 3650 | UnDecorateSymbolName(sym.sym.Name, (PSTR)name, 256, UNDNAME_COMPLETE); 3651 | 3652 | DWORD offset = 0; 3653 | IMAGEHLP_LINE line; 3654 | if (SymGetLineFromAddr(process, (ULONG64)t.addr, &offset, &line)) { 3655 | t.object_filename = line.FileName; 3656 | t.source.filename = line.FileName; 3657 | t.source.line = line.LineNumber; 3658 | t.source.col = offset; 3659 | } 3660 | 3661 | t.source.function = name; 3662 | t.object_filename = ""; 3663 | t.object_function = name; 3664 | 3665 | return t; 3666 | } 3667 | 3668 | DWORD machine_type() const { return image_type; } 3669 | 3670 | private: 3671 | DWORD image_type; 3672 | }; 3673 | 3674 | #endif 3675 | 3676 | class TraceResolver : public TraceResolverImpl {}; 3677 | 3678 | /*************** CODE SNIPPET ***************/ 3679 | 3680 | class SourceFile { 3681 | public: 3682 | typedef std::vector> lines_t; 3683 | 3684 | SourceFile() {} 3685 | SourceFile(const std::string &path) { 3686 | // 1. If BACKWARD_CXX_SOURCE_PREFIXES is set then assume it contains 3687 | // a colon-separated list of path prefixes. Try prepending each 3688 | // to the given path until a valid file is found. 3689 | const std::vector &prefixes = get_paths_from_env_variable(); 3690 | for (size_t i = 0; i < prefixes.size(); ++i) { 3691 | // Double slashes (//) should not be a problem. 3692 | std::string new_path = prefixes[i] + '/' + path; 3693 | _file.reset(new std::ifstream(new_path.c_str())); 3694 | if (is_open()) 3695 | break; 3696 | } 3697 | // 2. If no valid file found then fallback to opening the path as-is. 3698 | if (!_file || !is_open()) { 3699 | _file.reset(new std::ifstream(path.c_str())); 3700 | } 3701 | } 3702 | bool is_open() const { return _file->is_open(); } 3703 | 3704 | lines_t &get_lines(unsigned line_start, unsigned line_count, lines_t &lines) { 3705 | using namespace std; 3706 | // This function make uses of the dumbest algo ever: 3707 | // 1) seek(0) 3708 | // 2) read lines one by one and discard until line_start 3709 | // 3) read line one by one until line_start + line_count 3710 | // 3711 | // If you are getting snippets many time from the same file, it is 3712 | // somewhat a waste of CPU, feel free to benchmark and propose a 3713 | // better solution ;) 3714 | 3715 | _file->clear(); 3716 | _file->seekg(0); 3717 | string line; 3718 | unsigned line_idx; 3719 | 3720 | for (line_idx = 1; line_idx < line_start; ++line_idx) { 3721 | std::getline(*_file, line); 3722 | if (!*_file) { 3723 | return lines; 3724 | } 3725 | } 3726 | 3727 | // think of it like a lambda in C++98 ;) 3728 | // but look, I will reuse it two times! 3729 | // What a good boy am I. 3730 | struct isspace { 3731 | bool operator()(char c) { return std::isspace(c); } 3732 | }; 3733 | 3734 | bool started = false; 3735 | for (; line_idx < line_start + line_count; ++line_idx) { 3736 | getline(*_file, line); 3737 | if (!*_file) { 3738 | return lines; 3739 | } 3740 | if (!started) { 3741 | if (std::find_if(line.begin(), line.end(), not_isspace()) == line.end()) 3742 | continue; 3743 | started = true; 3744 | } 3745 | lines.push_back(make_pair(line_idx, line)); 3746 | } 3747 | 3748 | lines.erase( 3749 | std::find_if(lines.rbegin(), lines.rend(), not_isempty()).base(), 3750 | lines.end()); 3751 | return lines; 3752 | } 3753 | 3754 | lines_t get_lines(unsigned line_start, unsigned line_count) { 3755 | lines_t lines; 3756 | return get_lines(line_start, line_count, lines); 3757 | } 3758 | 3759 | // there is no find_if_not in C++98, lets do something crappy to 3760 | // workaround. 3761 | struct not_isspace { 3762 | bool operator()(char c) { return !std::isspace(c); } 3763 | }; 3764 | // and define this one here because C++98 is not happy with local defined 3765 | // struct passed to template functions, fuuuu. 3766 | struct not_isempty { 3767 | bool operator()(const lines_t::value_type &p) { 3768 | return !(std::find_if(p.second.begin(), p.second.end(), not_isspace()) == 3769 | p.second.end()); 3770 | } 3771 | }; 3772 | 3773 | void swap(SourceFile &b) { _file.swap(b._file); } 3774 | 3775 | #ifdef BACKWARD_ATLEAST_CXX11 3776 | SourceFile(SourceFile &&from) : _file(nullptr) { swap(from); } 3777 | SourceFile &operator=(SourceFile &&from) { 3778 | swap(from); 3779 | return *this; 3780 | } 3781 | #else 3782 | explicit SourceFile(const SourceFile &from) { 3783 | // some sort of poor man's move semantic. 3784 | swap(const_cast(from)); 3785 | } 3786 | SourceFile &operator=(const SourceFile &from) { 3787 | // some sort of poor man's move semantic. 3788 | swap(const_cast(from)); 3789 | return *this; 3790 | } 3791 | #endif 3792 | 3793 | private: 3794 | details::handle> 3795 | _file; 3796 | 3797 | std::vector get_paths_from_env_variable_impl() { 3798 | std::vector paths; 3799 | const char *prefixes_str = std::getenv("BACKWARD_CXX_SOURCE_PREFIXES"); 3800 | if (prefixes_str && prefixes_str[0]) { 3801 | paths = details::split_source_prefixes(prefixes_str); 3802 | } 3803 | return paths; 3804 | } 3805 | 3806 | const std::vector &get_paths_from_env_variable() { 3807 | static std::vector paths = get_paths_from_env_variable_impl(); 3808 | return paths; 3809 | } 3810 | 3811 | #ifdef BACKWARD_ATLEAST_CXX11 3812 | SourceFile(const SourceFile &) = delete; 3813 | SourceFile &operator=(const SourceFile &) = delete; 3814 | #endif 3815 | }; 3816 | 3817 | class SnippetFactory { 3818 | public: 3819 | typedef SourceFile::lines_t lines_t; 3820 | 3821 | lines_t get_snippet(const std::string &filename, unsigned line_start, 3822 | unsigned context_size) { 3823 | 3824 | SourceFile &src_file = get_src_file(filename); 3825 | unsigned start = line_start - context_size / 2; 3826 | return src_file.get_lines(start, context_size); 3827 | } 3828 | 3829 | lines_t get_combined_snippet(const std::string &filename_a, unsigned line_a, 3830 | const std::string &filename_b, unsigned line_b, 3831 | unsigned context_size) { 3832 | SourceFile &src_file_a = get_src_file(filename_a); 3833 | SourceFile &src_file_b = get_src_file(filename_b); 3834 | 3835 | lines_t lines = 3836 | src_file_a.get_lines(line_a - context_size / 4, context_size / 2); 3837 | src_file_b.get_lines(line_b - context_size / 4, context_size / 2, lines); 3838 | return lines; 3839 | } 3840 | 3841 | lines_t get_coalesced_snippet(const std::string &filename, unsigned line_a, 3842 | unsigned line_b, unsigned context_size) { 3843 | SourceFile &src_file = get_src_file(filename); 3844 | 3845 | using std::max; 3846 | using std::min; 3847 | unsigned a = min(line_a, line_b); 3848 | unsigned b = max(line_a, line_b); 3849 | 3850 | if ((b - a) < (context_size / 3)) { 3851 | return src_file.get_lines((a + b - context_size + 1) / 2, context_size); 3852 | } 3853 | 3854 | lines_t lines = src_file.get_lines(a - context_size / 4, context_size / 2); 3855 | src_file.get_lines(b - context_size / 4, context_size / 2, lines); 3856 | return lines; 3857 | } 3858 | 3859 | private: 3860 | typedef details::hashtable::type src_files_t; 3861 | src_files_t _src_files; 3862 | 3863 | SourceFile &get_src_file(const std::string &filename) { 3864 | src_files_t::iterator it = _src_files.find(filename); 3865 | if (it != _src_files.end()) { 3866 | return it->second; 3867 | } 3868 | SourceFile &new_src_file = _src_files[filename]; 3869 | new_src_file = SourceFile(filename); 3870 | return new_src_file; 3871 | } 3872 | }; 3873 | 3874 | /*************** PRINTER ***************/ 3875 | 3876 | namespace ColorMode { 3877 | enum type { automatic, never, always }; 3878 | } 3879 | 3880 | class cfile_streambuf : public std::streambuf { 3881 | public: 3882 | cfile_streambuf(FILE *_sink) : sink(_sink) {} 3883 | int_type underflow() override { return traits_type::eof(); } 3884 | int_type overflow(int_type ch) override { 3885 | if (traits_type::not_eof(ch) && fputc(ch, sink) != EOF) { 3886 | return ch; 3887 | } 3888 | return traits_type::eof(); 3889 | } 3890 | 3891 | std::streamsize xsputn(const char_type *s, std::streamsize count) override { 3892 | return static_cast( 3893 | fwrite(s, sizeof *s, static_cast(count), sink)); 3894 | } 3895 | 3896 | #ifdef BACKWARD_ATLEAST_CXX11 3897 | public: 3898 | cfile_streambuf(const cfile_streambuf &) = delete; 3899 | cfile_streambuf &operator=(const cfile_streambuf &) = delete; 3900 | #else 3901 | private: 3902 | cfile_streambuf(const cfile_streambuf &); 3903 | cfile_streambuf &operator=(const cfile_streambuf &); 3904 | #endif 3905 | 3906 | private: 3907 | FILE *sink; 3908 | std::vector buffer; 3909 | }; 3910 | 3911 | #ifdef BACKWARD_SYSTEM_LINUX 3912 | 3913 | namespace Color { 3914 | enum type { yellow = 33, purple = 35, reset = 39 }; 3915 | } // namespace Color 3916 | 3917 | class Colorize { 3918 | public: 3919 | Colorize(std::ostream &os) : _os(os), _reset(false), _enabled(false) {} 3920 | 3921 | void activate(ColorMode::type mode) { _enabled = mode == ColorMode::always; } 3922 | 3923 | void activate(ColorMode::type mode, FILE *fp) { activate(mode, fileno(fp)); } 3924 | 3925 | void set_color(Color::type ccode) { 3926 | if (!_enabled) 3927 | return; 3928 | 3929 | // I assume that the terminal can handle basic colors. Seriously I 3930 | // don't want to deal with all the termcap shit. 3931 | _os << "\033[" << static_cast(ccode) << "m"; 3932 | _reset = (ccode != Color::reset); 3933 | } 3934 | 3935 | ~Colorize() { 3936 | if (_reset) { 3937 | set_color(Color::reset); 3938 | } 3939 | } 3940 | 3941 | private: 3942 | void activate(ColorMode::type mode, int fd) { 3943 | activate(mode == ColorMode::automatic && isatty(fd) ? ColorMode::always 3944 | : mode); 3945 | } 3946 | 3947 | std::ostream &_os; 3948 | bool _reset; 3949 | bool _enabled; 3950 | }; 3951 | 3952 | #else // ndef BACKWARD_SYSTEM_LINUX 3953 | 3954 | namespace Color { 3955 | enum type { yellow = 0, purple = 0, reset = 0 }; 3956 | } // namespace Color 3957 | 3958 | class Colorize { 3959 | public: 3960 | Colorize(std::ostream &) {} 3961 | void activate(ColorMode::type) {} 3962 | void activate(ColorMode::type, FILE *) {} 3963 | void set_color(Color::type) {} 3964 | }; 3965 | 3966 | #endif // BACKWARD_SYSTEM_LINUX 3967 | 3968 | class Printer { 3969 | public: 3970 | bool snippet; 3971 | ColorMode::type color_mode; 3972 | bool address; 3973 | bool object; 3974 | int inliner_context_size; 3975 | int trace_context_size; 3976 | 3977 | Printer() 3978 | : snippet(true), color_mode(ColorMode::automatic), address(false), 3979 | object(false), inliner_context_size(5), trace_context_size(7) {} 3980 | 3981 | template FILE *print(ST &st, FILE *fp = stderr) { 3982 | cfile_streambuf obuf(fp); 3983 | std::ostream os(&obuf); 3984 | Colorize colorize(os); 3985 | colorize.activate(color_mode, fp); 3986 | print_stacktrace(st, os, colorize); 3987 | return fp; 3988 | } 3989 | 3990 | template std::ostream &print(ST &st, std::ostream &os) { 3991 | Colorize colorize(os); 3992 | colorize.activate(color_mode); 3993 | print_stacktrace(st, os, colorize); 3994 | return os; 3995 | } 3996 | 3997 | template 3998 | FILE *print(IT begin, IT end, FILE *fp = stderr, size_t thread_id = 0) { 3999 | cfile_streambuf obuf(fp); 4000 | std::ostream os(&obuf); 4001 | Colorize colorize(os); 4002 | colorize.activate(color_mode, fp); 4003 | print_stacktrace(begin, end, os, thread_id, colorize); 4004 | return fp; 4005 | } 4006 | 4007 | template 4008 | std::ostream &print(IT begin, IT end, std::ostream &os, 4009 | size_t thread_id = 0) { 4010 | Colorize colorize(os); 4011 | colorize.activate(color_mode); 4012 | print_stacktrace(begin, end, os, thread_id, colorize); 4013 | return os; 4014 | } 4015 | 4016 | TraceResolver const &resolver() const { return _resolver; } 4017 | 4018 | private: 4019 | TraceResolver _resolver; 4020 | SnippetFactory _snippets; 4021 | 4022 | template 4023 | void print_stacktrace(ST &st, std::ostream &os, Colorize &colorize) { 4024 | print_header(os, st.thread_id()); 4025 | _resolver.load_stacktrace(st); 4026 | for (size_t trace_idx = st.size(); trace_idx > 0; --trace_idx) { 4027 | print_trace(os, _resolver.resolve(st[trace_idx - 1]), colorize); 4028 | } 4029 | } 4030 | 4031 | template 4032 | void print_stacktrace(IT begin, IT end, std::ostream &os, size_t thread_id, 4033 | Colorize &colorize) { 4034 | print_header(os, thread_id); 4035 | for (; begin != end; ++begin) { 4036 | print_trace(os, *begin, colorize); 4037 | } 4038 | } 4039 | 4040 | void print_header(std::ostream &os, size_t thread_id) { 4041 | os << "Stack trace (most recent call last)"; 4042 | if (thread_id) { 4043 | os << " in thread " << thread_id; 4044 | } 4045 | os << ":\n"; 4046 | } 4047 | 4048 | void print_trace(std::ostream &os, const ResolvedTrace &trace, 4049 | Colorize &colorize) { 4050 | os << "#" << std::left << std::setw(2) << trace.idx << std::right; 4051 | bool already_indented = true; 4052 | 4053 | if (!trace.source.filename.size() || object) { 4054 | os << " Object \"" << trace.object_filename << "\", at " << trace.addr 4055 | << ", in " << trace.object_function << "\n"; 4056 | already_indented = false; 4057 | } 4058 | 4059 | for (size_t inliner_idx = trace.inliners.size(); inliner_idx > 0; 4060 | --inliner_idx) { 4061 | if (!already_indented) { 4062 | os << " "; 4063 | } 4064 | const ResolvedTrace::SourceLoc &inliner_loc = 4065 | trace.inliners[inliner_idx - 1]; 4066 | print_source_loc(os, " | ", inliner_loc); 4067 | if (snippet) { 4068 | print_snippet(os, " | ", inliner_loc, colorize, Color::purple, 4069 | inliner_context_size); 4070 | } 4071 | already_indented = false; 4072 | } 4073 | 4074 | if (trace.source.filename.size()) { 4075 | if (!already_indented) { 4076 | os << " "; 4077 | } 4078 | print_source_loc(os, " ", trace.source, trace.addr); 4079 | if (snippet) { 4080 | print_snippet(os, " ", trace.source, colorize, Color::yellow, 4081 | trace_context_size); 4082 | } 4083 | } 4084 | } 4085 | 4086 | void print_snippet(std::ostream &os, const char *indent, 4087 | const ResolvedTrace::SourceLoc &source_loc, 4088 | Colorize &colorize, Color::type color_code, 4089 | int context_size) { 4090 | using namespace std; 4091 | typedef SnippetFactory::lines_t lines_t; 4092 | 4093 | lines_t lines = _snippets.get_snippet(source_loc.filename, source_loc.line, 4094 | static_cast(context_size)); 4095 | 4096 | for (lines_t::const_iterator it = lines.begin(); it != lines.end(); ++it) { 4097 | if (it->first == source_loc.line) { 4098 | colorize.set_color(color_code); 4099 | os << indent << ">"; 4100 | } else { 4101 | os << indent << " "; 4102 | } 4103 | os << std::setw(4) << it->first << ": " << it->second << "\n"; 4104 | if (it->first == source_loc.line) { 4105 | colorize.set_color(Color::reset); 4106 | } 4107 | } 4108 | } 4109 | 4110 | void print_source_loc(std::ostream &os, const char *indent, 4111 | const ResolvedTrace::SourceLoc &source_loc, 4112 | void *addr = nullptr) { 4113 | os << indent << "Source \"" << source_loc.filename << "\", line " 4114 | << source_loc.line << ", in " << source_loc.function; 4115 | 4116 | if (address && addr != nullptr) { 4117 | os << " [" << addr << "]"; 4118 | } 4119 | os << "\n"; 4120 | } 4121 | }; 4122 | 4123 | /*************** SIGNALS HANDLING ***************/ 4124 | 4125 | #if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) 4126 | 4127 | class SignalHandling { 4128 | public: 4129 | static std::vector make_default_signals() { 4130 | const int posix_signals[] = { 4131 | // Signals for which the default action is "Core". 4132 | SIGABRT, // Abort signal from abort(3) 4133 | SIGBUS, // Bus error (bad memory access) 4134 | SIGFPE, // Floating point exception 4135 | SIGILL, // Illegal Instruction 4136 | SIGIOT, // IOT trap. A synonym for SIGABRT 4137 | SIGQUIT, // Quit from keyboard 4138 | SIGSEGV, // Invalid memory reference 4139 | SIGSYS, // Bad argument to routine (SVr4) 4140 | SIGTRAP, // Trace/breakpoint trap 4141 | SIGXCPU, // CPU time limit exceeded (4.2BSD) 4142 | SIGXFSZ, // File size limit exceeded (4.2BSD) 4143 | #if defined(BACKWARD_SYSTEM_DARWIN) 4144 | SIGEMT, // emulation instruction executed 4145 | #endif 4146 | }; 4147 | return std::vector(posix_signals, 4148 | posix_signals + 4149 | sizeof posix_signals / sizeof posix_signals[0]); 4150 | } 4151 | 4152 | SignalHandling(const std::vector &posix_signals = make_default_signals()) 4153 | : _loaded(false) { 4154 | bool success = true; 4155 | 4156 | const size_t stack_size = 1024 * 1024 * 8; 4157 | _stack_content.reset(static_cast(malloc(stack_size))); 4158 | if (_stack_content) { 4159 | stack_t ss; 4160 | ss.ss_sp = _stack_content.get(); 4161 | ss.ss_size = stack_size; 4162 | ss.ss_flags = 0; 4163 | if (sigaltstack(&ss, nullptr) < 0) { 4164 | success = false; 4165 | } 4166 | } else { 4167 | success = false; 4168 | } 4169 | 4170 | for (size_t i = 0; i < posix_signals.size(); ++i) { 4171 | struct sigaction action; 4172 | memset(&action, 0, sizeof action); 4173 | action.sa_flags = 4174 | static_cast(SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND); 4175 | sigfillset(&action.sa_mask); 4176 | sigdelset(&action.sa_mask, posix_signals[i]); 4177 | #if defined(__clang__) 4178 | #pragma clang diagnostic push 4179 | #pragma clang diagnostic ignored "-Wdisabled-macro-expansion" 4180 | #endif 4181 | action.sa_sigaction = &sig_handler; 4182 | #if defined(__clang__) 4183 | #pragma clang diagnostic pop 4184 | #endif 4185 | 4186 | int r = sigaction(posix_signals[i], &action, nullptr); 4187 | if (r < 0) 4188 | success = false; 4189 | } 4190 | 4191 | _loaded = success; 4192 | } 4193 | 4194 | bool loaded() const { return _loaded; } 4195 | 4196 | static void handleSignal(int, siginfo_t *info, void *_ctx) { 4197 | ucontext_t *uctx = static_cast(_ctx); 4198 | 4199 | StackTrace st; 4200 | void *error_addr = nullptr; 4201 | #ifdef REG_RIP // x86_64 4202 | error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); 4203 | #elif defined(REG_EIP) // x86_32 4204 | error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); 4205 | #elif defined(__arm__) 4206 | error_addr = reinterpret_cast(uctx->uc_mcontext.arm_pc); 4207 | #elif defined(__aarch64__) 4208 | #if defined(__APPLE__) 4209 | error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__pc); 4210 | #else 4211 | error_addr = reinterpret_cast(uctx->uc_mcontext.pc); 4212 | #endif 4213 | #elif defined(__mips__) 4214 | error_addr = reinterpret_cast( 4215 | reinterpret_cast(&uctx->uc_mcontext)->sc_pc); 4216 | #elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ 4217 | defined(__POWERPC__) 4218 | error_addr = reinterpret_cast(uctx->uc_mcontext.regs->nip); 4219 | #elif defined(__riscv) 4220 | error_addr = reinterpret_cast(uctx->uc_mcontext.__gregs[REG_PC]); 4221 | #elif defined(__s390x__) 4222 | error_addr = reinterpret_cast(uctx->uc_mcontext.psw.addr); 4223 | #elif defined(__APPLE__) && defined(__x86_64__) 4224 | error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__rip); 4225 | #elif defined(__APPLE__) 4226 | error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__eip); 4227 | #else 4228 | #warning ":/ sorry, ain't know no nothing none not of your architecture!" 4229 | #endif 4230 | if (error_addr) { 4231 | st.load_from(error_addr, 32, reinterpret_cast(uctx), 4232 | info->si_addr); 4233 | } else { 4234 | st.load_here(32, reinterpret_cast(uctx), info->si_addr); 4235 | } 4236 | 4237 | Printer printer; 4238 | printer.address = true; 4239 | printer.print(st, stderr); 4240 | 4241 | #if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L 4242 | psiginfo(info, nullptr); 4243 | #else 4244 | (void)info; 4245 | #endif 4246 | } 4247 | 4248 | private: 4249 | details::handle _stack_content; 4250 | bool _loaded; 4251 | 4252 | #ifdef __GNUC__ 4253 | __attribute__((noreturn)) 4254 | #endif 4255 | static void 4256 | sig_handler(int signo, siginfo_t *info, void *_ctx) { 4257 | handleSignal(signo, info, _ctx); 4258 | 4259 | // try to forward the signal. 4260 | raise(info->si_signo); 4261 | 4262 | // terminate the process immediately. 4263 | puts("watf? exit"); 4264 | _exit(EXIT_FAILURE); 4265 | } 4266 | }; 4267 | 4268 | #endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN 4269 | 4270 | #ifdef BACKWARD_SYSTEM_WINDOWS 4271 | 4272 | class SignalHandling { 4273 | public: 4274 | SignalHandling(const std::vector & = std::vector()) 4275 | : reporter_thread_([]() { 4276 | /* We handle crashes in a utility thread: 4277 | backward structures and some Windows functions called here 4278 | need stack space, which we do not have when we encounter a 4279 | stack overflow. 4280 | To support reporting stack traces during a stack overflow, 4281 | we create a utility thread at startup, which waits until a 4282 | crash happens or the program exits normally. */ 4283 | 4284 | { 4285 | std::unique_lock lk(mtx()); 4286 | cv().wait(lk, [] { return crashed() != crash_status::running; }); 4287 | } 4288 | if (crashed() == crash_status::crashed) { 4289 | handle_stacktrace(skip_recs()); 4290 | } 4291 | { 4292 | std::unique_lock lk(mtx()); 4293 | crashed() = crash_status::ending; 4294 | } 4295 | cv().notify_one(); 4296 | }) { 4297 | SetUnhandledExceptionFilter(crash_handler); 4298 | 4299 | signal(SIGABRT, signal_handler); 4300 | _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); 4301 | 4302 | std::set_terminate(&terminator); 4303 | #ifndef BACKWARD_ATLEAST_CXX17 4304 | std::set_unexpected(&terminator); 4305 | #endif 4306 | _set_purecall_handler(&terminator); 4307 | _set_invalid_parameter_handler(&invalid_parameter_handler); 4308 | } 4309 | bool loaded() const { return true; } 4310 | 4311 | ~SignalHandling() { 4312 | { 4313 | std::unique_lock lk(mtx()); 4314 | crashed() = crash_status::normal_exit; 4315 | } 4316 | 4317 | cv().notify_one(); 4318 | 4319 | reporter_thread_.join(); 4320 | } 4321 | 4322 | private: 4323 | static CONTEXT *ctx() { 4324 | static CONTEXT data; 4325 | return &data; 4326 | } 4327 | 4328 | enum class crash_status { running, crashed, normal_exit, ending }; 4329 | 4330 | static crash_status &crashed() { 4331 | static crash_status data; 4332 | return data; 4333 | } 4334 | 4335 | static std::mutex &mtx() { 4336 | static std::mutex data; 4337 | return data; 4338 | } 4339 | 4340 | static std::condition_variable &cv() { 4341 | static std::condition_variable data; 4342 | return data; 4343 | } 4344 | 4345 | static HANDLE &thread_handle() { 4346 | static HANDLE handle; 4347 | return handle; 4348 | } 4349 | 4350 | std::thread reporter_thread_; 4351 | 4352 | // TODO: how not to hardcode these? 4353 | static const constexpr int signal_skip_recs = 4354 | #ifdef __clang__ 4355 | // With clang, RtlCaptureContext also captures the stack frame of the 4356 | // current function Below that, there ar 3 internal Windows functions 4357 | 4 4358 | #else 4359 | // With MSVC cl, RtlCaptureContext misses the stack frame of the current 4360 | // function The first entries during StackWalk are the 3 internal Windows 4361 | // functions 4362 | 3 4363 | #endif 4364 | ; 4365 | 4366 | static int &skip_recs() { 4367 | static int data; 4368 | return data; 4369 | } 4370 | 4371 | static inline void terminator() { 4372 | crash_handler(signal_skip_recs); 4373 | abort(); 4374 | } 4375 | 4376 | static inline void signal_handler(int) { 4377 | crash_handler(signal_skip_recs); 4378 | abort(); 4379 | } 4380 | 4381 | static inline void __cdecl invalid_parameter_handler(const wchar_t *, 4382 | const wchar_t *, 4383 | const wchar_t *, 4384 | unsigned int, 4385 | uintptr_t) { 4386 | crash_handler(signal_skip_recs); 4387 | abort(); 4388 | } 4389 | 4390 | NOINLINE static LONG WINAPI crash_handler(EXCEPTION_POINTERS *info) { 4391 | // The exception info supplies a trace from exactly where the issue was, 4392 | // no need to skip records 4393 | crash_handler(0, info->ContextRecord); 4394 | return EXCEPTION_CONTINUE_SEARCH; 4395 | } 4396 | 4397 | NOINLINE static void crash_handler(int skip, CONTEXT *ct = nullptr) { 4398 | 4399 | if (ct == nullptr) { 4400 | RtlCaptureContext(ctx()); 4401 | } else { 4402 | memcpy(ctx(), ct, sizeof(CONTEXT)); 4403 | } 4404 | DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), 4405 | GetCurrentProcess(), &thread_handle(), 0, FALSE, 4406 | DUPLICATE_SAME_ACCESS); 4407 | 4408 | skip_recs() = skip; 4409 | 4410 | { 4411 | std::unique_lock lk(mtx()); 4412 | crashed() = crash_status::crashed; 4413 | } 4414 | 4415 | cv().notify_one(); 4416 | 4417 | { 4418 | std::unique_lock lk(mtx()); 4419 | cv().wait(lk, [] { return crashed() != crash_status::crashed; }); 4420 | } 4421 | } 4422 | 4423 | static void handle_stacktrace(int skip_frames = 0) { 4424 | // printer creates the TraceResolver, which can supply us a machine type 4425 | // for stack walking. Without this, StackTrace can only guess using some 4426 | // macros. 4427 | // StackTrace also requires that the PDBs are already loaded, which is done 4428 | // in the constructor of TraceResolver 4429 | Printer printer; 4430 | 4431 | StackTrace st; 4432 | st.set_machine_type(printer.resolver().machine_type()); 4433 | st.set_thread_handle(thread_handle()); 4434 | st.load_here(32 + skip_frames, ctx()); 4435 | st.skip_n_firsts(skip_frames); 4436 | 4437 | printer.address = true; 4438 | printer.print(st, std::cerr); 4439 | } 4440 | }; 4441 | 4442 | #endif // BACKWARD_SYSTEM_WINDOWS 4443 | 4444 | #ifdef BACKWARD_SYSTEM_UNKNOWN 4445 | 4446 | class SignalHandling { 4447 | public: 4448 | SignalHandling(const std::vector & = std::vector()) {} 4449 | bool init() { return false; } 4450 | bool loaded() { return false; } 4451 | }; 4452 | 4453 | #endif // BACKWARD_SYSTEM_UNKNOWN 4454 | 4455 | } // namespace backward 4456 | 4457 | #endif /* H_GUARD */ 4458 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | backward_ros 4 | 1.0.7 5 | The backward_ros package is a ros wrapper of backward-cpp from https://github.com/bombela/backward-cpp 6 | 7 | Victor López 8 | 9 | MIT 10 | 11 | https://github.com/pal-robotics/backward_ros 12 | 13 | cmake 14 | 15 | libdw-dev 16 | 17 | 18 | cmake 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/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 27 | 28 | namespace backward { 29 | 30 | backward::SignalHandling sh; 31 | 32 | } // namespace backward 33 | -------------------------------------------------------------------------------- /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 25 | #include 26 | #include "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 25 | 26 | #include 27 | #include 28 | #include "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 25 | #include 26 | #include "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 25 | 26 | #include 27 | #include 28 | #include "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 | volatile int zero = 0; 68 | 69 | int divide_by_zero() 70 | { 71 | std::cout << "And the wild black hole appears..." << std::endl; 72 | int v = 42 / zero; 73 | return v; 74 | } 75 | 76 | TEST_DIVZERO (divide_by_zero) 77 | { 78 | int v = divide_by_zero(); 79 | std::cout << "v=" << v << std::endl; 80 | } 81 | 82 | int bye_bye_stack(int i) { 83 | return bye_bye_stack(i + 1) + bye_bye_stack(i * 2); 84 | } 85 | 86 | TEST_SEGFAULT(stackoverflow) 87 | { 88 | struct rlimit limit; 89 | limit.rlim_max = 8096; 90 | setrlimit(RLIMIT_STACK, &limit); 91 | int r = bye_bye_stack(42); 92 | std::cout << "r=" << r << std::endl; 93 | } 94 | -------------------------------------------------------------------------------- /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.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 | --------------------------------------------------------------------------------