added small memory debugger to detect memory leaks
authorpk910 <philipp@zoelle1.de>
Mon, 30 Jan 2012 00:18:23 +0000 (01:18 +0100)
committerpk910 <philipp@zoelle1.de>
Mon, 30 Jan 2012 01:43:55 +0000 (02:43 +0100)
Makefile.am
configure.ac
src/bot_NeonServ.c
src/cmd_global.h
src/cmd_global_meminfo.c [new file with mode: 0644]
src/commands.c
src/main.c
src/main.h
src/memoryDebug.c [new file with mode: 0644]
src/memoryDebug.h [new file with mode: 0644]
src/memoryInfo.h [new file with mode: 0644]

index 2d4f78a3ba92620fc3acb1aa444a12491a41b40c..ee5d83f3627caa2406a3a93c286992575e4e324d 100644 (file)
@@ -135,9 +135,11 @@ neonserv_SOURCES = src/version.c \
       src/cmd_neonserv_halfopall.c \
       src/cmd_neonserv_dehalfopall.c \
       src/cmd_neonhelp_stats.c \
+      src/cmd_global_meminfo.c \
       src/cmd_funcmds.c \
       src/ConfigParser.c \
-      src/QServer.c
+      src/QServer.c \
+      src/memoryDebug.c
 
 neonserv_LDADD = $(MYSQL_LIBS) $(WINSOCK_LIBS)
 
index b118cae671fd144c8e7ed399d738769bee2b9d58..eb160e597fa517d1a7fe71ff4268c62b145204df 100644 (file)
@@ -62,6 +62,13 @@ AC_ARG_ENABLE([threads],
   ],
   [])
 
+AC_ARG_ENABLE([memory-debug],
+  [AS_HELP_STRING([--enable-memory-debug], [run memory debugger])],
+  [
+    AC_DEFINE([ENABLE_MEMORY_DEBUG], 1, [Define if you enable memoryDebug.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 a0937b9b5f0abb60e6713ae36681788c1a3ac3ca..75b1fd7dcf2258f9cc653b0f0cc0b69cfb9bf325 100644 (file)
@@ -374,6 +374,12 @@ static const struct default_language_entry msgtab[] = {
     {"NS_MODCMD_HEADER", "$bSettings for command %s:$b"}, /* {ARGS: "access"} */
     {"NS_MODCMD_OUTRANKED", "$b%s$b outranks you. (required access: %d)"}, /* {ARGS: "die", 1000} */
     {"NS_MODCMD_STATIC_FLAG", "This Flag is added statically. It can't be modified manually."},
+    {"NS_MEMINFO_DISABLED", "Memory Debugger is disabled!"},
+    {"NS_MEMINFO_NAME", "Name"},
+    {"NS_MEMINFO_COUNT", "Count"},
+    {"NS_MEMINFO_SIZE", "Size"},
+    {"NS_MEMINFO_LINE", "Line"},
+    {"NS_MEMINFO_TOTAL", "Total"},
     {NULL, NULL}
 };
 
index 48be0c8dcf3635ff4512abcb6fb1e1a8bdd70a9e..55bb5a7c6fffcccb2dd00b0da929e0a3ef349398 100644 (file)
@@ -46,6 +46,7 @@ CMD_BIND(global_cmd_delbot);
 CMD_BIND(global_cmd_die);
 CMD_BIND(global_cmd_emote);
 CMD_BIND(global_cmd_god);
+CMD_BIND(global_cmd_meminfo);
 CMD_BIND(global_cmd_modcmd);
 CMD_BIND(global_cmd_motd);
 CMD_BIND(global_cmd_netinfo);
diff --git a/src/cmd_global_meminfo.c b/src/cmd_global_meminfo.c
new file mode 100644 (file)
index 0000000..bdcf45a
--- /dev/null
@@ -0,0 +1,118 @@
+/* cmd_global_meminfo.c - NeonServ v5.3
+ * 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 "cmd_global.h"
+#include "memoryInfo.h"
+
+/*
+* no arguments
+*/
+
+CMD_BIND(global_cmd_meminfo) {
+    #ifndef ENABLE_MEMORY_DEBUG
+    reply(getTextBot(), user, "NS_MEMINFO_DISABLED");
+    #else
+    if(argc > 0) {
+        struct Table *table;
+        int elementcount = 0;
+        struct memoryInfoLines *element, *elements;
+        elements = getMemoryInfoLines(argv[0]);
+        for(element = elements; element; element = element->next) {
+            elementcount++;
+        }
+        table = table_init(4, elementcount+2, 0);
+        char *content[4];
+        content[0] = get_language_string(user, "NS_MEMINFO_LINE");
+        content[1] = get_language_string(user, "NS_MEMINFO_COUNT");
+        content[2] = get_language_string(user, "NS_MEMINFO_SIZE");
+        content[3] = get_language_string(user, "NS_MEMINFO_TOTAL");
+        table_add(table, content);
+        char lineBuf[20];
+        char countBuf[20];
+        char sizeBuf[20];
+        char totalBuf[50];
+        unsigned int total_allocations = 0;
+        unsigned int total_allocated = 0;
+        for(element = elements; element; element = element->next) {
+            sprintf(lineBuf, "%u", element->line);
+            content[0] = lineBuf;
+            sprintf(countBuf, "%u", element->allocations);
+            total_allocations += element->allocations;
+            content[1] = countBuf;
+            sprintf(sizeBuf, "%uB", element->allocate);
+            content[2] = sizeBuf;
+            sprintf(totalBuf, "%u (%.2f kB)", (element->allocate * element->allocations), ((float)(element->allocate * element->allocations) / 1024));
+            total_allocated += (element->allocate * element->allocations);
+            content[3] = totalBuf;
+            table_add(table, content);
+        }
+        content[0] = "Total";
+        sprintf(countBuf, "%u", total_allocations);
+        content[1] = countBuf;
+        content[2] = "*";
+        sprintf(sizeBuf, "%u (%.2f kB)", total_allocated, ((float)total_allocated / 1024));
+        content[3] = sizeBuf;
+        table_add(table, content);
+        char **table_lines = table_end(table);
+        int i;
+        for(i = 0; i < table->entrys; i++) {
+            reply(getTextBot(), user, table_lines[i]);
+        }
+        table_free(table);
+    } else {
+        struct Table *table;
+        int elementcount = 0;
+        struct memoryInfoFiles *element, *elements;
+        elements = getMemoryInfoFiles();
+        for(element = elements; element; element = element->next) {
+            elementcount++;
+        }
+        table = table_init(3, elementcount+2, 0);
+        char *content[3];
+        content[0] = get_language_string(user, "NS_MEMINFO_NAME");
+        content[1] = get_language_string(user, "NS_MEMINFO_COUNT");
+        content[2] = get_language_string(user, "NS_MEMINFO_SIZE");
+        table_add(table, content);
+        char countBuf[20];
+        char sizeBuf[50];
+        unsigned int total_allocations = 0;
+        unsigned int total_allocated = 0;
+        for(element = elements; element; element = element->next) {
+            content[0] = element->filename;
+            sprintf(countBuf, "%u", element->allocations);
+            total_allocations += element->allocations;
+            content[1] = countBuf;
+            sprintf(sizeBuf, "%u (%.2f kB)", element->allocated, ((float)element->allocated / 1024));
+            total_allocated += element->allocated;
+            content[2] = sizeBuf;
+            table_add(table, content);
+        }
+        content[0] = "Total";
+        sprintf(countBuf, "%u", total_allocations);
+        content[1] = countBuf;
+        sprintf(sizeBuf, "%u (%.2f kB)", total_allocated, ((float)total_allocated / 1024));
+        content[2] = sizeBuf;
+        table_add(table, content);
+        char **table_lines = table_end(table);
+        int i;
+        for(i = 0; i < table->entrys; i++) {
+            reply(getTextBot(), user, table_lines[i]);
+        }
+        table_free(table);
+    }
+    #endif
+}
\ No newline at end of file
index 3f22c7bc6c262c6f1082d63813a53ad9049129f1..3a1906c7adebecadabb5fc8207a2427d015cf755 100644 (file)
@@ -58,6 +58,7 @@ void register_commands() {
     OPER_COMMAND("delbot",       global_cmd_delbot,    1,     1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG);
     OPER_COMMAND("reconnect",    global_cmd_reconnect, 0,     1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG);
     OPER_COMMAND("modcmd",       global_cmd_modcmd,    1,     900,  CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG | CMDFLAG_REQUIRED | CMDFLAG_ESCAPE_ARGS);
+    OPER_COMMAND("meminfo",      global_cmd_meminfo,   0,     1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH);
     #undef OPER_COMMAND
     
     //NeonServ Commands
index 7e1839f1c9ca27127ef907388c861e19c4a3ac3d..600c519f3b4fa722fad902fbfaf83e1312e69951 100644 (file)
@@ -149,6 +149,10 @@ main:
     signal(SIGSEGV, sighandler);
     signal(SIGTERM, sighandler);
     
+    #ifdef ENABLE_MEMORY_DEBUG
+    initMemoryDebug();
+    #endif
+    
     start_time = time(0);
     
     #ifdef WIN32
index 329f391ad4f8c0120c0aed51f8a70f9c0c8de80b..b8d260b8ec68be3582645f48b5447b7757f33095 100644 (file)
 #define COMPILER "Unknown"
 #endif
 
+#ifdef ENABLE_MEMORY_DEBUG
+#include "memoryDebug.h"
+#endif
+
 #define SOCKET_SELECT_TIME    1
 #define SOCKET_RECONNECT_TIME 20
 
diff --git a/src/memoryDebug.c b/src/memoryDebug.c
new file mode 100644 (file)
index 0000000..442e618
--- /dev/null
@@ -0,0 +1,210 @@
+/* memoryDebug.c - NeonServ v5.3
+ * 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 "memoryDebug.h"
+#include "memoryInfo.h"
+
+#define FILE_NAME_LENGTH  256
+#define OUTPUT_FILE "leak_info.txt"
+
+#undef malloc
+#undef calloc
+#undef strdup
+#undef free
+
+struct MemoryNode {
+void *address;
+unsigned int size;
+char file_name[FILE_NAME_LENGTH];
+unsigned int line;
+};
+
+struct MemoryLeak {
+struct MemoryNode mem_info;
+struct MemoryLeak *next;
+};
+
+#ifdef HAVE_THREADS
+static pthread_mutex_t synchronized;
+#endif
+
+static struct MemoryLeak *ptr_start = NULL;
+
+static void add_mem_info(void * mem_ref, unsigned int size, const char *file, unsigned int line);
+static void remove_mem_info(void * mem_ref);
+
+void *xmalloc(unsigned int size, const char *file, unsigned int line) {
+    void *ptr = malloc(size);
+    if (ptr != NULL) {
+        add_mem_info(ptr, size, file, line);
+    }
+    return ptr;
+}
+
+void *xcalloc(unsigned int elements, unsigned int size, const char *file, unsigned int line) {
+    void *ptr = calloc(elements, size);
+    if(ptr != NULL) {
+        add_mem_info(ptr, (elements * size), file, line);
+    }
+    return ptr;
+}
+
+char *xstrdup(const char *data, const char *file, unsigned int line) {
+    int len = strlen(data)+1;
+    char *ptr = malloc(len);
+    if(ptr != NULL) {
+        strcpy(ptr, data);
+        add_mem_info(ptr, len, file, line);
+    }
+    return ptr;
+}
+
+void xfree(void *mem_ref) {
+    remove_mem_info(mem_ref);
+    free(mem_ref);
+}
+
+
+static void add_mem_info(void *mem_ref, unsigned int size, const char *file, unsigned int line) {
+    SYNCHRONIZE(synchronized);
+    struct MemoryLeak *mem_leak_info = malloc (sizeof(*mem_leak_info));
+    mem_leak_info->mem_info.address = mem_ref;
+    mem_leak_info->mem_info.size = size;
+    strcpy(mem_leak_info->mem_info.file_name, file);    
+    mem_leak_info->mem_info.line = line;
+    mem_leak_info->next = ptr_start;
+    ptr_start = mem_leak_info;
+    DESYNCHRONIZE(synchronized);
+}
+
+static void remove_mem_info(void *mem_ref) {
+    SYNCHRONIZE(synchronized);
+    struct MemoryLeak *leak_info, *next, *prev = NULL;
+    for(leak_info = ptr_start; leak_info; leak_info = next) {
+        next = leak_info->next;
+        if (leak_info->mem_info.address == mem_ref) {
+            if(prev)
+                prev->next = next;
+            else
+                ptr_start = next;
+            free(leak_info);
+            break;
+        } else 
+            prev = leak_info;
+    }
+    DESYNCHRONIZE(synchronized);
+}
+
+void report_mem_leak() {
+    SYNCHRONIZE(synchronized);
+    struct MemoryLeak *leak_info;
+    FILE *fp_write = fopen(OUTPUT_FILE, "wt");
+    char info[1024];
+
+    if(fp_write != NULL) {
+        sprintf(info, "%s\n", "Memory Leak Summary");
+        fwrite(info, (strlen(info)) , 1, fp_write);
+        sprintf(info, "%s\n", "-----------------------------------");    
+        fwrite(info, (strlen(info)) , 1, fp_write);
+        
+        for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next) {
+            sprintf(info, "address : %p\n", leak_info->mem_info.address);
+            fwrite(info, (strlen(info)) , 1, fp_write);
+            sprintf(info, "size    : %d bytes\n", leak_info->mem_info.size);            
+            fwrite(info, (strlen(info)) , 1, fp_write);
+            sprintf(info, "file    : %s\n", leak_info->mem_info.file_name);
+            fwrite(info, (strlen(info)) , 1, fp_write);
+            sprintf(info, "line    : %d\n", leak_info->mem_info.line);
+            fwrite(info, (strlen(info)) , 1, fp_write);
+            sprintf(info, "%s\n", "-----------------------------------");    
+            fwrite(info, (strlen(info)) , 1, fp_write);
+        }
+    }
+    fclose(fp_write);
+    DESYNCHRONIZE(synchronized);
+}
+
+void initMemoryDebug() {
+    THREAD_MUTEX_INIT(synchronized);
+}
+
+struct memoryInfoFiles *getMemoryInfoFiles() {
+    SYNCHRONIZE(synchronized);
+    struct MemoryLeak *leak_info;
+    struct memoryInfoFiles *list = NULL, *element;
+    for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next) {
+        for(element = list; element; element = element->next) {
+            if(!strcmp(leak_info->mem_info.file_name, element->filename)) {
+                break;
+            }
+        }
+        if(!element) {
+            element = malloc(sizeof(*element));
+            element->filename = strdup(leak_info->mem_info.file_name);
+            element->allocations = 0;
+            element->allocated = 0;
+            element->next = list;
+            list = element;
+        }
+        element->allocations += 1;
+        element->allocated += leak_info->mem_info.size;
+    }
+    DESYNCHRONIZE(synchronized);
+    return element;
+}
+
+void freeMemoryInfoFiles(struct memoryInfoFiles *files) {
+    struct memoryInfoFiles *next;
+    for(;files;files = next) {
+        next = files->next;
+        free(files->filename);
+        free(files);
+    }
+}
+
+struct memoryInfoLines *getMemoryInfoLines(const char *filename) {
+    SYNCHRONIZE(synchronized);
+    struct MemoryLeak *leak_info;
+    struct memoryInfoLines *list = NULL, *element;
+    for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next) {
+        if(stricmp(leak_info->mem_info.file_name, filename)) continue;
+        for(element = list; element; element = element->next) {
+            if(element->line == leak_info->mem_info.line && element->allocate == leak_info->mem_info.size) {
+                break;
+            }
+        }
+        if(!element) {
+            element = malloc(sizeof(*element));
+            element->line = leak_info->mem_info.line;
+            element->allocations = 0;
+            element->allocate = leak_info->mem_info.size;
+            element->next = list;
+            list = element;
+        }
+        element->allocations++;
+    }
+    DESYNCHRONIZE(synchronized);
+    return element;
+}
+
+void freeMemoryInfoLines(struct memoryInfoLines *lines) {
+    struct memoryInfoLines *next;
+    for(;lines;lines = next) {
+        next = lines->next;
+        free(lines);
+    }
+}
diff --git a/src/memoryDebug.h b/src/memoryDebug.h
new file mode 100644 (file)
index 0000000..c07d494
--- /dev/null
@@ -0,0 +1,33 @@
+/* memoryDebug.h - NeonServ v5.3
+ * 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 _memorydebug_h
+#define _memorydebug_h
+
+#define malloc(size) xmalloc (size, __FILE__, __LINE__)
+#define calloc(elements, size) xcalloc (elements, size, __FILE__, __LINE__)
+#define strdup(data) xstrdup (data, __FILE__, __LINE__)
+#define free(mem_ref) xfree(mem_ref)
+
+void *xmalloc(unsigned int size, const char *file, unsigned int line);
+void *xcalloc(unsigned int elements, unsigned int size, const char *file, unsigned int line);
+char *xstrdup(const char *data, const char *file, unsigned int line);
+void xfree(void *mem_ref);
+
+void report_mem_leak();
+void initMemoryDebug();
+
+#endif
diff --git a/src/memoryInfo.h b/src/memoryInfo.h
new file mode 100644 (file)
index 0000000..f1cca21
--- /dev/null
@@ -0,0 +1,42 @@
+/* memoryInfo.h - NeonServ v5.3
+ * 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 _memoryinfo_h
+#define _memoryinfo_h
+
+struct memoryInfoFiles {
+    char *filename;
+    unsigned int allocations;
+    unsigned int allocated;
+    struct memoryInfoFiles *next;
+};
+
+struct memoryInfoFiles *getMemoryInfoFiles();
+void freeMemoryInfoFiles(struct memoryInfoFiles *files);
+
+
+struct memoryInfoLines {
+    unsigned int line;
+    unsigned int allocations;
+    unsigned int allocate;
+    struct memoryInfoLines *next;
+};
+
+struct memoryInfoLines *getMemoryInfoLines(const char *filename);
+void freeMemoryInfoLines(struct memoryInfoLines *lines);
+
+
+#endif