]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
With --enable-bfd-backtraces, use binutils libbfd to resolve backtraces
authorMartin Willi <martin@revosec.ch>
Wed, 11 Jul 2012 15:37:09 +0000 (17:37 +0200)
committerMartin Willi <martin@revosec.ch>
Fri, 13 Jul 2012 11:23:29 +0000 (13:23 +0200)
The invocation of addr2line to resolve backtrace source locations
is slow and cumbersome. When using libbfd directly, we can eliminate
the overhead of the process invocation. Even better, we can cache
library symbol names, bringing wicked fast lookups. As a neat bonus,
we can resolve static function names.

configure.in
src/libstrongswan/Makefile.am
src/libstrongswan/library.c
src/libstrongswan/utils/backtrace.c
src/libstrongswan/utils/backtrace.h

index 90683c89563c457144941689039afac001437d24..c5fd1d3c68b2a9e5de6989e060fc9f86454b81af 100644 (file)
@@ -194,6 +194,7 @@ ARG_ENABL_SET([coupling],       [enable IKEv2 plugin to couple peer certificates
 ARG_ENABL_SET([radattr],        [enable plugin to inject and process custom RADIUS attributes as IKEv2 client.])
 ARG_ENABL_SET([vstr],           [enforce using the Vstr string library to replace glibc-like printf hooks.])
 ARG_ENABL_SET([monolithic],     [build monolithic version of libstrongswan that includes all enabled plugins. Similarly, the plugins of charon are assembled in libcharon.])
+ARG_ENABL_SET([bfd-backtraces], [use binutils libbfd to resolve backtraces for memory leaks and segfaults.])
 
 dnl =========================
 dnl  set up compiler and flags
@@ -785,6 +786,14 @@ if test x$integrity_test = xtrue; then
        )
 fi
 
+if test x$bfd_backtraces = xtrue; then
+       AC_HAVE_LIBRARY([bfd],[LIBS="$LIBS"],[AC_MSG_ERROR([binutils libbfd not found!])])
+       AC_CHECK_HEADER([bfd.h],[AC_DEFINE([HAVE_BFD_H],,[have binutils bfd.h])],
+               [AC_MSG_ERROR([binutils bfd.h header not found!])])
+       BFDLIB="-lbfd"
+       AC_SUBST(BFDLIB)
+fi
+
 AM_CONDITIONAL(USE_DEV_HEADERS, [test "x$dev_headers" != xno])
 if test x$dev_headers = xyes; then
        dev_headers="$includedir/strongswan"
index 4046f65a131de507c0dc6b3987e1e6756d254678..dc849d6f275ea76e3bb2b7c67936028c721839e5 100644 (file)
@@ -63,7 +63,7 @@ endif
 
 library.lo :   $(top_builddir)/config.status
 
-libstrongswan_la_LIBADD = $(PTHREADLIB) $(DLLIB) $(BTLIB) $(SOCKLIB) $(RTLIB)
+libstrongswan_la_LIBADD = $(PTHREADLIB) $(DLLIB) $(BTLIB) $(SOCKLIB) $(RTLIB) $(BFDLIB)
 
 INCLUDES = -I$(top_srcdir)/src/libstrongswan
 AM_CFLAGS = \
index cd6a41f4405ba2f81a0eaa6e35daad94d444140f..ed3f52027ae78f451a870c3dd07e966e2932c070 100644 (file)
@@ -23,6 +23,7 @@
 #include <utils/identification.h>
 #include <utils/host.h>
 #include <utils/hashtable.h>
+#include <utils/backtrace.h>
 #include <selectors/traffic_selector.h>
 
 #define CHECKSUM_LIBRARY IPSEC_LIB_DIR"/libchecksum.so"
@@ -88,6 +89,7 @@ void library_deinit()
        }
 
        threads_deinit();
+       backtrace_deinit();
 
        free(this);
        lib = NULL;
@@ -146,6 +148,7 @@ bool library_init(char *settings)
        );
        lib = &this->public;
 
+       backtrace_init();
        threads_init();
 
 #ifdef LEAK_DETECTIVE
@@ -204,6 +207,7 @@ bool library_init(char *settings)
                return FALSE;
 #endif /* INTEGRITY_TEST */
        }
+
        return TRUE;
 }
 
index db10c7844f7aace80cc50ecb5fa9257a945d3e09..b417129260ce5a6b0e8898b81f3aed9248d02b57 100644 (file)
@@ -50,6 +50,276 @@ struct private_backtrace_t {
        void *frames[];
 };
 
+#ifdef HAVE_DLADDR
+#ifdef HAVE_BFD_H
+
+#include <bfd.h>
+#include <utils/hashtable.h>
+#include <threading/mutex.h>
+
+/**
+ * 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;
+       u_int line;
+
+       if (!data->found || (bfd_get_section_flags(abfd, section) & SEC_ALLOC) != 0)
+       {
+               vma = bfd_get_section_vma(abfd, section);
+               if (data->vma >= vma)
+               {
+                       size = bfd_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)
+                                       {
+                                               fprintf(data->file, "    -> ");
+                                               if (function)
+                                               {
+                                                       fprintf(data->file, "\e[34m%s() ", function);
+                                               }
+                                               if (source)
+                                               {
+                                                       fprintf(data->file, "\e[32m@ %s:%d", source, line);
+                                               }
+                                               fprintf(data->file, "\e[0m\n");
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+/**
+ * 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;
+       }
+       entry->abfd->flags |= BFD_DECOMPRESS;
+       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);
+                       }
+                       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)
+{
+       bfd_entry_t *entry;
+       bfd_find_data_t data = {
+               .file = file,
+               .vma = (bfd_vma)ptr,
+       };
+       bool old = FALSE;
+
+       bfd_mutex->lock(bfd_mutex);
+       if (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->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)
+{
+       char cmd[1024];
+       FILE *output;
+       int c;
+
+       snprintf(cmd, sizeof(cmd), "addr2line -e %s %p", filename, ptr);
+       output = popen(cmd, "r");
+       if (output)
+       {
+               fprintf(file, "    -> \e[32m");
+               while (TRUE)
+               {
+                       c = getc(output);
+                       if (c == '\n' || c == EOF)
+                       {
+                               break;
+                       }
+                       fputc(c, file);
+               }
+               pclose(output);
+               fprintf(file, "\e[0m\n");
+       }
+}
+
+#endif /* HAVE_BFD_H */
+#endif /* HAVE_DLADDR */
+
 METHOD(backtrace_t, log_, void,
        private_backtrace_t *this, FILE *file, bool detailed)
 {
@@ -67,9 +337,6 @@ METHOD(backtrace_t, log_, void,
 
                if (dladdr(this->frames[i], &info))
                {
-                       char cmd[1024];
-                       FILE *output;
-                       int c;
                        void *ptr = this->frames[i];
 
                        if (strstr(info.dli_fname, ".so"))
@@ -89,37 +356,14 @@ METHOD(backtrace_t, log_, void,
                        }
                        if (detailed)
                        {
-                               fprintf(file, "    -> \e[32m");
-                               snprintf(cmd, sizeof(cmd), "addr2line -e %s %p",
-                                                info.dli_fname, ptr);
-                               output = popen(cmd, "r");
-                               if (output)
-                               {
-                                       while (TRUE)
-                                       {
-                                               c = getc(output);
-                                               if (c == '\n' || c == EOF)
-                                               {
-                                                       break;
-                                               }
-                                               fputc(c, file);
-                                       }
-                                       pclose(output);
-                               }
-                               else
-                               {
-       #endif /* HAVE_DLADDR */
-                                       fprintf(file, "    %s\n", strings[i]);
-       #ifdef HAVE_DLADDR
-                               }
-                               fprintf(file, "\n\e[0m");
+                               print_sourceline(file, (char*)info.dli_fname, ptr);
                        }
                }
                else
+#endif /* HAVE_DLADDR */
                {
                        fprintf(file, "    %s\n", strings[i]);
                }
-#endif /* HAVE_DLADDR */
        }
        free (strings);
 #else /* !HAVE_BACKTRACE */
index 32d09043febf107c68e7ec627ae4e6c9550cf9bd..aeeba4dd61eb1b4d30db0ea44950fd0149d04e65 100644 (file)
@@ -86,4 +86,14 @@ backtrace_t *backtrace_create(int skip);
  */
 void backtrace_dump(char *label, FILE *file, bool detailed);
 
+/**
+ * Initialize backtracing framework.
+ */
+void backtrace_init();
+
+/**
+ * Deinitialize backtracing framework.
+ */
+void backtrace_deinit();
+
 #endif /** BACKTRACE_H_ @}*/