backtrace: Support backtraces on Windows without DbgHelp

While DbgHelp provides a convenient API to create backtraces, any executable
linking against DbgHelp gets a more than a significant slow down. Further, it
can only lookup global symbols, as it expects PDB files we can't produce with a
MinGW build.

With some core Kernel32.dll functionality, we can capture stack traces much
faster. Together with the optional libbfd, we can print very fine backtraces.

When --enable-bfd-backtraces is used on Windows, a libbfd.dll is required for
the build. Such a DLL can be created from the binutils sources using:

  # build binutils with mingw...

  # extract archive members from binutils libraries
  x86_64-w64-mingw32-ar x $BINUTILS/bfd/.libs/libbfd.a
  x86_64-w64-mingw32-ar x $BINUTILS/intl/libintl.a
  x86_64-w64-mingw32-ar x $BINUTILS/libiberty/libiberty.a

  # create self-contained libbfd.a, with index
  x86_64-w64-mingw32-ar qs libbfd.a *.o

  # create DLL from static library
  x86_64-w64-mingw32-dlltool -e libbfd.o -l libbfd.lib libbfd.a
  x86_64-w64-mingw32-gcc -shared libbfd.a libbfd.o -o libbfd.dll
This commit is contained in:
Martin Willi
2014-06-04 15:52:58 +02:00
parent a7e943a640
commit 2127831cda
+61 -27
View File
@@ -30,6 +30,15 @@
#include <utils/debug.h>
#ifdef WIN32
# include <psapi.h>
/* missing in MinGW */
WINBOOL K32GetModuleInformation(HANDLE hProcess, HMODULE hModule,
LPMODULEINFO lpmodinfo, DWORD cb);
DWORD K32GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule,
LPTSTR lpFilename, DWORD nSize);
#endif
typedef struct private_backtrace_t private_backtrace_t;
/**
@@ -80,9 +89,34 @@ static void println(FILE *file, char *format, ...)
va_end(args);
}
#ifdef HAVE_DLADDR
#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
/**
* Same as tty_escape_get(), but for a potentially NULL FILE*
@@ -355,7 +389,6 @@ static void print_sourceline(FILE *file, char *filename, void *ptr, void* base)
snprintf(buf, sizeof(buf), "addr2line -e %s %p", filename, ptr);
#endif /* __APPLE__ */
output = popen(buf, "r");
if (output)
{
@@ -378,29 +411,6 @@ static void print_sourceline(FILE *file, char *filename, void *ptr, void* base)
#endif /* HAVE_BFD_H */
#elif defined(HAVE_DBGHELP) /* && !HAVE_DLADDR */
#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());
}
#else /* !HAVE_DLADDR && !HAVE_DBGHELP */
void backtrace_init() {}
@@ -411,7 +421,7 @@ void backtrace_deinit() {}
METHOD(backtrace_t, log_, void,
private_backtrace_t *this, FILE *file, bool detailed)
{
#if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) || defined(HAVE_DBGHELP)
#if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) || defined(WIN32)
size_t i;
char **strings = NULL;
@@ -508,6 +518,26 @@ METHOD(backtrace_t, log_, void,
}
}
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) &&
K32GetModuleInformation(GetCurrentProcess(), module,
&info, sizeof(info)) &&
K32GetModuleFileNameExA(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
@@ -709,7 +739,7 @@ static inline int backtrace_unwind(void **frames, int count)
#ifdef HAVE_DBGHELP
/**
* Windows variant for glibc backtrace()
* Windows dbghelp variant for glibc backtrace()
*/
static inline int backtrace_win(void **frames, int count)
{
@@ -763,6 +793,7 @@ static inline int backtrace_win(void **frames, int count)
return got;
}
#endif /* HAVE_DBGHELP */
/**
@@ -795,6 +826,9 @@ backtrace_t *backtrace_create(int skip)
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*));