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