added mutex debugger
authorpk910 <philipp@zoelle1.de>
Sat, 10 Nov 2012 16:13:00 +0000 (17:13 +0100)
committerpk910 <philipp@zoelle1.de>
Sat, 10 Nov 2012 16:26:14 +0000 (17:26 +0100)
Makefile.am
configure.ac
src/main.c
src/memoryDebug.c
src/modules.c
src/modules/module.h
src/mutexDebug.c [new file with mode: 0644]
src/mutexDebug.h [new file with mode: 0644]
src/overall.h

index e0105d6d5997131236477125d25ac3194c05d0de..6299256a59ab0d31942424ef88477c69f1be7439 100644 (file)
@@ -213,7 +213,8 @@ neonserv_SOURCES = src/version.c \
       src/ModuleFunctions.c \
       src/statistics.c \
       src/log.c \
-      src/memoryDebug.c
+      src/memoryDebug.c \
+      src/mutexDebug.c
 
 neonserv_LDADD = $(MYSQL_LIBS) $(SYSTEM_LIBS)
 
index 56d1e7f1ec3209fdf73519ff8ec9511c4db809a3..0a6c9d97fed62c693d182de37660b2d8bed28938 100644 (file)
@@ -68,6 +68,13 @@ AC_ARG_ENABLE([memory-debug],
   ],
   [])
 
+AC_ARG_ENABLE([mutex-debug],
+  [AS_HELP_STRING([--enable-mutex-debug], [run mutex debugger])],
+  [
+    AC_DEFINE([ENABLE_MUTEX_DEBUG], 1, [Define if you enable mutexDebug.c])
+  ],
+  [])
+
 AC_ARG_ENABLE([debug],
   [AS_HELP_STRING([--enable-debug], [debug mode (compile using -O0 -Wall -Wshadow -Werror)])],
   [CFLAGS='-g -O0 -Wall -Wshadow -Werror'],
index c88c02cbfcefc866a8b82526d2e3a65809cc1414..fb16300da97ac5697e13adfcc59b85811a8468d3 100644 (file)
@@ -305,6 +305,11 @@ int main(int argc, char *argv[]) {
     initMemoryDebug();
     #endif
     
+    //initialize mutex debugger BEFORE using any mutexes
+    #ifdef ENABLE_MUTEX_DEBUG
+    initMutexDebug();
+    #endif
+    
     //deny root startup
     #ifndef WIN32
     if(geteuid() == 0 || getuid() == 0) {
index c42559233023cc0c7e013c29f2974423082bc9af..d4e828f1aeb127ce84a8c00c41059e49a12518d2 100644 (file)
@@ -81,9 +81,10 @@ void xfree(void *mem_ref) {
     free(mem_ref);
 }
 
-
 static void add_mem_info(void *mem_ref, unsigned int size, const char *file, unsigned int line) {
-    SYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_lock(&synchronized);
+    #endif
     struct MemoryLeak *mem_leak_info = malloc(sizeof(*mem_leak_info));
     own_allocated_memleaks++;
     mem_leak_info->mem_info.address = mem_ref;
@@ -92,11 +93,15 @@ static void add_mem_info(void *mem_ref, unsigned int size, const char *file, uns
     mem_leak_info->mem_info.line = line;
     mem_leak_info->next = ptr_start;
     ptr_start = mem_leak_info;
-    DESYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_unlock(&synchronized);
+    #endif
 }
 
 static void remove_mem_info(void *mem_ref) {
-    SYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_lock(&synchronized);
+    #endif
     struct MemoryLeak *leak_info, *next, *prev = NULL;
     for(leak_info = ptr_start; leak_info; leak_info = next) {
         next = leak_info->next;
@@ -111,7 +116,9 @@ static void remove_mem_info(void *mem_ref) {
         } else 
             prev = leak_info;
     }
-    DESYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_unlock(&synchronized);
+    #endif
 }
 
 void initMemoryDebug() {
@@ -119,7 +126,9 @@ void initMemoryDebug() {
 }
 
 struct memoryInfoFiles *getMemoryInfoFiles() {
-    SYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_lock(&synchronized);
+    #endif
     struct MemoryLeak *leak_info;
     struct memoryInfoFiles *list = NULL, *element;
     for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next) {
@@ -145,7 +154,9 @@ struct memoryInfoFiles *getMemoryInfoFiles() {
     element->allocated = own_allocated_memleaks * sizeof(struct MemoryLeak);
     element->next = list;
     list = element;
-    DESYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_unlock(&synchronized);
+    #endif
     return list;
 }
 
@@ -159,7 +170,9 @@ void freeMemoryInfoFiles(struct memoryInfoFiles *files) {
 }
 
 struct memoryInfoLines *getMemoryInfoLines(const char *filename) {
-    SYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_lock(&synchronized);
+    #endif
     struct MemoryLeak *leak_info;
     struct memoryInfoLines *list = NULL, *element;
     for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next) {
@@ -187,7 +200,9 @@ struct memoryInfoLines *getMemoryInfoLines(const char *filename) {
         element->next = list;
         list = element;
     }
-    DESYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_unlock(&synchronized);
+    #endif
     return list;
 }
 
index 325621e9da06820c4e47a371608cafa262b15bf4..f48c41a0b2b0eda7a55987eda7522398057f807f 100644 (file)
@@ -21,7 +21,9 @@
 
 /* 000-001 */ #include "main.h"
 /* 002-004 */ #include "tools.h"
-/* 005-006 */ /* deprecated */
+#ifdef ENABLE_MUTEX_DEBUG
+/* 005-006 */ #include "mutexDebug.h"
+#endif
 /* 007-009 */ /* main.h */
 /* 010     */ #include "log.h"
 /* 011     */ /* main.h */
@@ -41,7 +43,9 @@
 /* 099-102 */ #include "memoryDebug.h"
 #endif
 /* 103-106 */ #include "memoryInfo.h"
-/* 107-125 */ #include "modcmd.h"
+/* 107-122 */ #include "modcmd.h"
+/* 123     */ /* deprecated */
+/* 124-125 */ /* modcmd.h */
 /* 126-136 */ #include "ModeNode.h"
 /* 137-142 */ #include "mysqlConn.h"
 /* 143-149 */ #include "timeq.h"
@@ -64,8 +68,13 @@ void *global_functions[] = {
 /* 002 */ (Function) getCurrentSecondsOfDay,
 /* 003 */ (Function) stricmp,
 /* 004 */ (Function) stricmplen,
-/* 005 */ (Function) NULL, /* deprecated */
-/* 006 */ (Function) NULL, /* deprecated */
+#ifdef ENABLE_MUTEX_DEBUG
+/* 005 */ (Function) xmutex,
+/* 006 */ (Function) mutex_debug,
+#else
+/* 005 */ (Function) NULL,
+/* 006 */ (Function) NULL,
+#endif
 /* 007 */ (Function) restart_bot,
 /* 008 */ (Function) stop_bot,
 /* 009 */ (Function) reload_config,
index 1b9dac7a55d75aa244c732b4ac012c004e57a235..4c55c40ef650cbc802e17dad1b183ee6b2f1c3af 100644 (file)
@@ -31,8 +31,10 @@ extern int module_id;
 /* 002 */ #define getCurrentSecondsOfDay ((int (*)(void))global[2])
 /* 003 */ #define stricmp ((int (*)(const char *, const char *))global[3])
 /* 004 */ #define stricmplen ((int (*)(const char *, const char *, int))global[4])
-/* 005 */ /* deprecated */
-/* 006 */ /* deprecated */
+#ifdef ENABLE_MUTEX_DEBUG
+/* 005 */ #define xmutex ((void (*)(int, pthread_mutex_t *, const char *, unsigned int))global[005])
+/* 006 */ #define mutex_debug ((void (*)(pthread_mutex_t *))global[006])
+#endif
 /* 007 */ #define restart_bot ((void (*)(int))global[7])
 /* 008 */ #define stop_bot ((void (*)(void))global[8])
 /* 009 */ #define reload_config ((void (*)(void))global[9])
diff --git a/src/mutexDebug.c b/src/mutexDebug.c
new file mode 100644 (file)
index 0000000..1170791
--- /dev/null
@@ -0,0 +1,178 @@
+/* mutexDebug.c - NeonServ v5.6
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#include "main.h"
+#include "mutexDebug.h"
+
+#ifdef ENABLE_MUTEX_DEBUG
+
+struct MutexLockEvent {
+    int locked;
+    const char *file;
+    unsigned int line;
+    struct MutexLockEvent *next;
+};
+
+struct MutexLock {
+    int thread;
+    int count;
+    struct MutexLockEvent *first_event, *last_event;
+    struct MutexLock *prev, *next;
+};
+
+struct MutexNode {
+    pthread_mutex_t *mutex;
+    struct MutexLock *first_lock, *last_lock;
+    struct MutexNode *next;
+};
+
+static pthread_mutex_t synchronized;
+static struct MutexNode *mutex_nodes = NULL;
+
+static struct MutexNode *getMutexNode(pthread_mutex_t *mutex, int create);
+static void lockMutex(struct MutexNode *node, const char *file, unsigned int line);
+static void unlockMutex(struct MutexNode *node, const char *file, unsigned int line);
+
+void xmutex(int lock, pthread_mutex_t *mutex, const char *file, unsigned int line) {
+    pthread_mutex_lock(&synchronized);
+    struct MutexNode *node = getMutexNode(mutex, 1);
+    if(lock)
+        lockMutex(node, file, line);
+    else
+        unlockMutex(node, file, line);
+    pthread_mutex_unlock(&synchronized);
+}
+
+void initMutexDebug() {
+    THREAD_MUTEX_INIT(synchronized);
+}
+
+static struct MutexNode *getMutexNode(pthread_mutex_t *mutex, int create) {
+    struct MutexNode *node;
+    for(node = mutex_nodes; node; node = node->next) {
+        if(node->mutex == mutex)
+            return node;
+    }
+    if(!create)
+        return NULL;
+    node = malloc(sizeof(*node));
+    node->first_lock = NULL;
+    node->last_lock = NULL;
+    node->mutex = mutex;
+    node->next = mutex_nodes;
+    mutex_nodes = node;
+    return node;
+}
+
+static void lockMutex(struct MutexNode *node, const char *file, unsigned int line) {
+    struct MutexLock *lock;
+    int thread = getCurrentThreadID();
+    for(lock = node->first_lock; lock; lock = lock->next) {
+        if(lock->thread == thread)
+            break;
+    }
+    if(!lock) {
+        lock = malloc(sizeof(*lock));
+        lock->thread = thread;
+        lock->count = 0;
+        lock->first_event = NULL;
+        lock->last_event = NULL;
+        lock->prev = node->last_lock;
+        lock->next = NULL;
+        node->last_lock = lock;
+        if(!node->first_lock)
+            node->first_lock = lock;
+    }
+    lock->count++;
+    //add event
+    struct MutexLockEvent *event = malloc(sizeof(*event));
+    event->locked = 1;
+    event->file = file;
+    event->line = line;
+    event->next = NULL;
+    if(lock->last_event) {
+        lock->last_event->next = event;
+        lock->last_event = event;
+    } else {
+        lock->first_event = event;
+        lock->last_event = event;
+    }
+}
+
+static void unlockMutex(struct MutexNode *node, const char *file, unsigned int line) {
+    struct MutexLock *lock;
+    int thread = getCurrentThreadID();
+    for(lock = node->first_lock; lock; lock = lock->next) {
+        if(lock->thread == thread)
+            break;
+    }
+    if(!lock)
+        return;
+    lock->count--;
+    if(lock->count <= 0) {
+        //remove lock
+        if(lock->prev)
+            lock->prev->next = lock->next;
+        else
+            node->first_lock = lock->next;
+        if(lock->next)
+            lock->next->prev = lock->prev;
+        else
+            node->last_lock = lock->prev;
+        //recursive free all events
+        struct MutexLockEvent *event, *next_event;
+        for(event = lock->first_event; event; event = next_event) {
+            next_event = event->next;
+            free(event);
+        }
+        free(lock);
+    } else {
+        //add event
+        struct MutexLockEvent *event = malloc(sizeof(*event));
+        event->locked = 0;
+        event->file = file;
+        event->line = line;
+        event->next = NULL;
+        if(lock->last_event) {
+            lock->last_event->next = event;
+            lock->last_event = event;
+        } else {
+            lock->first_event = event;
+            lock->last_event = event;
+        }
+    }
+}
+
+void mutex_debug(pthread_mutex_t *mutex) {
+    //replay mutex events to stdout
+    struct MutexNode *node = getMutexNode(mutex, 0);
+    if(!node) {
+        printf("[MUTEX_DEBUG] unknown mutex!\n");
+        return;
+    }
+    printf("[MUTEX_DEBUG] mutex replay:\n");
+    struct MutexLock *lock;
+    struct MutexLockEvent *event;
+    for(lock = node->first_lock; lock; lock = lock->next) {
+        printf("[MUTEX_DEBUG]  THREAD %d (%d locks):\n", lock->thread, lock->count);
+        for(event = lock->first_event; event; event = event->next) {
+            printf("[MUTEX_DEBUG]   %s in %s:%d\n", (event->locked ? "lock  " : "unlock"), event->file, event->line);
+        }
+    }
+    printf("[MUTEX_DEBUG] end of mutex replay.\n");
+}
+
+#endif
diff --git a/src/mutexDebug.h b/src/mutexDebug.h
new file mode 100644 (file)
index 0000000..2b2a995
--- /dev/null
@@ -0,0 +1,29 @@
+/* mutexDebug.h - NeonServ v5.6
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#ifndef _mutexdebug_h
+#define _mutexdebug_h
+
+#ifndef DND_FUNCTIONS
+#ifdef ENABLE_MUTEX_DEBUG
+/* MODULAR ACCESSIBLE */ void xmutex(int lock, pthread_mutex_t *mutex, const char *file, unsigned int line);
+/* MODULAR ACCESSIBLE */ void mutex_debug(pthread_mutex_t *mutex);
+
+void initMutexDebug();
+
+#endif
+#endif
+#endif
index d500e4771a47cd13d18d69c628ae49d16009da2a..ff6f40873ab71ee3e87d8cee6c06c4f104074e44 100644 (file)
     pthread_mutexattr_settype(&mutex_attr, type);\
     pthread_mutex_init(&var, &mutex_attr); \
 }
+#ifdef ENABLE_MUTEX_DEBUG
+#include "mutexDebug.h"
+#define SYNCHRONIZE(var) xmutex(1, &var, __FILE__, __LINE__); pthread_mutex_lock(&var)
+#define DESYNCHRONIZE(var) xmutex(0, &var, __FILE__, __LINE__); pthread_mutex_unlock(&var)
+#else
 #define SYNCHRONIZE(var) pthread_mutex_lock(&var)
-#define SET_SYNCHRONIZE(var) pthread_mutex_trylock(&var)
 #define DESYNCHRONIZE(var) pthread_mutex_unlock(&var)
+#endif
 #else
 #define THREAD_MUTEX_INIT(var)
 #define SYNCHRONIZE(var)
 #define DESYNCHRONIZE(var)
+#ifdef ENABLE_MUTEX_DEBUG
+#undef ENABLE_MUTEX_DEBUG
+#endif
 #endif
 
 #if __GNUC__