Teuchos Package Browser (Single Doxygen Collection) Version of the Day
Teuchos_stacktrace.cpp
Go to the documentation of this file.
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines