backtrace: Add DbgHelp based Windows support for creating/printing backtraces
authorMartin Willi <martin@revosec.ch>
Mon, 14 Oct 2013 09:55:12 +0000 (11:55 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 4 Jun 2014 13:52:57 +0000 (15:52 +0200)
configure.ac
src/libstrongswan/Makefile.am
src/libstrongswan/utils/backtrace.c

index 8b0b8d0..33aee4c 100644 (file)
@@ -272,6 +272,7 @@ ARG_DISBL_SET([tools],          [disable additional utilities (scepclient and pk
 ARG_ENABL_SET([aikgen],         [enable AIK generator.])
 # optional features
 ARG_ENABL_SET([bfd-backtraces], [use binutils libbfd to resolve backtraces for memory leaks and segfaults.])
+ARG_ENABL_SET([dbghelp-backtraces],[use dbghlp.dll on Windows to create and print backtraces for memory leaks and segfaults.])
 ARG_DISBL_SET([ikev1],          [disable IKEv1 protocol support in charon.])
 ARG_DISBL_SET([ikev2],          [disable IKEv2 protocol support in charon.])
 ARG_ENABL_SET([integrity-test], [enable integrity testing of libstrongswan and plugins.])
@@ -1410,6 +1411,7 @@ AM_CONDITIONAL(USE_TROUSERS, test x$tss = xtrousers -o x$aikgen = xtrue)
 AM_CONDITIONAL(MONOLITHIC, test x$monolithic = xtrue)
 AM_CONDITIONAL(USE_SILENT_RULES, test x$enable_silent_rules = xyes)
 AM_CONDITIONAL(COVERAGE, test x$coverage = xtrue)
+AM_CONDITIONAL(USE_DBGHELP, test x$dbghelp_backtraces = xtrue)
 AM_CONDITIONAL(USE_TKM, test x$tkm = xtrue)
 AM_CONDITIONAL(USE_CMD, test x$cmd = xtrue)
 AM_CONDITIONAL(USE_AIKGEN, test x$aikgen = xtrue)
index 180d27a..3560507 100644 (file)
@@ -114,6 +114,11 @@ if USE_WINDOWS
   libstrongswan_la_LIBADD += -lws2_32
 endif
 
+if USE_DBGHELP
+  libstrongswan_la_LIBADD += -ldbghelp
+  AM_CPPFLAGS += -DHAVE_DBGHELP
+endif
+
 if USE_LEAK_DETECTIVE
   AM_CPPFLAGS += -DLEAK_DETECTIVE
   libstrongswan_la_SOURCES += utils/leak_detective.c
index f158462..f367fe2 100644 (file)
@@ -1,6 +1,7 @@
 /*
- * Copyright (C) 2006-2008 Martin Willi
+ * Copyright (C) 2006-2013 Martin Willi
  * 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
 
 #define _GNU_SOURCE
 
-#ifdef HAVE_DLADDR
-# include <dlfcn.h>
-#endif /* HAVE_DLADDR */
-
 #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"
@@ -81,6 +82,8 @@ static void println(FILE *file, char *format, ...)
 
 #ifdef HAVE_DLADDR
 
+#include <dlfcn.h>
+
 /**
  * Same as tty_escape_get(), but for a potentially NULL FILE*
  */
@@ -375,7 +378,30 @@ static void print_sourceline(FILE *file, char *filename, void *ptr, void* base)
 
 #endif /* HAVE_BFD_H */
 
-#else /* !HAVE_DLADDR */
+#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() {}
 void backtrace_deinit() {}
@@ -385,7 +411,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)
+#if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) || defined(HAVE_DBGHELP)
        size_t i;
        char **strings = NULL;
 
@@ -425,7 +451,64 @@ METHOD(backtrace_t, log_, void,
                        }
                }
                else
-#endif /* HAVE_DLADDR */
+#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) &&
+                               symbol.hdr.Name)
+                       {
+                               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
+#endif /* HAVE_DLADDR/HAVE_DBGHELP */
                {
 #ifdef HAVE_BACKTRACE
                        if (!strings)
@@ -442,10 +525,13 @@ METHOD(backtrace_t, log_, void,
                                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 backtrace()/libunwind");
+       println(file, "no support for capturing backtraces");
 #endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H */
 }
 
@@ -470,7 +556,43 @@ METHOD(backtrace_t, contains_function, bool,
                        }
                }
        }
-#endif /* HAVE_DLADDR */
+#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))
+               {
+                       if (symbol.hdr.Name)
+                       {
+                               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;
 }
 
@@ -584,6 +706,65 @@ static inline int backtrace_unwind(void **frames, int count)
 }
 #endif /* HAVE_UNWIND */
 
+#ifdef HAVE_DBGHELP
+
+/**
+ * Windows 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
  */
@@ -612,7 +793,9 @@ backtrace_t *backtrace_create(int skip)
        frame_count = backtrace_unwind(frames, countof(frames));
 #elif defined(HAVE_BACKTRACE)
        frame_count = backtrace(frames, countof(frames));
-#endif /* HAVE_BACKTRACE */
+#elif defined(HAVE_DBGHELP)
+       frame_count = backtrace_win(frames, countof(frames));
+#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*));