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