tlx
backtrace.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * tlx/backtrace.cpp
3  *
4  * Part of tlx - http://panthema.net/tlx
5  *
6  * Copyright (C) 2008-2017 Timo Bingmann <tb@panthema.net>
7  *
8  * All rights reserved. Published under the Boost Software License, Version 1.0
9  ******************************************************************************/
10 
11 #include <tlx/backtrace.hpp>
12 
13 #include <tlx/unused.hpp>
14 
15 #include <cstdarg>
16 #include <cstdio>
17 #include <cstdlib>
18 
19 #if __linux__
20 
21 #include <cxxabi.h>
22 #include <execinfo.h>
23 #include <signal.h>
24 #include <unistd.h>
25 
26 #endif
27 
28 namespace tlx {
29 
30 void print_raw_backtrace(FILE* out, unsigned int max_frames,
31  const char* fmt, ...) {
32  char buffer[1024];
33  size_t p = 0;
34 
35  va_list args;
36  va_start(args, fmt);
37 
38  p += vsnprintf(buffer + p, sizeof(buffer) - p - 1, fmt, args);
39 
40 #if __linux__
41 
42  // storage array for stack trace address data
43  void** addrlist = reinterpret_cast<void**>(
44  alloca(sizeof(void*) * max_frames));
45 
46  // retrieve current stack addresses
47  int addrlen = backtrace(addrlist, max_frames);
48 
49  for (int i = 1; i < addrlen; ++i) {
50  if (addrlist[i] == nullptr)
51  break;
52 
53  p += snprintf(buffer + p, sizeof(buffer) - p - 1, " %p", addrlist[i]);
54  }
55 
56  buffer[p + 1] = 0;
57  fprintf(out, "%s\n", buffer);
58 
59 #else
60 
61  fprintf(out, "(backtrace not supported on this platform)\n");
62  tlx::unused(max_frames);
63 
64 #endif
65 
66  va_end(args);
67 }
68 
69 void print_raw_backtrace(FILE* out, unsigned int max_frames) {
70  return print_raw_backtrace(out, max_frames, "backtrace:");
71 }
72 
73 void print_cxx_backtrace(FILE* out, unsigned int max_frames) {
74  fprintf(out, "backtrace:\n");
75 
76 #if __linux__
77 
78  // storage array for stack trace address data
79  void** addrlist = reinterpret_cast<void**>(
80  alloca(sizeof(void*) * max_frames));
81 
82  // retrieve current stack addresses
83  int addrlen = backtrace(addrlist, max_frames);
84 
85  if (addrlen == 0) {
86  fprintf(out, " <empty, possibly corrupt>\n");
87  return;
88  }
89 
90  // resolve addresses into strings containing "filename(function+address)",
91  // this array must be free()-ed
92  char** symbollist = backtrace_symbols(addrlist, addrlen);
93 
94  // allocate string which will be filled with the demangled function name
95  size_t funcnamesize = 256;
96  char* funcname = reinterpret_cast<char*>(alloca(funcnamesize));
97 
98  // iterate over the returned symbol lines. skip the first, it is the
99  // address of this function.
100  for (int i = 1; i < addrlen; i++)
101  {
102  char* begin_name = 0, * begin_offset = 0, * end_offset = 0;
103 
104  // find parentheses and +address offset surrounding the mangled name:
105  // ./module(function+0x15c) [0x8048a6d]
106  for (char* p = symbollist[i]; *p; ++p)
107  {
108  if (*p == '(')
109  begin_name = p;
110  else if (*p == '+')
111  begin_offset = p;
112  else if (*p == ')' && begin_offset) {
113  end_offset = p;
114  break;
115  }
116  }
117 
118  if (begin_name && begin_offset && end_offset
119  && begin_name < begin_offset)
120  {
121  *begin_name++ = '\0';
122  *begin_offset++ = '\0';
123  *end_offset = '\0';
124 
125  // mangled name is now in [begin_name, begin_offset) and caller
126  // offset in [begin_offset, end_offset). now apply
127  // __cxa_demangle():
128 
129  int status;
130  char* ret = abi::__cxa_demangle(begin_name,
131  funcname, &funcnamesize, &status);
132  if (status == 0) {
133  funcname = ret; // use possibly realloc()-ed string
134  fprintf(out, " %s : %s+%s\n",
135  symbollist[i], funcname, begin_offset);
136  }
137  else {
138  // demangling failed. Output function name as a C function with
139  // no arguments.
140  fprintf(out, " %s : %s()+%s\n",
141  symbollist[i], begin_name, begin_offset);
142  }
143  }
144  else
145  {
146  // couldn't parse the line? print the whole line.
147  fprintf(out, " %s\n", symbollist[i]);
148  }
149  }
150 
151  free(symbollist);
152 
153 #else
154 
155  fprintf(out, " (not supported on this platform)\n");
156  tlx::unused(max_frames);
157 
158 #endif
159 }
160 
161 #if __linux__
162 
163 static void segv_backtrace_handler(int sig) {
164  tlx::unused(sig);
165 
166  void* addr[16];
167  size_t size;
168 
169  size = backtrace(addr, 16);
170 
171  fprintf(stderr, "Caught SIGSEGV (segmentation fault). Backtrace:\n");
172  backtrace_symbols_fd(addr + 1, size - 1, STDERR_FILENO);
173  exit(1);
174 }
175 
176 #endif
177 
179 #if __linux__
180  signal(SIGSEGV, segv_backtrace_handler);
181 #else
182  printf("enable_segv_backtrace(): not supported on this platform.\n");
183 #endif
184 }
185 
186 } // namespace tlx
187 
188 /******************************************************************************/
void unused(Types &&...)
Definition: unused.hpp:20
void print_cxx_backtrace(FILE *out, unsigned int max_frames)
Print a demangled stack backtrace of the caller function to FILE* out.
Definition: backtrace.cpp:73
void print_raw_backtrace(FILE *out, unsigned int max_frames, const char *fmt,...)
Print a plain hex stack backtrace of the called function to FILE* out, prefixed with the given printf...
Definition: backtrace.cpp:30
void enable_segv_backtrace()
Install SIGSEGV signal handler and output backtrace on segmentation fault.
Definition: backtrace.cpp:178