576 lines
10 KiB
C
576 lines
10 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dbgtrack.c
|
|
|
|
Abstract:
|
|
|
|
Allocation tracking implementation. From old debug.c
|
|
|
|
Author:
|
|
|
|
Marc R. Whitten (marcw) 09-Sept-1999
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
//
|
|
// Includes
|
|
//
|
|
|
|
#include "pch.h"
|
|
|
|
//
|
|
// NOTE: No code should appear outside the #ifdef DEBUG
|
|
//
|
|
|
|
#ifdef DEBUG
|
|
|
|
#pragma message("DEBUG macros enabled")
|
|
|
|
|
|
|
|
//
|
|
// Strings
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Constants
|
|
//
|
|
|
|
#define TRACK_BUCKETS 1501
|
|
#define BUCKET_ITEMS_PER_POOL 8192
|
|
|
|
|
|
|
|
|
|
//
|
|
// Macros
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Types
|
|
//
|
|
|
|
typedef UBINT ALLOCATION_ITEM_OFFSET;
|
|
|
|
typedef struct _tagTRACKBUCKETITEM {
|
|
struct _tagTRACKBUCKETITEM *Next;
|
|
struct _tagTRACKBUCKETITEM *Prev;
|
|
ALLOCTYPE Type;
|
|
PVOID Ptr;
|
|
ALLOCATION_ITEM_OFFSET ItemOffset;
|
|
} TRACKBUCKETITEM, *PTRACKBUCKETITEM;
|
|
|
|
typedef struct _tagBUCKETPOOL {
|
|
UINT Count;
|
|
TRACKBUCKETITEM Items[BUCKET_ITEMS_PER_POOL];
|
|
} TRACKBUCKETPOOL, *PTRACKBUCKETPOOL;
|
|
|
|
|
|
typedef struct {
|
|
ALLOCTYPE Type;
|
|
PVOID Ptr;
|
|
PCSTR FileName;
|
|
UINT Line;
|
|
BOOL Allocated;
|
|
} ALLOCATION_ITEM, *PALLOCATION_ITEM;
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
PTRACKBUCKETITEM g_TrackBuckets[TRACK_BUCKETS];
|
|
PTRACKBUCKETITEM g_TrackPoolDelHead;
|
|
PTRACKBUCKETPOOL g_TrackPool;
|
|
|
|
//
|
|
// The following pointer can be used to help identify memory leak sources.
|
|
// It is copied to the memory tracking log.
|
|
//
|
|
PCSTR g_TrackComment;
|
|
PCSTR g_TrackFile;
|
|
UINT g_TrackLine;
|
|
BOOL g_TrackAlloc;
|
|
INT g_UseCount;
|
|
UINT g_DisableTrackComment = 0;
|
|
GROWBUFFER g_AllocationList;
|
|
PVOID g_FirstDeletedAlloc;
|
|
|
|
//
|
|
// Macro expansion list
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Private function prototypes
|
|
//
|
|
|
|
//
|
|
// Macro expansion definition
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Code
|
|
//
|
|
|
|
VOID
|
|
DisableTrackComment (
|
|
VOID
|
|
)
|
|
{
|
|
g_DisableTrackComment ++;
|
|
}
|
|
|
|
VOID
|
|
EnableTrackComment (
|
|
VOID
|
|
)
|
|
{
|
|
if (g_DisableTrackComment > 0) {
|
|
g_DisableTrackComment --;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
PBYTE
|
|
pOurTrackedGbGrow (
|
|
IN PGROWBUFFER Buffer,
|
|
IN UINT Bytes
|
|
)
|
|
{
|
|
PBYTE p;
|
|
BOOL trackMsg = FALSE;
|
|
|
|
//
|
|
// Because grow buffers themselves cause tracking, we have to
|
|
// call the untracked version. To eliminate confusion, we
|
|
// give a helpful note.
|
|
//
|
|
|
|
if (!g_TrackFile) {
|
|
trackMsg = TRUE;
|
|
g_TrackFile = "<allocation tracking in dbgtrack.c, not a real leak>";
|
|
g_TrackLine = __LINE__;
|
|
}
|
|
|
|
p = (PSTR) RealGbGrow (Buffer, Bytes);
|
|
|
|
if (trackMsg) {
|
|
g_TrackFile = NULL;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
INT
|
|
TrackPushEx (
|
|
PCSTR Msg,
|
|
PCSTR File,
|
|
UINT Line,
|
|
BOOL Alloc
|
|
)
|
|
{
|
|
if (g_DisableTrackComment > 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (g_UseCount > 0) {
|
|
g_UseCount++;
|
|
return 0;
|
|
}
|
|
|
|
TrackPush (Msg, File, Line);
|
|
g_TrackAlloc = TRUE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
INT
|
|
TrackPush (
|
|
PCSTR Msg,
|
|
PCSTR File,
|
|
UINT Line
|
|
)
|
|
{
|
|
static CHAR Buffer[1024];
|
|
|
|
if (g_DisableTrackComment > 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (g_UseCount > 0) {
|
|
g_UseCount++;
|
|
return 0;
|
|
}
|
|
|
|
if (Msg) {
|
|
wsprintfA (Buffer, "%s line %u [%s]", File, Line, Msg);
|
|
} else {
|
|
wsprintfA (Buffer, "%s line %u", File, Line);
|
|
}
|
|
|
|
g_TrackFile = File;
|
|
g_TrackLine = Line;
|
|
g_TrackAlloc = FALSE;
|
|
|
|
g_TrackComment = Buffer;
|
|
g_UseCount = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
INT
|
|
TrackPop (
|
|
VOID
|
|
)
|
|
{
|
|
if (g_DisableTrackComment > 0) {
|
|
return 0;
|
|
}
|
|
|
|
g_UseCount--;
|
|
|
|
if (!g_UseCount) {
|
|
g_TrackComment = NULL;
|
|
g_TrackFile = NULL;
|
|
g_TrackLine = 0;
|
|
g_TrackAlloc = FALSE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
TrackDump (
|
|
VOID
|
|
)
|
|
{
|
|
if (g_UseCount > 0) {
|
|
DEBUGMSGA (("Caller", "%s line %u (%s)", g_TrackFile, g_TrackLine, g_TrackComment));
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
InitAllocationTracking (
|
|
VOID
|
|
)
|
|
{
|
|
ZeroMemory (&g_AllocationList, sizeof (g_AllocationList));
|
|
g_AllocationList.GrowSize = 65536;
|
|
g_FirstDeletedAlloc = NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeAllocationTracking (
|
|
VOID
|
|
)
|
|
{
|
|
UINT Size;
|
|
UINT u;
|
|
PALLOCATION_ITEM Item;
|
|
GROWBUFFER Msg = INIT_GROWBUFFER;
|
|
CHAR Text[1024];
|
|
PSTR p;
|
|
UINT Bytes;
|
|
|
|
Size = g_AllocationList.End / sizeof (ALLOCATION_ITEM);;
|
|
|
|
for (u = 0 ; u < Size ; u++) {
|
|
Item = (PALLOCATION_ITEM) g_AllocationList.Buf + u;
|
|
if (!Item->FileName) {
|
|
continue;
|
|
}
|
|
|
|
Bytes = (UINT) wsprintfA (Text, "%s line %u\r\n", Item->FileName, Item->Line);
|
|
p = (PSTR) pOurTrackedGbGrow (&Msg, Bytes);
|
|
|
|
if (p) {
|
|
CopyMemory (p, Text, Bytes);
|
|
}
|
|
}
|
|
|
|
if (Msg.End) {
|
|
|
|
p = (PSTR) pOurTrackedGbGrow (&Msg, 1);
|
|
if (p) {
|
|
*p = 0;
|
|
DEBUGMSGA (("Leaks", "%s", Msg.Buf));
|
|
}
|
|
|
|
GbFree (&Msg);
|
|
}
|
|
|
|
GbFree (&g_AllocationList);
|
|
g_FirstDeletedAlloc = NULL;
|
|
|
|
// Intentional leak -- who cares about track memory
|
|
g_TrackPoolDelHead = NULL;
|
|
g_TrackPool = NULL;
|
|
}
|
|
|
|
|
|
PTRACKBUCKETITEM
|
|
pAllocTrackBucketItem (
|
|
VOID
|
|
)
|
|
{
|
|
PTRACKBUCKETITEM BucketItem;
|
|
|
|
if (g_TrackPoolDelHead) {
|
|
BucketItem = g_TrackPoolDelHead;
|
|
g_TrackPoolDelHead = BucketItem->Next;
|
|
} else {
|
|
|
|
if (!g_TrackPool || g_TrackPool->Count == BUCKET_ITEMS_PER_POOL) {
|
|
g_TrackPool = (PTRACKBUCKETPOOL) SafeHeapAlloc (g_hHeap, 0, sizeof (TRACKBUCKETPOOL));
|
|
if (!g_TrackPool) {
|
|
return NULL;
|
|
}
|
|
|
|
g_TrackPool->Count = 0;
|
|
}
|
|
|
|
BucketItem = g_TrackPool->Items + g_TrackPool->Count;
|
|
g_TrackPool->Count++;
|
|
}
|
|
|
|
return BucketItem;
|
|
}
|
|
|
|
VOID
|
|
pFreeTrackBucketItem (
|
|
PTRACKBUCKETITEM BucketItem
|
|
)
|
|
{
|
|
BucketItem->Next = g_TrackPoolDelHead;
|
|
g_TrackPoolDelHead = BucketItem;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
pComputeTrackHashVal (
|
|
IN ALLOCTYPE Type,
|
|
IN PVOID Ptr
|
|
)
|
|
{
|
|
DWORD Hash;
|
|
|
|
Hash = (DWORD) ((DWORD)Type << 16) ^ (DWORD)(UBINT)Ptr;
|
|
return Hash % TRACK_BUCKETS;
|
|
}
|
|
|
|
|
|
VOID
|
|
pTrackHashTableInsert (
|
|
IN PBYTE Base,
|
|
IN ALLOCATION_ITEM_OFFSET ItemOffset
|
|
)
|
|
{
|
|
DWORD Hash;
|
|
PTRACKBUCKETITEM BucketItem;
|
|
PALLOCATION_ITEM Item;
|
|
|
|
Item = (PALLOCATION_ITEM) (Base + ItemOffset);
|
|
|
|
Hash = pComputeTrackHashVal (Item->Type, Item->Ptr);
|
|
|
|
BucketItem = pAllocTrackBucketItem();
|
|
|
|
if (!BucketItem) {
|
|
DEBUGMSG ((DBG_WHOOPS, "pTrackHashTableInsert failed to alloc memory"));
|
|
return;
|
|
}
|
|
|
|
BucketItem->Prev = NULL;
|
|
BucketItem->Next = g_TrackBuckets[Hash];
|
|
BucketItem->Type = Item->Type;
|
|
BucketItem->Ptr = Item->Ptr;
|
|
BucketItem->ItemOffset = ItemOffset;
|
|
|
|
if (BucketItem->Next) {
|
|
BucketItem->Next->Prev = BucketItem;
|
|
}
|
|
|
|
g_TrackBuckets[Hash] = BucketItem;
|
|
}
|
|
|
|
VOID
|
|
pTrackHashTableDelete (
|
|
IN PTRACKBUCKETITEM BucketItem
|
|
)
|
|
{
|
|
DWORD Hash;
|
|
|
|
Hash = pComputeTrackHashVal (BucketItem->Type, BucketItem->Ptr);
|
|
|
|
if (BucketItem->Prev) {
|
|
BucketItem->Prev->Next = BucketItem->Next;
|
|
} else {
|
|
g_TrackBuckets[Hash] = BucketItem->Next;
|
|
}
|
|
|
|
if (BucketItem->Next) {
|
|
BucketItem->Next->Prev = BucketItem->Prev;
|
|
}
|
|
|
|
pFreeTrackBucketItem (BucketItem);
|
|
}
|
|
|
|
PTRACKBUCKETITEM
|
|
pTrackHashTableFind (
|
|
IN ALLOCTYPE Type,
|
|
IN PVOID Ptr
|
|
)
|
|
{
|
|
PTRACKBUCKETITEM BucketItem;
|
|
DWORD Hash;
|
|
|
|
Hash = pComputeTrackHashVal (Type, Ptr);
|
|
|
|
BucketItem = g_TrackBuckets[Hash];
|
|
while (BucketItem) {
|
|
if (BucketItem->Type == Type && BucketItem->Ptr == Ptr) {
|
|
return BucketItem;
|
|
}
|
|
|
|
BucketItem = BucketItem->Next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
DebugRegisterAllocationEx (
|
|
IN ALLOCTYPE Type,
|
|
IN PVOID Ptr,
|
|
IN PCSTR File,
|
|
IN UINT Line,
|
|
IN BOOL Alloc
|
|
)
|
|
{
|
|
PALLOCATION_ITEM Item;
|
|
|
|
MYASSERT (File);
|
|
|
|
if (!g_FirstDeletedAlloc) {
|
|
Item = (PALLOCATION_ITEM) pOurTrackedGbGrow (&g_AllocationList,sizeof(ALLOCATION_ITEM));
|
|
} else {
|
|
Item = (PALLOCATION_ITEM) g_FirstDeletedAlloc;
|
|
g_FirstDeletedAlloc = Item->Ptr;
|
|
}
|
|
|
|
if (Item) {
|
|
Item->Type = Type;
|
|
Item->Ptr = Ptr;
|
|
if (Alloc) {
|
|
Item->FileName = SafeHeapAlloc (g_hHeap, 0, SizeOfStringA (File));
|
|
if (Item->FileName) {
|
|
StringCopyA ((PSTR)Item->FileName, File);
|
|
}
|
|
Item->Allocated = TRUE;
|
|
} else {
|
|
Item->FileName = File;
|
|
Item->Allocated = FALSE;
|
|
}
|
|
Item->Line = Line;
|
|
|
|
pTrackHashTableInsert (
|
|
g_AllocationList.Buf,
|
|
(ALLOCATION_ITEM_OFFSET) ((PBYTE) Item - g_AllocationList.Buf)
|
|
);
|
|
//DEBUGMSG ((DBG_VERBOSE, "Allocation: File=%s, Line=%d, Size=%d", File, Line, Size));
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DebugRegisterAllocation (
|
|
IN ALLOCTYPE Type,
|
|
IN PVOID Ptr,
|
|
IN PCSTR File,
|
|
IN UINT Line
|
|
)
|
|
{
|
|
PALLOCATION_ITEM Item;
|
|
|
|
MYASSERT (File);
|
|
|
|
if (!g_FirstDeletedAlloc) {
|
|
Item = (PALLOCATION_ITEM) pOurTrackedGbGrow (&g_AllocationList,sizeof(ALLOCATION_ITEM));
|
|
} else {
|
|
Item = (PALLOCATION_ITEM) g_FirstDeletedAlloc;
|
|
g_FirstDeletedAlloc = Item->Ptr;
|
|
}
|
|
|
|
if (Item) {
|
|
Item->Type = Type;
|
|
Item->Ptr = Ptr;
|
|
Item->FileName = File;
|
|
Item->Line = Line;
|
|
Item->Allocated = FALSE;
|
|
|
|
pTrackHashTableInsert (
|
|
g_AllocationList.Buf,
|
|
(ALLOCATION_ITEM_OFFSET) ((PBYTE) Item - g_AllocationList.Buf)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DebugUnregisterAllocation (
|
|
IN ALLOCTYPE Type,
|
|
IN PVOID Ptr
|
|
)
|
|
{
|
|
PALLOCATION_ITEM Item;
|
|
PTRACKBUCKETITEM BucketItem;
|
|
|
|
BucketItem = pTrackHashTableFind (Type, Ptr);
|
|
if (!g_AllocationList.Buf) {
|
|
DEBUGMSG ((DBG_WARNING, "Unregister allocation: Allocation buffer already freed"));
|
|
return;
|
|
}
|
|
|
|
if (BucketItem) {
|
|
Item = (PALLOCATION_ITEM) (g_AllocationList.Buf + BucketItem->ItemOffset);
|
|
|
|
if (Item->Allocated) {
|
|
HeapFree (g_hHeap, 0, (PSTR)Item->FileName);
|
|
}
|
|
Item->FileName = NULL;
|
|
Item->Type = (ALLOCTYPE) -1;
|
|
Item->Ptr = g_FirstDeletedAlloc;
|
|
g_FirstDeletedAlloc = Item;
|
|
|
|
pTrackHashTableDelete (BucketItem);
|
|
|
|
} else {
|
|
DEBUGMSG ((DBG_WARNING, "Unregister allocation: Pointer not registered"));
|
|
}
|
|
}
|
|
|
|
|
|
#endif
|