added mutex debugger
[NeonServV5.git] / src / mutexDebug.c
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