windows-nt/Source/XPSP1/NT/net/qos/tc/dll/dbgmem.c
2020-09-26 16:20:57 +08:00

1041 lines
22 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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