一个简单而有效的方案:
分配的时候多分配一点空间作头,记下分配的文件名和行号,把所有的头连成一个环形双
链表,释放的时候从双链表中删除,并释放头。
结束的时候只要检查这个双链表是否唯恐就知道哪里泄漏内存了
在Windows DDK里的简化代码:
in xxx.h
// the real heap function
void* sys_malloc(size_t size);
void sys_free(void* p);
//
// Heap tracking mechanism
#if DBG
void StartHeapTracking(void);
void StopHeapTracking(void);
void* sys_malloc_track(size_t size, const char* File, unsigned int Line);
void sys_free_track(void* mem);
// these macros will cover real heap functions in debug version
#define sys_malloc(size) sys_malloc_track(size, __FILE__, __LINE__)
#define sys_free sys_free_track
#else
#define StartHeapTracking()
#define StopHeapTracking()
#endif
in xxx_dbg.c
//
// Heap tracking
//
#if DBG
struct HEAP_TRACKING_RECORD
{
LIST_ENTRY Link;
size_t Size;
const char* File;
unsigned int Line;
};
// the doubly linked list header
static LIST_ENTRY HeapTrack;
static KSPIN_LOCK HeapTrackLock;
void StartHeapTracking(void)
{
InitializeListHead(&HeapTrack);
KeInitializeSpinLock(&HeapTrackLock);
}
void StopHeapTracking(void)
{
// check memory leak
if(!IsListEmpty(&HeapTrack))
{
LIST_ENTRY* p;
ASSERT(FALSE); // break here if memory leak
for(p=HeapTrack.Flink; p!=&HeapTrack; p=p->Flink)
{
struct HEAP_TRACKING_RECORD* q =
CONTAINING_RECORD(p, struct HEAP_TRACKING_RECORD, Link);
DbgPrint(
"Memory leak: %u bytes allocated in %s:%u./n",
(unsigned)q->Size,
q->File,
q->Line
);
}
}
}
// following code using real heap function
#undef sys_malloc
#undef sys_free
void* sys_malloc_track(size_t size, const char* File, unsigned int Line)
{
struct HEAP_TRACKING_RECORD* mem =
sys_malloc(sizeof(struct HEAP_TRACKING_RECORD)+size);
if(mem)
{
mem->Size = size;
mem->File = File;
mem->Line = Line;
ExInterlockedInsertTailList(&HeapTrack, &mem->Link, &HeapTrackLock);
return mem+1;
}
return NULL;
}
void sys_free_track(void* mem)
{
if(mem)
{
struct HEAP_TRACKING_RECORD* p =
(struct HEAP_TRACKING_RECORD*)
((char*)mem-sizeof(struct HEAP_TRACKING_RECORD));
RemoveEntryList(&p->Link);
sys_free(p);
}
}
#endif
使用的时候包含xxx.h,
程序开始的时候先调用StartHeapTracking初始化,然后直接用sys_malloc, sys_free
分配和释放即可,结束的时候调用StopHeapTracking,就会把泄漏的内存块分配的地方
大小打印出来。这样在调试版每次分配要多耗费20字节的内存。如果不定义DBG预处理
符号,则等于用原来的sys_malloc和sys_free,不增加开销。
代码中用了Windows DDK中的双链表函数,其他操作系统下比如Linux有类似的实现。
没有的话自己写也很简单。
代码是驱动程序中的一部分,其中的spinlock是为了保护共享资源。
如果你的程序是单线程的,则可以不用加锁。
如果你没有DDK, 那么可以参考这些双链表函数相关的代码:
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
VOID
FORCEINLINE
InitializeListHead(
IN PLIST_ENTRY ListHead
)
{
ListHead->Flink = ListHead->Blink = ListHead;
}
//
// BOOLEAN
// IsListEmpty(
// PLIST_ENTRY ListHead
// );
//
#define IsListEmpty(ListHead) /
((ListHead)->Flink == (ListHead))
VOID
FORCEINLINE
RemoveEntryList(
IN PLIST_ENTRY Entry
)
{
PLIST_ENTRY Blink;
PLIST_ENTRY Flink;
Flink = Entry->Flink;
Blink = Entry->Blink;
Blink->Flink = Flink;
Flink->Blink = Blink;
}
PLIST_ENTRY
FORCEINLINE
RemoveHeadList(
IN PLIST_ENTRY ListHead
)
{
PLIST_ENTRY Flink;
PLIST_ENTRY Entry;
Entry = ListHead->Flink;
Flink = Entry->Flink;
ListHead->Flink = Flink;
Flink->Blink = ListHead;
return Entry;
}
PLIST_ENTRY
FORCEINLINE
RemoveTailList(
IN PLIST_ENTRY ListHead
)
{
PLIST_ENTRY Blink;
PLIST_ENTRY Entry;
Entry = ListHead->Blink;
Blink = Entry->Blink;
ListHead->Blink = Blink;
Blink->Flink = ListHead;
return Entry;
}
VOID
FORCEINLINE
InsertTailList(
IN PLIST_ENTRY ListHead,
IN PLIST_ENTRY Entry
)
{
PLIST_ENTRY Blink;
Blink = ListHead->Blink;
Entry->Flink = ListHead;
Entry->Blink = Blink;
Blink->Flink = Entry;
ListHead->Blink = Entry;
}
VOID
FORCEINLINE
InsertHeadList(
IN PLIST_ENTRY ListHead,
IN PLIST_ENTRY Entry
)
{
PLIST_ENTRY Flink;
Flink = ListHead->Flink;
Entry->Flink = Flink;
Entry->Blink = ListHead;
Flink->Blink = Entry;
ListHead->Flink = Entry;
}
分配的时候多分配一点空间作头,记下分配的文件名和行号,把所有的头连成一个环形双
链表,释放的时候从双链表中删除,并释放头。
结束的时候只要检查这个双链表是否唯恐就知道哪里泄漏内存了
在Windows DDK里的简化代码:
in xxx.h
// the real heap function
void* sys_malloc(size_t size);
void sys_free(void* p);
//
// Heap tracking mechanism
#if DBG
void StartHeapTracking(void);
void StopHeapTracking(void);
void* sys_malloc_track(size_t size, const char* File, unsigned int Line);
void sys_free_track(void* mem);
// these macros will cover real heap functions in debug version
#define sys_malloc(size) sys_malloc_track(size, __FILE__, __LINE__)
#define sys_free sys_free_track
#else
#define StartHeapTracking()
#define StopHeapTracking()
#endif
in xxx_dbg.c
//
// Heap tracking
//
#if DBG
struct HEAP_TRACKING_RECORD
{
LIST_ENTRY Link;
size_t Size;
const char* File;
unsigned int Line;
};
// the doubly linked list header
static LIST_ENTRY HeapTrack;
static KSPIN_LOCK HeapTrackLock;
void StartHeapTracking(void)
{
InitializeListHead(&HeapTrack);
KeInitializeSpinLock(&HeapTrackLock);
}
void StopHeapTracking(void)
{
// check memory leak
if(!IsListEmpty(&HeapTrack))
{
LIST_ENTRY* p;
ASSERT(FALSE); // break here if memory leak
for(p=HeapTrack.Flink; p!=&HeapTrack; p=p->Flink)
{
struct HEAP_TRACKING_RECORD* q =
CONTAINING_RECORD(p, struct HEAP_TRACKING_RECORD, Link);
DbgPrint(
"Memory leak: %u bytes allocated in %s:%u./n",
(unsigned)q->Size,
q->File,
q->Line
);
}
}
}
// following code using real heap function
#undef sys_malloc
#undef sys_free
void* sys_malloc_track(size_t size, const char* File, unsigned int Line)
{
struct HEAP_TRACKING_RECORD* mem =
sys_malloc(sizeof(struct HEAP_TRACKING_RECORD)+size);
if(mem)
{
mem->Size = size;
mem->File = File;
mem->Line = Line;
ExInterlockedInsertTailList(&HeapTrack, &mem->Link, &HeapTrackLock);
return mem+1;
}
return NULL;
}
void sys_free_track(void* mem)
{
if(mem)
{
struct HEAP_TRACKING_RECORD* p =
(struct HEAP_TRACKING_RECORD*)
((char*)mem-sizeof(struct HEAP_TRACKING_RECORD));
RemoveEntryList(&p->Link);
sys_free(p);
}
}
#endif
使用的时候包含xxx.h,
程序开始的时候先调用StartHeapTracking初始化,然后直接用sys_malloc, sys_free
分配和释放即可,结束的时候调用StopHeapTracking,就会把泄漏的内存块分配的地方
大小打印出来。这样在调试版每次分配要多耗费20字节的内存。如果不定义DBG预处理
符号,则等于用原来的sys_malloc和sys_free,不增加开销。
代码中用了Windows DDK中的双链表函数,其他操作系统下比如Linux有类似的实现。
没有的话自己写也很简单。
代码是驱动程序中的一部分,其中的spinlock是为了保护共享资源。
如果你的程序是单线程的,则可以不用加锁。
如果你没有DDK, 那么可以参考这些双链表函数相关的代码:
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
VOID
FORCEINLINE
InitializeListHead(
IN PLIST_ENTRY ListHead
)
{
ListHead->Flink = ListHead->Blink = ListHead;
}
//
// BOOLEAN
// IsListEmpty(
// PLIST_ENTRY ListHead
// );
//
#define IsListEmpty(ListHead) /
((ListHead)->Flink == (ListHead))
VOID
FORCEINLINE
RemoveEntryList(
IN PLIST_ENTRY Entry
)
{
PLIST_ENTRY Blink;
PLIST_ENTRY Flink;
Flink = Entry->Flink;
Blink = Entry->Blink;
Blink->Flink = Flink;
Flink->Blink = Blink;
}
PLIST_ENTRY
FORCEINLINE
RemoveHeadList(
IN PLIST_ENTRY ListHead
)
{
PLIST_ENTRY Flink;
PLIST_ENTRY Entry;
Entry = ListHead->Flink;
Flink = Entry->Flink;
ListHead->Flink = Flink;
Flink->Blink = ListHead;
return Entry;
}
PLIST_ENTRY
FORCEINLINE
RemoveTailList(
IN PLIST_ENTRY ListHead
)
{
PLIST_ENTRY Blink;
PLIST_ENTRY Entry;
Entry = ListHead->Blink;
Blink = Entry->Blink;
ListHead->Blink = Blink;
Blink->Flink = ListHead;
return Entry;
}
VOID
FORCEINLINE
InsertTailList(
IN PLIST_ENTRY ListHead,
IN PLIST_ENTRY Entry
)
{
PLIST_ENTRY Blink;
Blink = ListHead->Blink;
Entry->Flink = ListHead;
Entry->Blink = Blink;
Blink->Flink = Entry;
ListHead->Blink = Entry;
}
VOID
FORCEINLINE
InsertHeadList(
IN PLIST_ENTRY ListHead,
IN PLIST_ENTRY Entry
)
{
PLIST_ENTRY Flink;
Flink = ListHead->Flink;
Entry->Flink = Flink;
Entry->Blink = ListHead;
Flink->Blink = Entry;
ListHead->Flink = Entry;
}