From 14c5204e1fb8b97feae94be02e324a87617ce0f5 Mon Sep 17 00:00:00 2001 From: pk910 Date: Sat, 10 Nov 2012 17:13:00 +0100 Subject: [PATCH] added mutex debugger --- Makefile.am | 3 +- configure.ac | 7 ++ src/main.c | 5 ++ src/memoryDebug.c | 33 +++++--- src/modules.c | 17 ++++- src/modules/module.h | 6 +- src/mutexDebug.c | 178 +++++++++++++++++++++++++++++++++++++++++++ src/mutexDebug.h | 29 +++++++ src/overall.h | 10 ++- 9 files changed, 271 insertions(+), 17 deletions(-) create mode 100644 src/mutexDebug.c create mode 100644 src/mutexDebug.h diff --git a/Makefile.am b/Makefile.am index e0105d6..6299256 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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) diff --git a/configure.ac b/configure.ac index 56d1e7f..0a6c9d9 100644 --- a/configure.ac +++ b/configure.ac @@ -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'], diff --git a/src/main.c b/src/main.c index c88c02c..fb16300 100644 --- a/src/main.c +++ b/src/main.c @@ -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) { diff --git a/src/memoryDebug.c b/src/memoryDebug.c index c425592..d4e828f 100644 --- a/src/memoryDebug.c +++ b/src/memoryDebug.c @@ -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; } diff --git a/src/modules.c b/src/modules.c index 325621e..f48c41a 100644 --- a/src/modules.c +++ b/src/modules.c @@ -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, diff --git a/src/modules/module.h b/src/modules/module.h index 1b9dac7..4c55c40 100644 --- a/src/modules/module.h +++ b/src/modules/module.h @@ -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 index 0000000..1170791 --- /dev/null +++ b/src/mutexDebug.c @@ -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 . + */ +#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 index 0000000..2b2a995 --- /dev/null +++ b/src/mutexDebug.h @@ -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 . + */ +#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 diff --git a/src/overall.h b/src/overall.h index d500e47..ff6f408 100644 --- a/src/overall.h +++ b/src/overall.h @@ -77,13 +77,21 @@ 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__ -- 2.20.1