[evolution-data-server] Be able to provide backtraces for e_ptr_tracker* functions



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]