/src/strongswan/src/libstrongswan/utils/backtrace.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2006-2013 Martin Willi |
3 | | * |
4 | | * Copyright (C) secunet Security Networks AG |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify it |
7 | | * under the terms of the GNU General Public License as published by the |
8 | | * Free Software Foundation; either version 2 of the License, or (at your |
9 | | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, but |
12 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
13 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | | * for more details. |
15 | | */ |
16 | | |
17 | | #define _GNU_SOURCE |
18 | | #include <string.h> |
19 | | |
20 | | #ifdef HAVE_BACKTRACE |
21 | | #include <execinfo.h> |
22 | | #endif /* HAVE_BACKTRACE */ |
23 | | |
24 | | #ifdef WIN32 |
25 | | #include <winsock2.h> |
26 | | #include <windows.h> |
27 | | #ifdef HAVE_DBGHELP |
28 | | #include <dbghelp.h> |
29 | | #endif /* HAVE_DBGHELP */ |
30 | | |
31 | | #include <psapi.h> |
32 | | /* missing in MinGW */ |
33 | | #ifdef WIN64 |
34 | | #ifndef GetModuleInformation |
35 | | WINBOOL K32GetModuleInformation(HANDLE hProcess, HMODULE hModule, |
36 | | LPMODULEINFO lpmodinfo, DWORD cb); |
37 | | #define GetModuleInformation K32GetModuleInformation |
38 | | #endif /* !GetModuleInformation */ |
39 | | #ifndef GetModuleFileNameEx |
40 | | DWORD K32GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, |
41 | | LPTSTR lpFilename, DWORD nSize); |
42 | | #define GetModuleFileNameEx K32GetModuleFileNameExA |
43 | | #endif /* !GetModuleFileNameEx */ |
44 | | #endif /* WIN64 */ |
45 | | #endif |
46 | | |
47 | | #include "backtrace.h" |
48 | | |
49 | | #include <utils/debug.h> |
50 | | |
51 | | typedef struct private_backtrace_t private_backtrace_t; |
52 | | |
53 | | /** |
54 | | * Private data of an backtrace_t object. |
55 | | */ |
56 | | struct private_backtrace_t { |
57 | | |
58 | | /** |
59 | | * Public backtrace_t interface. |
60 | | */ |
61 | | backtrace_t public; |
62 | | |
63 | | /** |
64 | | * Number of stacks frames obtained in stack_frames |
65 | | */ |
66 | | int frame_count; |
67 | | |
68 | | /** |
69 | | * Recorded stack frames. |
70 | | */ |
71 | | void *frames[]; |
72 | | }; |
73 | | |
74 | | /** |
75 | | * Forward declaration of method getter |
76 | | */ |
77 | | static backtrace_t get_methods(); |
78 | | |
79 | | /** |
80 | | * Write a format string with arguments to a FILE line, if it is NULL to DBG |
81 | | */ |
82 | | static void println(FILE *file, char *format, ...) |
83 | 0 | { |
84 | 0 | char buf[512]; |
85 | 0 | va_list args; |
86 | |
|
87 | 0 | va_start(args, format); |
88 | 0 | if (file) |
89 | 0 | { |
90 | 0 | vfprintf(file, format, args); |
91 | 0 | fputs("\n", file); |
92 | 0 | } |
93 | 0 | else |
94 | 0 | { |
95 | 0 | vsnprintf(buf, sizeof(buf), format, args); |
96 | 0 | DBG1(DBG_LIB, "%s", buf); |
97 | 0 | } |
98 | 0 | va_end(args); |
99 | 0 | } |
100 | | |
101 | | #if ((defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H)) && \ |
102 | | defined(HAVE_DLADDR)) || defined(WIN32) |
103 | | |
104 | | /** |
105 | | * Same as tty_escape_get(), but for a potentially NULL FILE* |
106 | | */ |
107 | | static inline char* esc(FILE *file, tty_escape_t escape) |
108 | 0 | { |
109 | 0 | if (file) |
110 | 0 | { |
111 | 0 | return tty_escape_get(fileno(file), escape); |
112 | 0 | } |
113 | 0 | return ""; |
114 | 0 | } |
115 | | |
116 | | #endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H/WIN32 */ |
117 | | |
118 | | #ifdef HAVE_DBGHELP |
119 | | |
120 | | #include <dbghelp.h> |
121 | | #include <threading/mutex.h> |
122 | | |
123 | | /** |
124 | | * Mutex to access non-thread-safe dbghelp functions |
125 | | */ |
126 | | static mutex_t *dbghelp_mutex; |
127 | | |
128 | | void backtrace_init() |
129 | | { |
130 | | SymSetOptions(SYMOPT_LOAD_LINES); |
131 | | SymInitialize(GetCurrentProcess(), NULL, TRUE); |
132 | | dbghelp_mutex = mutex_create(MUTEX_TYPE_DEFAULT); |
133 | | } |
134 | | |
135 | | void backtrace_deinit() |
136 | | { |
137 | | dbghelp_mutex->destroy(dbghelp_mutex); |
138 | | SymCleanup(GetCurrentProcess()); |
139 | | } |
140 | | |
141 | | #elif defined(HAVE_DLADDR) || defined(HAVE_BFD_H) |
142 | | |
143 | | #ifdef HAVE_DLADDR |
144 | | #include <dlfcn.h> |
145 | | #endif |
146 | | |
147 | | #ifdef HAVE_BFD_H |
148 | | |
149 | | #include <bfd.h> |
150 | | #include <collections/hashtable.h> |
151 | | #include <threading/mutex.h> |
152 | | |
153 | | /* interface changes for newer BFD versions, note that older versions declared |
154 | | * some of the new functions as macros but with different arguments */ |
155 | | #if HAVE_DECL_BFD_GET_SECTION_FLAGS |
156 | | #define get_section_flags(a, s) bfd_get_section_flags(a, s) |
157 | | #elif HAVE_DECL_BFD_SECTION_FLAGS |
158 | | #define get_section_flags(a, s) bfd_section_flags(s) |
159 | | #else |
160 | | #error Unknown BFD API |
161 | | #endif |
162 | | |
163 | | #if HAVE_DECL_BFD_GET_SECTION_VMA |
164 | | #define get_section_vma(a, s) bfd_get_section_vma(a, s) |
165 | | #elif HAVE_DECL_BFD_SECTION_VMA |
166 | | #define get_section_vma(a, s) bfd_section_vma(s) |
167 | | #else |
168 | | #error Unknown BFD API |
169 | | #endif |
170 | | |
171 | | #if HAVE_DECL_BFD_GET_SECTION_SIZE |
172 | | #define get_section_size bfd_get_section_size |
173 | | #elif HAVE_DECL_BFD_SECTION_SIZE |
174 | | #define get_section_size bfd_section_size |
175 | | #else |
176 | | #error Unknown BFD API |
177 | | #endif |
178 | | |
179 | | /** |
180 | | * Hashtable-cached bfd handle |
181 | | */ |
182 | | typedef struct { |
183 | | /** binary file name on disk */ |
184 | | char *filename; |
185 | | /** bfd handle */ |
186 | | bfd *abfd; |
187 | | /** loaded symbols */ |
188 | | asymbol **syms; |
189 | | } bfd_entry_t; |
190 | | |
191 | | /** |
192 | | * Destroy a bfd_entry |
193 | | */ |
194 | | static void bfd_entry_destroy(bfd_entry_t *this) |
195 | | { |
196 | | free(this->filename); |
197 | | free(this->syms); |
198 | | bfd_close(this->abfd); |
199 | | free(this); |
200 | | } |
201 | | |
202 | | /** |
203 | | * Data to pass to find_addr() |
204 | | */ |
205 | | typedef struct { |
206 | | /** used bfd entry */ |
207 | | bfd_entry_t *entry; |
208 | | /** backtrace address */ |
209 | | bfd_vma vma; |
210 | | /** TRUE if address found */ |
211 | | bool found; |
212 | | /** optional stream to log to */ |
213 | | FILE *file; |
214 | | /** optional list of function names to match */ |
215 | | char **list; |
216 | | /** optional number of names in list */ |
217 | | int count; |
218 | | /** TRUE if found function name is in list */ |
219 | | bool in_list; |
220 | | } bfd_find_data_t; |
221 | | |
222 | | /** |
223 | | * bfd entry cache |
224 | | */ |
225 | | static hashtable_t *bfds; |
226 | | |
227 | | static mutex_t *bfd_mutex; |
228 | | |
229 | | /** |
230 | | * Hashtable hash function |
231 | | */ |
232 | | static u_int bfd_hash(char *key) |
233 | | { |
234 | | return chunk_hash(chunk_create(key, strlen(key))); |
235 | | } |
236 | | |
237 | | /** |
238 | | * Hashtable equals function |
239 | | */ |
240 | | static bool bfd_equals(char *a, char *b) |
241 | | { |
242 | | return streq(a, b); |
243 | | } |
244 | | |
245 | | /** |
246 | | * Do not print internal errors by libbfd as we get quite a lot of |
247 | | * "DWARF error: could not find variable specification" messages when running |
248 | | * against Ubuntu 20.04's libcrypto (same with addr2line and still in 22.10). |
249 | | */ |
250 | | void suppress_bfd_errors (const char *fmt, va_list args) |
251 | | { |
252 | | } |
253 | | |
254 | | /** |
255 | | * See header. |
256 | | */ |
257 | | void backtrace_init() |
258 | | { |
259 | | bfd_init(); |
260 | | bfds = hashtable_create((hashtable_hash_t)bfd_hash, |
261 | | (hashtable_equals_t)bfd_equals, 8); |
262 | | bfd_mutex = mutex_create(MUTEX_TYPE_DEFAULT); |
263 | | bfd_set_error_handler(suppress_bfd_errors); |
264 | | } |
265 | | |
266 | | /** |
267 | | * See header. |
268 | | */ |
269 | | void backtrace_deinit() |
270 | | { |
271 | | enumerator_t *enumerator; |
272 | | bfd_entry_t *entry; |
273 | | char *key; |
274 | | |
275 | | enumerator = bfds->create_enumerator(bfds); |
276 | | while (enumerator->enumerate(enumerator, &key, &entry)) |
277 | | { |
278 | | bfds->remove_at(bfds, enumerator); |
279 | | bfd_entry_destroy(entry); |
280 | | } |
281 | | enumerator->destroy(enumerator); |
282 | | |
283 | | bfds->destroy(bfds); |
284 | | bfd_mutex->destroy(bfd_mutex); |
285 | | } |
286 | | |
287 | | /** |
288 | | * Find and print information to an address |
289 | | */ |
290 | | static void find_addr(bfd *abfd, asection *section, bfd_find_data_t *data) |
291 | | { |
292 | | bfd_size_type size; |
293 | | bfd_vma vma; |
294 | | const char *source; |
295 | | const char *function; |
296 | | char fbuf[512] = "", sbuf[512] = ""; |
297 | | u_int line; |
298 | | int i; |
299 | | |
300 | | if (data->found || (get_section_flags(abfd, section) & SEC_ALLOC) == 0) |
301 | | { |
302 | | return; |
303 | | } |
304 | | vma = get_section_vma(abfd, section); |
305 | | if (data->vma < vma) |
306 | | { |
307 | | return; |
308 | | } |
309 | | size = get_section_size(section); |
310 | | if (data->vma >= vma + size) |
311 | | { |
312 | | return; |
313 | | } |
314 | | |
315 | | data->found = bfd_find_nearest_line(abfd, section, data->entry->syms, |
316 | | data->vma - vma, &source, &function, |
317 | | &line); |
318 | | if (!data->found) |
319 | | { |
320 | | return; |
321 | | } |
322 | | if (data->count && function) |
323 | | { |
324 | | for (i = 0; i < data->count; i++) |
325 | | { |
326 | | if (streq(function, data->list[i])) |
327 | | { |
328 | | data->in_list = TRUE; |
329 | | break; |
330 | | } |
331 | | } |
332 | | } |
333 | | else if (data->file && (source || function)) |
334 | | { |
335 | | if (function) |
336 | | { |
337 | | snprintf(fbuf, sizeof(fbuf), "%s%s() ", |
338 | | esc(data->file, TTY_FG_BLUE), function); |
339 | | } |
340 | | if (source) |
341 | | { |
342 | | snprintf(sbuf, sizeof(sbuf), "%s@ %s:%d", |
343 | | esc(data->file, TTY_FG_GREEN), source, line); |
344 | | } |
345 | | println(data->file, " -> %s%s%s", fbuf, sbuf, |
346 | | esc(data->file, TTY_FG_DEF)); |
347 | | } |
348 | | } |
349 | | |
350 | | /** |
351 | | * Find a cached bfd entry, create'n'cache if not found |
352 | | */ |
353 | | static bfd_entry_t *get_bfd_entry(char *filename) |
354 | | { |
355 | | bool dynamic = FALSE, ok = FALSE; |
356 | | bfd_entry_t *entry; |
357 | | long size; |
358 | | |
359 | | /* check cache */ |
360 | | entry = bfds->get(bfds, filename); |
361 | | if (entry) |
362 | | { |
363 | | return entry; |
364 | | } |
365 | | |
366 | | INIT(entry, |
367 | | .abfd = bfd_openr(filename, NULL), |
368 | | ); |
369 | | |
370 | | if (!entry->abfd) |
371 | | { |
372 | | free(entry); |
373 | | return NULL; |
374 | | } |
375 | | #ifdef BFD_DECOMPRESS |
376 | | entry->abfd->flags |= BFD_DECOMPRESS; |
377 | | #endif |
378 | | if (bfd_check_format(entry->abfd, bfd_archive) == 0 && |
379 | | bfd_check_format_matches(entry->abfd, bfd_object, NULL)) |
380 | | { |
381 | | if (bfd_get_file_flags(entry->abfd) & HAS_SYMS) |
382 | | { |
383 | | size = bfd_get_symtab_upper_bound(entry->abfd); |
384 | | if (size == 0) |
385 | | { |
386 | | size = bfd_get_dynamic_symtab_upper_bound(entry->abfd); |
387 | | dynamic = TRUE; |
388 | | } |
389 | | if (size >= 0) |
390 | | { |
391 | | entry->syms = malloc(size); |
392 | | if (dynamic) |
393 | | { |
394 | | ok = bfd_canonicalize_dynamic_symtab(entry->abfd, |
395 | | entry->syms) >= 0; |
396 | | } |
397 | | else |
398 | | { |
399 | | ok = bfd_canonicalize_symtab(entry->abfd, |
400 | | entry->syms) >= 0; |
401 | | } |
402 | | } |
403 | | } |
404 | | } |
405 | | if (ok) |
406 | | { |
407 | | entry->filename = strdup(filename); |
408 | | bfds->put(bfds, entry->filename, entry); |
409 | | return entry; |
410 | | } |
411 | | bfd_entry_destroy(entry); |
412 | | return NULL; |
413 | | } |
414 | | |
415 | | /** |
416 | | * Lookup the given address |
417 | | */ |
418 | | static void lookup_addr(char *filename, bfd_find_data_t *data) |
419 | | { |
420 | | bfd_entry_t *entry; |
421 | | bool old = FALSE; |
422 | | |
423 | | bfd_mutex->lock(bfd_mutex); |
424 | | if (lib && lib->leak_detective) |
425 | | { |
426 | | old = lib->leak_detective->set_state(lib->leak_detective, FALSE); |
427 | | } |
428 | | entry = get_bfd_entry(filename); |
429 | | if (entry) |
430 | | { |
431 | | data->entry = entry; |
432 | | bfd_map_over_sections(entry->abfd, (void*)find_addr, data); |
433 | | } |
434 | | if (lib && lib->leak_detective) |
435 | | { |
436 | | lib->leak_detective->set_state(lib->leak_detective, old); |
437 | | } |
438 | | bfd_mutex->unlock(bfd_mutex); |
439 | | } |
440 | | |
441 | | /** |
442 | | * Print the source file with line number to file, libbfd variant |
443 | | */ |
444 | | static void print_sourceline(FILE *file, char *filename, void *ptr, void *base) |
445 | | { |
446 | | bfd_find_data_t data = { |
447 | | .file = file, |
448 | | .vma = (uintptr_t)ptr, |
449 | | }; |
450 | | |
451 | | lookup_addr(filename, &data); |
452 | | } |
453 | | |
454 | | /** |
455 | | * Check if the function name of the source line is in the given list |
456 | | */ |
457 | | static bool contains_function_bfd(char *filename, void *ptr, char *list[], |
458 | | int count) |
459 | | { |
460 | | bfd_find_data_t data = { |
461 | | .vma = (uintptr_t)ptr, |
462 | | .list = list, |
463 | | .count = count, |
464 | | }; |
465 | | |
466 | | lookup_addr(filename, &data); |
467 | | return data.in_list; |
468 | | } |
469 | | |
470 | | #else /* !HAVE_BFD_H */ |
471 | | |
472 | 3.92k | void backtrace_init() {} |
473 | 3.92k | void backtrace_deinit() {} |
474 | | |
475 | | #if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) || defined(WIN32) |
476 | | |
477 | | /** |
478 | | * Print the source file with line number to file, slow addr2line variant |
479 | | */ |
480 | | static void print_sourceline(FILE *file, char *filename, void *ptr, void* base) |
481 | 0 | { |
482 | 0 | char buf[1024]; |
483 | 0 | FILE *output; |
484 | 0 | int c, i = 0; |
485 | |
|
486 | | #ifdef __APPLE__ |
487 | | snprintf(buf, sizeof(buf), "atos -o %s -l %p %p 2>&1 | tail -n1", |
488 | | filename, base, ptr); |
489 | | #else /* !__APPLE__ */ |
490 | 0 | snprintf(buf, sizeof(buf), "addr2line -e %s %p", filename, ptr); |
491 | 0 | #endif /* __APPLE__ */ |
492 | |
|
493 | 0 | output = popen(buf, "r"); |
494 | 0 | if (output) |
495 | 0 | { |
496 | 0 | while (i < sizeof(buf)) |
497 | 0 | { |
498 | 0 | c = getc(output); |
499 | 0 | if (c == '\n' || c == EOF) |
500 | 0 | { |
501 | 0 | buf[i++] = 0; |
502 | 0 | break; |
503 | 0 | } |
504 | 0 | buf[i++] = c; |
505 | 0 | } |
506 | 0 | pclose(output); |
507 | |
|
508 | 0 | println(file, " -> %s%s%s", esc(file, TTY_FG_GREEN), buf, |
509 | 0 | esc(file, TTY_FG_DEF)); |
510 | 0 | } |
511 | 0 | } |
512 | | |
513 | | #endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H/WIN32 */ |
514 | | |
515 | | #endif /* HAVE_BFD_H */ |
516 | | |
517 | | #else /* !HAVE_DLADDR && !HAVE_DBGHELP */ |
518 | | |
519 | | void backtrace_init() {} |
520 | | void backtrace_deinit() {} |
521 | | |
522 | | #endif /* HAVE_DLADDR */ |
523 | | |
524 | | METHOD(backtrace_t, log_, void, |
525 | | private_backtrace_t *this, FILE *file, bool detailed) |
526 | 0 | { |
527 | 0 | #if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) || defined(WIN32) |
528 | 0 | size_t i; |
529 | 0 | char **strings = NULL; |
530 | |
|
531 | 0 | println(file, " dumping %d stack frame addresses:", this->frame_count); |
532 | 0 | for (i = 0; i < this->frame_count; i++) |
533 | 0 | { |
534 | 0 | #ifdef HAVE_DLADDR |
535 | 0 | Dl_info info; |
536 | |
|
537 | 0 | if (dladdr(this->frames[i], &info)) |
538 | 0 | { |
539 | 0 | void *ptr = this->frames[i]; |
540 | |
|
541 | 0 | if (strstr(info.dli_fname, ".so")) |
542 | 0 | { |
543 | 0 | ptr = (void*)(this->frames[i] - info.dli_fbase); |
544 | 0 | } |
545 | 0 | if (info.dli_sname) |
546 | 0 | { |
547 | 0 | println(file, " %s%s%s @ %p (%s%s%s+0x%tx) [%p]", |
548 | 0 | esc(file, TTY_FG_YELLOW), info.dli_fname, |
549 | 0 | esc(file, TTY_FG_DEF), info.dli_fbase, |
550 | 0 | esc(file, TTY_FG_RED), info.dli_sname, |
551 | 0 | esc(file, TTY_FG_DEF), this->frames[i] - info.dli_saddr, |
552 | 0 | this->frames[i]); |
553 | 0 | } |
554 | 0 | else |
555 | 0 | { |
556 | 0 | println(file, " %s%s%s @ %p [%p]", |
557 | 0 | esc(file, TTY_FG_YELLOW), info.dli_fname, |
558 | 0 | esc(file, TTY_FG_DEF), info.dli_fbase, this->frames[i]); |
559 | 0 | } |
560 | 0 | if (detailed && info.dli_fname[0]) |
561 | 0 | { |
562 | 0 | print_sourceline(file, (char*)info.dli_fname, |
563 | 0 | ptr, info.dli_fbase); |
564 | 0 | } |
565 | 0 | } |
566 | 0 | else |
567 | | #elif defined(HAVE_DBGHELP) |
568 | | struct { |
569 | | SYMBOL_INFO hdr; |
570 | | char buf[128]; |
571 | | } symbol; |
572 | | char filename[MAX_PATH]; |
573 | | HINSTANCE module; |
574 | | HANDLE process; |
575 | | DWORD64 displace, frame; |
576 | | |
577 | | process = GetCurrentProcess(); |
578 | | frame = (uintptr_t)this->frames[i]; |
579 | | |
580 | | memset(&symbol, 0, sizeof(symbol)); |
581 | | symbol.hdr.SizeOfStruct = sizeof(symbol.hdr); |
582 | | symbol.hdr.MaxNameLen = sizeof(symbol.buf) - 1; |
583 | | |
584 | | dbghelp_mutex->lock(dbghelp_mutex); |
585 | | |
586 | | module = (HINSTANCE)SymGetModuleBase64(process, frame); |
587 | | |
588 | | if (module && GetModuleFileName(module, filename, sizeof(filename))) |
589 | | { |
590 | | if (SymFromAddr(process, frame, &displace, &symbol.hdr)) |
591 | | { |
592 | | println(file, " %s%s%s @ %p (%s%s%s+0x%tx) [%p]", |
593 | | esc(file, TTY_FG_YELLOW), filename, |
594 | | esc(file, TTY_FG_DEF), (void*)module, |
595 | | esc(file, TTY_FG_RED), symbol.hdr.Name, |
596 | | esc(file, TTY_FG_DEF), displace, |
597 | | this->frames[i]); |
598 | | } |
599 | | else |
600 | | { |
601 | | println(file, " %s%s%s @ %p [%p]", |
602 | | esc(file, TTY_FG_YELLOW), filename, |
603 | | esc(file, TTY_FG_DEF), (void*)module, this->frames[i]); |
604 | | } |
605 | | if (detailed) |
606 | | { |
607 | | IMAGEHLP_LINE64 line; |
608 | | DWORD off; |
609 | | |
610 | | memset(&line, 0, sizeof(line)); |
611 | | line.SizeOfStruct = sizeof(line); |
612 | | |
613 | | if (SymGetLineFromAddr64(process, frame, &off, &line)) |
614 | | { |
615 | | |
616 | | println(file, " -> %s%s:%u%s", esc(file, TTY_FG_GREEN), |
617 | | line.FileName, line.LineNumber, |
618 | | esc(file, TTY_FG_DEF)); |
619 | | } |
620 | | } |
621 | | } |
622 | | else |
623 | | #elif defined(WIN32) |
624 | | HMODULE module; |
625 | | MODULEINFO info; |
626 | | char filename[MAX_PATH]; |
627 | | |
628 | | if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, |
629 | | this->frames[i], &module) && |
630 | | GetModuleInformation(GetCurrentProcess(), module, |
631 | | &info, sizeof(info)) && |
632 | | GetModuleFileNameEx(GetCurrentProcess(), module, |
633 | | filename, sizeof(filename))) |
634 | | { |
635 | | println(file, " %s%s%s @ %p [%p]", |
636 | | esc(file, TTY_FG_YELLOW), filename, |
637 | | esc(file, TTY_FG_DEF), info.lpBaseOfDll, this->frames[i]); |
638 | | #ifdef HAVE_BFD_H |
639 | | print_sourceline(file, filename, this->frames[i], info.lpBaseOfDll); |
640 | | #endif /* HAVE_BFD_H */ |
641 | | } |
642 | | else |
643 | | #endif /* HAVE_DLADDR/HAVE_DBGHELP/WIN32 */ |
644 | 0 | { |
645 | 0 | #ifdef HAVE_BACKTRACE |
646 | 0 | if (!strings) |
647 | 0 | { |
648 | 0 | strings = backtrace_symbols(this->frames, this->frame_count); |
649 | 0 | } |
650 | 0 | if (strings) |
651 | 0 | { |
652 | 0 | println(file, " %s", strings[i]); |
653 | 0 | } |
654 | 0 | else |
655 | 0 | #endif /* HAVE_BACKTRACE */ |
656 | 0 | { |
657 | 0 | println(file, " %p", this->frames[i]); |
658 | 0 | } |
659 | 0 | } |
660 | | #ifdef HAVE_DBGHELP |
661 | | dbghelp_mutex->unlock(dbghelp_mutex); |
662 | | #endif |
663 | 0 | } |
664 | 0 | free(strings); |
665 | | #else /* !HAVE_BACKTRACE && !HAVE_LIBUNWIND_H */ |
666 | | println(file, "no support for capturing backtraces"); |
667 | | #endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H */ |
668 | 0 | } |
669 | | |
670 | | METHOD(backtrace_t, contains_function, bool, |
671 | | private_backtrace_t *this, char *function[], int count) |
672 | 0 | { |
673 | 0 | #ifdef HAVE_DLADDR |
674 | 0 | int i, j; |
675 | |
|
676 | 0 | for (i = 0; i< this->frame_count; i++) |
677 | 0 | { |
678 | 0 | Dl_info info; |
679 | |
|
680 | 0 | if (dladdr(this->frames[i], &info)) |
681 | 0 | { |
682 | 0 | if (info.dli_sname) |
683 | 0 | { |
684 | 0 | for (j = 0; j < count; j++) |
685 | 0 | { |
686 | 0 | if (streq(info.dli_sname, function[j])) |
687 | 0 | { |
688 | 0 | return TRUE; |
689 | 0 | } |
690 | 0 | } |
691 | 0 | } |
692 | | #ifdef HAVE_BFD_H |
693 | | else if (info.dli_fname[0]) |
694 | | { |
695 | | void *ptr = this->frames[i]; |
696 | | |
697 | | if (strstr(info.dli_fname, ".so")) |
698 | | { |
699 | | ptr = (void*)(this->frames[i] - info.dli_fbase); |
700 | | } |
701 | | if (contains_function_bfd((char*)info.dli_fname, ptr, |
702 | | function, count)) |
703 | | { |
704 | | return TRUE; |
705 | | } |
706 | | } |
707 | | #endif /* HAVE_BFD_H */ |
708 | 0 | } |
709 | 0 | } |
710 | | #elif defined(HAVE_DBGHELP) |
711 | | int i, j; |
712 | | HANDLE process; |
713 | | |
714 | | process = GetCurrentProcess(); |
715 | | |
716 | | dbghelp_mutex->lock(dbghelp_mutex); |
717 | | |
718 | | for (i = 0; i < this->frame_count; i++) |
719 | | { |
720 | | struct { |
721 | | SYMBOL_INFO hdr; |
722 | | char buf[128]; |
723 | | } symbol; |
724 | | |
725 | | memset(&symbol, 0, sizeof(symbol)); |
726 | | symbol.hdr.SizeOfStruct = sizeof(symbol.hdr); |
727 | | symbol.hdr.MaxNameLen = sizeof(symbol.buf) - 1; |
728 | | |
729 | | if (SymFromAddr(process, (DWORD64)this->frames[i], NULL, &symbol.hdr)) |
730 | | { |
731 | | for (j = 0; j < count; j++) |
732 | | { |
733 | | if (streq(symbol.hdr.Name, function[j])) |
734 | | { |
735 | | dbghelp_mutex->unlock(dbghelp_mutex); |
736 | | return TRUE; |
737 | | } |
738 | | } |
739 | | } |
740 | | } |
741 | | |
742 | | dbghelp_mutex->unlock(dbghelp_mutex); |
743 | | #endif /* HAVE_DLADDR/HAVE_DBGHELP */ |
744 | 0 | return FALSE; |
745 | 0 | } |
746 | | |
747 | | METHOD(backtrace_t, equals, bool, |
748 | | private_backtrace_t *this, backtrace_t *other_public) |
749 | 0 | { |
750 | 0 | private_backtrace_t *other = (private_backtrace_t*)other_public; |
751 | 0 | int i; |
752 | |
|
753 | 0 | if (this == other) |
754 | 0 | { |
755 | 0 | return TRUE; |
756 | 0 | } |
757 | 0 | if (this->frame_count != other->frame_count) |
758 | 0 | { |
759 | 0 | return FALSE; |
760 | 0 | } |
761 | 0 | for (i = 0; i < this->frame_count; i++) |
762 | 0 | { |
763 | 0 | if (this->frames[i] != other->frames[i]) |
764 | 0 | { |
765 | 0 | return FALSE; |
766 | 0 | } |
767 | 0 | } |
768 | 0 | return TRUE; |
769 | 0 | } |
770 | | |
771 | | /** |
772 | | * Frame enumerator |
773 | | */ |
774 | | typedef struct { |
775 | | /** implements enumerator_t */ |
776 | | enumerator_t public; |
777 | | /** reference to backtrace */ |
778 | | private_backtrace_t *bt; |
779 | | /** current position */ |
780 | | int i; |
781 | | } frame_enumerator_t; |
782 | | |
783 | | METHOD(enumerator_t, frame_enumerate, bool, |
784 | | frame_enumerator_t *this, va_list args) |
785 | 0 | { |
786 | 0 | void **addr; |
787 | |
|
788 | 0 | VA_ARGS_VGET(args, addr); |
789 | |
|
790 | 0 | if (this->i < this->bt->frame_count) |
791 | 0 | { |
792 | 0 | *addr = this->bt->frames[this->i++]; |
793 | 0 | return TRUE; |
794 | 0 | } |
795 | 0 | return FALSE; |
796 | 0 | } |
797 | | |
798 | | METHOD(backtrace_t, create_frame_enumerator, enumerator_t*, |
799 | | private_backtrace_t *this) |
800 | 0 | { |
801 | 0 | frame_enumerator_t *enumerator; |
802 | |
|
803 | 0 | INIT(enumerator, |
804 | 0 | .public = { |
805 | 0 | .enumerate = enumerator_enumerate_default, |
806 | 0 | .venumerate = _frame_enumerate, |
807 | 0 | .destroy = (void*)free, |
808 | 0 | }, |
809 | 0 | .bt = this, |
810 | 0 | ); |
811 | 0 | return &enumerator->public; |
812 | 0 | } |
813 | | |
814 | | METHOD(backtrace_t, clone_, backtrace_t*, |
815 | | private_backtrace_t *this) |
816 | 0 | { |
817 | 0 | private_backtrace_t *clone; |
818 | |
|
819 | 0 | clone = malloc(sizeof(private_backtrace_t) + |
820 | 0 | this->frame_count * sizeof(void*)); |
821 | 0 | memcpy(clone->frames, this->frames, this->frame_count * sizeof(void*)); |
822 | 0 | clone->frame_count = this->frame_count; |
823 | |
|
824 | 0 | clone->public = get_methods(); |
825 | |
|
826 | 0 | return &clone->public; |
827 | 0 | } |
828 | | |
829 | | METHOD(backtrace_t, destroy, void, |
830 | | private_backtrace_t *this) |
831 | 0 | { |
832 | 0 | free(this); |
833 | 0 | } |
834 | | |
835 | | #ifdef HAVE_LIBUNWIND_H |
836 | | #define UNW_LOCAL_ONLY |
837 | | #include <libunwind.h> |
838 | | |
839 | | /** |
840 | | * libunwind variant for glibc backtrace() |
841 | | */ |
842 | | static inline int backtrace_unwind(void **frames, int count) |
843 | | { |
844 | | unw_context_t context; |
845 | | unw_cursor_t cursor; |
846 | | unw_word_t ip; |
847 | | int depth = 0; |
848 | | |
849 | | unw_getcontext(&context); |
850 | | unw_init_local(&cursor, &context); |
851 | | do |
852 | | { |
853 | | unw_get_reg(&cursor, UNW_REG_IP, &ip); |
854 | | frames[depth++] = (void*)ip; |
855 | | } |
856 | | while (depth < count && unw_step(&cursor) > 0); |
857 | | |
858 | | return depth; |
859 | | } |
860 | | #endif /* HAVE_UNWIND */ |
861 | | |
862 | | #ifdef HAVE_DBGHELP |
863 | | |
864 | | /** |
865 | | * Windows dbghelp variant for glibc backtrace() |
866 | | */ |
867 | | static inline int backtrace_win(void **frames, int count) |
868 | | { |
869 | | STACKFRAME frame; |
870 | | HANDLE process, thread; |
871 | | DWORD machine; |
872 | | CONTEXT context; |
873 | | int got = 0; |
874 | | |
875 | | memset(&frame, 0, sizeof(frame)); |
876 | | memset(&context, 0, sizeof(context)); |
877 | | |
878 | | process = GetCurrentProcess(); |
879 | | thread = GetCurrentThread(); |
880 | | |
881 | | #ifdef __x86_64 |
882 | | machine = IMAGE_FILE_MACHINE_AMD64; |
883 | | |
884 | | frame.AddrPC.Offset = context.Rip; |
885 | | frame.AddrPC.Mode = AddrModeFlat; |
886 | | frame.AddrStack.Offset = context.Rsp; |
887 | | frame.AddrStack.Mode = AddrModeFlat; |
888 | | frame.AddrFrame.Offset = context.Rbp; |
889 | | frame.AddrFrame.Mode = AddrModeFlat; |
890 | | #else /* x86 */ |
891 | | machine = IMAGE_FILE_MACHINE_I386; |
892 | | |
893 | | frame.AddrPC.Offset = context.Eip; |
894 | | frame.AddrPC.Mode = AddrModeFlat; |
895 | | frame.AddrStack.Offset = context.Esp; |
896 | | frame.AddrStack.Mode = AddrModeFlat; |
897 | | frame.AddrFrame.Offset = context.Ebp; |
898 | | frame.AddrFrame.Mode = AddrModeFlat; |
899 | | #endif /* x86_64/x86 */ |
900 | | |
901 | | dbghelp_mutex->lock(dbghelp_mutex); |
902 | | |
903 | | RtlCaptureContext(&context); |
904 | | |
905 | | while (got < count) |
906 | | { |
907 | | if (!StackWalk64(machine, process, thread, &frame, &context, |
908 | | NULL, SymFunctionTableAccess, SymGetModuleBase, NULL)) |
909 | | { |
910 | | break; |
911 | | } |
912 | | frames[got++] = (void*)frame.AddrPC.Offset; |
913 | | } |
914 | | |
915 | | dbghelp_mutex->unlock(dbghelp_mutex); |
916 | | |
917 | | return got; |
918 | | } |
919 | | |
920 | | #endif /* HAVE_DBGHELP */ |
921 | | |
922 | | /** |
923 | | * Get implementation methods of backtrace_t |
924 | | */ |
925 | | static backtrace_t get_methods() |
926 | 0 | { |
927 | 0 | return (backtrace_t) { |
928 | 0 | .log = _log_, |
929 | 0 | .contains_function = _contains_function, |
930 | 0 | .equals = _equals, |
931 | 0 | .clone = _clone_, |
932 | 0 | .create_frame_enumerator = _create_frame_enumerator, |
933 | 0 | .destroy = _destroy, |
934 | 0 | }; |
935 | 0 | } |
936 | | |
937 | | /** |
938 | | * See header |
939 | | */ |
940 | | backtrace_t *backtrace_create(int skip) |
941 | 0 | { |
942 | 0 | private_backtrace_t *this; |
943 | 0 | void *frames[50]; |
944 | 0 | int frame_count = 0; |
945 | |
|
946 | | #ifdef HAVE_LIBUNWIND_H |
947 | | frame_count = backtrace_unwind(frames, countof(frames)); |
948 | | #elif defined(HAVE_BACKTRACE) |
949 | 0 | frame_count = backtrace(frames, countof(frames)); |
950 | | #elif defined(HAVE_DBGHELP) |
951 | | frame_count = backtrace_win(frames, countof(frames)); |
952 | | #elif defined(WIN32) |
953 | | frame_count = CaptureStackBackTrace(skip, countof(frames), frames, NULL); |
954 | | skip = 0; |
955 | | #endif |
956 | 0 | frame_count = max(frame_count - skip, 0); |
957 | 0 | this = malloc(sizeof(private_backtrace_t) + frame_count * sizeof(void*)); |
958 | 0 | memcpy(this->frames, frames + skip, frame_count * sizeof(void*)); |
959 | 0 | this->frame_count = frame_count; |
960 | |
|
961 | 0 | this->public = get_methods(); |
962 | |
|
963 | 0 | return &this->public; |
964 | 0 | } |
965 | | |
966 | | /** |
967 | | * See header |
968 | | */ |
969 | | void backtrace_dump(char *label, FILE *file, bool detailed) |
970 | 0 | { |
971 | 0 | backtrace_t *backtrace; |
972 | |
|
973 | 0 | backtrace = backtrace_create(2); |
974 | |
|
975 | 0 | if (label) |
976 | 0 | { |
977 | 0 | println(file, "Debug backtrace: %s", label); |
978 | 0 | } |
979 | 0 | backtrace->log(backtrace, file, detailed); |
980 | 0 | backtrace->destroy(backtrace); |
981 | 0 | } |