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
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -22,16 +22,178 @@
|
||||
* (useful to test fragmentation).
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "threadheap.h"
|
||||
#include "processheap.h"
|
||||
#include "arch-specific.h"
|
||||
|
||||
#include <image.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user