]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
backtrace: Support backtraces on Windows without DbgHelp
authorMartin Willi <martin@revosec.ch>
Tue, 19 Nov 2013 17:40:51 +0000 (18:40 +0100)
committerMartin Willi <martin@revosec.ch>
Wed, 4 Jun 2014 13:52:58 +0000 (15:52 +0200)
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

src/libstrongswan/utils/backtrace.c

index f367fe233aa327472aaf5549fc7404f71125f0b6..6101fb9c2fa60631c999de83843eb0de3a826938 100644 (file)
 
 #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*));