/*++ 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 = ""; 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