X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=blobdiff_plain;f=ircd%2Fmemdebug.c;fp=ircd%2Fmemdebug.c;h=af7e53011c9da53eaf8529d7fc9db160371846d9;hp=0000000000000000000000000000000000000000;hb=0400a5a6479398d82526785c18c0df8bc8b92dce;hpb=d17e10da972ce5776c60b4c317267c6abe0e1ead diff --git a/ircd/memdebug.c b/ircd/memdebug.c new file mode 100644 index 0000000..af7e530 --- /dev/null +++ b/ircd/memdebug.c @@ -0,0 +1,235 @@ +#include +#include "ircd.h" +#include "ircd_alloc.h" +#include "ircd_log.h" +#include "client.h" +#include "s_debug.h" +#include "send.h" +#include +#include + +/* #include -- Now using assert in ircd_log.h */ + +#ifdef MDEBUG + +/* To use this you need to get gc6.0 from: + * http://www.hpl.hp.com/personal/Hans_Boehm/gc/ + * and you need to apply the patch in + * doc/debug_memleak_gc.patch to your gc6.0 tree, and reconfigure your ircd using + --with-leak-detect=path-to-gc6.0/.lib/ + * You should only do this for debugging builds as it can slow things down + * a bit. + */ + +#include "ircd_string.h" + +void *GC_malloc(size_t size); +void GC_free(void *ptr); +void GC_set_leak_handler(void (*)(void*, int)); +void GC_gcollect(void); +extern int GC_find_leak; + +/** Header block to track an allocated block's information. */ +struct MemHeader +{ + uint32_t magic; + char type[32]; + char file[32]; + int line; + size_t length; + time_t since; +}; + +/** Overwrite \a len byte at \a p with 0xDEADBEEF. + * @param[out] p Memory buffer to overwrite. + * @param[in] len Number of bytes to overwrite. + */ +void +memfrob(void *p, size_t len) +{ + /* deadbeef */ + int i = 0; + const char *pat = "\xde\xad\xbe\xef"; + char *s, *se; + + for (s = (char*)p, se = s + (len & ~3) - 4; + s <= se; + s += 4) + *(uint32_t*)s = *(uint32_t*)pat; + for (se = s; se < s; s++) + *s = pat[i++]; +} + +/** Total number of bytes allocated. */ +static size_t mdbg_bytes_allocated = 0; +/** Total number of blocks allocated. */ +static size_t mdbg_blocks_allocated = 0; +/** Last Unix time that we ran garbage collection. */ +static time_t last_gcollect = 0; +/** Minimum interval for garbage collection. */ +#define GC_FREQ 5 + +/** Check whether we should run garbage collection. + * If so, do it. + */ +void +dbg_check_gcollect(void) +{ + if (CurrentTime - last_gcollect < GC_FREQ) + return; + GC_gcollect(); + last_gcollect = CurrentTime; +} + +/** Allocate a block of memory. + * @param[in] size Number of bytes needed. + * @param[in] type Memory operation for block. + * @param[in] file File name of allocating function. + * @param[in] line Line number of allocating function. + * @return Pointer to the newly allocated block. + */ +void* +dbg_malloc(size_t size, const char *type, const char *file, int line) +{ + struct MemHeader *mh = GC_malloc(size + sizeof(*mh)); + if (mh == NULL) + return mh; + memfrob((void*)(mh + 1), size); + mh->magic = 0xA110CA7E; + ircd_strncpy(mh->type, type, sizeof(mh->type) - 1)[sizeof(mh->type) - 1] = 0; + ircd_strncpy(mh->file, file, sizeof(mh->file) - 1)[sizeof(mh->file) - 1] = 0; + mh->line = line; + mh->length = size; + mh->since = CurrentTime; + mdbg_bytes_allocated += size; + mdbg_blocks_allocated++; + dbg_check_gcollect(); + return (void*)(mh + 1); +} + +/** Allocate a zero-initialized block of memory. + * @param[in] size Number of bytes needed. + * @param[in] type Memory operation for block. + * @param[in] file File name of allocating function. + * @param[in] line Line number of allocating function. + * @return Pointer to the newly allocated block. + */ +void* +dbg_malloc_zero(size_t size, const char *type, const char *file, int line) +{ + struct MemHeader *mh = GC_malloc(size + sizeof(*mh)); + if (mh == NULL) + return mh; + memset((void*)(mh + 1), 0, size); + mh->magic = 0xA110CA7E; + ircd_strncpy(mh->type, type, sizeof(mh->type) - 1)[sizeof(mh->type) - 1] = 0; + ircd_strncpy(mh->file, file, sizeof(mh->file) - 1)[sizeof(mh->file) - 1] = 0; + mh->line = line; + mh->length = size; + mdbg_bytes_allocated += size; + mdbg_blocks_allocated++; + dbg_check_gcollect(); + return (void*)(mh + 1); +} + +/** Extend an allocated block of memory. + * @param[in] ptr Pointer to currently allocated block of memory (may be NULL). + * @param[in] size Minimum number of bytes for new block. + * @param[in] file File name of allocating function. + * @param[in] line Line number of allocating function. + * @return Pointer to the extended block of memory. + */ +void* +dbg_realloc(void *ptr, size_t size, const char *file, int line) +{ + struct MemHeader *mh, *mh2; + if (ptr == NULL) + return dbg_malloc(size, "realloc", file, line); + mh = (struct MemHeader*)ptr - 1; + assert(mh->magic == 0xA110CA7E); + if (mh->length >= size) + return mh; + mh2 = dbg_malloc(size, "realloc", file, line); + if (mh2 == NULL) + { + dbg_free(mh+1, file, line); + return NULL; + } + memcpy(mh2+1, mh+1, mh->length); + dbg_free(mh+1, file, line); + return (void*)(mh2+1); +} + +/** Deallocate a block of memory. + * @param[in] ptr Pointer to currently allocated block of memory (may be NULL). + * @param[in] file File name of deallocating function. + * @param[in] line Line number of deallocating function. + */ +void +dbg_free(void *ptr, const char *file, int line) +{ + struct MemHeader *mh = (struct MemHeader*)ptr - 1; + /* XXX but bison gives us NULLs */ + if (ptr == NULL) + return; + assert(mh->magic == 0xA110CA7E); + /* XXX can we get boehmgc to check for references to it? */ + memfrob(mh, mh->length + sizeof(*mh)); + mdbg_bytes_allocated -= mh->length; + mdbg_blocks_allocated--; + GC_free(mh); + dbg_check_gcollect(); +} + +/** Report number of bytes currently allocated. + * @return Number of bytes allocated. + */ +size_t +fda_get_byte_count(void) +{ + dbg_check_gcollect(); + return mdbg_bytes_allocated; +} + +/** Report number of blocks currently allocated. + * @return Number of blocks allocated. + */ +size_t +fda_get_block_count(void) +{ + return mdbg_blocks_allocated; +} + +#include + +/** Callback for when the garbage collector detects a memory leak. + * @param[in] p Pointer to leaked memory. + * @param[in] sz Length of the block. + */ +void +dbg_memory_leaked(void *p, int sz) +{ + struct MemHeader *mh; + /* We have to return because the gc "leaks". */ + mh = p; + if (mh->magic != 0xA110CA7E) + return; + sendto_opmask_butone(NULL, SNO_OLDSNO, + "%s leak at %s:%u(%u bytes for %u seconds)", + mh->type, mh->file, mh->line, mh->length, + CurrentTime - mh->since); + Debug((DEBUG_ERROR, + "%s leak at %s:%u(%u bytes for %u seconds)", + mh->type, mh->file, mh->line, mh->length, + CurrentTime - mh->since)); +} + +/** Initialize the memory debugging subsystem. */ +void +mem_dbg_initialise(void) +{ + GC_find_leak = 1; + GC_set_leak_handler(dbg_memory_leaked); +} + +#endif