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