[evolution-data-server] Be able to provide backtraces for e_ptr_tracker* functions
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Be able to provide backtraces for e_ptr_tracker* functions
- Date: Fri, 29 Jul 2011 15:07:34 +0000 (UTC)
commit f523e2e097b2a7a6f14ec0de5ebbe3338ae832a3
Author: Milan Crha <mcrha redhat com>
Date: Fri Jul 29 17:06:16 2011 +0200
Be able to provide backtraces for e_ptr_tracker* functions
The e_ptr_tracker* functions from e-data-server-util.h track
pointers and reports those left in the queue at the end of
the application. With this change, when evolution-data-server
is configured with --enable-backtraces and required tools are
available, then also a backtrace when the pointer was added
to the queue is shown.
Thanks to Jan Kratochvil for his help with the dwfl part.
configure.ac | 52 +++++++++
libedataserver/Makefile.am | 5 +-
libedataserver/e-data-server-util.c | 217 ++++++++++++++++++++++++++++++++++-
3 files changed, 267 insertions(+), 7 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 7c0bed8..8952ba5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -539,6 +539,58 @@ AC_RUN_IFELSE([AC_LANG_SOURCE([[
CFLAGS="$save_CFLAGS"
LIBS="$save_LIBS"
+AC_ARG_ENABLE([backtraces],
+ [AS_HELP_STRING([--enable-backtraces],
+ [enable backtraces for e_ptr_tracker (default=no)])],
+ [enable_backtraces=$enableval], [enable_backtraces=no])
+
+if test "x$enable_backtraces" = xyes; then
+ dnl ****************************
+ dnl Check for backtrace_symbols function and dwfl from elfutils
+ dnl ****************************
+ AC_MSG_CHECKING([libc backtrace_symbols function])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[#include <execinfo.h>]],
+ [[{ void *bt[1]; backtrace_symbols (bt, backtrace(bt, 1)); }]])],
+ [AC_DEFINE(HAVE_BACKTRACE_SYMBOLS, 1, [libc provides backtrace_symbols function]) ac_cv_have_bsf=yes],[ac_cv_have_bsf=no])
+ AC_MSG_RESULT([$ac_cv_have_bsf])
+
+ if test "x$ac_cv_have_bsf" = xyes; then
+ LIBS="$LIBS -ldw"
+
+ AC_MSG_CHECKING([elfutils/libdwfl])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+ [[#include <elfutils/libdwfl.h>]],
+ [[{
+ Dwfl *dwfl;
+ Dwfl_Module *module;
+ Dwarf_Addr module_low_addr;
+ Dwfl_Line *line;
+
+ dwfl_standard_find_debuginfo;
+ dwfl_linux_proc_find_elf;
+ dwfl_begin (NULL);
+ dwfl_linux_proc_report (NULL, 1);
+ dwfl_report_end (NULL, NULL, NULL);
+ dwfl_end (NULL);
+ dwfl_module_addrname (NULL, NULL);
+ dwfl_module_getsrc (NULL, NULL);
+ dwfl_lineinfo (NULL, NULL, NULL, NULL, NULL, NULL);
+ DWARF_CB_ABORT; DWARF_CB_OK;
+ dwfl_getmodules (NULL, NULL, NULL, 0);
+ }]])],
+ [AC_DEFINE(HAVE_ELFUTILS_LIBDWFL, 1, [have elfutils/libdwfl.h functions]) ac_cv_have_elfdwfl=yes],[ac_cv_have_elfdwfl=no])
+ AC_MSG_RESULT([$ac_cv_have_elfdwfl])
+
+ LIBS="$save_LIBS"
+
+ if test "x$ac_cv_have_elfdwfl" = xyes; then
+ LIBDWFL_LIBS="-ldw"
+ AC_SUBST(LIBDWFL_LIBS)
+ fi
+ fi
+fi
+
dnl **********************************
dnl Check for nl_langinfo and CODESET
dnl **********************************
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index 3dee9c0..bcb60c0 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -48,11 +48,12 @@ libedataserver_1_2_la_SOURCES = \
libedataserver-private.h \
eds-version.c
-libedataserver_1_2_la_LIBADD = \
+libedataserver_1_2_la_LIBADD = \
$(E_DATA_SERVER_LIBS) \
$(ICONV_LIBS) \
$(SOCKET_LIBS) \
- $(SOUP_LIBS)
+ $(SOUP_LIBS) \
+ $(LIBDWFL_LIBS)
libedataserver_1_2_la_LDFLAGS = \
-version-info $(LIBEDATASERVER_CURRENT):$(LIBEDATASERVER_REVISION):$(LIBEDATASERVER_AGE) $(NO_UNDEFINED)
diff --git a/libedataserver/e-data-server-util.c b/libedataserver/e-data-server-util.c
index daac2bd..676c862 100644
--- a/libedataserver/e-data-server-util.c
+++ b/libedataserver/e-data-server-util.c
@@ -26,6 +26,14 @@
#include <time.h>
#include <unistd.h>
+#ifdef HAVE_BACKTRACE_SYMBOLS
+#include <execinfo.h>
+#ifdef HAVE_ELFUTILS_LIBDWFL
+#include <elfutils/libdwfl.h>
+#include <errno.h>
+#endif
+#endif
+
#ifdef G_OS_WIN32
#include <mbstring.h>
#endif
@@ -912,10 +920,35 @@ e_data_server_util_get_dbus_call_timeout (void)
G_LOCK_DEFINE_STATIC (ptr_tracker);
static GHashTable *ptr_tracker = NULL;
+struct pt_data {
+ gchar *info;
+ GString *backtrace;
+};
+
+static void
+free_pt_data (gpointer ptr)
+{
+ struct pt_data *ptd = ptr;
+
+ if (!ptd)
+ return;
+
+ g_free (ptd->info);
+ if (ptd->backtrace)
+ g_string_free (ptd->backtrace, TRUE);
+ g_free (ptd);
+}
+
static void
dump_left_ptrs_cb (gpointer ptr, gpointer info, gpointer user_data)
{
- g_print (" %p %s%s%s\n", ptr, info ? "(" : "", info ? (const gchar *) info : "", info ? ")" : "");
+ guint *left = user_data;
+ struct pt_data *ptd = info;
+ gboolean have_info = ptd && ptd->info;
+ gboolean have_bt = ptd && ptd->backtrace && ptd->backtrace->str && *ptd->backtrace->str;
+
+ *left = (*left) - 1;
+ g_print (" %p %s%s%s%s%s%s\n", ptr, have_info ? "(" : "", have_info ? ptd->info : "", have_info ? ")" : "", have_bt ? "\n" : "", have_bt ? ptd->backtrace->str : "", have_bt && *left > 0 ? "\n" : "");
}
static void
@@ -928,8 +961,9 @@ dump_tracked_ptrs (gboolean is_at_exit)
if (g_hash_table_size (ptr_tracker) == 0) {
g_print (" All tracked pointers were properly removed\n");
} else {
- g_print (" Left %d tracked pointers:\n", g_hash_table_size (ptr_tracker));
- g_hash_table_foreach (ptr_tracker, dump_left_ptrs_cb, NULL);
+ guint count = g_hash_table_size (ptr_tracker);
+ g_print (" Left %d tracked pointers:\n", count);
+ g_hash_table_foreach (ptr_tracker, dump_left_ptrs_cb, &count);
}
g_print ("----------------------------------------------------------\n");
} else if (!is_at_exit) {
@@ -941,6 +975,173 @@ dump_tracked_ptrs (gboolean is_at_exit)
G_UNLOCK (ptr_tracker);
}
+#ifdef HAVE_BACKTRACE_SYMBOLS
+
+#ifdef HAVE_ELFUTILS_LIBDWFL
+static Dwfl *
+dwfl_get (gboolean reload)
+{
+ static gchar *debuginfo_path = NULL;
+ static Dwfl *dwfl = NULL;
+ static gboolean checked_for_dwfl = FALSE;
+ static GStaticMutex dwfl_mutex = G_STATIC_MUTEX_INIT;
+ static const Dwfl_Callbacks proc_callbacks = {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+ .find_elf = dwfl_linux_proc_find_elf
+ };
+
+ g_static_mutex_lock (&dwfl_mutex);
+
+ if (checked_for_dwfl) {
+ if (!reload) {
+ g_static_mutex_unlock (&dwfl_mutex);
+ return dwfl;
+ }
+
+ dwfl_end (dwfl);
+ dwfl = NULL;
+ }
+
+ checked_for_dwfl = TRUE;
+
+ dwfl = dwfl_begin (&proc_callbacks);
+ if (!dwfl) {
+ g_static_mutex_unlock (&dwfl_mutex);
+ return NULL;
+ }
+
+ errno = 0;
+ if (dwfl_linux_proc_report (dwfl, getpid ()) != 0 || dwfl_report_end (dwfl, NULL, NULL) != 0) {
+ dwfl_end (dwfl);
+ dwfl = NULL;
+ }
+
+ g_static_mutex_unlock (&dwfl_mutex);
+
+ return dwfl;
+}
+
+struct getmodules_callback_arg
+{
+ gpointer addr;
+ const gchar *func_name;
+ const gchar *file_path;
+ gint lineno;
+};
+
+static gint
+getmodules_callback (Dwfl_Module *module, gpointer *module_userdata_pointer, const gchar *module_name, Dwarf_Addr module_low_addr, gpointer arg_voidp)
+{
+ struct getmodules_callback_arg *arg = arg_voidp;
+ Dwfl_Line *line;
+
+ arg->func_name = dwfl_module_addrname (module, (GElf_Addr) arg->addr);
+ line = dwfl_module_getsrc (module, (GElf_Addr) arg->addr);
+ if (line) {
+ arg->file_path = dwfl_lineinfo (line, NULL, &arg->lineno, NULL, NULL, NULL);
+ } else {
+ arg->file_path = NULL;
+ }
+
+ return arg->func_name ? DWARF_CB_ABORT : DWARF_CB_OK;
+}
+#endif /* HAVE_ELFUTILS_LIBDWFL */
+
+static const gchar *
+addr_lookup (gpointer addr, const gchar **file_path, gint *lineno, const gchar *fallback)
+{
+#ifdef HAVE_ELFUTILS_LIBDWFL
+ Dwfl *dwfl = dwfl_get (FALSE);
+ struct getmodules_callback_arg arg;
+
+ if (!dwfl)
+ return NULL;
+
+ arg.addr = addr;
+ arg.func_name = NULL;
+ arg.file_path = NULL;
+ arg.lineno = -1;
+
+ dwfl_getmodules (dwfl, getmodules_callback, &arg, 0);
+
+ if (!arg.func_name && fallback && strstr (fallback, "/lib") != fallback && strstr (fallback, "/usr/lib") != fallback) {
+ dwfl = dwfl_get (TRUE);
+ if (dwfl)
+ dwfl_getmodules (dwfl, getmodules_callback, &arg, 0);
+ }
+
+ *file_path = arg.file_path;
+ *lineno = arg.lineno;
+
+ return arg.func_name;
+#else /* HAVE_ELFUTILS_LIBDWFL */
+ return NULL;
+#endif /* HAVE_ELFUTILS_LIBDWFL */
+}
+
+#endif /* HAVE_BACKTRACE_SYMBOLS */
+
+static GString *
+get_current_backtrace (void)
+{
+#ifdef HAVE_BACKTRACE_SYMBOLS
+ #define MAX_BT_DEPTH 50
+ gint nptrs, ii;
+ gpointer bt[MAX_BT_DEPTH + 1];
+ gchar **bt_syms;
+ GString *bt_str;
+
+ nptrs = backtrace (bt, MAX_BT_DEPTH + 1);
+ if (nptrs <= 2)
+ return NULL;
+
+ bt_syms = backtrace_symbols (bt, nptrs);
+ if (!bt_syms)
+ return NULL;
+
+ bt_str = g_string_new ("");
+ for (ii = 2; ii < nptrs; ii++) {
+ gint lineno = -1;
+ const gchar *file_path = NULL;
+ const gchar *str = addr_lookup (bt[ii], &file_path, &lineno, bt_syms[ii]);
+ if (!str) {
+ str = bt_syms[ii];
+ file_path = NULL;
+ lineno = -1;
+ }
+ if (!str)
+ continue;
+
+ if (bt_str->len)
+ g_string_append (bt_str, "\n\t by ");
+ g_string_append (bt_str, str);
+ if (str != bt_syms[ii])
+ g_string_append (bt_str, "()");
+
+ if (file_path && lineno > 0) {
+ const gchar *lastsep = strrchr (file_path, G_DIR_SEPARATOR);
+ g_string_append_printf (bt_str, " at %s:%d", lastsep ? lastsep + 1 : file_path, lineno);
+ }
+ }
+
+ g_free (bt_syms);
+
+ if (bt_str->len == 0) {
+ g_string_free (bt_str, TRUE);
+ bt_str = NULL;
+ } else {
+ g_string_insert (bt_str, 0, "\t at ");
+ }
+
+ return bt_str;
+
+ #undef MAX_BT_DEPTH
+#else /* HAVE_BACKTRACE_SYMBOLS */
+ return NULL;
+#endif /* HAVE_BACKTRACE_SYMBOLS */
+}
+
static void
dump_left_at_exit_cb (void)
{
@@ -957,15 +1158,21 @@ dump_left_at_exit_cb (void)
void
e_pointer_tracker_track_with_info (gpointer ptr, const gchar *info)
{
+ struct pt_data *ptd;
+
g_return_if_fail (ptr != NULL);
G_LOCK (ptr_tracker);
if (!ptr_tracker) {
- ptr_tracker = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
+ ptr_tracker = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, free_pt_data);
g_atexit (dump_left_at_exit_cb);
}
- g_hash_table_insert (ptr_tracker, ptr, g_strdup (info));
+ ptd = g_new0 (struct pt_data, 1);
+ ptd->info = g_strdup (info);
+ ptd->backtrace = get_current_backtrace ();
+
+ g_hash_table_insert (ptr_tracker, ptr, ptd);
G_UNLOCK (ptr_tracker);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]