[libgda] Added the --enable-mutex-debug configure flag



commit 337454a8c8bf2f2d34f098c3acf213e9ddcc0acf
Author: Vivien Malerba <malerba gnome-db org>
Date:   Sun Jan 9 16:26:55 2011 +0100

    Added the --enable-mutex-debug configure flag
    
    this flag add some functions to help debug GdaMutex errors

 configure.ac       |   14 ++++
 libgda/Makefile.am |    3 +-
 libgda/gda-mutex.c |  191 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 libgda/gda-mutex.h |    8 ++-
 4 files changed, 211 insertions(+), 5 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b8d1b9d..c40f44b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -716,6 +716,7 @@ AC_SUBST(FIREBIRD_CFLAGS)
 dnl
 dnl Check if lib should be build with the debug mode
 dnl
+gda_debug_flags=""
 AC_ARG_ENABLE(debug,
 	AS_HELP_STRING([--enable-debug], [Enable debug mode [default=no]]),
         [use_debug=$enableval], [use_debug="no"])
@@ -727,6 +728,19 @@ then
 	AC_SUBST(GDA_DEBUG_FLAGS)
 fi
 
+AC_ARG_ENABLE(mutex-debug,
+	AS_HELP_STRING([--enable-mutex-debug], [Enable debug mode for GdaMutex [default=no]]),
+        [use_debug=$enableval], [use_debug="no"])
+
+if test "x$use_debug" = "xyes"
+then
+	AC_DEFINE(GDA_DEBUG_MUTEX)
+	GDA_DEBUG_FLAGS="$GDA_DEBUG_FLAGS -DGDA_DEBUG_MUTEX"
+	GDA_DEBUG_LDFLAGS=-rdynamic
+	AC_SUBST(GDA_DEBUG_FLAGS)
+	AC_SUBST(GDA_DEBUG_LDFLAGS)
+fi
+
 dnl Disable rebuild of glib-mkenum -generated source code:
 AC_ARG_ENABLE(rebuilds,
 	AS_HELP_STRING([--disable-rebuilds], [disable all source autogeneration rules]),
diff --git a/libgda/Makefile.am b/libgda/Makefile.am
index 03570af..d692a69 100644
--- a/libgda/Makefile.am
+++ b/libgda/Makefile.am
@@ -223,7 +223,8 @@ libgda_4_0_la_SOURCES = \
 
 libgda_4_0_la_LDFLAGS = \
 	-version-info $(GDA_CURRENT):$(GDA_REVISION):$(GDA_AGE) \
-	-export-dynamic $(NO_UNDEFINED) $(LIBTOOL_EXPORT_OPTIONS)
+	-export-dynamic $(NO_UNDEFINED) $(LIBTOOL_EXPORT_OPTIONS) \
+	$(GDA_DEBUG_LDFLAGS)
 
 libgda_4_0_la_LIBADD = \
 	sql-parser/libgda_sql_parser-4.0.la \
diff --git a/libgda/gda-mutex.c b/libgda/gda-mutex.c
index 09823d8..1cedac1 100644
--- a/libgda/gda-mutex.c
+++ b/libgda/gda-mutex.c
@@ -1,5 +1,5 @@
 /* GDA Library
- * Copyright (C) 2008 The GNOME Foundation.
+ * Copyright (C) 2008 - 2011 The GNOME Foundation.
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -19,6 +19,14 @@
 
 #include <libgda/gda-mutex.h>
 
+#ifdef GDA_DEBUG_MUTEX
+#define FRAMES_SIZE 10
+#include <glib/gprintf.h>
+#include <execinfo.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+
 enum MutexRecStatus {
 	UNKNOWN,
 	RECURSIVE,
@@ -28,13 +36,156 @@ enum MutexRecStatus {
 
 static enum MutexRecStatus impl_status = UNKNOWN;
 
+#ifdef GDA_DEBUG_MUTEX
+static GStaticMutex debug_mutex = G_STATIC_MUTEX_INIT;
+
+typedef enum {
+	USAGE_LOCK,
+	USAGE_UNLOCK,
+} GdaMutexUsageType;
+
+typedef struct {
+	GThread            *thread;
+	GdaMutexUsageType   usage;
+	gchar             **frames; /* array terminated by a NULL */
+} GdaMutexUsage;
+#endif
+
 struct _GdaMutex {
 	GMutex  *mutex; /* internal mutex to access the structure's data */
 	GCond   *cond;  /* condition to lock on */
 	GThread *owner; /* current owner of the mutex, or NULL if not owned */
 	short    depth;
+#ifdef GDA_DEBUG_MUTEX
+	gboolean debug; /* set tu %TRUE to debug this mutex */
+	GArray  *usages; /* Array of GdaMutexUsage */
+	gint     nb_locked_unlocked;
+#endif
 };
 
+#ifdef GDA_DEBUG_MUTEX
+void
+gda_mutex_debug (GdaMutex *mutex, gboolean debug)
+{
+	g_static_mutex_lock (&debug_mutex);
+	mutex->debug = debug;
+	g_static_mutex_unlock (&debug_mutex);
+}
+
+void
+gda_mutex_dump_usage (GdaMutex *mutex, FILE *stream)
+{
+	guint i;
+	FILE *st;
+	if (stream)
+		st = stream;
+	else
+		st = stdout;
+
+	g_static_mutex_lock (&debug_mutex);
+	if (mutex->debug) {
+		g_fprintf (st, "%s (mutex=>%p): locked&unlocked %d times\n", __FUNCTION__, mutex,
+			   mutex->nb_locked_unlocked);
+		for (i = mutex->usages->len; i > 0; i--) {
+			GdaMutexUsage *usage;
+			gint j;
+			usage = &g_array_index (mutex->usages, GdaMutexUsage, i - 1);
+			g_fprintf (st, "%d\t------ BEGIN GdaMutex %p usage\n", i - 1, mutex);
+			g_fprintf (st, "\t%s, thread %p\n",
+				   usage->usage == USAGE_LOCK ? "LOCK" : "UNLOCK",
+				   usage->thread);
+			for (j = 0; usage->frames[j]; j++)
+				g_fprintf (st, "\t%s\n", usage->frames[j]);
+			g_fprintf (st, "\t------ END GdaMutex %p usage\n", mutex);
+		}
+	}
+	g_static_mutex_unlock (&debug_mutex);
+}
+
+static void
+gda_mutex_usage_locked (GdaMutex *mutex)
+{
+	g_static_mutex_lock (&debug_mutex);
+
+	if (mutex->debug) {
+		GdaMutexUsage usage;
+		usage.thread = g_thread_self ();
+		usage.usage = USAGE_LOCK;
+		
+		void *array[FRAMES_SIZE];
+		size_t size;
+		char **strings;
+		size_t i;
+		
+		size = backtrace (array, 10);
+		strings = backtrace_symbols (array, size);
+		usage.frames = g_new (gchar *, size + 1);
+		usage.frames[size] = NULL;
+		for (i = 0; i < size; i++)
+			usage.frames[i] = g_strdup (strings[i]);
+		free (strings);
+
+		g_array_prepend_val (mutex->usages, usage);
+	}
+	g_static_mutex_unlock (&debug_mutex);
+	gda_mutex_dump_usage (mutex, NULL);
+}
+
+static void
+gda_mutex_usage_unlocked (GdaMutex *mutex)
+{
+	g_static_mutex_lock (&debug_mutex);
+	if (mutex->debug) {
+		void *array[FRAMES_SIZE];
+		size_t size;
+		char **strings;
+		size_t i;
+		gboolean matched = FALSE;
+		
+		size = backtrace (array, 10);
+		strings = backtrace_symbols (array, size);
+		
+		if (mutex->usages->len > 0) {
+			GdaMutexUsage *last;
+			last = &g_array_index (mutex->usages, GdaMutexUsage, 0);
+			if ((size > 3) &&
+			    (last->usage == USAGE_LOCK) &&
+			    (last->thread == g_thread_self ())) {
+				for (i = 3; i < size; i++) {
+					if (! last->frames[i] || (last->frames[i] &&
+								  strcmp (last->frames[i], strings[i])))
+						break;
+				}
+				if ((i == size) && ! last->frames[i]) {
+					/* same stack => delete @last */
+					g_strfreev (last->frames);
+					g_array_remove_index (mutex->usages, 0);
+					matched = TRUE;
+					mutex->nb_locked_unlocked++;
+				}
+			}
+		}
+		
+		if (! matched) {
+			GdaMutexUsage usage;
+			usage.thread = g_thread_self ();
+			usage.usage = USAGE_UNLOCK;
+			
+			usage.frames = g_new (gchar *, size + 1);
+			usage.frames[size] = NULL;
+			for (i = 0; i < size; i++)
+				usage.frames[i] = g_strdup (strings[i]);
+			
+			g_array_prepend_val (mutex->usages, usage);
+		}
+		free (strings);
+	}
+	g_static_mutex_unlock (&debug_mutex);
+	gda_mutex_dump_usage (mutex, NULL);
+}
+
+#endif
+
 /**
  * gda_mutex_new: (skip):
  *
@@ -77,6 +228,10 @@ gda_mutex_new ()
 	if (impl_status == NON_SUPPORTED) {
 		GdaMutex *m;
 		m = g_new0 (GdaMutex, 1);
+#ifdef GDA_DEBUG_MUTEX
+		m->usages = g_array_new (FALSE, FALSE, sizeof (GdaMutexUsage));
+		m->debug = FALSE;
+#endif
 		return m;
 	}
 	else {
@@ -86,7 +241,10 @@ gda_mutex_new ()
 		m->cond = g_cond_new ();
 		m->owner = NULL;
 		m->depth = 0;
-		
+#ifdef GDA_DEBUG_MUTEX
+		m->usages = g_array_new (FALSE, FALSE, sizeof (GdaMutexUsage));
+		m->debug = FALSE;
+#endif
 		return m;
 	}
 }
@@ -128,6 +286,9 @@ gda_mutex_lock (GdaMutex *mutex)
                 }
 		g_mutex_unlock (mutex->mutex);
 	}
+#ifdef GDA_DEBUG_MUTEX
+	gda_mutex_usage_locked (mutex);
+#endif
 }
 
 /**
@@ -147,8 +308,17 @@ gda_mutex_lock (GdaMutex *mutex)
 gboolean
 gda_mutex_trylock (GdaMutex *mutex)
 {
-	if (impl_status == RECURSIVE)
+	if (impl_status == RECURSIVE) {
+#ifdef GDA_DEBUG_MUTEX
+		gboolean retval;
+		retval = g_mutex_trylock (mutex->mutex);
+		if (retval)
+			gda_mutex_usage_locked (mutex);
+		return retval;
+#else
 		return g_mutex_trylock (mutex->mutex);
+#endif
+	}
 	else if (impl_status == NON_SUPPORTED)
 		return TRUE;
 	else {
@@ -167,6 +337,10 @@ gda_mutex_trylock (GdaMutex *mutex)
 		else
 			retval = FALSE;
 		g_mutex_unlock (mutex->mutex);
+#ifdef GDA_DEBUG_MUTEX
+		if (retval)
+			gda_mutex_usage_locked (mutex);
+#endif
 		return retval;
 	}
 }
@@ -182,6 +356,9 @@ gda_mutex_trylock (GdaMutex *mutex)
 void
 gda_mutex_unlock (GdaMutex *mutex)
 {
+#ifdef GDA_DEBUG_MUTEX
+	gda_mutex_usage_unlocked (mutex);
+#endif
 	if (impl_status == RECURSIVE)
 		g_mutex_unlock (mutex->mutex);
 	else if (impl_status == NON_SUPPORTED)
@@ -215,5 +392,13 @@ gda_mutex_free (GdaMutex *mutex)
 	if (mutex->mutex)
 		g_mutex_free (mutex->mutex);
 	mutex->mutex = NULL;
+#ifdef GDA_DEBUG_MUTEX
+	guint i;
+	for (i = 0; i < mutex->usages->len; i++) {
+		GdaMutexUsage *usage;
+		usage = &g_array_index (mutex->usages, GdaMutexUsage, i);
+		g_strfreev (usage->frames);
+	}
+#endif
 	g_free (mutex);
 }
diff --git a/libgda/gda-mutex.h b/libgda/gda-mutex.h
index 5793cce..402c3f5 100644
--- a/libgda/gda-mutex.h
+++ b/libgda/gda-mutex.h
@@ -1,5 +1,5 @@
 /* GDA library
- * Copyright (C) 2008 The GNOME Foundation.
+ * Copyright (C) 2008 - 2011 The GNOME Foundation.
  *
  * AUTHORS:
  *      Vivien Malerba <malerba gnome-db org>
@@ -35,6 +35,12 @@ gboolean    gda_mutex_trylock   (GdaMutex *mutex);
 void        gda_mutex_unlock    (GdaMutex *mutex);
 void        gda_mutex_free      (GdaMutex *mutex);
 
+#ifdef GDA_DEBUG_MUTEX
+#include <stdio.h>
+void        gda_mutex_debug      (GdaMutex *mutex, gboolean debug);
+void        gda_mutex_dump_usage (GdaMutex *mutex, FILE *stream);
+#endif
+
 G_END_DECLS
 
 #endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]