|
Teuchos - Trilinos Tools Package
Version of the Day
|
00001 /* 00002 Copyright (c) 2010, Ondrej Certik 00003 All rights reserved. 00004 00005 Redistribution and use in source and binary forms, with or without 00006 modification, are permitted provided that the following conditions are met: 00007 00008 * Redistributions of source code must retain the above copyright notice, this 00009 list of conditions and the following disclaimer. 00010 * Redistributions in binary form must reproduce the above copyright notice, 00011 this list of conditions and the following disclaimer in the documentation 00012 and/or other materials provided with the distribution. 00013 * Neither the name of the Sandia Corporation nor the names of its contributors 00014 may be used to endorse or promote products derived from this software without 00015 specific prior written permission. 00016 00017 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 00018 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00019 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00020 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 00021 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00022 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 00023 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00024 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 00025 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00026 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00027 */ 00028 00029 00030 #include "Teuchos_stacktrace.hpp" 00031 #include "Teuchos_RCP.hpp" 00032 #include "Teuchos_VerboseObject.hpp" 00033 00034 00035 #ifdef HAVE_TEUCHOS_STACKTRACE 00036 00037 00038 #include <string> 00039 #include <iostream> 00040 #include <fstream> 00041 00042 // free() and abort() functions 00043 #include <cstdlib> 00044 00045 // For handling variable number of arguments using va_start/va_end functions 00046 #include <cstdarg> 00047 00048 // For registering SIGSEGV callbacks 00049 #include <csignal> 00050 00051 00052 // The following C headers are needed for some specific C functionality (see 00053 // the comments), which is not available in C++: 00054 00055 // backtrace() function for retrieving the stacktrace 00056 #include <execinfo.h> 00057 00058 // For demangling function names 00059 #include <cxxabi.h> 00060 00061 #ifdef HAVE_TEUCHOS_LINK 00062 // For dl_iterate_phdr() functionality 00063 #include <link.h> 00064 #endif 00065 00066 #ifdef HAVE_TEUCHOS_BFD 00067 // For bfd_* family of functions for loading debugging symbols from the binary 00068 // This is the only nonstandard header file and the binary needs to be linked 00069 // with "-lbfd". 00070 # include <bfd.h> 00071 #else 00072 typedef long long unsigned bfd_vma; 00073 #endif 00074 00075 using Teuchos::RCP; 00076 using Teuchos::rcp; 00077 using Teuchos::null; 00078 00079 namespace { 00080 00081 /* This struct is used to pass information between 00082 addr2str() and process_section(). 00083 */ 00084 struct line_data { 00085 #ifdef HAVE_TEUCHOS_BFD 00086 asymbol **symbol_table; /* Symbol table. */ 00087 #endif 00088 bfd_vma addr; 00089 std::string filename; 00090 std::string function_name; 00091 unsigned int line; 00092 int line_found; 00093 }; 00094 00095 00096 /* Return if given char is whitespace or not. */ 00097 bool is_whitespace_char(const char c) 00098 { 00099 return c == ' ' || c == '\t'; 00100 } 00101 00102 00103 /* Removes the leading whitespace from a string and returnes the new 00104 * string. 00105 */ 00106 std::string remove_leading_whitespace(const std::string &str) 00107 { 00108 if (str.length() && is_whitespace_char(str[0])) { 00109 int first_nonwhitespace_index = 0; 00110 for (int i = 0; i < static_cast<int>(str.length()); ++i) { 00111 if (!is_whitespace_char(str[i])) { 00112 first_nonwhitespace_index = i; 00113 break; 00114 } 00115 } 00116 return str.substr(first_nonwhitespace_index); 00117 } 00118 return str; 00119 } 00120 00121 00122 /* Reads the 'line_number'th line from the file filename. */ 00123 std::string read_line_from_file(std::string filename, unsigned int line_number) 00124 { 00125 std::ifstream in(filename.c_str()); 00126 if (!in.is_open()) { 00127 return ""; 00128 } 00129 if (line_number == 0) { 00130 return "Line number must be positive"; 00131 } 00132 unsigned int n = 0; 00133 std::string line; 00134 while (n < line_number) { 00135 if (in.eof()) 00136 return "Line not found"; 00137 getline(in, line); 00138 n += 1; // loop update 00139 } 00140 return line; 00141 } 00142 00143 /* Demangles the function name if needed (if the 'name' is coming from C, it 00144 doesn't have to be demangled, if it's coming from C++, it needs to be). 00145 00146 Makes sure that it ends with (), which is automatic in C++, but it has to be 00147 added by hand in C. 00148 */ 00149 std::string demangle_function_name(std::string name) 00150 { 00151 std::string s; 00152 00153 if (name.length() == 0) { 00154 s = "??"; 00155 } else { 00156 int status = 0; 00157 char *d = 0; 00158 d = abi::__cxa_demangle(name.c_str(), 0, 0, &status); 00159 if (d) { 00160 s = d; 00161 free(d); 00162 } else { 00163 s = name + "()"; 00164 } 00165 } 00166 00167 return s; 00168 } 00169 00170 00171 #ifdef HAVE_TEUCHOS_BFD 00172 00173 00174 /* Look for an address in a section. This is called via 00175 bfd_map_over_sections over all sections in abfd. 00176 00177 If the correct line is found, store the result in 'data' and set 00178 data->line_found, so that subsequent calls to process_section exit 00179 immediately. 00180 */ 00181 void process_section(bfd *abfd, asection *section, void *_data) 00182 { 00183 line_data *data = (line_data*)_data; 00184 if (data->line_found) { 00185 // If we already found the line, exit 00186 return; 00187 } 00188 if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) { 00189 return; 00190 } 00191 00192 bfd_vma section_vma = bfd_get_section_vma(abfd, section); 00193 if (data->addr < section_vma) { 00194 // If the addr lies above the section, exit 00195 return; 00196 } 00197 00198 bfd_size_type section_size = bfd_section_size(abfd, section); 00199 if (data->addr >= section_vma + section_size) { 00200 // If the addr lies below the section, exit 00201 return; 00202 } 00203 00204 // Calculate the correct offset of our line in the section 00205 bfd_vma offset = data->addr - section_vma - 1; 00206 00207 // Finds the line corresponding to the offset 00208 00209 const char *filename=NULL, *function_name=NULL; 00210 data->line_found = bfd_find_nearest_line(abfd, section, data->symbol_table, 00211 offset, &filename, &function_name, &data->line); 00212 00213 if (filename == NULL) 00214 data->filename = ""; 00215 else 00216 data->filename = filename; 00217 00218 if (function_name == NULL) 00219 data->function_name = ""; 00220 else 00221 data->function_name = function_name; 00222 } 00223 00224 00225 /* Loads the symbol table into 'data->symbol_table'. */ 00226 int load_symbol_table(bfd *abfd, line_data *data) 00227 { 00228 if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) 00229 // If we don't have any symbols, return 00230 return 0; 00231 00232 void **symbol_table_ptr = reinterpret_cast<void **>(&data->symbol_table); 00233 long n_symbols; 00234 unsigned int symbol_size; 00235 n_symbols = bfd_read_minisymbols(abfd, false, symbol_table_ptr, &symbol_size); 00236 if (n_symbols == 0) { 00237 // If the bfd_read_minisymbols() already allocated the table, we need 00238 // to free it first: 00239 if (data->symbol_table != NULL) 00240 free(data->symbol_table); 00241 // dynamic 00242 n_symbols = bfd_read_minisymbols(abfd, true, symbol_table_ptr, &symbol_size); 00243 } 00244 00245 if (n_symbols < 0) { 00246 // bfd_read_minisymbols() failed 00247 return 1; 00248 } 00249 00250 return 0; 00251 } 00252 00253 00254 #endif // HAVE_TEUCHOS_BFD 00255 00256 00257 /* Returns a string of 2 lines for the function with address 'addr' in the file 00258 'file_name'. 00259 00260 Example: 00261 00262 File "/home/ondrej/repos/rcp/src/Teuchos_RCP.hpp", line 428, in Teuchos::RCP<A>::assert_not_null() const 00263 throw_null_ptr_error(typeName(*this)); 00264 */ 00265 std::string addr2str(std::string file_name, bfd_vma addr) 00266 { 00267 #ifdef HAVE_TEUCHOS_BFD 00268 // Initialize 'abfd' and do some sanity checks 00269 bfd *abfd; 00270 abfd = bfd_openr(file_name.c_str(), NULL); 00271 if (abfd == NULL) 00272 return "Cannot open the binary file '" + file_name + "'\n"; 00273 if (bfd_check_format(abfd, bfd_archive)) 00274 return "Cannot get addresses from the archive '" + file_name + "'\n"; 00275 char **matching; 00276 if (!bfd_check_format_matches(abfd, bfd_object, &matching)) 00277 return "Unknown format of the binary file '" + file_name + "'\n"; 00278 line_data data; 00279 data.addr = addr; 00280 data.symbol_table = NULL; 00281 data.line_found = false; 00282 // This allocates the symbol_table: 00283 if (load_symbol_table(abfd, &data) == 1) 00284 return "Failed to load the symbol table from '" + file_name + "'\n"; 00285 // Loops over all sections and try to find the line 00286 bfd_map_over_sections(abfd, process_section, &data); 00287 // Deallocates the symbol table 00288 if (data.symbol_table != NULL) free(data.symbol_table); 00289 bfd_close(abfd); 00290 #else 00291 line_data data; 00292 data.line_found = 0; 00293 #endif 00294 00295 std::ostringstream s; 00296 // Do the printing --- print as much information as we were able to 00297 // find out 00298 if (!data.line_found) { 00299 // If we didn't find the line, at least print the address itself 00300 s << " File unknown, address: 0x" << (long long unsigned int) addr; 00301 } else { 00302 std::string name = demangle_function_name(data.function_name); 00303 if (data.filename.length() > 0) { 00304 // Nicely format the filename + function name + line 00305 s << " File \"" << data.filename << "\", line " 00306 << data.line << ", in " << name; 00307 const std::string line_text = remove_leading_whitespace( 00308 read_line_from_file(data.filename, data.line)); 00309 if (line_text != "") { 00310 s << "\n " << line_text; 00311 } 00312 } else { 00313 // The file is unknown (and data.line == 0 in this case), so the 00314 // only meaningful thing to print is the function name: 00315 s << " File unknown, in " << name; 00316 } 00317 } 00318 s << "\n"; 00319 return s.str(); 00320 } 00321 00322 struct match_data { 00323 bfd_vma addr; 00324 00325 std::string filename; 00326 bfd_vma addr_in_file; 00327 }; 00328 00329 00330 #ifdef HAVE_TEUCHOS_LINK 00331 00332 00333 /* Tries to find the 'data.addr' in the current shared lib (as passed in 00334 'info'). If it succeeds, returns (in the 'data') the full path to the shared 00335 lib and the local address in the file. 00336 */ 00337 int shared_lib_callback(struct dl_phdr_info *info, 00338 size_t size, void *_data) 00339 { 00340 struct match_data *data = (struct match_data *)_data; 00341 for (int i=0; i < info->dlpi_phnum; i++) { 00342 if (info->dlpi_phdr[i].p_type == PT_LOAD) { 00343 ElfW(Addr) min_addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr; 00344 ElfW(Addr) max_addr = min_addr + info->dlpi_phdr[i].p_memsz; 00345 if ((data->addr >= min_addr) && (data->addr < max_addr)) { 00346 data->filename = info->dlpi_name; 00347 data->addr_in_file = data->addr - info->dlpi_addr; 00348 // We found a match, return a non-zero value 00349 return 1; 00350 } 00351 } 00352 } 00353 // We didn't find a match, return a zero value 00354 return 0; 00355 } 00356 00357 00358 #endif // HAVE_TEUCHOS_LINK 00359 00360 // Class for creating a safe C++ interface to the raw void** stacktrace 00361 // pointers, that we get from the backtrace() libc function. We make a copy of 00362 // the addresses, so the caller can free the memory. We use std::vector to 00363 // store the addresses internally, but this can be changed. 00364 class StacktraceAddresses { 00365 std::vector<bfd_vma> stacktrace_buffer; 00366 int impl_stacktrace_depth; 00367 public: 00368 StacktraceAddresses(void *const *_stacktrace_buffer, int _size, int _impl_stacktrace_depth) 00369 : impl_stacktrace_depth(_impl_stacktrace_depth) 00370 { 00371 for (int i=0; i < _size; i++) 00372 stacktrace_buffer.push_back((bfd_vma) _stacktrace_buffer[i]); 00373 } 00374 bfd_vma get_address(int i) const { 00375 return this->stacktrace_buffer[i]; 00376 } 00377 int get_size() const { 00378 return this->stacktrace_buffer.size(); 00379 } 00380 int get_impl_stacktrace_depth() const { 00381 return this->impl_stacktrace_depth; 00382 } 00383 }; 00384 00385 00386 /* 00387 Returns a std::string with the stacktrace corresponding to the 00388 list of addresses (of functions on the stack) in 'buffer'. 00389 00390 It converts addresses to filenames, line numbers, function names and the 00391 line text. 00392 */ 00393 std::string stacktrace2str(const StacktraceAddresses &stacktrace_addresses) 00394 { 00395 int stack_depth = stacktrace_addresses.get_size() - 1; 00396 00397 std::string full_stacktrace_str("Traceback (most recent call last):\n"); 00398 00399 #ifdef HAVE_TEUCHOS_BFD 00400 bfd_init(); 00401 #endif 00402 // Loop over the stack 00403 const int stack_depth_start = stack_depth; 00404 const int stack_depth_end = stacktrace_addresses.get_impl_stacktrace_depth(); 00405 for (int i=stack_depth_start; i >= stack_depth_end; i--) { 00406 // Iterate over all loaded shared libraries (see dl_iterate_phdr(3) - 00407 // Linux man page for more documentation) 00408 struct match_data match; 00409 match.addr = stacktrace_addresses.get_address(i); 00410 #ifdef HAVE_TEUCHOS_BFD 00411 if (dl_iterate_phdr(shared_lib_callback, &match) == 0) 00412 return "dl_iterate_phdr() didn't find a match\n"; 00413 #else 00414 match.filename = ""; 00415 match.addr_in_file = match.addr; 00416 #endif 00417 00418 if (match.filename.length() > 0) { 00419 // This happens for shared libraries (like /lib/libc.so.6, or any 00420 // other shared library that the project uses). 'match.filename' 00421 // then contains the full path to the .so library. 00422 full_stacktrace_str += addr2str(match.filename, match.addr_in_file); 00423 } else { 00424 // The 'addr_in_file' is from the current executable binary, that 00425 // one can find at '/proc/self/exe'. So we'll use that. 00426 full_stacktrace_str += addr2str("/proc/self/exe", match.addr_in_file); 00427 } 00428 } 00429 00430 return full_stacktrace_str; 00431 } 00432 00433 00434 void loc_segfault_callback_print_stack(int sig_num) 00435 { 00436 const Teuchos::RCP<Teuchos::FancyOStream> out = 00437 Teuchos::VerboseObjectBase::getDefaultOStream(); 00438 *out << "\nSegfault caught. Printing stacktrace:\n\n"; 00439 Teuchos::show_stacktrace(); 00440 *out << "\nDone. Exiting the program.\n"; 00441 // Deregister our abort callback: 00442 signal(SIGABRT, SIG_DFL); 00443 abort(); 00444 } 00445 00446 00447 void loc_abort_callback_print_stack(int sig_num) 00448 { 00449 const Teuchos::RCP<Teuchos::FancyOStream> out = 00450 Teuchos::VerboseObjectBase::getDefaultOStream(); 00451 *out << "\nAbort caught. Printing stacktrace:\n\n"; 00452 Teuchos::show_stacktrace(); 00453 *out << "\nDone.\n"; 00454 } 00455 00456 00457 RCP<StacktraceAddresses> get_stacktrace_addresses(int impl_stacktrace_depth) 00458 { 00459 const int STACKTRACE_ARRAY_SIZE = 100; // 2010/05/22: rabartl: Is this large enough? 00460 void *stacktrace_array[STACKTRACE_ARRAY_SIZE]; 00461 const size_t stacktrace_size = backtrace(stacktrace_array, 00462 STACKTRACE_ARRAY_SIZE); 00463 return rcp(new StacktraceAddresses(stacktrace_array, stacktrace_size, 00464 impl_stacktrace_depth+1)); 00465 } 00466 00467 00468 RCP<StacktraceAddresses> last_stacktrace; 00469 00470 } // Unnamed namespace 00471 00472 00473 // Public functions 00474 00475 00476 void Teuchos::store_stacktrace() 00477 { 00478 const int impl_stacktrace_depth=1; 00479 last_stacktrace = get_stacktrace_addresses(impl_stacktrace_depth); 00480 } 00481 00482 00483 std::string Teuchos::get_stored_stacktrace() 00484 { 00485 if (last_stacktrace == null) { 00486 return ""; 00487 } 00488 else { 00489 return stacktrace2str(*last_stacktrace); 00490 } 00491 } 00492 00493 00494 std::string Teuchos::get_stacktrace(int impl_stacktrace_depth) 00495 { 00496 RCP<StacktraceAddresses> addresses = 00497 get_stacktrace_addresses(impl_stacktrace_depth+1); 00498 return stacktrace2str(*addresses); 00499 } 00500 00501 00502 void Teuchos::show_stacktrace() 00503 { 00504 const Teuchos::RCP<Teuchos::FancyOStream> out = 00505 Teuchos::VerboseObjectBase::getDefaultOStream(); 00506 const int impl_stacktrace_depth=1; 00507 *out << Teuchos::get_stacktrace(impl_stacktrace_depth); 00508 } 00509 00510 00511 void Teuchos::print_stack_on_segfault() 00512 { 00513 signal(SIGSEGV, loc_segfault_callback_print_stack); 00514 signal(SIGABRT, loc_abort_callback_print_stack); 00515 } 00516 00517 00518 #endif // HAVE_TEUCHOS_STACKTRACE 00519
1.7.6.1