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