1045 lines
23 KiB
C
1045 lines
23 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1996 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
debug.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Debug helpers and memory allocation wrappers
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Jim Schmidt (jimschm) 13-Aug-1996
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
Marc R. Whitten (marcw) 27-May-1997
|
||
|
Added DEBUGLOGTIME() functions and support for the /#U:DOLOG cmd line option.
|
||
|
Ovidiu Temereanca (ovidiut) 06-Nov-1998
|
||
|
Took out log related functions and put them in log.c file
|
||
|
--*/
|
||
|
|
||
|
#include "pch.h"
|
||
|
#include "migutilp.h"
|
||
|
|
||
|
//
|
||
|
// NOTE: No code should appear outside the #ifdef DEBUG
|
||
|
//
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
|
||
|
#pragma message("DEBUG macros enabled")
|
||
|
|
||
|
#define PCVOID LPCVOID
|
||
|
|
||
|
typedef DWORD ALLOCATION_ITEM_OFFSET;
|
||
|
|
||
|
typedef struct _tagTRACKBUCKETITEM {
|
||
|
struct _tagTRACKBUCKETITEM *Next;
|
||
|
struct _tagTRACKBUCKETITEM *Prev;
|
||
|
ALLOCTYPE Type;
|
||
|
PVOID Ptr;
|
||
|
ALLOCATION_ITEM_OFFSET ItemOffset;
|
||
|
} TRACKBUCKETITEM, *PTRACKBUCKETITEM;
|
||
|
|
||
|
#define TRACK_BUCKETS 1501
|
||
|
|
||
|
PTRACKBUCKETITEM g_TrackBuckets[TRACK_BUCKETS];
|
||
|
|
||
|
#define BUCKET_ITEMS_PER_POOL 8192
|
||
|
|
||
|
typedef struct _tagBUCKETPOOL {
|
||
|
UINT Count;
|
||
|
TRACKBUCKETITEM Items[BUCKET_ITEMS_PER_POOL];
|
||
|
} TRACKBUCKETPOOL, *PTRACKBUCKETPOOL;
|
||
|
|
||
|
PTRACKBUCKETITEM g_TrackPoolDelHead;
|
||
|
PTRACKBUCKETPOOL g_TrackPool;
|
||
|
|
||
|
typedef struct _tagTRACKSTRUCT {
|
||
|
DWORD Signature;
|
||
|
PCSTR File;
|
||
|
DWORD Line;
|
||
|
DWORD Size;
|
||
|
PSTR Comment;
|
||
|
struct _tagTRACKSTRUCT *PrevAlloc;
|
||
|
struct _tagTRACKSTRUCT *NextAlloc;
|
||
|
} TRACKSTRUCT, *PTRACKSTRUCT;
|
||
|
|
||
|
PTRACKSTRUCT TrackHead = NULL;
|
||
|
#define TRACK_SIGNATURE 0x30405060
|
||
|
|
||
|
DWORD
|
||
|
pDebugHeapValidatePtrUnlocked (
|
||
|
HANDLE hHeap,
|
||
|
PCVOID CallerPtr,
|
||
|
PCSTR File,
|
||
|
DWORD Line
|
||
|
);
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// 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;
|
||
|
INT g_UseCount;
|
||
|
UINT g_DisableTrackComment = 0;
|
||
|
|
||
|
VOID
|
||
|
DisableTrackComment (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
g_DisableTrackComment ++;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
EnableTrackComment (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
if (g_DisableTrackComment > 0) {
|
||
|
g_DisableTrackComment --;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
SetTrackComment (
|
||
|
PCSTR Msg,
|
||
|
PCSTR File,
|
||
|
UINT Line
|
||
|
)
|
||
|
{
|
||
|
static CHAR Buffer[1024];
|
||
|
static CHAR FileCopy[1024];
|
||
|
|
||
|
if (g_DisableTrackComment > 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (g_UseCount > 0) {
|
||
|
g_UseCount++;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (Msg) {
|
||
|
wsprintfA (Buffer, "%s (%s line %u)", Msg, File, Line);
|
||
|
} else {
|
||
|
wsprintfA (Buffer, "%s line %u", File, Line);
|
||
|
}
|
||
|
|
||
|
StringCopyA (FileCopy, File);
|
||
|
g_TrackFile = FileCopy;
|
||
|
g_TrackLine = Line;
|
||
|
|
||
|
g_TrackComment = Buffer;
|
||
|
g_UseCount = 1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
ClrTrackComment (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
if (g_DisableTrackComment > 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
g_UseCount--;
|
||
|
|
||
|
if (!g_UseCount) {
|
||
|
g_TrackComment=NULL;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
pTrackInsert (
|
||
|
PCSTR File,
|
||
|
DWORD Line,
|
||
|
DWORD Size,
|
||
|
PTRACKSTRUCT p
|
||
|
)
|
||
|
{
|
||
|
p->Signature = TRACK_SIGNATURE;
|
||
|
p->File = File;
|
||
|
p->Line = Line;
|
||
|
p->Size = Size;
|
||
|
p->Comment = g_TrackComment ? SafeHeapAlloc (g_hHeap, 0, SizeOfStringA (g_TrackComment)) : NULL;
|
||
|
p->PrevAlloc = NULL;
|
||
|
p->NextAlloc = TrackHead;
|
||
|
|
||
|
if (p->Comment) {
|
||
|
StringCopyA (p->Comment, g_TrackComment);
|
||
|
}
|
||
|
|
||
|
if (TrackHead) {
|
||
|
TrackHead->PrevAlloc = p;
|
||
|
}
|
||
|
|
||
|
TrackHead = p;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
pTrackDelete (
|
||
|
PTRACKSTRUCT p
|
||
|
)
|
||
|
{
|
||
|
if (p->Signature != TRACK_SIGNATURE) {
|
||
|
DEBUGMSG ((DBG_WARNING, "A tracking signature is invalid. "
|
||
|
"This suggests memory corruption."));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (p->PrevAlloc) {
|
||
|
p->PrevAlloc->NextAlloc = p->NextAlloc;
|
||
|
} else {
|
||
|
TrackHead = p->NextAlloc;
|
||
|
}
|
||
|
|
||
|
if (p->NextAlloc) {
|
||
|
p->NextAlloc->PrevAlloc = p->PrevAlloc;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
pWriteTrackLog (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
HANDLE File;
|
||
|
CHAR LineBuf[2048];
|
||
|
PTRACKSTRUCT p;
|
||
|
DWORD DontCare;
|
||
|
DWORD Count;
|
||
|
BOOL BadMem = FALSE;
|
||
|
CHAR TempPath[MAX_TCHAR_PATH];
|
||
|
CHAR memtrackLogPath[] = "c:\\memtrack.log";
|
||
|
|
||
|
if (!TrackHead) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (ISPC98()) {
|
||
|
GetSystemDirectory(TempPath, MAX_TCHAR_PATH);
|
||
|
memtrackLogPath[0] = TempPath[0];
|
||
|
}
|
||
|
File = CreateFileA (memtrackLogPath, GENERIC_WRITE, 0, NULL,
|
||
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
|
||
|
);
|
||
|
|
||
|
if (File != INVALID_HANDLE_VALUE) {
|
||
|
Count = 0;
|
||
|
__try {
|
||
|
for (p = TrackHead ; p ; p = p->NextAlloc) {
|
||
|
Count++;
|
||
|
__try {
|
||
|
if (p->Comment) {
|
||
|
wsprintfA (LineBuf, "%s line %u\r\n %s\r\n\r\n", p->File, p->Line, p->Comment);
|
||
|
} else {
|
||
|
wsprintfA (LineBuf, "%s line %u\r\n\r\n", p->File, p->Line);
|
||
|
}
|
||
|
}
|
||
|
__except (TRUE) {
|
||
|
wsprintfA (LineBuf, "Address %Xh was freed, but not by MemFree!!\r\n", p);
|
||
|
BadMem = TRUE;
|
||
|
}
|
||
|
WriteFile (File, LineBuf, ByteCountA (LineBuf), &DontCare, NULL);
|
||
|
|
||
|
if (BadMem) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
__except (TRUE) {
|
||
|
}
|
||
|
|
||
|
wsprintfA (LineBuf, "\r\n%i item%s allocated but not freed.\r\n", Count, Count == 1 ? "":"s");
|
||
|
WriteFile (File, LineBuf, ByteCountA (LineBuf), &DontCare, NULL);
|
||
|
|
||
|
CloseHandle (File);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
typedef struct {
|
||
|
ALLOCTYPE Type;
|
||
|
PVOID Ptr;
|
||
|
PCSTR FileName;
|
||
|
UINT Line;
|
||
|
} ALLOCATION_ITEM, *PALLOCATION_ITEM;
|
||
|
|
||
|
GROWBUFFER g_AllocationList;
|
||
|
PVOID g_FirstDeletedAlloc;
|
||
|
|
||
|
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 = GROWBUF_INIT;
|
||
|
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 = wsprintfA (Text, "%s line %u\r\n", Item->FileName, Item->Line);
|
||
|
|
||
|
p = (PSTR) RealGrowBuffer (&Msg, Bytes);
|
||
|
if (p) {
|
||
|
CopyMemory (p, Text, Bytes);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Msg.End) {
|
||
|
|
||
|
p = (PSTR) RealGrowBuffer (&Msg, 1);
|
||
|
if (p) {
|
||
|
*p = 0;
|
||
|
DEBUGMSGA (("Leaks", "%s", Msg.Buf));
|
||
|
}
|
||
|
|
||
|
FreeGrowBuffer (&Msg);
|
||
|
}
|
||
|
|
||
|
FreeGrowBuffer (&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) (Type << 16) ^ (DWORD) 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
|
||
|
DebugRegisterAllocation (
|
||
|
IN ALLOCTYPE Type,
|
||
|
IN PVOID Ptr,
|
||
|
IN PCSTR File,
|
||
|
IN UINT Line
|
||
|
)
|
||
|
{
|
||
|
PALLOCATION_ITEM Item;
|
||
|
|
||
|
MYASSERT (File);
|
||
|
|
||
|
if (!g_FirstDeletedAlloc) {
|
||
|
Item = (PALLOCATION_ITEM) RealGrowBuffer (&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;
|
||
|
|
||
|
pTrackHashTableInsert (g_AllocationList.Buf, (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);
|
||
|
|
||
|
Item->FileName = NULL;
|
||
|
Item->Type = -1;
|
||
|
Item->Ptr = g_FirstDeletedAlloc;
|
||
|
g_FirstDeletedAlloc = Item;
|
||
|
|
||
|
pTrackHashTableDelete (BucketItem);
|
||
|
|
||
|
} else {
|
||
|
DEBUGMSG ((DBG_WARNING, "Unregister allocation: Pointer not registered"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// File and Line settings
|
||
|
//
|
||
|
|
||
|
static PCSTR g_File;
|
||
|
static DWORD g_Line;
|
||
|
|
||
|
void
|
||
|
HeapCallFailed (
|
||
|
PCSTR Msg,
|
||
|
PCSTR File,
|
||
|
DWORD Line
|
||
|
)
|
||
|
{
|
||
|
CHAR Msg2[2048];
|
||
|
|
||
|
wsprintfA (Msg2, "Error in %s line %u\n\n", File, Line);
|
||
|
strcat (Msg2, Msg);
|
||
|
strcat (Msg2, "\n\nBreak execution now?");
|
||
|
|
||
|
if (IDYES == MessageBoxA (GetFocus(), Msg2, "Heap Call Failed", MB_YESNO|MB_APPLMODAL)) {
|
||
|
DebugBreak ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#define INVALID_PTR 0xffffffff
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
DebugHeapValidatePtr (
|
||
|
HANDLE hHeap,
|
||
|
PCVOID CallerPtr,
|
||
|
PCSTR File,
|
||
|
DWORD Line
|
||
|
)
|
||
|
{
|
||
|
DWORD rc;
|
||
|
|
||
|
EnterCriticalSection (&g_MemAllocCs);
|
||
|
|
||
|
rc = pDebugHeapValidatePtrUnlocked (hHeap, CallerPtr, File, Line);
|
||
|
|
||
|
LeaveCriticalSection (&g_MemAllocCs);
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
pDebugHeapValidatePtrUnlocked (
|
||
|
HANDLE hHeap,
|
||
|
PCVOID CallerPtr,
|
||
|
PCSTR File,
|
||
|
DWORD Line
|
||
|
)
|
||
|
{
|
||
|
DWORD dwSize;
|
||
|
PCVOID RealPtr;
|
||
|
DWORD SizeAdjust;
|
||
|
|
||
|
SizeAdjust = sizeof (TRACKSTRUCT);
|
||
|
RealPtr = (PCVOID) ((PBYTE) CallerPtr - SizeAdjust);
|
||
|
|
||
|
if (IsBadWritePtr ((PBYTE) RealPtr - 8, 8)) {
|
||
|
CHAR BadPtrMsg[256];
|
||
|
|
||
|
wsprintfA (
|
||
|
BadPtrMsg,
|
||
|
"Attempt to free memory at 0x%08x. This address is not valid.",
|
||
|
CallerPtr
|
||
|
);
|
||
|
|
||
|
HeapCallFailed (BadPtrMsg, File, Line);
|
||
|
|
||
|
return INVALID_PTR;
|
||
|
}
|
||
|
|
||
|
dwSize = HeapSize (hHeap, 0, RealPtr);
|
||
|
if (dwSize == 0xffffffff) {
|
||
|
CHAR BadPtrMsg[256];
|
||
|
|
||
|
wsprintfA (
|
||
|
BadPtrMsg,
|
||
|
"Attempt to free memory at 0x%08x. "
|
||
|
"This address is not the start of a memory block.",
|
||
|
CallerPtr
|
||
|
);
|
||
|
|
||
|
HeapCallFailed (BadPtrMsg, File, Line);
|
||
|
|
||
|
return INVALID_PTR;
|
||
|
}
|
||
|
|
||
|
return dwSize;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Heap debug statistics
|
||
|
//
|
||
|
|
||
|
static DWORD g_dwTotalBytesAllocated = 0;
|
||
|
static DWORD g_dwMaxBytesInUse = 0;
|
||
|
static DWORD g_dwHeapAllocs = 0;
|
||
|
static DWORD g_dwHeapReAllocs = 0;
|
||
|
static DWORD g_dwHeapFrees = 0;
|
||
|
static DWORD g_dwHeapAllocFails = 0;
|
||
|
static DWORD g_dwHeapReAllocFails = 0;
|
||
|
static DWORD g_dwHeapFreeFails = 0;
|
||
|
#define TRAIL_SIG 0x708aa210
|
||
|
|
||
|
PVOID
|
||
|
DebugHeapAlloc (
|
||
|
PCSTR File,
|
||
|
DWORD Line,
|
||
|
HANDLE hHeap,
|
||
|
DWORD Flags,
|
||
|
DWORD BytesToAlloc
|
||
|
)
|
||
|
{
|
||
|
PVOID RealPtr;
|
||
|
PVOID ReturnPtr = NULL;
|
||
|
DWORD SizeAdjust;
|
||
|
DWORD TrackStructSize;
|
||
|
DWORD OrgError;
|
||
|
|
||
|
EnterCriticalSection (&g_MemAllocCs);
|
||
|
|
||
|
__try {
|
||
|
|
||
|
OrgError = GetLastError();
|
||
|
|
||
|
SizeAdjust = sizeof (TRACKSTRUCT) + sizeof (DWORD);
|
||
|
TrackStructSize = sizeof (TRACKSTRUCT);
|
||
|
|
||
|
if (!HeapValidate (hHeap, 0, NULL)) {
|
||
|
HeapCallFailed ("Heap is corrupt!", File, Line);
|
||
|
g_dwHeapAllocFails++;
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
RealPtr = SafeHeapAlloc(hHeap, Flags, BytesToAlloc + SizeAdjust);
|
||
|
if (RealPtr) {
|
||
|
g_dwHeapAllocs++;
|
||
|
g_dwTotalBytesAllocated += HeapSize (hHeap, 0, RealPtr);
|
||
|
g_dwMaxBytesInUse = max (g_dwMaxBytesInUse, g_dwTotalBytesAllocated);
|
||
|
|
||
|
pTrackInsert (File, Line, BytesToAlloc, (PTRACKSTRUCT) RealPtr);
|
||
|
*((PDWORD) ((PBYTE) RealPtr + TrackStructSize + BytesToAlloc)) = TRAIL_SIG;
|
||
|
}
|
||
|
else {
|
||
|
g_dwHeapAllocFails++;
|
||
|
}
|
||
|
|
||
|
if (RealPtr) {
|
||
|
ReturnPtr = (PVOID) ((PBYTE) RealPtr + TrackStructSize);
|
||
|
}
|
||
|
|
||
|
if (ReturnPtr && !(Flags & HEAP_ZERO_MEMORY)) {
|
||
|
FillMemory (ReturnPtr, BytesToAlloc, 0xAA);
|
||
|
}
|
||
|
|
||
|
if (RealPtr) {
|
||
|
SetLastError(OrgError);
|
||
|
}
|
||
|
}
|
||
|
__finally {
|
||
|
LeaveCriticalSection (&g_MemAllocCs);
|
||
|
}
|
||
|
|
||
|
return ReturnPtr;
|
||
|
}
|
||
|
|
||
|
PVOID
|
||
|
DebugHeapReAlloc (
|
||
|
PCSTR File,
|
||
|
DWORD Line,
|
||
|
HANDLE hHeap,
|
||
|
DWORD Flags,
|
||
|
PCVOID CallerPtr,
|
||
|
DWORD BytesToAlloc
|
||
|
)
|
||
|
{
|
||
|
DWORD dwLastSize;
|
||
|
PVOID NewRealPtr;
|
||
|
PCVOID RealPtr;
|
||
|
PVOID ReturnPtr = NULL;
|
||
|
DWORD SizeAdjust;
|
||
|
DWORD OrgError;
|
||
|
DWORD TrackStructSize;
|
||
|
DWORD OrgSize;
|
||
|
PTRACKSTRUCT pts = NULL;
|
||
|
|
||
|
EnterCriticalSection (&g_MemAllocCs);
|
||
|
|
||
|
__try {
|
||
|
|
||
|
OrgError = GetLastError();
|
||
|
|
||
|
SizeAdjust = sizeof (TRACKSTRUCT) + sizeof (DWORD);
|
||
|
TrackStructSize = sizeof (TRACKSTRUCT);
|
||
|
RealPtr = (PCVOID) ((PBYTE) CallerPtr - TrackStructSize);
|
||
|
pts = (PTRACKSTRUCT) RealPtr;
|
||
|
OrgSize = pts->Size;
|
||
|
|
||
|
if (!HeapValidate (hHeap, 0, NULL)) {
|
||
|
HeapCallFailed ("Heap is corrupt!", File, Line);
|
||
|
g_dwHeapReAllocFails++;
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
dwLastSize = pDebugHeapValidatePtrUnlocked (hHeap, CallerPtr, File, Line);
|
||
|
if (dwLastSize == INVALID_PTR) {
|
||
|
g_dwHeapReAllocFails++;
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
pTrackDelete (pts);
|
||
|
|
||
|
NewRealPtr = SafeHeapReAlloc (hHeap, Flags, (PVOID) RealPtr, BytesToAlloc + SizeAdjust);
|
||
|
if (NewRealPtr) {
|
||
|
g_dwHeapReAllocs++;
|
||
|
g_dwTotalBytesAllocated -= dwLastSize;
|
||
|
g_dwTotalBytesAllocated += HeapSize (hHeap, 0, NewRealPtr);
|
||
|
g_dwMaxBytesInUse = max (g_dwMaxBytesInUse, g_dwTotalBytesAllocated);
|
||
|
|
||
|
pTrackInsert (File, Line, BytesToAlloc, (PTRACKSTRUCT) NewRealPtr);
|
||
|
*((PDWORD) ((PBYTE) NewRealPtr + TrackStructSize + BytesToAlloc)) = TRAIL_SIG;
|
||
|
}
|
||
|
else {
|
||
|
g_dwHeapReAllocFails++;
|
||
|
|
||
|
// Put original address back in
|
||
|
pTrackInsert (
|
||
|
pts->File,
|
||
|
pts->Line,
|
||
|
pts->Size,
|
||
|
pts
|
||
|
);
|
||
|
|
||
|
}
|
||
|
|
||
|
if (NewRealPtr) {
|
||
|
ReturnPtr = (PVOID) ((PBYTE) NewRealPtr + TrackStructSize);
|
||
|
}
|
||
|
|
||
|
if (ReturnPtr && BytesToAlloc > OrgSize && !(Flags & HEAP_ZERO_MEMORY)) {
|
||
|
FillMemory ((PBYTE) ReturnPtr + OrgSize, BytesToAlloc - OrgSize, 0xAA);
|
||
|
}
|
||
|
|
||
|
if (ReturnPtr) {
|
||
|
SetLastError (OrgError);
|
||
|
}
|
||
|
}
|
||
|
__finally {
|
||
|
LeaveCriticalSection (&g_MemAllocCs);
|
||
|
}
|
||
|
|
||
|
return ReturnPtr;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
DebugHeapFree (
|
||
|
PCSTR File,
|
||
|
DWORD Line,
|
||
|
HANDLE hHeap,
|
||
|
DWORD Flags,
|
||
|
PCVOID CallerPtr
|
||
|
)
|
||
|
{
|
||
|
DWORD dwSize;
|
||
|
PCVOID RealPtr;
|
||
|
DWORD SizeAdjust;
|
||
|
DWORD OrgError;
|
||
|
BOOL Result = FALSE;
|
||
|
PTRACKSTRUCT pts = NULL;
|
||
|
|
||
|
EnterCriticalSection (&g_MemAllocCs);
|
||
|
|
||
|
__try {
|
||
|
OrgError = GetLastError();
|
||
|
|
||
|
SizeAdjust = sizeof (TRACKSTRUCT);
|
||
|
RealPtr = (PCVOID) ((PBYTE) CallerPtr - SizeAdjust);
|
||
|
pts = (PTRACKSTRUCT) RealPtr;
|
||
|
|
||
|
if (*((PDWORD) ((PBYTE) CallerPtr + pts->Size)) != TRAIL_SIG) {
|
||
|
HeapCallFailed ("Heap tag was overwritten!", File, Line);
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
if (!HeapValidate (hHeap, 0, NULL)) {
|
||
|
HeapCallFailed ("Heap is corrupt!", File, Line);
|
||
|
g_dwHeapFreeFails++;
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
dwSize = pDebugHeapValidatePtrUnlocked (hHeap, CallerPtr, File, Line);
|
||
|
if (dwSize == INVALID_PTR) {
|
||
|
g_dwHeapFreeFails++;
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
pTrackDelete ((PTRACKSTRUCT) RealPtr);
|
||
|
|
||
|
if (!HeapFree (hHeap, Flags, (PVOID) RealPtr)) {
|
||
|
CHAR BadPtrMsg[256];
|
||
|
|
||
|
wsprintf (BadPtrMsg,
|
||
|
"Attempt to free memory at 0x%08x with flags 0x%08x. "
|
||
|
"HeapFree() failed.",
|
||
|
CallerPtr, Flags);
|
||
|
|
||
|
HeapCallFailed (BadPtrMsg, File, Line);
|
||
|
g_dwHeapFreeFails++;
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
g_dwHeapFrees++;
|
||
|
if (g_dwTotalBytesAllocated < dwSize) {
|
||
|
DEBUGMSG ((DBG_WARNING, "Total bytes allocated is less than amount being freed. "
|
||
|
"This suggests memory corruption."));
|
||
|
g_dwTotalBytesAllocated = 0;
|
||
|
} else {
|
||
|
g_dwTotalBytesAllocated -= dwSize;
|
||
|
}
|
||
|
|
||
|
SetLastError (OrgError);
|
||
|
Result = TRUE;
|
||
|
}
|
||
|
__finally {
|
||
|
LeaveCriticalSection (&g_MemAllocCs);
|
||
|
}
|
||
|
|
||
|
return Result;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
DumpHeapStats (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
CHAR OutputMsg[4096];
|
||
|
|
||
|
pWriteTrackLog();
|
||
|
|
||
|
wsprintfA (OutputMsg,
|
||
|
"Bytes currently allocated: %u\n"
|
||
|
"Peak bytes allocated: %u\n"
|
||
|
"Allocation count: %u\n"
|
||
|
"Reallocation count: %u\n"
|
||
|
"Free count: %u\n",
|
||
|
g_dwTotalBytesAllocated,
|
||
|
g_dwMaxBytesInUse,
|
||
|
g_dwHeapAllocs,
|
||
|
g_dwHeapReAllocs,
|
||
|
g_dwHeapFrees
|
||
|
);
|
||
|
|
||
|
if (g_dwHeapAllocFails) {
|
||
|
wsprintfA (strchr (OutputMsg, 0),
|
||
|
"***Allocation failures: %u\n",
|
||
|
g_dwHeapAllocFails);
|
||
|
}
|
||
|
if (g_dwHeapReAllocFails) {
|
||
|
wsprintfA (strchr (OutputMsg, 0),
|
||
|
"***Reallocation failures: %u\n",
|
||
|
g_dwHeapReAllocFails);
|
||
|
}
|
||
|
if (g_dwHeapFreeFails) {
|
||
|
wsprintfA (strchr (OutputMsg, 0),
|
||
|
"***Free failures: %u\n",
|
||
|
g_dwHeapFreeFails);
|
||
|
}
|
||
|
|
||
|
DEBUGMSG ((DBG_STATS, "%s", OutputMsg));
|
||
|
|
||
|
#ifdef CONSOLE
|
||
|
printf ("%s", OutputMsg);
|
||
|
#else // i.e. ifndef CONSOLE
|
||
|
|
||
|
#if 0
|
||
|
if (0) {
|
||
|
PROCESS_HEAP_ENTRY he;
|
||
|
CHAR FlagMsg[256];
|
||
|
|
||
|
ZeroMemory (&he, sizeof (he));
|
||
|
|
||
|
while (HeapWalk (g_hHeap, &he)) {
|
||
|
FlagMsg[0] = 0;
|
||
|
if (he.wFlags & PROCESS_HEAP_REGION) {
|
||
|
strcpy (FlagMsg, "PROCESS_HEAP_REGION");
|
||
|
}
|
||
|
if (he.wFlags & PROCESS_HEAP_UNCOMMITTED_RANGE) {
|
||
|
if (FlagMsg[0])
|
||
|
strcat (FlagMsg, ", ");
|
||
|
|
||
|
strcat (FlagMsg, "PROCESS_HEAP_UNCOMMITTED_RANGE");
|
||
|
}
|
||
|
if (he.wFlags & PROCESS_HEAP_ENTRY_BUSY) {
|
||
|
if (FlagMsg[0])
|
||
|
strcat (FlagMsg, ", ");
|
||
|
|
||
|
strcat (FlagMsg, "PROCESS_HEAP_ENTRY_BUSY");
|
||
|
}
|
||
|
if (he.wFlags & PROCESS_HEAP_ENTRY_MOVEABLE) {
|
||
|
if (FlagMsg[0])
|
||
|
strcat (FlagMsg, ", ");
|
||
|
|
||
|
strcat (FlagMsg, "PROCESS_HEAP_ENTRY_MOVEABLE");
|
||
|
}
|
||
|
if (he.wFlags & PROCESS_HEAP_ENTRY_DDESHARE) {
|
||
|
if (FlagMsg[0])
|
||
|
strcat (FlagMsg, ", ");
|
||
|
|
||
|
strcat (FlagMsg, "PROCESS_HEAP_ENTRY_DDESHARE");
|
||
|
}
|
||
|
|
||
|
wsprintfA (OutputMsg,
|
||
|
"Address of Data: %Xh\n"
|
||
|
"Size of Data: %u byte%s\n"
|
||
|
"OS Overhead: %u byte%s\n"
|
||
|
"Region index: %u\n"
|
||
|
"Flags: %s\n\n"
|
||
|
"Examine Data?",
|
||
|
he.lpData,
|
||
|
he.cbData, he.cbData == 1 ? "" : "s",
|
||
|
he.cbOverhead, he.cbOverhead == 1 ? "" : "s",
|
||
|
he.iRegionIndex,
|
||
|
FlagMsg
|
||
|
);
|
||
|
|
||
|
rc = MessageBoxA (GetFocus(), OutputMsg, "Memory Allocation Statistics", MB_YESNOCANCEL|MB_APPLMODAL|MB_SETFOREGROUND);
|
||
|
|
||
|
if (rc == IDCANCEL) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (rc == IDYES) {
|
||
|
int i, j, k, l;
|
||
|
PBYTE p;
|
||
|
PSTR p2;
|
||
|
OutputMsg[0] = 0;
|
||
|
|
||
|
p = he.lpData;
|
||
|
p2 = OutputMsg;
|
||
|
j = min (256, he.cbData);
|
||
|
for (i = 0 ; i < j ; i += 16) {
|
||
|
l = i + 16;
|
||
|
for (k = i ; k < l ; k++) {
|
||
|
if (k < j) {
|
||
|
wsprintfA (p2, "%02X ", (DWORD) (p[k]));
|
||
|
} else {
|
||
|
wsprintfA (p2, " ");
|
||
|
}
|
||
|
|
||
|
p2 = strchr (p2, 0);
|
||
|
}
|
||
|
|
||
|
l = min (l, j);
|
||
|
for (k = i ; k < l ; k++) {
|
||
|
if (isprint (p[k])) {
|
||
|
*p2 = (CHAR) p[k];
|
||
|
} else {
|
||
|
*p2 = '.';
|
||
|
}
|
||
|
p2++;
|
||
|
}
|
||
|
|
||
|
*p2 = '\n';
|
||
|
p2++;
|
||
|
*p2 = 0;
|
||
|
}
|
||
|
|
||
|
MessageBoxA (GetFocus(), OutputMsg, "Memory Allocation Statistics", MB_OK|MB_APPLMODAL|MB_SETFOREGROUND);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif // #if 0
|
||
|
|
||
|
#endif // #ifndef CONSOLE
|
||
|
}
|
||
|
|
||
|
void
|
||
|
DebugHeapCheck (
|
||
|
PCSTR File,
|
||
|
DWORD Line,
|
||
|
HANDLE hHeap
|
||
|
)
|
||
|
{
|
||
|
EnterCriticalSection (&g_MemAllocCs);
|
||
|
|
||
|
if (!HeapValidate (hHeap, 0, NULL)) {
|
||
|
HeapCallFailed ("HeapCheck failed: Heap is corrupt!", File, Line);
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection (&g_MemAllocCs);
|
||
|
}
|
||
|
|
||
|
#endif
|