You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1040 lines
23 KiB
1040 lines
23 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dbgmem.c
|
|
|
|
Abstract:
|
|
|
|
This module contains memory debug routines for catching memory leaks and memory
|
|
overwrites.
|
|
|
|
Author:
|
|
|
|
Jim Stewart January 8, 1997
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include"precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include<imagehlp.h>
|
|
|
|
|
|
#ifdef DBG
|
|
|
|
|
|
#define ulCheckByteEnd 0x9ABCDEF0
|
|
#define cbExtraBytes (sizeof(MEM_TRACKER) + sizeof(DWORD))
|
|
#define dwStackLimit 0x00010000 // 64KB for NT
|
|
|
|
|
|
// Protect access to allocated memory chain
|
|
CRITICAL_SECTION critsMemory;
|
|
BOOL SymbolsInitialized = FALSE;
|
|
|
|
//
|
|
// Head of allocated memory chain
|
|
//
|
|
LIST_ENTRY MemList;
|
|
|
|
//
|
|
// The type of machine we are on - needed to figure out the call stack
|
|
//
|
|
DWORD MachineType;
|
|
HANDLE OurProcess;
|
|
|
|
VOID
|
|
InitSymbols(
|
|
);
|
|
|
|
|
|
|
|
BOOL
|
|
InitDebugMemory(
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine initializes the debug memory functionality.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
BOOL - pass or fail
|
|
|
|
--*/
|
|
{
|
|
BOOL status;
|
|
SYSTEM_INFO SysInfo;
|
|
|
|
__try {
|
|
|
|
InitializeCriticalSection(&critsMemory);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
InitializeListHead( &MemList );
|
|
|
|
OurProcess = GetCurrentProcess();
|
|
|
|
GetSystemInfo( &SysInfo );
|
|
switch (SysInfo.wProcessorArchitecture) {
|
|
|
|
default:
|
|
case PROCESSOR_ARCHITECTURE_INTEL:
|
|
MachineType = IMAGE_FILE_MACHINE_I386;
|
|
break;
|
|
|
|
case PROCESSOR_ARCHITECTURE_MIPS:
|
|
//
|
|
// note this may not detect R10000 machines correctly
|
|
//
|
|
MachineType = IMAGE_FILE_MACHINE_R4000;
|
|
break;
|
|
|
|
case PROCESSOR_ARCHITECTURE_ALPHA:
|
|
MachineType = IMAGE_FILE_MACHINE_ALPHA;
|
|
break;
|
|
|
|
case PROCESSOR_ARCHITECTURE_PPC:
|
|
MachineType = IMAGE_FILE_MACHINE_POWERPC;
|
|
break;
|
|
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
VOID
|
|
DeInitDebugMemory(
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine deinitializes the critical section used by the dbg mem functions.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
DeleteCriticalSection(&critsMemory);
|
|
}
|
|
|
|
|
|
VOID
|
|
InitSymbols(
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine initializes the debug memory functionality.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
BOOL - pass or fail
|
|
|
|
--*/
|
|
{
|
|
BOOL status;
|
|
|
|
//
|
|
// only load the symbols if we are going to track the call stack
|
|
//
|
|
|
|
IF_DEBUG(MEM_CALLSTACK) {
|
|
status = SymInitialize( OurProcess,NULL,TRUE );
|
|
}
|
|
|
|
SymbolsInitialized = TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
UpdateCheckBytes(
|
|
IN PMEM_TRACKER MemTracker
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine adds check bytes at the end of allocatedmemory. These check bytes are used to check
|
|
for memory overwrites. Also a check sum in the MEM_TRACKER structure is also set here.
|
|
|
|
Arguments:
|
|
|
|
MemTracker newly allocated memory block
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
*((DWORD*)(((PUCHAR)MemTracker) + MemTracker->nSize + sizeof(MEM_TRACKER))) = ulCheckByteEnd;
|
|
|
|
MemTracker->ulCheckSum = ulCheckByteEnd +
|
|
PtrToUlong(MemTracker->szFile) +
|
|
MemTracker->nLine +
|
|
MemTracker->nSize +
|
|
PtrToUlong(MemTracker->Linkage.Blink) +
|
|
PtrToUlong(MemTracker->Linkage.Flink);
|
|
}
|
|
|
|
|
|
BOOL
|
|
FCheckCheckBytes(
|
|
IN PMEM_TRACKER MemTracker
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine checks the check sum in the MEM_TRACKER structure, called before freeing the allocated
|
|
memory.
|
|
|
|
Arguments:
|
|
|
|
MemTracker memory block whose check sum needs to be validated
|
|
|
|
Return Value:
|
|
|
|
TRUE if check sum is correct
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
DWORD ul;
|
|
|
|
ul = *((DWORD*)(((PUCHAR)MemTracker)+MemTracker->nSize+sizeof(MEM_TRACKER))) +
|
|
PtrToUlong(MemTracker->szFile) +
|
|
MemTracker->nLine +
|
|
MemTracker->nSize +
|
|
PtrToUlong(MemTracker->Linkage.Blink) +
|
|
PtrToUlong(MemTracker->Linkage.Flink);
|
|
|
|
if (ul != MemTracker->ulCheckSum) {
|
|
|
|
WSPRINT(( "Memory overwrite on location 0x%08lx\n",
|
|
PtrToUlong(MemTracker+sizeof(MEM_TRACKER)) ));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FCheckAllocatedMemory()
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine walks the allocated memory list and checks for validity of check sum and check
|
|
bytes.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE if all the allocated memory pass the above two checks.
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
PMEM_TRACKER MemTracker;
|
|
BOOL check = TRUE;
|
|
PLIST_ENTRY Entry;
|
|
|
|
IF_DEBUG(CHKSUM_ALLMEM) {
|
|
|
|
EnterCriticalSection(&critsMemory);
|
|
|
|
for (Entry = MemList.Flink; Entry != &MemList; Entry = Entry->Flink ) {
|
|
|
|
MemTracker = CONTAINING_RECORD( Entry,MEM_TRACKER,Linkage );
|
|
if (!FCheckCheckBytes(MemTracker)) {
|
|
check = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
LeaveCriticalSection(&critsMemory);
|
|
|
|
}
|
|
|
|
return check;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
AddMemTracker(
|
|
IN PMEM_TRACKER MemTracker
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Adds the supplied MEM_TRACKER at the tail of the doubly linked allocated memory list and
|
|
set the check sum also.
|
|
|
|
Arguments:
|
|
|
|
MemTracker MEM_TRACKER * to be added to the list
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PMEM_TRACKER Tracker;
|
|
|
|
ASSERT(MemTracker);
|
|
|
|
|
|
InsertTailList( &MemList,&MemTracker->Linkage );
|
|
|
|
UpdateCheckBytes( MemTracker );
|
|
FCheckCheckBytes( MemTracker );
|
|
|
|
//
|
|
// if there are other blocks in the list then change their check sum
|
|
// since we have just changed their Flink to point to us
|
|
//
|
|
if (MemTracker->Linkage.Blink != &MemList) {
|
|
|
|
Tracker = CONTAINING_RECORD( MemTracker->Linkage.Blink,MEM_TRACKER,Linkage );
|
|
UpdateCheckBytes( Tracker );
|
|
FCheckCheckBytes( Tracker );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
RemoveMemTracker(
|
|
IN PMEM_TRACKER MemTracker
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Removes the supplied MEM_TRACKER * from the list of allocated memory. Also checks
|
|
for memory overwites and updated the check sum for the entries before and
|
|
after the entry being removed
|
|
|
|
Arguments:
|
|
|
|
MemTracker MEM_TRACKER to remove from the list
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
ASSERT(MemTracker);
|
|
|
|
//
|
|
// Validate the check sum before
|
|
// removing from the list
|
|
//
|
|
|
|
FCheckCheckBytes(MemTracker);
|
|
|
|
//
|
|
// Remove MemTracker from the list
|
|
//
|
|
|
|
RemoveEntryList( &MemTracker->Linkage );
|
|
|
|
//
|
|
// Since the check sum is based on next and
|
|
// prev pointers, need to update the check
|
|
// sum for prev entry
|
|
//
|
|
|
|
if (MemTracker->Linkage.Blink != &MemList) {
|
|
UpdateCheckBytes((MEM_TRACKER*)MemTracker->Linkage.Blink);
|
|
FCheckCheckBytes((MEM_TRACKER*)MemTracker->Linkage.Blink);
|
|
}
|
|
|
|
if (MemTracker->Linkage.Flink != &MemList) {
|
|
UpdateCheckBytes((MEM_TRACKER*)MemTracker->Linkage.Flink);
|
|
FCheckCheckBytes((MEM_TRACKER*)MemTracker->Linkage.Flink);
|
|
}
|
|
|
|
}
|
|
|
|
BOOL
|
|
ReadMem(
|
|
IN HANDLE hProcess,
|
|
IN ULONG_PTR BaseAddr,
|
|
IN PVOID Buffer,
|
|
IN DWORD Size,
|
|
IN PDWORD NumBytes )
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This is a callback routine that StackWalk uses - it just calls teh system ReadProcessMemory
|
|
routine with this process's handle
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL status;
|
|
SIZE_T RealNumBytes;
|
|
|
|
status = ReadProcessMemory( GetCurrentProcess(),
|
|
(LPCVOID)BaseAddr,
|
|
Buffer,
|
|
Size,
|
|
&RealNumBytes );
|
|
*NumBytes = (DWORD)RealNumBytes;
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
VOID
|
|
GetCallStack(
|
|
IN PCALLER_SYM Caller,
|
|
IN int Skip,
|
|
IN int cFind
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine walks te stack to find the return address of caller. The number of callers
|
|
and the number of callers on top to be skipped can be specified.
|
|
|
|
Arguments:
|
|
|
|
pdwCaller array of DWORD to return callers
|
|
return addresses
|
|
Skip no. of callers to skip
|
|
cFInd no. of callers to find
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
BOOL status;
|
|
CONTEXT ContextRecord;
|
|
PUCHAR Buffer[sizeof(IMAGEHLP_SYMBOL)-1 + MAX_FUNCTION_INFO_SIZE];
|
|
PIMAGEHLP_SYMBOL Symbol = (PIMAGEHLP_SYMBOL)Buffer;
|
|
STACKFRAME StackFrame;
|
|
INT i;
|
|
DWORD Count;
|
|
|
|
memset(Caller, 0, cFind * sizeof(CALLER_SYM));
|
|
|
|
ZeroMemory( &ContextRecord,sizeof( CONTEXT ) );
|
|
ContextRecord.ContextFlags = CONTEXT_CONTROL;
|
|
status = GetThreadContext( GetCurrentThread(),&ContextRecord );
|
|
|
|
ZeroMemory( &StackFrame,sizeof(STACKFRAME) );
|
|
StackFrame.AddrPC.Segment = 0;
|
|
StackFrame.AddrPC.Mode = AddrModeFlat;
|
|
|
|
#ifdef _M_IX86
|
|
StackFrame.AddrFrame.Offset = ContextRecord.Ebp;
|
|
StackFrame.AddrFrame.Mode = AddrModeFlat;
|
|
|
|
StackFrame.AddrStack.Offset = ContextRecord.Esp;
|
|
StackFrame.AddrStack.Mode = AddrModeFlat;
|
|
|
|
StackFrame.AddrPC.Offset = (DWORD)ContextRecord.Eip;
|
|
#elif defined(_M_MRX000)
|
|
StackFrame.AddrPC.Offset = (DWORD)ContextRecord.Fir;
|
|
#elif defined(_M_ALPHA)
|
|
StackFrame.AddrPC.Offset = (DWORD)ContextRecord.Fir;
|
|
#elif defined(_M_PPC)
|
|
StackFrame.AddrPC.Offset = (DWORD)ContextRecord.Iar;
|
|
#endif
|
|
|
|
Count = 0;
|
|
for (i=0;i<cFind+Skip ;i++ ) {
|
|
status = StackWalk( MachineType,
|
|
OurProcess,
|
|
GetCurrentThread(),
|
|
&StackFrame,
|
|
(PVOID)&ContextRecord,
|
|
ReadMem,
|
|
SymFunctionTableAccess,
|
|
SymGetModuleBase,
|
|
NULL );
|
|
|
|
|
|
if (status && i >= Skip) {
|
|
DWORD_PTR Displacement;
|
|
|
|
ZeroMemory( Symbol,sizeof(IMAGEHLP_SYMBOL)-1 + MAX_FUNCTION_INFO_SIZE );
|
|
Symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
|
|
Symbol->Address = StackFrame.AddrPC.Offset;
|
|
Symbol->MaxNameLength = MAX_FUNCTION_INFO_SIZE-1;
|
|
Symbol->Flags = SYMF_OMAP_GENERATED;
|
|
|
|
status = SymGetSymFromAddr( OurProcess,
|
|
StackFrame.AddrPC.Offset,
|
|
&Displacement,
|
|
Symbol );
|
|
|
|
//
|
|
// save the name of the function and the displacement into it for later printing
|
|
//
|
|
|
|
if (status) {
|
|
strcpy( Caller[Count].Buff,Symbol->Name );
|
|
Caller[Count].Displacement = Displacement;
|
|
}
|
|
Count++;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
PVOID
|
|
AllocMemory(
|
|
IN DWORD nSize,
|
|
IN BOOL Calloc,
|
|
IN PSZ szFileName,
|
|
IN DWORD nLine
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine is the memory allocator (like malloc) for DBG builds. This routine allocated
|
|
more memory than requested by the caller. In this extra space this routine save info to
|
|
track memory leaks, overwrite, callers etc. All the info is stored in a MEM_TRACKER structure
|
|
which preceed the buffer to be returned.
|
|
|
|
Arguments:
|
|
|
|
nSize size of the required buffer.
|
|
Calloc if true then call Calloc ( which initializes memory to zero )
|
|
szFileName name of the file which contains
|
|
the routine asking for memory.
|
|
nLine line number in the above file
|
|
which has the call to PvAlloc.
|
|
|
|
Return Value:
|
|
|
|
address of the allocated buffer.
|
|
|
|
--*/
|
|
{
|
|
PVOID pvRet;
|
|
PMEM_TRACKER MemTracker;
|
|
static DWORD ulAllocs = 0;
|
|
PUCHAR FileName;
|
|
|
|
if (!SymbolsInitialized){
|
|
InitSymbols();
|
|
}
|
|
|
|
EnterCriticalSection(&critsMemory);
|
|
++ulAllocs;
|
|
|
|
|
|
//
|
|
// Check entire allocated memory for overwite
|
|
//
|
|
|
|
if ( !FCheckAllocatedMemory() ) {
|
|
WSPRINT(("Memory Overwrite detected in AllocMemory\n" ));
|
|
ASSERT(0);
|
|
}
|
|
|
|
//
|
|
// Size of the allocated memory is always
|
|
// a multiple of sizeof(DWORD)
|
|
//
|
|
|
|
nSize = ((nSize +3) /4) * 4;
|
|
|
|
//
|
|
// shorten file name to just be the file name and not the path too
|
|
//
|
|
|
|
FileName = strrchr( szFileName,'\\' );
|
|
if (!FileName) {
|
|
FileName = szFileName;
|
|
} else {
|
|
FileName++; // skip /
|
|
}
|
|
|
|
//
|
|
// Allocate extra for MEM_TRACKER and guard byte at end
|
|
//
|
|
|
|
if (!Calloc) {
|
|
pvRet = malloc( nSize + cbExtraBytes );
|
|
} else {
|
|
//
|
|
// this routine will initialize the memory to zero
|
|
//
|
|
pvRet = calloc( 1,(nSize + cbExtraBytes) );
|
|
}
|
|
if (!pvRet) {
|
|
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
WSPRINT(( "Memory alloc failed size=%li, %s line %li\n",
|
|
nSize,
|
|
FileName,
|
|
nLine ));
|
|
}
|
|
|
|
LeaveCriticalSection(&critsMemory);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Fill in new alloc with 0xFA.
|
|
//
|
|
|
|
if (!Calloc) {
|
|
memset(pvRet, 0xFA, nSize+cbExtraBytes);
|
|
}
|
|
|
|
//
|
|
// Save all the debug info needed in MEM_TRACKER
|
|
//
|
|
|
|
MemTracker = pvRet;
|
|
MemTracker->szFile = FileName;
|
|
MemTracker->nLine = nLine;
|
|
MemTracker->nSize = nSize;
|
|
MemTracker->ulAllocNum = ulAllocs;
|
|
|
|
//
|
|
// only save the call stack info if it is turned on
|
|
//
|
|
|
|
IF_DEBUG(MEM_CALLSTACK) {
|
|
GetCallStack( MemTracker->Callers,
|
|
3,
|
|
NCALLERS);
|
|
|
|
}
|
|
|
|
//
|
|
// Add to the list
|
|
//
|
|
|
|
AddMemTracker(MemTracker);
|
|
|
|
LeaveCriticalSection(&critsMemory);
|
|
|
|
IF_DEBUG(MEMORY_ALLOC) {
|
|
WSPRINT(( "Memory alloc (0x%08lX) size=%li, %s line %li\n",
|
|
PtrToUlong(pvRet)+sizeof(MEM_TRACKER),
|
|
nSize,
|
|
FileName,
|
|
nLine ));
|
|
}
|
|
|
|
//
|
|
// Return the address following the MEM_TRACKER as
|
|
// address of the buffer allocated.
|
|
//
|
|
|
|
return (PVOID)((PUCHAR)pvRet+sizeof(MEM_TRACKER));
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
ReAllocMemory(
|
|
IN PVOID pvOld,
|
|
IN DWORD nSizeNew,
|
|
IN PSZ szFileName,
|
|
IN DWORD nLine
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine is the DBG version of realloc memory allocator function. This routine
|
|
works just like PvAlloc function.
|
|
|
|
Arguments:
|
|
|
|
pvOld address of the buffer whose size
|
|
needs to be changed.
|
|
nSizeNew new size of the required buffer.
|
|
szFileName name of the file which contains
|
|
the routine asking for memory.
|
|
nLine line number in the above file
|
|
which has the call to PvAlloc.
|
|
|
|
Return Value:
|
|
|
|
address of the buffer with the new size.
|
|
|
|
--*/
|
|
{
|
|
PVOID pvRet;
|
|
PMEM_TRACKER MemTracker;
|
|
|
|
//
|
|
// Check the entire allocated memory for
|
|
// overwrites.
|
|
//
|
|
|
|
if ( !FCheckAllocatedMemory() ) {
|
|
WSPRINT(("Memory Overwrite detected in ReAllocMemory\n" ));
|
|
ASSERT(0);
|
|
}
|
|
|
|
|
|
ASSERT(pvOld);
|
|
|
|
//
|
|
// Size of the memory allocated is always
|
|
// a multiple of sizeof(DWORD)
|
|
//
|
|
|
|
nSizeNew = ((nSizeNew + 3)/4) *4;
|
|
|
|
//
|
|
// Extra space for MEM_TRACKER and Guard bytes
|
|
//
|
|
|
|
pvRet = realloc(pvOld, nSizeNew+cbExtraBytes);
|
|
if (!pvRet) {
|
|
|
|
IF_DEBUG(MEMORY_ALLOC) {
|
|
WSPRINT(( "Memory realloc failed (0x%08lX) size=%li, %s line %li\n",
|
|
PtrToUlong(pvOld) + sizeof(MEM_TRACKER),
|
|
nSizeNew,
|
|
szFileName,
|
|
nLine ));
|
|
}
|
|
} else {
|
|
|
|
IF_DEBUG(MEMORY_ALLOC) {
|
|
WSPRINT(( "Memory realloc succeeded (0x%08lX) size=%li, %s line %li\n",
|
|
PtrToUlong(pvOld) + sizeof(MEM_TRACKER),
|
|
PtrToUlong(pvRet)+sizeof(MEM_TRACKER),
|
|
nSizeNew,
|
|
szFileName,
|
|
nLine ));
|
|
}
|
|
|
|
MemTracker = (PMEM_TRACKER)pvRet;
|
|
|
|
if (nSizeNew > (DWORD)MemTracker->nSize) {
|
|
|
|
//
|
|
// Fill in extra alloc with 0xEA.
|
|
//
|
|
|
|
memset((PUCHAR)pvRet+sizeof(MEM_TRACKER)+MemTracker->nSize, 0xEA, nSizeNew - MemTracker->nSize);
|
|
}
|
|
|
|
MemTracker = pvRet;
|
|
MemTracker->szFile = szFileName;
|
|
MemTracker->nLine = nLine;
|
|
MemTracker->nSize = nSizeNew;
|
|
}
|
|
|
|
//
|
|
// Add the new buffer to the list and update check sum
|
|
//
|
|
|
|
AddMemTracker(MemTracker);
|
|
|
|
LeaveCriticalSection(&critsMemory);
|
|
|
|
if (pvRet)
|
|
return (PVOID)((PUCHAR)pvRet+sizeof(MEM_TRACKER));
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeMemory(
|
|
IN PVOID pv,
|
|
IN PSZ szFileName,
|
|
IN DWORD nLine
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This is the DBG version of free function. This routine checks for memory overwrites in the
|
|
block of memory being freed before removing from the list.
|
|
|
|
Arguments:
|
|
|
|
pv address of the buffer to be freed
|
|
szFileName name of the file from which this
|
|
block of memory is being freed.
|
|
nLine line number in the above file
|
|
which has the call to FreePvFn.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PMEM_TRACKER MemTracker;
|
|
|
|
ASSERT(pv);
|
|
if (NULL == pv)
|
|
return;
|
|
|
|
EnterCriticalSection(&critsMemory);
|
|
|
|
MemTracker = (PMEM_TRACKER)((PUCHAR)pv-sizeof(MEM_TRACKER));
|
|
|
|
//
|
|
// Check for memory overwrites
|
|
//
|
|
|
|
if (!FCheckCheckBytes(MemTracker)) {
|
|
WSPRINT(( "Memory Overwrite detected when freeing memory\n" ));
|
|
ASSERT(0);
|
|
}
|
|
|
|
if ( !FCheckAllocatedMemory() ){
|
|
WSPRINT(("Memory Overwrite - detected when checking allocated mem when freeing a block\n" ));
|
|
ASSERT(0);
|
|
}
|
|
|
|
IF_DEBUG(MEMORY_FREE) {
|
|
PUCHAR FileName;
|
|
|
|
//
|
|
// shorten file name to just be the file name and not the path too
|
|
//
|
|
|
|
FileName = strrchr( szFileName,'\\' );
|
|
if (!FileName) {
|
|
FileName = szFileName;
|
|
} else {
|
|
FileName++; // skip /
|
|
}
|
|
WSPRINT(( "Memory freed (0x%08lX) size=%li, %s line %li\n",
|
|
PtrToUlong(pv),
|
|
MemTracker->nSize,
|
|
FileName,
|
|
nLine ));
|
|
|
|
}
|
|
//
|
|
// Remove from the list
|
|
//
|
|
|
|
RemoveMemTracker(MemTracker);
|
|
|
|
//
|
|
// Fill in freed alloc with 0xCC.
|
|
//
|
|
|
|
memset(MemTracker, 0xCC, MemTracker->nSize+cbExtraBytes);
|
|
|
|
free( MemTracker );
|
|
|
|
LeaveCriticalSection(&critsMemory);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DumpAllocatedMemory()
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine is called during shutdown to dump out any unfreed memory blocks.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE if there are any unfreed memory blocks.
|
|
FALSE if all the allocated memory has been freed.
|
|
|
|
--*/
|
|
{
|
|
|
|
BOOL status;
|
|
PMEM_TRACKER MemTracker;
|
|
DWORD ulNumBlocks = 0;
|
|
DWORD ulTotalMemory = 0;
|
|
PLIST_ENTRY Entry;
|
|
|
|
//
|
|
// If the head of the chain is NULL,
|
|
// all memory has been freed.
|
|
//
|
|
|
|
IF_DEBUG(DUMP_MEM) {
|
|
EnterCriticalSection(&critsMemory);
|
|
|
|
WSPRINT(("\n\n*** Start dumping unfreed memory ***\n\n",0 ));
|
|
|
|
for (Entry = MemList.Flink; Entry != &MemList; Entry = Entry->Flink) {
|
|
INT i;
|
|
|
|
MemTracker = CONTAINING_RECORD( Entry,MEM_TRACKER,Linkage );
|
|
|
|
ulNumBlocks++;
|
|
ulTotalMemory += MemTracker->nSize;
|
|
|
|
WSPRINT(( "(0x%08lX) size=%li, %s line %li alloc# 0x%lx\n",
|
|
PtrToUlong(MemTracker)+sizeof(MEM_TRACKER),
|
|
MemTracker->nSize,
|
|
MemTracker->szFile,
|
|
MemTracker->nLine,
|
|
MemTracker->ulAllocNum ));
|
|
|
|
|
|
//
|
|
// dump the call stack if that debugging is on
|
|
//
|
|
|
|
IF_DEBUG(MEM_CALLSTACK) {
|
|
for (i = 0; i < NCALLERS && MemTracker->Callers[i].Buff[0] != 0; i++) {
|
|
|
|
WSPRINT(( "%d %s + 0x%X \n",i,MemTracker->Callers[i].Buff,MemTracker->Callers[i].Displacement ));
|
|
}
|
|
}
|
|
|
|
FCheckCheckBytes( MemTracker );
|
|
}
|
|
|
|
|
|
if (ulNumBlocks > 0) {
|
|
|
|
WSPRINT(( "%li blocks allocated, and %li bytes\n",
|
|
ulNumBlocks,
|
|
ulTotalMemory ));
|
|
|
|
status = TRUE;
|
|
|
|
} else {
|
|
status = FALSE;
|
|
}
|
|
|
|
WSPRINT(( "\n\n*** Finished dumping memory ***\n\n",0 ));
|
|
|
|
LeaveCriticalSection(&critsMemory);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SearchAllocatedMemory(
|
|
IN PSZ szFile,
|
|
IN DWORD nLine
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine dumps details about memory allocated by a given line of code in a given file.
|
|
|
|
Arguments:
|
|
|
|
szFile name of the file
|
|
nLine line number of code whose memory allocationto be displayed
|
|
|
|
Return Value:
|
|
|
|
TRUE if there was atleast one memory block allocated by the given line number
|
|
in the given file.
|
|
FALE otherwise.
|
|
|
|
--*/
|
|
{
|
|
PMEM_TRACKER MemTracker;
|
|
BOOL fFound = FALSE;
|
|
PLIST_ENTRY Entry;
|
|
|
|
EnterCriticalSection(&critsMemory);
|
|
|
|
WSPRINT(( "Searching memory\n", 0 ));
|
|
|
|
for (Entry = MemList.Flink; Entry != &MemList; Entry = Entry->Flink ) {
|
|
|
|
MemTracker = CONTAINING_RECORD( Entry,MEM_TRACKER,Linkage );
|
|
|
|
//
|
|
// Look for a match on filename and line number
|
|
//
|
|
|
|
if ( strcmp(MemTracker->szFile, szFile) == 0 && MemTracker->nLine == nLine ) {
|
|
|
|
ASSERT(FALSE);
|
|
WSPRINT(( "(0x%08lX) size=%li, %s line %li alloc# 0x%lx\n",
|
|
PtrToUlong(MemTracker)+sizeof(MEM_TRACKER),
|
|
MemTracker->nSize,
|
|
MemTracker->szFile,
|
|
MemTracker->nLine,
|
|
MemTracker->ulAllocNum));
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&critsMemory);
|
|
|
|
WSPRINT(( "Finished searching memory\n",0 ));
|
|
|
|
return fFound;
|
|
}
|
|
|
|
|
|
|
|
#endif // DBG
|