[libgda] Added the --enable-mutex-debug configure flag
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] Added the --enable-mutex-debug configure flag
- Date: Sun, 9 Jan 2011 18:54:51 +0000 (UTC)
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]