From: pk910 Date: Mon, 30 Jan 2012 00:18:23 +0000 (+0100) Subject: added small memory debugger to detect memory leaks X-Git-Tag: v5.3~38 X-Git-Url: http://git.pk910.de/?p=NeonServV5.git;a=commitdiff_plain;h=3b03d79e0abdb8ed15dcc8943cf761f2a2775cf1 added small memory debugger to detect memory leaks --- diff --git a/Makefile.am b/Makefile.am index 2d4f78a..ee5d83f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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) diff --git a/configure.ac b/configure.ac index b118cae..eb160e5 100644 --- a/configure.ac +++ b/configure.ac @@ -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'], diff --git a/src/bot_NeonServ.c b/src/bot_NeonServ.c index a0937b9..75b1fd7 100644 --- a/src/bot_NeonServ.c +++ b/src/bot_NeonServ.c @@ -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} }; diff --git a/src/cmd_global.h b/src/cmd_global.h index 48be0c8..55bb5a7 100644 --- a/src/cmd_global.h +++ b/src/cmd_global.h @@ -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 index 0000000..bdcf45a --- /dev/null +++ b/src/cmd_global_meminfo.c @@ -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 . + */ + +#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 diff --git a/src/commands.c b/src/commands.c index 3f22c7b..3a1906c 100644 --- a/src/commands.c +++ b/src/commands.c @@ -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 diff --git a/src/main.c b/src/main.c index 7e1839f..600c519 100644 --- a/src/main.c +++ b/src/main.c @@ -149,6 +149,10 @@ main: signal(SIGSEGV, sighandler); signal(SIGTERM, sighandler); + #ifdef ENABLE_MEMORY_DEBUG + initMemoryDebug(); + #endif + start_time = time(0); #ifdef WIN32 diff --git a/src/main.h b/src/main.h index 329f391..b8d260b 100644 --- a/src/main.h +++ b/src/main.h @@ -101,6 +101,10 @@ #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 index 0000000..442e618 --- /dev/null +++ b/src/memoryDebug.c @@ -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 . + */ +#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 index 0000000..c07d494 --- /dev/null +++ b/src/memoryDebug.h @@ -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 . + */ +#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 index 0000000..f1cca21 --- /dev/null +++ b/src/memoryInfo.h @@ -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 . + */ +#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