From 3a8f1f31bdf6ff1402b6d8a6f2c54588dafe6bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 13 Mar 2006 16:10:32 +0000 Subject: [PATCH] Implemented a very simplistic and slow leak checker as compile time option (defaults to turned off). To make any use of this, you have to call the __dump_allocated() function at the point you want to check the leaks. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16765 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/system/libroot/posix/malloc/block.h | 31 ++++ src/system/libroot/posix/malloc/config.h | 3 + src/system/libroot/posix/malloc/wrapper.cpp | 194 +++++++++++++++++++- 3 files changed, 219 insertions(+), 9 deletions(-) diff --git a/src/system/libroot/posix/malloc/block.h b/src/system/libroot/posix/malloc/block.h index 3798be0727..4d60cc1254 100644 --- a/src/system/libroot/posix/malloc/block.h +++ b/src/system/libroot/posix/malloc/block.h @@ -110,6 +110,32 @@ class block { return _next; } +#if HEAP_LEAK_CHECK + void + setCallStack(int index, void *address) + { + _callStack[index] = address; + } + + void * + getCallStack(int index) + { + return _callStack[index]; + } + + void + setAllocatedSize(size_t size) + { + _allocatedSize = size; + } + + size_t + getAllocatedSize() + { + return _allocatedSize; + } +#endif + private: #if USE_PRIVATE_HEAPS #if HEAP_DEBUG @@ -139,6 +165,11 @@ class block { superblock *_mySuperblock; // A pointer to my superblock. #endif // USE_PRIVATE_HEAPS +#if HEAP_LEAK_CHECK + void *_callStack[HEAP_CALL_STACK_SIZE]; + size_t _allocatedSize; +#endif + #if HEAP_FRAG_STATS union { double _d4; // This is just for alignment purposes. diff --git a/src/system/libroot/posix/malloc/config.h b/src/system/libroot/posix/malloc/config.h index d7ff98e66c..de83a40b61 100644 --- a/src/system/libroot/posix/malloc/config.h +++ b/src/system/libroot/posix/malloc/config.h @@ -59,6 +59,9 @@ enum { SUPERBLOCK_FULLNESS_GROUP = 9 }; #define HEAP_STATS 0 // If non-zero, maintain blowup statistics. #define HEAP_FRAG_STATS 0 // If non-zero, maintain fragmentation statistics. +// A simple (and slow) leak checker +#define HEAP_LEAK_CHECK 0 +#define HEAP_CALL_STACK_SIZE 8 // CACHE_LINE = The number of bytes in a cache line. diff --git a/src/system/libroot/posix/malloc/wrapper.cpp b/src/system/libroot/posix/malloc/wrapper.cpp index 1422c20c96..8810bcae37 100644 --- a/src/system/libroot/posix/malloc/wrapper.cpp +++ b/src/system/libroot/posix/malloc/wrapper.cpp @@ -22,16 +22,178 @@ * (useful to test fragmentation). */ -#include - #include "config.h" #include "threadheap.h" #include "processheap.h" #include "arch-specific.h" +#include + +#include + using namespace BPrivate; +#if HEAP_LEAK_CHECK +static block* sUsedList = NULL; +static hoardLockType sUsedLock = 0; + + +/*! + Finds the closest symbol that comes before the given address. +*/ +static status_t +get_symbol_for_address(void* address, char *imageBuffer, size_t imageBufferSize, + char* buffer, size_t bufferSize, int32& offset) +{ + offset = -1; + + image_info info; + int32 cookie = 0; + while (get_next_image_info(0, &cookie, &info) == B_OK) { + if (((addr_t)info.text > (addr_t)address + || (addr_t)info.text + info.text_size < (addr_t)address) + && ((addr_t)info.data > (addr_t)address + || (addr_t)info.data + info.data_size < (addr_t)address)) + continue; + + char name[256]; + int32 index = 0; + int32 nameLength = sizeof(name); + int32 symbolType; + void* location; + while (get_nth_image_symbol(info.id, index, name, &nameLength, + &symbolType, &location) == B_OK) { + if ((addr_t)address >= (addr_t)location) { + // see if this is better than what we have + int32 newOffset = (addr_t)address - (addr_t)location; + + if (offset == -1 || offset > newOffset) { + const char* imageName = strrchr(info.name, '/'); + if (imageName != NULL) + strlcpy(imageBuffer, imageName + 1, imageBufferSize); + else + strlcpy(imageBuffer, info.name, imageBufferSize); + + strlcpy(buffer, name, bufferSize); + offset = newOffset; + } + } + + nameLength = sizeof(name); + index++; + } + } + + return offset != -1 ? B_OK : B_ENTRY_NOT_FOUND; +} + + +static void +dump_block(block* b) +{ + printf(" %p, %ld bytes: call stack", b + 1, b->getAllocatedSize()); + + for (int i = 0; i < HEAP_CALL_STACK_SIZE; i++) { + if (b->getCallStack(i) != NULL) { + char image[256]; + char name[256]; + int32 offset; + if (get_symbol_for_address(b->getCallStack(i), image, sizeof(image), + name, sizeof(name), offset) != B_OK) { + strcpy(name, "???"); + offset = 0; + } + + printf(": %p (%s:%s+0x%lx)", b->getCallStack(i), image, name, offset); + } + } + putchar('\n'); +} + + +extern "C" void __dump_allocated(void); + +extern "C" void +__dump_allocated(void) +{ + hoardLock(sUsedLock); + + puts("allocated:\n"); + + block* b = sUsedList; + while (b != NULL) { + dump_block(b); + + b = b->getNext(); + } + + hoardUnlock(sUsedLock); +} + + +static void +add_address(void* address, size_t size) +{ + block *b = (block *)address - 1; + +#ifdef __INTEL__ + // set call stack + struct stack_frame { + struct stack_frame* previous; + void* return_address; + }; + + stack_frame* frame = (stack_frame*)get_stack_frame(); + + for (int i = 0; i < HEAP_CALL_STACK_SIZE; i++) { + if (frame != NULL) { + b->setCallStack(i, frame->return_address); + frame = frame->previous; + } else + b->setCallStack(i, NULL); + } + + b->setAllocatedSize(size); +#endif + + hoardLock(sUsedLock); + + b->setNext(sUsedList); + sUsedList = b; + + hoardUnlock(sUsedLock); +} + + +static void +remove_address(void* address) +{ + block* b = (block *)address - 1; + hoardLock(sUsedLock); + + if (sUsedList == b) { + // we're lucky, it's the first block in the list + sUsedList = b->getNext(); + } else { + // search for block in the used list (very slow!) + block* last = sUsedList; + while (last != NULL && last->getNext() != b) { + last = last->getNext(); + } + + if (last == NULL) { + printf("freed block not in used list!\n"); + dump_block(b); + } else + last->setNext(b->getNext()); + } + + hoardUnlock(sUsedLock); +} +#endif // HEAP_LEAK_CHECK + + inline static processHeap * getAllocator(void) { @@ -78,6 +240,9 @@ malloc(size_t size) static processHeap *pHeap = getAllocator(); void *addr = pHeap->getHeap(pHeap->getHeapIndex()).malloc(size); +#if HEAP_LEAK_CHECK + add_address(addr, size); +#endif return addr; } @@ -88,6 +253,10 @@ calloc(size_t nelem, size_t elsize) static processHeap *pHeap = getAllocator(); void *ptr = pHeap->getHeap(pHeap->getHeapIndex()).malloc(nelem * elsize); +#if HEAP_LEAK_CHECK + add_address(ptr, nelem * elsize); +#endif + // Zero out the malloc'd block. memset(ptr, 0, nelem * elsize); return ptr; @@ -98,6 +267,10 @@ extern "C" void free(void *ptr) { static processHeap *pHeap = getAllocator(); +#if HEAP_LEAK_CHECK + if (ptr != NULL) + remove_address(ptr); +#endif pHeap->free(ptr); } @@ -106,7 +279,10 @@ extern "C" void * memalign(size_t alignment, size_t size) { static processHeap *pHeap = getAllocator(); - void *addr = pHeap->getHeap(pHeap->getHeapIndex()).memalign (alignment, size); + void *addr = pHeap->getHeap(pHeap->getHeapIndex()).memalign(alignment, size); +#if HEAP_LEAK_CHECK + add_address(addr, size); +#endif return addr; } @@ -120,12 +296,12 @@ valloc(size_t size) extern "C" void * -realloc(void *ptr, size_t sz) +realloc(void *ptr, size_t size) { if (ptr == NULL) - return malloc(sz); + return malloc(size); - if (sz == 0) { + if (size == 0) { free(ptr); return NULL; } @@ -134,16 +310,16 @@ realloc(void *ptr, size_t sz) // just return it. size_t objSize = threadHeap::objectSize(ptr); - if (objSize >= sz) + if (objSize >= size) return ptr; // Allocate a new block of size sz. - void *buffer = malloc(sz); + void *buffer = malloc(size); // Copy the contents of the original object // up to the size of the new block. - size_t minSize = (objSize < sz) ? objSize : sz; + size_t minSize = (objSize < size) ? objSize : size; memcpy(buffer, ptr, minSize); // Free the old block.