Asterisk - The Open Source Telephony Project  18.5.0
backtrace.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2013, Digium, Inc.
5  *
6  * Matt Jordan <[email protected]>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  * \brief Asterisk backtrace generation
21  *
22  * This file provides backtrace generation utilities
23  */
24 
25 /*** MODULEINFO
26  <support_level>core</support_level>
27  ***/
28 
29 /*
30  * Block automatic include of asterisk/lock.h to allow use of pthread_mutex
31  * functions directly. We don't need or want the lock.h overhead.
32  */
33 #define _ASTERISK_LOCK_H
34 
35 /*
36  * The astmm ast_ memory management functions can cause ast_bt_get_symbols
37  * to be invoked so we must not use them.
38  */
39 #define ASTMM_LIBC ASTMM_IGNORE
40 
41 #include "asterisk.h"
42 #include "asterisk/backtrace.h"
43 
44 /*
45  * As stated above, the vector macros call the ast_ functions so
46  * we need to remap those back to the libc ones.
47  */
48 #undef ast_free
49 #undef ast_calloc
50 #undef ast_malloc
51 #define ast_free(x) free(x)
52 #define ast_calloc(n, x) calloc(n, x)
53 #define ast_malloc(x) malloc(x)
54 
55 #include "asterisk/vector.h"
56 
57 #ifdef HAVE_BKTR
58 #include <execinfo.h>
59 #if defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES)
60 #include <dlfcn.h>
61 #include <bfd.h>
62 #ifndef bfd_get_section_size
63 #define bfd_get_section_size(x) bfd_section_size(x)
64 #endif
65 #ifndef bfd_get_section_vma
66 #define bfd_get_section_vma(x, y) bfd_section_vma(y)
67 #endif
68 #ifndef bfd_get_section_flags
69 #define bfd_get_section_flags(x, y) bfd_section_flags(y)
70 #endif
71 #endif
72 
73 #include <pthread.h>
74 
75 /* simple definition of S_OR so we don't have include strings.h */
76 #define S_OR(a, b) (a && a[0] != '\0') ? a : b
77 
78 struct ast_bt *__ast_bt_create(void)
79 {
80  struct ast_bt *bt = calloc(1, sizeof(*bt));
81 
82  if (!bt) {
83  return NULL;
84  }
85  bt->alloced = 1;
86 
88 
89  return bt;
90 }
91 
93 {
94  bt->num_frames = backtrace(bt->addresses, AST_MAX_BT_FRAMES);
95  return 0;
96 }
97 
98 void *__ast_bt_destroy(struct ast_bt *bt)
99 {
100  if (bt && bt->alloced) {
101  free(bt);
102  }
103  return NULL;
104 }
105 
106 #ifdef BETTER_BACKTRACES
107 
108 struct bfd_data {
109  struct ast_vector_string *return_strings;
110  bfd_vma pc; /* bfd.h */
111  asymbol **syms; /* bfd.h */
112  Dl_info dli; /* dlfcn.h */
113  const char *libname;
114  int dynamic;
115  int has_syms;
116  char *msg;
117  int found;
118 };
119 
120 #define MSG_BUFF_LEN 1024
121 
122 static void process_section(bfd *bfdobj, asection *section, void *obj)
123 {
124  struct bfd_data *data = obj;
125  const char *file, *func;
126  unsigned int line;
127  bfd_vma offset;
128  bfd_vma vma;
129  bfd_size_type size;
130  bfd_boolean line_found = 0;
131  char *fn;
132  int inlined = 0;
133 
134  offset = data->pc - (data->dynamic ? (bfd_vma)(uintptr_t) data->dli.dli_fbase : 0);
135 
136  if (!(bfd_get_section_flags(bfdobj, section) & SEC_ALLOC)) {
137  return;
138  }
139 
140  vma = bfd_get_section_vma(bfdobj, section);
141  size = bfd_get_section_size(section);
142 
143  if (offset < vma || offset >= vma + size) {
144  /* Not in this section */
145  return;
146  }
147 
148  line_found = bfd_find_nearest_line(bfdobj, section, data->syms, offset - vma, &file,
149  &func, &line);
150  if (!line_found) {
151  return;
152  }
153 
154  /*
155  * If we find a line, we will want to continue calling bfd_find_inliner_info
156  * to capture any inlined functions that don't have their own stack frames.
157  */
158  do {
159  data->found++;
160  /* file can possibly be null even with a success result from bfd_find_nearest_line */
161  file = file ? file : "";
162  fn = strrchr(file, '/');
163 #define FMT_INLINED "[%s] %s %s:%u %s()"
164 #define FMT_NOT_INLINED "[%p] %s %s:%u %s()"
165 
166  snprintf(data->msg, MSG_BUFF_LEN, inlined ? FMT_INLINED : FMT_NOT_INLINED,
167  inlined ? "inlined" : (char *)(uintptr_t) data->pc,
168  data->libname,
169  fn ? fn + 1 : file,
170  line, S_OR(func, "???"));
171 
172  if (AST_VECTOR_APPEND(data->return_strings, strdup(data->msg))) {
173  return;
174  }
175 
176  inlined++;
177  /* Let's see if there are any inlined functions */
178  } while (bfd_find_inliner_info(bfdobj, &file, &func, &line));
179 }
180 
181 struct ast_vector_string *__ast_bt_get_symbols(void **addresses, size_t num_frames)
182 {
183  struct ast_vector_string *return_strings;
184  int stackfr;
185  bfd *bfdobj;
186  long allocsize;
187  char msg[MSG_BUFF_LEN];
188  static pthread_mutex_t bfd_mutex = PTHREAD_MUTEX_INITIALIZER;
189 
190  return_strings = malloc(sizeof(struct ast_vector_string));
191  if (!return_strings) {
192  return NULL;
193  }
194  if (AST_VECTOR_INIT(return_strings, num_frames)) {
195  free(return_strings);
196  return NULL;
197  }
198 
199  for (stackfr = 0; stackfr < num_frames; stackfr++) {
200  int symbolcount;
201  struct bfd_data data = {
202  .return_strings = return_strings,
203  .msg = msg,
204  .pc = (bfd_vma)(uintptr_t) addresses[stackfr],
205  .found = 0,
206  .dynamic = 0,
207  };
208 
209  msg[0] = '\0';
210 
211  if (!dladdr((void *)(uintptr_t) data.pc, &data.dli)) {
212  continue;
213  }
214  data.libname = strrchr(data.dli.dli_fname, '/');
215  if (!data.libname) {
216  data.libname = data.dli.dli_fname;
217  } else {
218  data.libname++;
219  }
220 
221  pthread_mutex_lock(&bfd_mutex);
222  /* Using do while(0) here makes it easier to escape and clean up */
223  do {
224  bfdobj = bfd_openr(data.dli.dli_fname, NULL);
225  if (!bfdobj) {
226  break;
227  }
228 
229  /* bfd_check_format does more than check. It HAS to be called */
230  if (!bfd_check_format(bfdobj, bfd_object)) {
231  break;
232  }
233 
234  data.has_syms = !!(bfd_get_file_flags(bfdobj) & HAS_SYMS);
235  data.dynamic = !!(bfd_get_file_flags(bfdobj) & DYNAMIC);
236 
237  if (!data.has_syms) {
238  break;
239  }
240 
241  allocsize = data.dynamic ?
242  bfd_get_dynamic_symtab_upper_bound(bfdobj) : bfd_get_symtab_upper_bound(bfdobj);
243  if (allocsize < 0) {
244  break;
245  }
246 
247  data.syms = malloc(allocsize);
248  if (!data.syms) {
249  break;
250  }
251 
252  symbolcount = data.dynamic ?
253  bfd_canonicalize_dynamic_symtab(bfdobj, data.syms) : bfd_canonicalize_symtab(bfdobj, data.syms);
254  if (symbolcount < 0) {
255  break;
256  }
257 
258  bfd_map_over_sections(bfdobj, process_section, &data);
259  } while(0);
260 
261  if (bfdobj) {
262  bfd_close(bfdobj);
263  free(data.syms);
264  data.syms = NULL;
265  }
266  pthread_mutex_unlock(&bfd_mutex);
267 
268  /* Default output, if we cannot find the information within BFD */
269  if (!data.found) {
270  snprintf(msg, sizeof(msg), "%s %s()",
271  data.libname,
272  S_OR(data.dli.dli_sname, "<unknown>"));
273  AST_VECTOR_APPEND(return_strings, strdup(msg));
274  }
275  }
276 
277  return return_strings;
278 }
279 
280 #else
281 struct ast_vector_string *__ast_bt_get_symbols(void **addresses, size_t num_frames)
282 {
283  char **strings;
284  struct ast_vector_string *return_strings;
285  int i;
286 
287  return_strings = malloc(sizeof(struct ast_vector_string));
288  if (!return_strings) {
289  return NULL;
290  }
291  if (AST_VECTOR_INIT(return_strings, num_frames)) {
292  free(return_strings);
293  return NULL;
294  }
295 
296  strings = backtrace_symbols(addresses, num_frames);
297  if (strings) {
298  for (i = 0; i < num_frames; i++) {
299  AST_VECTOR_APPEND(return_strings, strdup(strings[i]));
300  }
301  free(strings);
302  }
303 
304  return return_strings;
305 }
306 #endif /* BETTER_BACKTRACES */
307 
308 void __ast_bt_free_symbols(struct ast_vector_string *symbols)
309 {
310  AST_VECTOR_CALLBACK_VOID(symbols, free);
311  AST_VECTOR_PTR_FREE(symbols);
312 }
313 
314 #endif /* HAVE_BKTR */
Asterisk main include file. File version handling, generic pbx functions.
Asterisk backtrace generation.
#define AST_MAX_BT_FRAMES
Definition: backtrace.h:29
int __ast_bt_get_addresses(struct ast_bt *bt)
Definition: backtrace.c:92
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
struct ast_bt * __ast_bt_create(void)
Definition: backtrace.c:78
#define NULL
Definition: resample.c:96
struct ast_vector_string * __ast_bt_get_symbols(void **addresses, size_t num_frames)
Definition: backtrace.c:281
#define pthread_mutex_t
Definition: lock.h:620
#define calloc(a, b)
Definition: astmm.h:157
char * malloc()
void __ast_bt_free_symbols(struct ast_vector_string *symbols)
Definition: backtrace.c:308
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
void free()
#define pthread_mutex_lock
Definition: lock.h:623
unsigned int alloced
Definition: backtrace.h:56
Vector container support.
void * addresses[AST_MAX_BT_FRAMES]
Definition: backtrace.h:52
#define AST_VECTOR_PTR_FREE(vec)
Deallocates this vector pointer.
Definition: vector.h:189
int num_frames
Definition: backtrace.h:54
#define pthread_mutex_unlock
Definition: lock.h:624
#define strdup(a)
Definition: astmm.h:165
#define S_OR(a, b)
Definition: backtrace.c:76
#define ast_bt_get_addresses(bt)
Definition: backtrace.h:38
#define AST_VECTOR_CALLBACK_VOID(vec, callback,...)
Execute a callback on every element in a vector disregarding callback return.
Definition: vector.h:865
void * __ast_bt_destroy(struct ast_bt *bt)
Definition: backtrace.c:98