Teuchos - Trilinos Tools Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Teuchos_stacktrace.cpp
1 /*
2  Copyright (c) 2010, 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 "Teuchos_stacktrace.hpp"
31 #include "Teuchos_RCP.hpp"
32 #include "Teuchos_VerboseObject.hpp"
33 
34 
35 #ifdef HAVE_TEUCHOS_STACKTRACE
36 
37 
38 #include <string>
39 #include <iostream>
40 #include <fstream>
41 
42 // free() and abort() functions
43 #include <cstdlib>
44 
45 // For handling variable number of arguments using va_start/va_end functions
46 #include <cstdarg>
47 
48 // For registering SIGSEGV callbacks
49 #include <csignal>
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 <execinfo.h>
57 
58 // For demangling function names
59 #include <cxxabi.h>
60 
61 #ifdef HAVE_TEUCHOS_LINK
62 // For dl_iterate_phdr() functionality
63 #include <link.h>
64 #endif
65 
66 #ifdef HAVE_TEUCHOS_BFD
67 // For bfd_* family of functions for loading debugging symbols from the binary
68 // This is the only nonstandard header file and the binary needs to be linked
69 // with "-lbfd".
70 # include <bfd.h>
71 #else
72 typedef long long unsigned bfd_vma;
73 #endif
74 
75 using Teuchos::RCP;
76 using Teuchos::rcp;
77 using Teuchos::null;
78 
79 namespace {
80 
81 /* This struct is used to pass information between
82  addr2str() and process_section().
83 */
84 struct line_data {
85 #ifdef HAVE_TEUCHOS_BFD
86  asymbol **symbol_table; /* Symbol table. */
87 #endif
88  bfd_vma addr;
89  std::string filename;
90  std::string function_name;
91  unsigned int line;
92  int line_found;
93 };
94 
95 
96 /* Return if given char is whitespace or not. */
97 bool is_whitespace_char(const char c)
98 {
99  return c == ' ' || c == '\t';
100 }
101 
102 
103 /* Removes the leading whitespace from a string and returnes the new
104  * string.
105  */
106 std::string remove_leading_whitespace(const std::string &str)
107 {
108  if (str.length() && is_whitespace_char(str[0])) {
109  int first_nonwhitespace_index = 0;
110  for (int i = 0; i < static_cast<int>(str.length()); ++i) {
111  if (!is_whitespace_char(str[i])) {
112  first_nonwhitespace_index = i;
113  break;
114  }
115  }
116  return str.substr(first_nonwhitespace_index);
117  }
118  return str;
119 }
120 
121 
122 /* Reads the 'line_number'th line from the file filename. */
123 std::string read_line_from_file(std::string filename, unsigned int line_number)
124 {
125  std::ifstream in(filename.c_str());
126  if (!in.is_open()) {
127  return "";
128  }
129  if (line_number == 0) {
130  return "Line number must be positive";
131  }
132  unsigned int n = 0;
133  std::string line;
134  while (n < line_number) {
135  if (in.eof())
136  return "Line not found";
137  getline(in, line);
138  n += 1; // loop update
139  }
140  return line;
141 }
142 
143 /* Demangles the function name if needed (if the 'name' is coming from C, it
144  doesn't have to be demangled, if it's coming from C++, it needs to be).
145 
146  Makes sure that it ends with (), which is automatic in C++, but it has to be
147  added by hand in C.
148 */
149 std::string demangle_function_name(std::string name)
150 {
151  std::string s;
152 
153  if (name.length() == 0) {
154  s = "??";
155  } else {
156  int status = 0;
157  char *d = 0;
158  d = abi::__cxa_demangle(name.c_str(), 0, 0, &status);
159  if (d) {
160  s = d;
161  free(d);
162  } else {
163  s = name + "()";
164  }
165  }
166 
167  return s;
168 }
169 
170 
171 #ifdef HAVE_TEUCHOS_BFD
172 
173 
174 /* Look for an address in a section. This is called via
175  bfd_map_over_sections over all sections in abfd.
176 
177  If the correct line is found, store the result in 'data' and set
178  data->line_found, so that subsequent calls to process_section exit
179  immediately.
180 */
181 void process_section(bfd *abfd, asection *section, void *_data)
182 {
183  line_data *data = (line_data*)_data;
184  if (data->line_found) {
185  // If we already found the line, exit
186  return;
187  }
188  if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) {
189  return;
190  }
191 
192  bfd_vma section_vma = bfd_get_section_vma(abfd, section);
193  if (data->addr < section_vma) {
194  // If the addr lies above the section, exit
195  return;
196  }
197 
198  bfd_size_type section_size = bfd_section_size(abfd, section);
199  if (data->addr >= section_vma + section_size) {
200  // If the addr lies below the section, exit
201  return;
202  }
203 
204  // Calculate the correct offset of our line in the section
205  bfd_vma offset = data->addr - section_vma - 1;
206 
207  // Finds the line corresponding to the offset
208 
209  const char *filename=NULL, *function_name=NULL;
210  data->line_found = bfd_find_nearest_line(abfd, section, data->symbol_table,
211  offset, &filename, &function_name, &data->line);
212 
213  if (filename == NULL)
214  data->filename = "";
215  else
216  data->filename = filename;
217 
218  if (function_name == NULL)
219  data->function_name = "";
220  else
221  data->function_name = function_name;
222 }
223 
224 
225 /* Loads the symbol table into 'data->symbol_table'. */
226 int load_symbol_table(bfd *abfd, line_data *data)
227 {
228  if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
229  // If we don't have any symbols, return
230  return 0;
231 
232  void **symbol_table_ptr = reinterpret_cast<void **>(&data->symbol_table);
233  long n_symbols;
234  unsigned int symbol_size;
235  n_symbols = bfd_read_minisymbols(abfd, false, symbol_table_ptr, &symbol_size);
236  if (n_symbols == 0) {
237  // If the bfd_read_minisymbols() already allocated the table, we need
238  // to free it first:
239  if (data->symbol_table != NULL)
240  free(data->symbol_table);
241  // dynamic
242  n_symbols = bfd_read_minisymbols(abfd, true, symbol_table_ptr, &symbol_size);
243  }
244 
245  if (n_symbols < 0) {
246  // bfd_read_minisymbols() failed
247  return 1;
248  }
249 
250  return 0;
251 }
252 
253 
254 #endif // HAVE_TEUCHOS_BFD
255 
256 
257 /* Returns a string of 2 lines for the function with address 'addr' in the file
258  'file_name'.
259 
260  Example:
261 
262  File "/home/ondrej/repos/rcp/src/Teuchos_RCP.hpp", line 428, in Teuchos::RCP<A>::assert_not_null() const
263  throw_null_ptr_error(typeName(*this));
264 */
265 std::string addr2str(std::string file_name, bfd_vma addr)
266 {
267 #ifdef HAVE_TEUCHOS_BFD
268  // Initialize 'abfd' and do some sanity checks
269  bfd *abfd;
270  abfd = bfd_openr(file_name.c_str(), NULL);
271  if (abfd == NULL)
272  return "Cannot open the binary file '" + file_name + "'\n";
273  if (bfd_check_format(abfd, bfd_archive))
274  return "Cannot get addresses from the archive '" + file_name + "'\n";
275  char **matching;
276  if (!bfd_check_format_matches(abfd, bfd_object, &matching))
277  return "Unknown format of the binary file '" + file_name + "'\n";
278  line_data data;
279  data.addr = addr;
280  data.symbol_table = NULL;
281  data.line_found = false;
282  // This allocates the symbol_table:
283  if (load_symbol_table(abfd, &data) == 1)
284  return "Failed to load the symbol table from '" + file_name + "'\n";
285  // Loops over all sections and try to find the line
286  bfd_map_over_sections(abfd, process_section, &data);
287  // Deallocates the symbol table
288  if (data.symbol_table != NULL) free(data.symbol_table);
289  bfd_close(abfd);
290 #else
291  line_data data;
292  data.line_found = 0;
293 #endif
294 
295  std::ostringstream s;
296  // Do the printing --- print as much information as we were able to
297  // find out
298  if (!data.line_found) {
299  // If we didn't find the line, at least print the address itself
300  s << " File unknown, address: 0x" << (long long unsigned int) addr;
301  } else {
302  std::string name = demangle_function_name(data.function_name);
303  if (data.filename.length() > 0) {
304  // Nicely format the filename + function name + line
305  s << " File \"" << data.filename << "\", line "
306  << data.line << ", in " << name;
307  const std::string line_text = remove_leading_whitespace(
308  read_line_from_file(data.filename, data.line));
309  if (line_text != "") {
310  s << "\n " << line_text;
311  }
312  } else {
313  // The file is unknown (and data.line == 0 in this case), so the
314  // only meaningful thing to print is the function name:
315  s << " File unknown, in " << name;
316  }
317  }
318  s << "\n";
319  return s.str();
320 }
321 
322 struct match_data {
323  bfd_vma addr;
324 
325  std::string filename;
326  bfd_vma addr_in_file;
327 };
328 
329 
330 #ifdef HAVE_TEUCHOS_LINK
331 
332 
333 /* Tries to find the 'data.addr' in the current shared lib (as passed in
334  'info'). If it succeeds, returns (in the 'data') the full path to the shared
335  lib and the local address in the file.
336 */
337 int shared_lib_callback(struct dl_phdr_info *info,
338  size_t size, void *_data)
339 {
340  struct match_data *data = (struct match_data *)_data;
341  for (int i=0; i < info->dlpi_phnum; i++) {
342  if (info->dlpi_phdr[i].p_type == PT_LOAD) {
343  ElfW(Addr) min_addr = info->dlpi_addr + info->dlpi_phdr[i].p_vaddr;
344  ElfW(Addr) max_addr = min_addr + info->dlpi_phdr[i].p_memsz;
345  if ((data->addr >= min_addr) && (data->addr < max_addr)) {
346  data->filename = info->dlpi_name;
347  data->addr_in_file = data->addr - info->dlpi_addr;
348  // We found a match, return a non-zero value
349  return 1;
350  }
351  }
352  }
353  // We didn't find a match, return a zero value
354  return 0;
355 }
356 
357 
358 #endif // HAVE_TEUCHOS_LINK
359 
360 // Class for creating a safe C++ interface to the raw void** stacktrace
361 // pointers, that we get from the backtrace() libc function. We make a copy of
362 // the addresses, so the caller can free the memory. We use std::vector to
363 // store the addresses internally, but this can be changed.
364 class StacktraceAddresses {
365  std::vector<bfd_vma> stacktrace_buffer;
366  int impl_stacktrace_depth;
367 public:
368  StacktraceAddresses(void *const *_stacktrace_buffer, int _size, int _impl_stacktrace_depth)
369  : impl_stacktrace_depth(_impl_stacktrace_depth)
370  {
371  for (int i=0; i < _size; i++)
372  stacktrace_buffer.push_back((bfd_vma) _stacktrace_buffer[i]);
373  }
374  bfd_vma get_address(int i) const {
375  return this->stacktrace_buffer[i];
376  }
377  int get_size() const {
378  return this->stacktrace_buffer.size();
379  }
380  int get_impl_stacktrace_depth() const {
381  return this->impl_stacktrace_depth;
382  }
383 };
384 
385 
386 /*
387  Returns a std::string with the stacktrace corresponding to the
388  list of addresses (of functions on the stack) in 'buffer'.
389 
390  It converts addresses to filenames, line numbers, function names and the
391  line text.
392 */
393 std::string stacktrace2str(const StacktraceAddresses &stacktrace_addresses)
394 {
395  int stack_depth = stacktrace_addresses.get_size() - 1;
396 
397  std::string full_stacktrace_str("Traceback (most recent call last):\n");
398 
399 #ifdef HAVE_TEUCHOS_BFD
400  bfd_init();
401 #endif
402  // Loop over the stack
403  const int stack_depth_start = stack_depth;
404  const int stack_depth_end = stacktrace_addresses.get_impl_stacktrace_depth();
405  for (int i=stack_depth_start; i >= stack_depth_end; i--) {
406  // Iterate over all loaded shared libraries (see dl_iterate_phdr(3) -
407  // Linux man page for more documentation)
408  struct match_data match;
409  match.addr = stacktrace_addresses.get_address(i);
410 #ifdef HAVE_TEUCHOS_BFD
411  if (dl_iterate_phdr(shared_lib_callback, &match) == 0)
412  return "dl_iterate_phdr() didn't find a match\n";
413 #else
414  match.filename = "";
415  match.addr_in_file = match.addr;
416 #endif
417 
418  if (match.filename.length() > 0) {
419  // This happens for shared libraries (like /lib/libc.so.6, or any
420  // other shared library that the project uses). 'match.filename'
421  // then contains the full path to the .so library.
422  full_stacktrace_str += addr2str(match.filename, match.addr_in_file);
423  } else {
424  // The 'addr_in_file' is from the current executable binary, that
425  // one can find at '/proc/self/exe'. So we'll use that.
426  full_stacktrace_str += addr2str("/proc/self/exe", match.addr_in_file);
427  }
428  }
429 
430  return full_stacktrace_str;
431 }
432 
433 
434 void loc_segfault_callback_print_stack(int sig_num)
435 {
438  *out << "\nSegfault caught. Printing stacktrace:\n\n";
439  Teuchos::show_stacktrace();
440  *out << "\nDone. Exiting the program.\n";
441  // Deregister our abort callback:
442  signal(SIGABRT, SIG_DFL);
443  abort();
444 }
445 
446 
447 void loc_abort_callback_print_stack(int sig_num)
448 {
451  *out << "\nAbort caught. Printing stacktrace:\n\n";
452  Teuchos::show_stacktrace();
453  *out << "\nDone.\n";
454 }
455 
456 
457 RCP<StacktraceAddresses> get_stacktrace_addresses(int impl_stacktrace_depth)
458 {
459  const int STACKTRACE_ARRAY_SIZE = 100; // 2010/05/22: rabartl: Is this large enough?
460  void *stacktrace_array[STACKTRACE_ARRAY_SIZE];
461  const size_t stacktrace_size = backtrace(stacktrace_array,
462  STACKTRACE_ARRAY_SIZE);
463  return rcp(new StacktraceAddresses(stacktrace_array, stacktrace_size,
464  impl_stacktrace_depth+1));
465 }
466 
467 
468 RCP<StacktraceAddresses> last_stacktrace;
469 
470 } // Unnamed namespace
471 
472 
473 // Public functions
474 
475 
476 void Teuchos::store_stacktrace()
477 {
478  const int impl_stacktrace_depth=1;
479  last_stacktrace = get_stacktrace_addresses(impl_stacktrace_depth);
480 }
481 
482 
483 std::string Teuchos::get_stored_stacktrace()
484 {
485  if (last_stacktrace == null) {
486  return "";
487  }
488  else {
489  return stacktrace2str(*last_stacktrace);
490  }
491 }
492 
493 
494 std::string Teuchos::get_stacktrace(int impl_stacktrace_depth)
495 {
496  RCP<StacktraceAddresses> addresses =
497  get_stacktrace_addresses(impl_stacktrace_depth+1);
498  return stacktrace2str(*addresses);
499 }
500 
501 
502 void Teuchos::show_stacktrace()
503 {
506  const int impl_stacktrace_depth=1;
507  *out << Teuchos::get_stacktrace(impl_stacktrace_depth);
508 }
509 
510 
511 void Teuchos::print_stack_on_segfault()
512 {
513  signal(SIGSEGV, loc_segfault_callback_print_stack);
514  signal(SIGABRT, loc_abort_callback_print_stack);
515 }
516 
517 
518 #endif // HAVE_TEUCHOS_STACKTRACE
519 
RCP< T > rcp(const boost::shared_ptr< T > &sptr)
Conversion function that takes in a boost::shared_ptr object and spits out a Teuchos::RCP object...
Functions for returning stacktrace info (GCC only initially).
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.
static RCP< FancyOStream > getDefaultOStream()
Get the default output stream object.
int size(const Comm< Ordinal > &comm)
Get the number of processes in the communicator.
Smart reference counting pointer class for automatic garbage collection.
Reference-counted pointer class and non-member templated function implementations.