├── LICENSE ├── README.rst ├── Teuchos_ConfigDefs.hpp ├── c_stacktrace.cpp ├── c_stacktrace.h ├── compile ├── stacktrace.cpp ├── stacktrace.hpp ├── test1.cpp └── test2.c /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, 2011 Ondrej Certik 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of the Sandia Corporation nor the names of its contributors 13 | may be used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Stacktrace 2 | ========== 3 | 4 | This library allows you to print the stacktrace just like Python, with 5 | filenames, line numbers, function names and the line itself. 6 | 7 | It works for shared libraries too. 8 | 9 | License 10 | ------- 11 | 12 | Stacktrace is BSD licensed. See the LICENSE file for more details. 13 | 14 | Example of a stacktrace 15 | ----------------------- 16 | 17 | This example uses a shared library (and you can see that everything works):: 18 | 19 | Traceback (most recent call last): 20 | File "/build/buildd/eglibc-2.10.1/csu/../sysdeps/x86_64/elf/start.S", line 113, in _start 21 | File "/build/buildd/eglibc-2.10.1/csu/libc-start.c", line 220, in __libc_start_main 22 | File "/home/ondrej/repos/hermes2d/benchmarks/bessel/main.cpp", line 140, in main 23 | rs.assemble(); 24 | File "/home/ondrej/repos/hermes2d/src/refsystem.cpp", line 178, in RefSystem::assemble(bool) 25 | if (this->linear == true) LinSystem::assemble(rhsonly); 26 | File "/home/ondrej/repos/hermes2d/src/linsystem.cpp", line 618, in LinSystem::assemble(bool) 27 | bi = eval_form(jfv, NULL, fu, fv, &refmap[n], &refmap[m]) * an->coef[j] * am->coef[i]; 28 | File "/home/ondrej/repos/hermes2d/src/linsystem.cpp", line 837, in LinSystem::eval_form(WeakForm::JacFormVol*, Solution**, PrecalcShapeset*, PrecalcShapeset*, RefMap*, RefMap*) 29 | show_backtrace(); 30 | File "/home/ondrej/repos/cpp_stacktrace/backtrace-symbols.c", line 420, in show_backtrace() 31 | size = backtrace (array, 10); 32 | 33 | Prerequisites 34 | ------------- 35 | 36 | The only requirement is the ``binutils`` package. On Ubuntu, do:: 37 | 38 | sudo apt-get install binutils-dev 39 | 40 | Stacktrace is written in C++ and besides binutils doesn't have any other 41 | dependencies. C wrapper is included for usage in programs written in C. 42 | 43 | Usage 44 | ----- 45 | 46 | Include ``stacktrace.cpp`` and ``stacktrace.hpp`` in your projects, generate 47 | ``Teuchos_ConfigDefs.hpp`` (included from ``stacktrace.hpp``) using your 48 | configure scripts (e.g. cmake, autotools, ...), or just leave everything 49 | enabled in there. 50 | 51 | You can run the example ``compile`` script to compile tests. Consult it for 52 | examples how to use stacktrace in C programs. 53 | -------------------------------------------------------------------------------- /Teuchos_ConfigDefs.hpp: -------------------------------------------------------------------------------- 1 | #define HAVE_TEUCHOS_STACKTRACE 2 | #define HAVE_TEUCHOS_LINK 3 | #define HAVE_TEUCHOS_BFD 4 | -------------------------------------------------------------------------------- /c_stacktrace.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010, 2011 Ondrej Certik 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of the Sandia Corporation nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | // C interface to the stacktrace code 30 | 31 | #include 32 | #include "stacktrace.hpp" 33 | #include "c_stacktrace.h" 34 | 35 | void show_stacktrace() 36 | { 37 | const int impl_stacktrace_depth=1; 38 | std::cout << Teuchos::get_stacktrace(impl_stacktrace_depth); 39 | } 40 | 41 | void print_stack_on_segfault() 42 | { 43 | Teuchos::print_stack_on_segfault(); 44 | } 45 | -------------------------------------------------------------------------------- /c_stacktrace.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010, 2011 Ondrej Certik 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of the Sandia Corporation nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef TEUCHOS_STACKTRACE_H 30 | #define TEUCHOS_STACKTRACE_H 31 | 32 | // C interface to the stacktrace code 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | /** \brief Prints the current stacktrace to stdout. 39 | * 40 | * \ingroup TeuchosStackTrace_grp 41 | */ 42 | void show_stacktrace(); 43 | 44 | /** \brief Prints the current stacktrace to stdout on segfault. 45 | * 46 | * \ingroup TeuchosStackTrace_grp 47 | */ 48 | void print_stack_on_segfault(); 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif // TEUCHOS_STACKTRACE_HPP 55 | -------------------------------------------------------------------------------- /compile: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -e 4 | 5 | # ----------------------------------------------------------------------- 6 | 7 | # C++ usage: 8 | 9 | # Compile stacktrace as a regular C++ program 10 | echo "Compiling stacktrace.cpp" 11 | g++ -fPIC -g -o stacktrace.o -c stacktrace.cpp 12 | 13 | # Use it in your C++ 14 | echo "Compiling test1.cpp" 15 | g++ -fPIC -g -o test1.o -c test1.cpp 16 | # And link, using C++ 17 | echo "Linking test1" 18 | g++ -o test1 test1.o stacktrace.o -lbfd -liberty 19 | 20 | # ----------------------------------------------------------------------- 21 | 22 | # C Usage (linking with C++) 23 | 24 | # For C, first compile a tiny C wrapper using a C++ compiler 25 | echo "Compiling C wrappers to stacktrace.cpp" 26 | g++ -fPIC -g -o c_stacktrace.o -c c_stacktrace.cpp 27 | # Compile your C code using C compiler 28 | echo "Compiling test2.c" 29 | gcc -fPIC -g -o test2.o -c test2.c 30 | # Link everything together using C++ compiler (you *need* to use C++ for 31 | # linking, because stacktrace.o depends on bunch of C++ runtime that only g++ 32 | # knows how to link properly) 33 | echo "Linking test2" 34 | g++ -o test2 test2.o stacktrace.o c_stacktrace.o -lbfd -liberty 35 | 36 | # ----------------------------------------------------------------------- 37 | 38 | # C Usage (linking with C, using a shared library) 39 | 40 | # For C, first compile a tiny C wrapper using a C++ compiler 41 | echo "Compiling C wrappers to stacktrace.cpp" 42 | g++ -fPIC -g -o c_stacktrace.o -c c_stacktrace.cpp 43 | # Create a shared library (that contains all the C++ runtime) using a C++ 44 | # compiler: 45 | g++ -shared -o libstacktrace.so stacktrace.o c_stacktrace.o -lbfd -liberty 46 | 47 | # Compile your C code using C compiler 48 | echo "Compiling test2.c" 49 | gcc -fPIC -g -o test2.o -c test2.c 50 | # Link test2 using C compiler 51 | echo "Linking test2shared" 52 | gcc -o test2shared test2.o -L. -lstacktrace -Wl,-rpath=. 53 | -------------------------------------------------------------------------------- /stacktrace.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010, 2011 Ondrej Certik 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of the Sandia Corporation nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | 30 | #include "stacktrace.hpp" 31 | 32 | 33 | #ifdef HAVE_TEUCHOS_STACKTRACE 34 | 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | // free() and abort() functions 43 | #include 44 | 45 | // For handling variable number of arguments using va_start/va_end functions 46 | #include 47 | 48 | // For registering SIGSEGV callbacks 49 | #include 50 | 51 | 52 | // The following C headers are needed for some specific C functionality (see 53 | // the comments), which is not available in C++: 54 | 55 | // backtrace() function for retrieving the stacktrace 56 | #include 57 | 58 | // For demangling function names 59 | #include 60 | 61 | #ifdef HAVE_TEUCHOS_LINK 62 | // For dl_iterate_phdr() functionality. This is needed in order to read symbols 63 | // from shared libraries. Without it, stacktrace will still work, but only for 64 | // symbols in the current executable, all symbols in shared libraries will just 65 | // print "File unknown, address: 0x140420334841216". 66 | #include 67 | #endif 68 | 69 | #ifdef HAVE_TEUCHOS_BFD 70 | // For bfd_* family of functions for loading debugging symbols from the binary 71 | // This is the only nonstandard header file and the binary needs to be linked 72 | // with "-lbfd -liberty" (the libiberty library is sometimes included in the 73 | // bfd, so doesn't need to be linked with on some systems) 74 | // 75 | // Without it, the stacktrace will only print a list of raw addresses 76 | // "File unknown, address: 0x4200328" 77 | // instead of: 78 | // File "/home/ondrej/repos/stacktrace/test1.cpp", line 23, in main() 79 | # include 80 | #else 81 | typedef long long unsigned bfd_vma; 82 | #endif 83 | 84 | namespace { 85 | 86 | /* This struct is used to pass information between 87 | addr2str() and process_section(). 88 | */ 89 | struct line_data { 90 | #ifdef HAVE_TEUCHOS_BFD 91 | asymbol **symbol_table; /* Symbol table. */ 92 | #endif 93 | bfd_vma addr; 94 | std::string filename; 95 | std::string function_name; 96 | unsigned int line; 97 | int line_found; 98 | }; 99 | 100 | 101 | /* Return if given char is whitespace or not. */ 102 | bool is_whitespace_char(const char c) 103 | { 104 | return c == ' ' || c == '\t'; 105 | } 106 | 107 | 108 | /* Removes the leading whitespace from a string and returnes the new 109 | * string. 110 | */ 111 | std::string remove_leading_whitespace(const std::string &str) 112 | { 113 | if (str.length() && is_whitespace_char(str[0])) { 114 | int first_nonwhitespace_index = 0; 115 | for (int i = 0; i < static_cast(str.length()); ++i) { 116 | if (!is_whitespace_char(str[i])) { 117 | first_nonwhitespace_index = i; 118 | break; 119 | } 120 | } 121 | return str.substr(first_nonwhitespace_index); 122 | } 123 | return str; 124 | } 125 | 126 | 127 | /* Reads the 'line_number'th line from the file filename. */ 128 | std::string read_line_from_file(std::string filename, unsigned int line_number) 129 | { 130 | std::ifstream in(filename.c_str()); 131 | if (!in.is_open()) { 132 | return ""; 133 | } 134 | if (line_number == 0) { 135 | return "Line number must be positive"; 136 | } 137 | unsigned int n = 0; 138 | std::string line; 139 | while (n < line_number) { 140 | if (in.eof()) 141 | return "Line not found"; 142 | getline(in, line); 143 | n += 1; // loop update 144 | } 145 | return line; 146 | } 147 | 148 | /* Demangles the function name if needed (if the 'name' is coming from C, it 149 | doesn't have to be demangled, if it's coming from C++, it needs to be). 150 | 151 | Makes sure that it ends with (), which is automatic in C++, but it has to be 152 | added by hand in C. 153 | */ 154 | std::string demangle_function_name(std::string name) 155 | { 156 | std::string s; 157 | 158 | if (name.length() == 0) { 159 | s = "??"; 160 | } else { 161 | int status = 0; 162 | char *d = 0; 163 | d = abi::__cxa_demangle(name.c_str(), 0, 0, &status); 164 | if (d) { 165 | s = d; 166 | free(d); 167 | if (s.size() > 0) { 168 | if (s[s.size()-1] != ')') { 169 | // This means that the C++ demangler failed for some 170 | // reason, as each symbol needs to end with ")". As such, 171 | // just return the original version: 172 | s = name + "()"; 173 | } 174 | } 175 | } else { 176 | s = name + "()"; 177 | } 178 | } 179 | 180 | return s; 181 | } 182 | 183 | 184 | #ifdef HAVE_TEUCHOS_BFD 185 | 186 | 187 | /* Look for an address in a section. This is called via 188 | bfd_map_over_sections over all sections in abfd. 189 | 190 | If the correct line is found, store the result in 'data' and set 191 | data->line_found, so that subsequent calls to process_section exit 192 | immediately. 193 | */ 194 | void process_section(bfd *abfd, asection *section, void *_data) 195 | { 196 | line_data *data = (line_data*)_data; 197 | if (data->line_found) { 198 | // If we already found the line, exit 199 | return; 200 | } 201 | if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) { 202 | return; 203 | } 204 | 205 | bfd_vma section_vma = bfd_get_section_vma(abfd, section); 206 | if (data->addr < section_vma) { 207 | // If the addr lies above the section, exit 208 | return; 209 | } 210 | 211 | bfd_size_type section_size = bfd_section_size(abfd, section); 212 | if (data->addr >= section_vma + section_size) { 213 | // If the addr lies below the section, exit 214 | return; 215 | } 216 | 217 | // Calculate the correct offset of our line in the section 218 | bfd_vma offset = data->addr - section_vma - 1; 219 | 220 | // Finds the line corresponding to the offset 221 | 222 | const char *filename=NULL, *function_name=NULL; 223 | data->line_found = bfd_find_nearest_line(abfd, section, data->symbol_table, 224 | offset, &filename, &function_name, &data->line); 225 | 226 | if (filename == NULL) 227 | data->filename = ""; 228 | else 229 | data->filename = filename; 230 | 231 | if (function_name == NULL) 232 | data->function_name = ""; 233 | else 234 | data->function_name = function_name; 235 | } 236 | 237 | 238 | /* Loads the symbol table into 'data->symbol_table'. */ 239 | int load_symbol_table(bfd *abfd, line_data *data) 240 | { 241 | if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) 242 | // If we don't have any symbols, return 243 | return 0; 244 | 245 | void **symbol_table_ptr = reinterpret_cast(&data->symbol_table); 246 | long n_symbols; 247 | unsigned int symbol_size; 248 | n_symbols = bfd_read_minisymbols(abfd, false, symbol_table_ptr, &symbol_size); 249 | if (n_symbols == 0) { 250 | // If the bfd_read_minisymbols() already allocated the table, we need 251 | // to free it first: 252 | if (data->symbol_table != NULL) 253 | free(data->symbol_table); 254 | // dynamic 255 | n_symbols = bfd_read_minisymbols(abfd, true, symbol_table_ptr, &symbol_size); 256 | } 257 | 258 | if (n_symbols < 0) { 259 | // bfd_read_minisymbols() failed 260 | return 1; 261 | } 262 | 263 | return 0; 264 | } 265 | 266 | 267 | #endif // HAVE_TEUCHOS_BFD 268 | 269 | 270 | /* Returns a string of 2 lines for the function with address 'addr' in the file 271 | 'file_name'. 272 | 273 | Example: 274 | 275 | File "/home/ondrej/repos/rcp/src/Teuchos_RCP.hpp", line 428, in Teuchos::RCP::assert_not_null() const 276 | throw_null_ptr_error(typeName(*this)); 277 | */ 278 | std::string addr2str(std::string file_name, bfd_vma addr) 279 | { 280 | #ifdef HAVE_TEUCHOS_BFD 281 | // Initialize 'abfd' and do some sanity checks 282 | bfd *abfd; 283 | abfd = bfd_openr(file_name.c_str(), NULL); 284 | if (abfd == NULL) 285 | return "Cannot open the binary file '" + file_name + "'\n"; 286 | if (bfd_check_format(abfd, bfd_archive)) 287 | return "Cannot get addresses from the archive '" + file_name + "'\n"; 288 | char **matching; 289 | if (!bfd_check_format_matches(abfd, bfd_object, &matching)) 290 | return "Unknown format of the binary file '" + file_name + "'\n"; 291 | line_data data; 292 | data.addr = addr; 293 | data.symbol_table = NULL; 294 | data.line_found = false; 295 | // This allocates the symbol_table: 296 | if (load_symbol_table(abfd, &data) == 1) 297 | return "Failed to load the symbol table from '" + file_name + "'\n"; 298 | // Loops over all sections and try to find the line 299 | bfd_map_over_sections(abfd, process_section, &data); 300 | // Deallocates the symbol table 301 | if (data.symbol_table != NULL) free(data.symbol_table); 302 | bfd_close(abfd); 303 | #else 304 | line_data data; 305 | data.line_found = 0; 306 | #endif 307 | 308 | std::ostringstream s; 309 | // Do the printing --- print as much information as we were able to 310 | // find out 311 | if (!data.line_found) { 312 | // If we didn't find the line, at least print the address itself 313 | s << " File unknown, address: 0x" << (long long unsigned int) addr; 314 | } else { 315 | std::string name = demangle_function_name(data.function_name); 316 | if (data.filename.length() > 0) { 317 | // Nicely format the filename + function name + line 318 | s << " File \"" << data.filename << "\", line " 319 | << data.line << ", in " << name; 320 | const std::string line_text = remove_leading_whitespace( 321 | read_line_from_file(data.filename, data.line)); 322 | if (line_text != "") { 323 | s << "\n " << line_text; 324 | } 325 | } else { 326 | // The file is unknown (and data.line == 0 in this case), so the 327 | // only meaningful thing to print is the function name. This 328 | // usually happens for the kernel functions at the 329 | // beginning of each stacktrace: 330 | // File unknown, in _start() 331 | // File unknown, in __libc_start_main() 332 | // File "/.../stacktrace/test1.cpp", line 23, in main() 333 | // ... 334 | s << " File unknown, in " << name; 335 | } 336 | } 337 | s << "\n"; 338 | return s.str(); 339 | } 340 | 341 | struct match_data { 342 | bfd_vma addr; 343 | 344 | std::string filename; 345 | bfd_vma addr_in_file; 346 | }; 347 | 348 | 349 | #ifdef HAVE_TEUCHOS_LINK 350 | 351 | 352 | /* Tries to find the 'data.addr' in the current shared lib (as passed in 353 | 'info'). If it succeeds, returns (in the 'data') the full path to the shared 354 | lib and the local address in the file. 355 | */ 356 | int shared_lib_callback(struct dl_phdr_info *info, 357 | size_t size, void *_data) 358 | { 359 | struct match_data *data = (struct match_data *)_data; 360 | for (int i=0; i < info->dlpi_phnum; i++) { 361 | if (info->dlpi_phdr[i].p_type == PT_LOAD) { 362 | ElfW(Addr) min_addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr; 363 | ElfW(Addr) max_addr = min_addr + info->dlpi_phdr[i].p_memsz; 364 | if ((data->addr >= min_addr) && (data->addr < max_addr)) { 365 | data->filename = info->dlpi_name; 366 | data->addr_in_file = data->addr - info->dlpi_addr; 367 | // We found a match, return a non-zero value 368 | return 1; 369 | } 370 | } 371 | } 372 | // We didn't find a match, return a zero value 373 | return 0; 374 | } 375 | 376 | 377 | #endif // HAVE_TEUCHOS_LINK 378 | 379 | // Class for creating a safe C++ interface to the raw void** stacktrace 380 | // pointers, that we get from the backtrace() libc function. We make a copy of 381 | // the addresses, so the caller can free the memory. We use std::vector to 382 | // store the addresses internally, but this can be changed. 383 | class StacktraceAddresses { 384 | std::vector stacktrace_buffer; 385 | int impl_stacktrace_depth; 386 | public: 387 | StacktraceAddresses(): impl_stacktrace_depth(0) {} 388 | void add_addresses(void *const *_stacktrace_buffer, int _size, 389 | int _impl_stacktrace_depth) 390 | { 391 | // Remove old addresses first 392 | this->stacktrace_buffer.clear(); 393 | 394 | this->impl_stacktrace_depth = _impl_stacktrace_depth; 395 | for (int i=0; i < _size; i++) 396 | this->stacktrace_buffer.push_back((bfd_vma) _stacktrace_buffer[i]); 397 | } 398 | bfd_vma get_address(int i) const { 399 | return this->stacktrace_buffer[i]; 400 | } 401 | int get_size() const { 402 | return this->stacktrace_buffer.size(); 403 | } 404 | int get_impl_stacktrace_depth() const { 405 | return this->impl_stacktrace_depth; 406 | } 407 | }; 408 | 409 | 410 | /* 411 | Returns a std::string with the stacktrace corresponding to the 412 | list of addresses (of functions on the stack) in 'buffer'. 413 | 414 | It converts addresses to filenames, line numbers, function names and the 415 | line text. 416 | */ 417 | std::string stacktrace2str(const StacktraceAddresses &stacktrace_addresses) 418 | { 419 | int stack_depth = stacktrace_addresses.get_size() - 1; 420 | 421 | std::string full_stacktrace_str("Traceback (most recent call last):\n"); 422 | 423 | #ifdef HAVE_TEUCHOS_BFD 424 | bfd_init(); 425 | #endif 426 | // Loop over the stack 427 | const int stack_depth_start = stack_depth; 428 | const int stack_depth_end = stacktrace_addresses.get_impl_stacktrace_depth(); 429 | for (int i=stack_depth_start; i >= stack_depth_end; i--) { 430 | // Iterate over all loaded shared libraries (see dl_iterate_phdr(3) - 431 | // Linux man page for more documentation) 432 | struct match_data match; 433 | match.addr = stacktrace_addresses.get_address(i); 434 | #ifdef HAVE_TEUCHOS_LINK 435 | if (dl_iterate_phdr(shared_lib_callback, &match) == 0) 436 | return "dl_iterate_phdr() didn't find a match\n"; 437 | #else 438 | match.filename = ""; 439 | match.addr_in_file = match.addr; 440 | #endif 441 | 442 | if (match.filename.length() > 0) { 443 | // This happens for shared libraries (like /lib/libc.so.6, or any 444 | // other shared library that the project uses). 'match.filename' 445 | // then contains the full path to the .so library. 446 | full_stacktrace_str += addr2str(match.filename, match.addr_in_file); 447 | } else { 448 | // The 'addr_in_file' is from the current executable binary, that 449 | // one can find at '/proc/self/exe'. So we'll use that. 450 | full_stacktrace_str += addr2str("/proc/self/exe", match.addr_in_file); 451 | } 452 | } 453 | #ifndef HAVE_TEUCHOS_BFD 454 | // This means that we cannot access the symbols 455 | full_stacktrace_str += "\nOnly raw addresses are printed, because the stacktrace code is compiled without\nBFD support. Enable BFD to see file names, line numbers and symbol names.\n"; 456 | #else 457 | #ifndef HAVE_TEUCHOS_LINK 458 | // This means that we cannot access shared libs 459 | full_stacktrace_str += "\nSymbols in shared libraries cannot be accessed, because the stacktrace code\nis compiled without LINK support. Enable LINK to see file names, line numbers\nand symbol names in shared libraries.\n"; 460 | #endif 461 | #endif 462 | 463 | return full_stacktrace_str; 464 | } 465 | 466 | 467 | void loc_segfault_callback_print_stack(int sig_num) 468 | { 469 | std::cout << "\nSegfault caught. Printing stacktrace:\n\n"; 470 | Teuchos::show_stacktrace(); 471 | std::cout << "\nDone. Exiting the program.\n"; 472 | // Deregister our abort callback: 473 | signal(SIGABRT, SIG_DFL); 474 | abort(); 475 | } 476 | 477 | 478 | void loc_abort_callback_print_stack(int sig_num) 479 | { 480 | std::cout << "\nAbort caught. Printing stacktrace:\n\n"; 481 | Teuchos::show_stacktrace(); 482 | std::cout << "\nDone.\n"; 483 | } 484 | 485 | 486 | void get_stacktrace_addresses(int impl_stacktrace_depth, 487 | StacktraceAddresses &stacktrace_addresses) 488 | { 489 | const int STACKTRACE_ARRAY_SIZE = 100; // 2010/09/22: rabartl: Is this large enough? 490 | void *stacktrace_array[STACKTRACE_ARRAY_SIZE]; 491 | const size_t stacktrace_size = backtrace(stacktrace_array, 492 | STACKTRACE_ARRAY_SIZE); 493 | stacktrace_addresses.add_addresses(stacktrace_array, stacktrace_size, 494 | impl_stacktrace_depth+1); 495 | } 496 | 497 | 498 | StacktraceAddresses last_stacktrace; 499 | 500 | } // Unnamed namespace 501 | 502 | 503 | // Public functions 504 | 505 | 506 | void Teuchos::store_stacktrace() 507 | { 508 | const int impl_stacktrace_depth=1; 509 | get_stacktrace_addresses(impl_stacktrace_depth, last_stacktrace); 510 | } 511 | 512 | 513 | std::string Teuchos::get_stored_stacktrace() 514 | { 515 | if (last_stacktrace.get_size() == 0) { 516 | return ""; 517 | } 518 | else { 519 | return stacktrace2str(last_stacktrace); 520 | } 521 | } 522 | 523 | 524 | std::string Teuchos::get_stacktrace(int impl_stacktrace_depth) 525 | { 526 | StacktraceAddresses addresses; 527 | get_stacktrace_addresses(impl_stacktrace_depth+1, addresses); 528 | return stacktrace2str(addresses); 529 | } 530 | 531 | 532 | void Teuchos::show_stacktrace() 533 | { 534 | const int impl_stacktrace_depth=1; 535 | std::cout << Teuchos::get_stacktrace(impl_stacktrace_depth); 536 | } 537 | // 2010/09/21: rabartl: Above, you should never print directly to std::cout 538 | // (see TCDG 1.0 GCG 17). At the very least, we should provide a "seam" to 539 | // allow the stream to be set to a different stream. 540 | 541 | 542 | void Teuchos::print_stack_on_segfault() 543 | { 544 | signal(SIGSEGV, loc_segfault_callback_print_stack); 545 | signal(SIGABRT, loc_abort_callback_print_stack); 546 | } 547 | 548 | 549 | #endif // HAVE_TEUCHOS_STACKTRACE 550 | 551 | -------------------------------------------------------------------------------- /stacktrace.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010, 2011 Ondrej Certik 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of the Sandia Corporation nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef TEUCHOS_STACKTRACE_HPP 30 | #define TEUCHOS_STACKTRACE_HPP 31 | 32 | /*! \file Teuchos_stacktrace.hpp 33 | 34 | \brief Functions for returning stacktrace info (GCC only initially). 35 | */ 36 | 37 | #include 38 | #include "Teuchos_ConfigDefs.hpp" 39 | 40 | 41 | #ifdef HAVE_TEUCHOS_STACKTRACE 42 | 43 | 44 | /*! \defgroup TeuchosStackTrace_grp Utility code for generating stacktraces. 45 | * 46 | * \ingroup teuchos_language_support_grp 47 | */ 48 | 49 | namespace Teuchos { 50 | 51 | 52 | /** \brief Stores the current stacktrace into an internal global variable. 53 | * 54 | * \ingroup TeuchosStackTrace_grp 55 | */ 56 | void store_stacktrace(); 57 | 58 | /** \brief Returns the last stored stacktrace as a string. 59 | * 60 | * \ingroup TeuchosStackTrace_grp 61 | */ 62 | std::string get_stored_stacktrace(); 63 | 64 | /** \brief Returns the current stacktrace as a string. 65 | * 66 | * \param impl_stacktrace_depth [in] The stacktrace depth to remove from the 67 | * stacktrace printout to avoid showing users implementation functions in the 68 | * stacktrace. 69 | * 70 | * \ingroup TeuchosStackTrace_grp 71 | */ 72 | std::string get_stacktrace(int impl_stacktrace_depth=0); 73 | 74 | /** \brief Prints the current stacktrace to stdout. 75 | * 76 | * \ingroup TeuchosStackTrace_grp 77 | */ 78 | void show_stacktrace(); 79 | 80 | /** \brief Prints the current stacktrace to stdout on segfault. 81 | * 82 | * \ingroup TeuchosStackTrace_grp 83 | */ 84 | void print_stack_on_segfault(); 85 | 86 | } // end namespace Teuchos 87 | 88 | #endif // HAVE_TEUCHOS_STACKTRACE 89 | 90 | #endif // TEUCHOS_STACKTRACE_HPP 91 | 92 | -------------------------------------------------------------------------------- /test1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "stacktrace.hpp" 5 | 6 | void g() 7 | { 8 | Teuchos::show_stacktrace(); 9 | } 10 | 11 | void f() 12 | { 13 | g(); 14 | } 15 | 16 | int main() 17 | { 18 | f(); 19 | 20 | Teuchos::print_stack_on_segfault(); 21 | 22 | // This will segfault: 23 | char *p; *p=0; 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /test2.c: -------------------------------------------------------------------------------- 1 | #include "c_stacktrace.h" 2 | 3 | void g() 4 | { 5 | show_stacktrace(); 6 | } 7 | 8 | void f() 9 | { 10 | g(); 11 | } 12 | 13 | int main() 14 | { 15 | f(); 16 | 17 | print_stack_on_segfault(); 18 | 19 | // This will segfault: 20 | char *p; *p=0; 21 | 22 | return 0; 23 | } 24 | --------------------------------------------------------------------------------