| /* |
| * Copyright (C) 2006-2013 Martin Willi |
| * HSR Hochschule fuer Technik Rapperswil |
| * Copyright (C) 2013 revosec AG |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * for more details. |
| */ |
| |
| #define _GNU_SOURCE |
| |
| #ifdef HAVE_BACKTRACE |
| # include <execinfo.h> |
| #endif /* HAVE_BACKTRACE */ |
| #ifdef HAVE_DBGHELP |
| # include <winsock2.h> |
| # include <windows.h> |
| # include <dbghelp.h> |
| #endif /* HAVE_DBGHELP */ |
| #include <string.h> |
| |
| #include "backtrace.h" |
| |
| #include <utils/debug.h> |
| |
| #ifdef WIN32 |
| # include <psapi.h> |
| /* missing in MinGW */ |
| #ifdef WIN64 |
| #ifndef GetModuleInformation |
| WINBOOL K32GetModuleInformation(HANDLE hProcess, HMODULE hModule, |
| LPMODULEINFO lpmodinfo, DWORD cb); |
| #define GetModuleInformation K32GetModuleInformation |
| #endif /* !GetModuleInformation */ |
| #ifndef GetModuleFileNameEx |
| DWORD K32GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, |
| LPTSTR lpFilename, DWORD nSize); |
| #define GetModuleFileNameEx K32GetModuleFileNameExA |
| #endif /* !GetModuleFileNameEx */ |
| #endif /* WIN64 */ |
| #endif |
| |
| typedef struct private_backtrace_t private_backtrace_t; |
| |
| /** |
| * Private data of an backtrace_t object. |
| */ |
| struct private_backtrace_t { |
| |
| /** |
| * Public backtrace_t interface. |
| */ |
| backtrace_t public; |
| |
| /** |
| * Number of stacks frames obtained in stack_frames |
| */ |
| int frame_count; |
| |
| /** |
| * Recorded stack frames. |
| */ |
| void *frames[]; |
| }; |
| |
| /** |
| * Forward declaration of method getter |
| */ |
| static backtrace_t get_methods(); |
| |
| /** |
| * Write a format string with arguments to a FILE line, if it is NULL to DBG |
| */ |
| static void println(FILE *file, char *format, ...) |
| { |
| char buf[512]; |
| va_list args; |
| |
| va_start(args, format); |
| if (file) |
| { |
| vfprintf(file, format, args); |
| fputs("\n", file); |
| } |
| else |
| { |
| vsnprintf(buf, sizeof(buf), format, args); |
| DBG1(DBG_LIB, "%s", buf); |
| } |
| va_end(args); |
| } |
| |
| /** |
| * Same as tty_escape_get(), but for a potentially NULL FILE* |
| */ |
| static inline char* esc(FILE *file, tty_escape_t escape) |
| { |
| if (file) |
| { |
| return tty_escape_get(fileno(file), escape); |
| } |
| return ""; |
| } |
| |
| #ifdef HAVE_DBGHELP |
| |
| #include <dbghelp.h> |
| #include <threading/mutex.h> |
| |
| /** |
| * Mutex to access non-thread-safe dbghelp functions |
| */ |
| static mutex_t *dbghelp_mutex; |
| |
| void backtrace_init() |
| { |
| SymSetOptions(SYMOPT_LOAD_LINES); |
| SymInitialize(GetCurrentProcess(), NULL, TRUE); |
| dbghelp_mutex = mutex_create(MUTEX_TYPE_DEFAULT); |
| } |
| |
| void backtrace_deinit() |
| { |
| dbghelp_mutex->destroy(dbghelp_mutex); |
| SymCleanup(GetCurrentProcess()); |
| } |
| |
| #elif defined(HAVE_DLADDR) || defined(HAVE_BFD_H) |
| |
| #ifdef HAVE_DLADDR |
| #include <dlfcn.h> |
| #endif |
| |
| #ifdef HAVE_BFD_H |
| |
| #include <bfd.h> |
| #include <collections/hashtable.h> |
| #include <threading/mutex.h> |
| |
| /* interface changes for newer BFD versions, note that older versions declared |
| * some of the new functions as macros but with different arguments */ |
| #if HAVE_DECL_BFD_GET_SECTION_FLAGS |
| #define get_section_flags(a, s) bfd_get_section_flags(a, s) |
| #elif HAVE_DECL_BFD_SECTION_FLAGS |
| #define get_section_flags(a, s) bfd_section_flags(s) |
| #else |
| #error Unknown BFD API |
| #endif |
| |
| #if HAVE_DECL_BFD_GET_SECTION_VMA |
| #define get_section_vma(a, s) bfd_get_section_vma(a, s) |
| #elif HAVE_DECL_BFD_SECTION_VMA |
| #define get_section_vma(a, s) bfd_section_vma(s) |
| #else |
| #error Unknown BFD API |
| #endif |
| |
| #if HAVE_DECL_BFD_GET_SECTION_SIZE |
| #define get_section_size bfd_get_section_size |
| #elif HAVE_DECL_BFD_SECTION_SIZE |
| #define get_section_size bfd_section_size |
| #else |
| #error Unknown BFD API |
| #endif |
| |
| /** |
| * Hashtable-cached bfd handle |
| */ |
| typedef struct { |
| /** binary file name on disk */ |
| char *filename; |
| /** bfd handle */ |
| bfd *abfd; |
| /** loaded symbols */ |
| asymbol **syms; |
| } bfd_entry_t; |
| |
| /** |
| * Destroy a bfd_entry |
| */ |
| static void bfd_entry_destroy(bfd_entry_t *this) |
| { |
| free(this->filename); |
| free(this->syms); |
| bfd_close(this->abfd); |
| free(this); |
| } |
| |
| /** |
| * Data to pass to find_addr() |
| */ |
| typedef struct { |
| /** used bfd entry */ |
| bfd_entry_t *entry; |
| /** backtrace address */ |
| bfd_vma vma; |
| /** stream to log to */ |
| FILE *file; |
| /** TRUE if complete */ |
| bool found; |
| } bfd_find_data_t; |
| |
| /** |
| * bfd entry cache |
| */ |
| static hashtable_t *bfds; |
| |
| static mutex_t *bfd_mutex; |
| |
| /** |
| * Hashtable hash function |
| */ |
| static u_int bfd_hash(char *key) |
| { |
| return chunk_hash(chunk_create(key, strlen(key))); |
| } |
| |
| /** |
| * Hashtable equals function |
| */ |
| static bool bfd_equals(char *a, char *b) |
| { |
| return streq(a, b); |
| } |
| |
| /** |
| * See header. |
| */ |
| void backtrace_init() |
| { |
| bfd_init(); |
| bfds = hashtable_create((hashtable_hash_t)bfd_hash, |
| (hashtable_equals_t)bfd_equals, 8); |
| bfd_mutex = mutex_create(MUTEX_TYPE_DEFAULT); |
| } |
| |
| /** |
| * See header. |
| */ |
| void backtrace_deinit() |
| { |
| enumerator_t *enumerator; |
| bfd_entry_t *entry; |
| char *key; |
| |
| enumerator = bfds->create_enumerator(bfds); |
| while (enumerator->enumerate(enumerator, &key, &entry)) |
| { |
| bfds->remove_at(bfds, enumerator); |
| bfd_entry_destroy(entry); |
| } |
| enumerator->destroy(enumerator); |
| |
| bfds->destroy(bfds); |
| bfd_mutex->destroy(bfd_mutex); |
| } |
| |
| /** |
| * Find and print information to an address |
| */ |
| static void find_addr(bfd *abfd, asection *section, bfd_find_data_t *data) |
| { |
| bfd_size_type size; |
| bfd_vma vma; |
| const char *source; |
| const char *function; |
| char fbuf[512] = "", sbuf[512] = ""; |
| u_int line; |
| |
| if (!data->found || (get_section_flags(abfd, section) & SEC_ALLOC) != 0) |
| { |
| vma = get_section_vma(abfd, section); |
| if (data->vma >= vma) |
| { |
| size = get_section_size(section); |
| if (data->vma < vma + size) |
| { |
| data->found = bfd_find_nearest_line(abfd, section, |
| data->entry->syms, data->vma - vma, |
| &source, &function, &line); |
| if (data->found) |
| { |
| if (source || function) |
| { |
| if (function) |
| { |
| snprintf(fbuf, sizeof(fbuf), "%s%s() ", |
| esc(data->file, TTY_FG_BLUE), function); |
| } |
| if (source) |
| { |
| snprintf(sbuf, sizeof(sbuf), "%s@ %s:%d", |
| esc(data->file, TTY_FG_GREEN), source, line); |
| } |
| println(data->file, " -> %s%s%s", fbuf, sbuf, |
| esc(data->file, TTY_FG_DEF)); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Find a cached bfd entry, create'n'cache if not found |
| */ |
| static bfd_entry_t *get_bfd_entry(char *filename) |
| { |
| bool dynamic = FALSE, ok = FALSE; |
| bfd_entry_t *entry; |
| long size; |
| |
| /* check cache */ |
| entry = bfds->get(bfds, filename); |
| if (entry) |
| { |
| return entry; |
| } |
| |
| INIT(entry, |
| .abfd = bfd_openr(filename, NULL), |
| ); |
| |
| if (!entry->abfd) |
| { |
| free(entry); |
| return NULL; |
| } |
| #ifdef BFD_DECOMPRESS |
| entry->abfd->flags |= BFD_DECOMPRESS; |
| #endif |
| if (bfd_check_format(entry->abfd, bfd_archive) == 0 && |
| bfd_check_format_matches(entry->abfd, bfd_object, NULL)) |
| { |
| if (bfd_get_file_flags(entry->abfd) & HAS_SYMS) |
| { |
| size = bfd_get_symtab_upper_bound(entry->abfd); |
| if (size == 0) |
| { |
| size = bfd_get_dynamic_symtab_upper_bound(entry->abfd); |
| dynamic = TRUE; |
| } |
| if (size >= 0) |
| { |
| entry->syms = malloc(size); |
| if (dynamic) |
| { |
| ok = bfd_canonicalize_dynamic_symtab(entry->abfd, |
| entry->syms) >= 0; |
| } |
| else |
| { |
| ok = bfd_canonicalize_symtab(entry->abfd, |
| entry->syms) >= 0; |
| } |
| } |
| } |
| } |
| if (ok) |
| { |
| entry->filename = strdup(filename); |
| bfds->put(bfds, entry->filename, entry); |
| return entry; |
| } |
| bfd_entry_destroy(entry); |
| return NULL; |
| } |
| |
| /** |
| * Print the source file with line number to file, libbfd variant |
| */ |
| static void print_sourceline(FILE *file, char *filename, void *ptr, void *base) |
| { |
| bfd_entry_t *entry; |
| bfd_find_data_t data = { |
| .file = file, |
| .vma = (uintptr_t)ptr, |
| }; |
| bool old = FALSE; |
| |
| bfd_mutex->lock(bfd_mutex); |
| if (lib && lib->leak_detective) |
| { |
| old = lib->leak_detective->set_state(lib->leak_detective, FALSE); |
| } |
| entry = get_bfd_entry(filename); |
| if (entry) |
| { |
| data.entry = entry; |
| bfd_map_over_sections(entry->abfd, (void*)find_addr, &data); |
| } |
| if (lib && lib->leak_detective) |
| { |
| lib->leak_detective->set_state(lib->leak_detective, old); |
| } |
| bfd_mutex->unlock(bfd_mutex); |
| } |
| |
| #else /* !HAVE_BFD_H */ |
| |
| void backtrace_init() {} |
| void backtrace_deinit() {} |
| |
| /** |
| * Print the source file with line number to file, slow addr2line variant |
| */ |
| static void print_sourceline(FILE *file, char *filename, void *ptr, void* base) |
| { |
| char buf[1024]; |
| FILE *output; |
| int c, i = 0; |
| |
| #ifdef __APPLE__ |
| snprintf(buf, sizeof(buf), "atos -o %s -l %p %p 2>&1 | tail -n1", |
| filename, base, ptr); |
| #else /* !__APPLE__ */ |
| snprintf(buf, sizeof(buf), "addr2line -e %s %p", filename, ptr); |
| #endif /* __APPLE__ */ |
| |
| output = popen(buf, "r"); |
| if (output) |
| { |
| while (i < sizeof(buf)) |
| { |
| c = getc(output); |
| if (c == '\n' || c == EOF) |
| { |
| buf[i++] = 0; |
| break; |
| } |
| buf[i++] = c; |
| } |
| pclose(output); |
| |
| println(file, " -> %s%s%s", esc(file, TTY_FG_GREEN), buf, |
| esc(file, TTY_FG_DEF)); |
| } |
| } |
| |
| #endif /* HAVE_BFD_H */ |
| |
| #else /* !HAVE_DLADDR && !HAVE_DBGHELP */ |
| |
| void backtrace_init() {} |
| void backtrace_deinit() {} |
| |
| #endif /* HAVE_DLADDR */ |
| |
| METHOD(backtrace_t, log_, void, |
| private_backtrace_t *this, FILE *file, bool detailed) |
| { |
| #if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) || defined(WIN32) |
| size_t i; |
| char **strings = NULL; |
| |
| println(file, " dumping %d stack frame addresses:", this->frame_count); |
| for (i = 0; i < this->frame_count; i++) |
| { |
| #ifdef HAVE_DLADDR |
| Dl_info info; |
| |
| if (dladdr(this->frames[i], &info)) |
| { |
| void *ptr = this->frames[i]; |
| |
| if (strstr(info.dli_fname, ".so")) |
| { |
| ptr = (void*)(this->frames[i] - info.dli_fbase); |
| } |
| if (info.dli_sname) |
| { |
| println(file, " %s%s%s @ %p (%s%s%s+0x%tx) [%p]", |
| esc(file, TTY_FG_YELLOW), info.dli_fname, |
| esc(file, TTY_FG_DEF), info.dli_fbase, |
| esc(file, TTY_FG_RED), info.dli_sname, |
| esc(file, TTY_FG_DEF), this->frames[i] - info.dli_saddr, |
| this->frames[i]); |
| } |
| else |
| { |
| println(file, " %s%s%s @ %p [%p]", |
| esc(file, TTY_FG_YELLOW), info.dli_fname, |
| esc(file, TTY_FG_DEF), info.dli_fbase, this->frames[i]); |
| } |
| if (detailed && info.dli_fname[0]) |
| { |
| print_sourceline(file, (char*)info.dli_fname, |
| ptr, info.dli_fbase); |
| } |
| } |
| else |
| #elif defined(HAVE_DBGHELP) |
| struct { |
| SYMBOL_INFO hdr; |
| char buf[128]; |
| } symbol; |
| char filename[MAX_PATH]; |
| HINSTANCE module; |
| HANDLE process; |
| DWORD64 displace, frame; |
| |
| process = GetCurrentProcess(); |
| frame = (uintptr_t)this->frames[i]; |
| |
| memset(&symbol, 0, sizeof(symbol)); |
| symbol.hdr.SizeOfStruct = sizeof(symbol.hdr); |
| symbol.hdr.MaxNameLen = sizeof(symbol.buf) - 1; |
| |
| dbghelp_mutex->lock(dbghelp_mutex); |
| |
| module = (HINSTANCE)SymGetModuleBase64(process, frame); |
| |
| if (module && GetModuleFileName(module, filename, sizeof(filename))) |
| { |
| if (SymFromAddr(process, frame, &displace, &symbol.hdr)) |
| { |
| println(file, " %s%s%s @ %p (%s%s%s+0x%tx) [%p]", |
| esc(file, TTY_FG_YELLOW), filename, |
| esc(file, TTY_FG_DEF), (void*)module, |
| esc(file, TTY_FG_RED), symbol.hdr.Name, |
| esc(file, TTY_FG_DEF), displace, |
| this->frames[i]); |
| } |
| else |
| { |
| println(file, " %s%s%s @ %p [%p]", |
| esc(file, TTY_FG_YELLOW), filename, |
| esc(file, TTY_FG_DEF), (void*)module, this->frames[i]); |
| } |
| if (detailed) |
| { |
| IMAGEHLP_LINE64 line; |
| DWORD off; |
| |
| memset(&line, 0, sizeof(line)); |
| line.SizeOfStruct = sizeof(line); |
| |
| if (SymGetLineFromAddr64(process, frame, &off, &line)) |
| { |
| |
| println(file, " -> %s%s:%u%s", esc(file, TTY_FG_GREEN), |
| line.FileName, line.LineNumber, |
| esc(file, TTY_FG_DEF)); |
| } |
| } |
| } |
| else |
| #elif defined(WIN32) |
| HMODULE module; |
| MODULEINFO info; |
| char filename[MAX_PATH]; |
| |
| if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, |
| this->frames[i], &module) && |
| GetModuleInformation(GetCurrentProcess(), module, |
| &info, sizeof(info)) && |
| GetModuleFileNameEx(GetCurrentProcess(), module, |
| filename, sizeof(filename))) |
| { |
| println(file, " %s%s%s @ %p [%p]", |
| esc(file, TTY_FG_YELLOW), filename, |
| esc(file, TTY_FG_DEF), info.lpBaseOfDll, this->frames[i]); |
| #ifdef HAVE_BFD_H |
| print_sourceline(file, filename, this->frames[i], info.lpBaseOfDll); |
| #endif /* HAVE_BFD_H */ |
| } |
| else |
| #endif /* HAVE_DLADDR/HAVE_DBGHELP */ |
| { |
| #ifdef HAVE_BACKTRACE |
| if (!strings) |
| { |
| strings = backtrace_symbols(this->frames, this->frame_count); |
| } |
| if (strings) |
| { |
| println(file, " %s", strings[i]); |
| } |
| else |
| #endif /* HAVE_BACKTRACE */ |
| { |
| println(file, " %p", this->frames[i]); |
| } |
| } |
| #ifdef HAVE_DBGHELP |
| dbghelp_mutex->unlock(dbghelp_mutex); |
| #endif |
| } |
| free(strings); |
| #else /* !HAVE_BACKTRACE && !HAVE_LIBUNWIND_H */ |
| println(file, "no support for capturing backtraces"); |
| #endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H */ |
| } |
| |
| METHOD(backtrace_t, contains_function, bool, |
| private_backtrace_t *this, char *function[], int count) |
| { |
| #ifdef HAVE_DLADDR |
| int i, j; |
| |
| for (i = 0; i< this->frame_count; i++) |
| { |
| Dl_info info; |
| |
| if (dladdr(this->frames[i], &info) && info.dli_sname) |
| { |
| for (j = 0; j < count; j++) |
| { |
| if (streq(info.dli_sname, function[j])) |
| { |
| return TRUE; |
| } |
| } |
| } |
| } |
| #elif defined(HAVE_DBGHELP) |
| int i, j; |
| HANDLE process; |
| |
| process = GetCurrentProcess(); |
| |
| dbghelp_mutex->lock(dbghelp_mutex); |
| |
| for (i = 0; i < this->frame_count; i++) |
| { |
| struct { |
| SYMBOL_INFO hdr; |
| char buf[128]; |
| } symbol; |
| |
| memset(&symbol, 0, sizeof(symbol)); |
| symbol.hdr.SizeOfStruct = sizeof(symbol.hdr); |
| symbol.hdr.MaxNameLen = sizeof(symbol.buf) - 1; |
| |
| if (SymFromAddr(process, (DWORD64)this->frames[i], NULL, &symbol.hdr)) |
| { |
| for (j = 0; j < count; j++) |
| { |
| if (streq(symbol.hdr.Name, function[j])) |
| { |
| dbghelp_mutex->unlock(dbghelp_mutex); |
| return TRUE; |
| } |
| } |
| } |
| } |
| |
| dbghelp_mutex->unlock(dbghelp_mutex); |
| #endif /* HAVE_DLADDR/HAVE_DBGHELP */ |
| return FALSE; |
| } |
| |
| METHOD(backtrace_t, equals, bool, |
| private_backtrace_t *this, backtrace_t *other_public) |
| { |
| private_backtrace_t *other = (private_backtrace_t*)other_public; |
| int i; |
| |
| if (this == other) |
| { |
| return TRUE; |
| } |
| if (this->frame_count != other->frame_count) |
| { |
| return FALSE; |
| } |
| for (i = 0; i < this->frame_count; i++) |
| { |
| if (this->frames[i] != other->frames[i]) |
| { |
| return FALSE; |
| } |
| } |
| return TRUE; |
| } |
| |
| /** |
| * Frame enumerator |
| */ |
| typedef struct { |
| /** implements enumerator_t */ |
| enumerator_t public; |
| /** reference to backtrace */ |
| private_backtrace_t *bt; |
| /** current position */ |
| int i; |
| } frame_enumerator_t; |
| |
| METHOD(enumerator_t, frame_enumerate, bool, |
| frame_enumerator_t *this, va_list args) |
| { |
| void **addr; |
| |
| VA_ARGS_VGET(args, addr); |
| |
| if (this->i < this->bt->frame_count) |
| { |
| *addr = this->bt->frames[this->i++]; |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| METHOD(backtrace_t, create_frame_enumerator, enumerator_t*, |
| private_backtrace_t *this) |
| { |
| frame_enumerator_t *enumerator; |
| |
| INIT(enumerator, |
| .public = { |
| .enumerate = enumerator_enumerate_default, |
| .venumerate = _frame_enumerate, |
| .destroy = (void*)free, |
| }, |
| .bt = this, |
| ); |
| return &enumerator->public; |
| } |
| |
| METHOD(backtrace_t, clone_, backtrace_t*, |
| private_backtrace_t *this) |
| { |
| private_backtrace_t *clone; |
| |
| clone = malloc(sizeof(private_backtrace_t) + |
| this->frame_count * sizeof(void*)); |
| memcpy(clone->frames, this->frames, this->frame_count * sizeof(void*)); |
| clone->frame_count = this->frame_count; |
| |
| clone->public = get_methods(); |
| |
| return &clone->public; |
| } |
| |
| METHOD(backtrace_t, destroy, void, |
| private_backtrace_t *this) |
| { |
| free(this); |
| } |
| |
| #ifdef HAVE_LIBUNWIND_H |
| #define UNW_LOCAL_ONLY |
| #include <libunwind.h> |
| |
| /** |
| * libunwind variant for glibc backtrace() |
| */ |
| static inline int backtrace_unwind(void **frames, int count) |
| { |
| unw_context_t context; |
| unw_cursor_t cursor; |
| unw_word_t ip; |
| int depth = 0; |
| |
| unw_getcontext(&context); |
| unw_init_local(&cursor, &context); |
| do |
| { |
| unw_get_reg(&cursor, UNW_REG_IP, &ip); |
| frames[depth++] = (void*)ip; |
| } |
| while (depth < count && unw_step(&cursor) > 0); |
| |
| return depth; |
| } |
| #endif /* HAVE_UNWIND */ |
| |
| #ifdef HAVE_DBGHELP |
| |
| /** |
| * Windows dbghelp variant for glibc backtrace() |
| */ |
| static inline int backtrace_win(void **frames, int count) |
| { |
| STACKFRAME frame; |
| HANDLE process, thread; |
| DWORD machine; |
| CONTEXT context; |
| int got = 0; |
| |
| memset(&frame, 0, sizeof(frame)); |
| memset(&context, 0, sizeof(context)); |
| |
| process = GetCurrentProcess(); |
| thread = GetCurrentThread(); |
| |
| #ifdef __x86_64 |
| machine = IMAGE_FILE_MACHINE_AMD64; |
| |
| frame.AddrPC.Offset = context.Rip; |
| frame.AddrPC.Mode = AddrModeFlat; |
| frame.AddrStack.Offset = context.Rsp; |
| frame.AddrStack.Mode = AddrModeFlat; |
| frame.AddrFrame.Offset = context.Rbp; |
| frame.AddrFrame.Mode = AddrModeFlat; |
| #else /* x86 */ |
| machine = IMAGE_FILE_MACHINE_I386; |
| |
| frame.AddrPC.Offset = context.Eip; |
| frame.AddrPC.Mode = AddrModeFlat; |
| frame.AddrStack.Offset = context.Esp; |
| frame.AddrStack.Mode = AddrModeFlat; |
| frame.AddrFrame.Offset = context.Ebp; |
| frame.AddrFrame.Mode = AddrModeFlat; |
| #endif /* x86_64/x86 */ |
| |
| dbghelp_mutex->lock(dbghelp_mutex); |
| |
| RtlCaptureContext(&context); |
| |
| while (got < count) |
| { |
| if (!StackWalk64(machine, process, thread, &frame, &context, |
| NULL, SymFunctionTableAccess, SymGetModuleBase, NULL)) |
| { |
| break; |
| } |
| frames[got++] = (void*)frame.AddrPC.Offset; |
| } |
| |
| dbghelp_mutex->unlock(dbghelp_mutex); |
| |
| return got; |
| } |
| |
| #endif /* HAVE_DBGHELP */ |
| |
| /** |
| * Get implementation methods of backtrace_t |
| */ |
| static backtrace_t get_methods() |
| { |
| return (backtrace_t) { |
| .log = _log_, |
| .contains_function = _contains_function, |
| .equals = _equals, |
| .clone = _clone_, |
| .create_frame_enumerator = _create_frame_enumerator, |
| .destroy = _destroy, |
| }; |
| } |
| |
| /** |
| * See header |
| */ |
| backtrace_t *backtrace_create(int skip) |
| { |
| private_backtrace_t *this; |
| void *frames[50]; |
| int frame_count = 0; |
| |
| #ifdef HAVE_LIBUNWIND_H |
| frame_count = backtrace_unwind(frames, countof(frames)); |
| #elif defined(HAVE_BACKTRACE) |
| frame_count = backtrace(frames, countof(frames)); |
| #elif defined(HAVE_DBGHELP) |
| frame_count = backtrace_win(frames, countof(frames)); |
| #elif defined(WIN32) |
| frame_count = CaptureStackBackTrace(skip, countof(frames), frames, NULL); |
| skip = 0; |
| #endif |
| frame_count = max(frame_count - skip, 0); |
| this = malloc(sizeof(private_backtrace_t) + frame_count * sizeof(void*)); |
| memcpy(this->frames, frames + skip, frame_count * sizeof(void*)); |
| this->frame_count = frame_count; |
| |
| this->public = get_methods(); |
| |
| return &this->public; |
| } |
| |
| /** |
| * See header |
| */ |
| void backtrace_dump(char *label, FILE *file, bool detailed) |
| { |
| backtrace_t *backtrace; |
| |
| backtrace = backtrace_create(2); |
| |
| if (label) |
| { |
| println(file, "Debug backtrace: %s", label); |
| } |
| backtrace->log(backtrace, file, detailed); |
| backtrace->destroy(backtrace); |
| } |