451 lines
9.2 KiB
C
451 lines
9.2 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
memdebug.c
|
||
|
||
Abstract:
|
||
|
||
Functions for debugging memory leaks and corruptions
|
||
|
||
Environment:
|
||
|
||
Windows NT printer drivers
|
||
|
||
Revision History:
|
||
|
||
07/31/96 -davidx-
|
||
Created it.
|
||
|
||
mm/dd/yy -author-
|
||
description
|
||
|
||
--*/
|
||
|
||
|
||
#if DBG && defined(MEMDEBUG)
|
||
|
||
#include "lib.h"
|
||
|
||
//
|
||
// Doubly-linked list of all currently allocated memory blocks
|
||
//
|
||
|
||
#define MAX_DEBUGMEM_FILENAME 16
|
||
|
||
typedef struct _DEBUGMEMHDR {
|
||
|
||
struct _DEBUGMEMHDR *pPrev;
|
||
struct _DEBUGMEMHDR *pNext;
|
||
CHAR strFilename[MAX_DEBUGMEM_FILENAME];
|
||
INT iLineNumber;
|
||
DWORD dwSize;
|
||
LPVOID pvSignature;
|
||
|
||
} DEBUGMEMHDR, *PDEBUGMEMHDR;
|
||
|
||
DEBUGMEMHDR gMemListHdr;
|
||
|
||
//
|
||
// Critical section for protecting shared global data
|
||
//
|
||
|
||
HSEMAPHORE ghDebugMemSemaphore;
|
||
|
||
//
|
||
// Statistics about memory usage:
|
||
// total number of allocations
|
||
// maximum amount of memory use
|
||
// largest chunk of memory allocated
|
||
//
|
||
|
||
DWORD gdwNumMemAllocs, gdwMaxMemUsage, gdwMaxMemChunk, gdwCurMemUsage;
|
||
|
||
//
|
||
// The frequency of simulated memory allocation failures
|
||
//
|
||
|
||
DWORD gdwMemErrFreq;
|
||
|
||
|
||
|
||
VOID
|
||
MemDebugInit(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Perform necessary initialization when memory debug option is enabled
|
||
|
||
Arguments:
|
||
|
||
NONE
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
Note:
|
||
|
||
This should be called from the driver's DrvEnableDriver entrypoint.
|
||
|
||
--*/
|
||
|
||
{
|
||
gdwNumMemAllocs = gdwMaxMemUsage = gdwMaxMemChunk = gdwCurMemUsage = 0;
|
||
gdwMemErrFreq = 0;
|
||
gMemListHdr.pNext = gMemListHdr.pPrev = &gMemListHdr;
|
||
|
||
ghDebugMemSemaphore = EngCreateSemaphore();
|
||
ASSERTMSG(ghDebugMemSemaphore, ("EngCreateSemaphore failed: %d\n", GetLastError()));
|
||
}
|
||
|
||
|
||
|
||
BOOL
|
||
BCheckDebugMemory(
|
||
PDEBUGMEMHDR pMemHdr
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Check if the specified memory block has been corrupted
|
||
|
||
Arguments:
|
||
|
||
pMemHdr - Specifies the memory block to be verified
|
||
|
||
Return Value:
|
||
|
||
FALSE if the memory block is corrupted, TRUE otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
if (pMemHdr != pMemHdr->pvSignature)
|
||
{
|
||
RIP(("Corrupted memory block header: 0x%0x\n", pMemHdr));
|
||
return FALSE;
|
||
}
|
||
else
|
||
{
|
||
PBYTE pub = (PBYTE) pMemHdr + (sizeof(DEBUGMEMHDR) + pMemHdr->dwSize);
|
||
INT iPadding = sizeof(DWORD) - (pMemHdr->dwSize % sizeof(DWORD));
|
||
BYTE ubPattern = (BYTE) pMemHdr;
|
||
|
||
while (iPadding--)
|
||
{
|
||
if (*pub++ != ubPattern++)
|
||
{
|
||
RIP(("Corrupted memory block: 0x%x (%d bytes), allocated in %s, line %d\n",
|
||
pMemHdr, pMemHdr->dwSize, pMemHdr->strFilename, pMemHdr->iLineNumber));
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
MemDebugCheck(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Perform necessary checks when memory debug option is enabled
|
||
|
||
Arguments:
|
||
|
||
NONE
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
Note:
|
||
|
||
This should be called during the driver's DrvDisablePDEV entrypoint.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEBUGMEMHDR pMemHdr;
|
||
|
||
EngAcquireSemaphore(ghDebugMemSemaphore);
|
||
|
||
pMemHdr = gMemListHdr.pNext;
|
||
|
||
while (pMemHdr != &gMemListHdr)
|
||
{
|
||
if (BCheckDebugMemory(pMemHdr))
|
||
{
|
||
WARNING(("Possible memory leak: 0x%x (%d bytes), allocated in %s, line %d\n",
|
||
pMemHdr, pMemHdr->dwSize, pMemHdr->strFilename, pMemHdr->iLineNumber));
|
||
}
|
||
|
||
pMemHdr = pMemHdr->pNext;
|
||
}
|
||
|
||
TERSE(("Total number of memory allocations: %d\n", gdwNumMemAllocs));
|
||
TERSE(("Maximum amount of memory usage: %d\n", gdwMaxMemUsage));
|
||
TERSE(("Large chunk of memory allocated: %d\n", gdwMaxMemChunk));
|
||
|
||
EngReleaseSemaphore(ghDebugMemSemaphore);
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
MemDebugCleanup(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Perform necessary cleanup when memory debug option is enabled
|
||
|
||
Arguments:
|
||
|
||
NONE
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
Note:
|
||
|
||
This should be called during the driver's DrvDisableDriver entrypoint.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEBUGMEMHDR pMemHdr, pTemp;
|
||
|
||
pMemHdr = gMemListHdr.pNext;
|
||
|
||
while (pMemHdr != &gMemListHdr)
|
||
{
|
||
if (BCheckDebugMemory(pMemHdr))
|
||
{
|
||
WARNING(("Possible memory leak: 0x%x (%d bytes), allocated in %s, line %d\n",
|
||
pMemHdr, pMemHdr->dwSize, pMemHdr->strFilename, pMemHdr->iLineNumber));
|
||
}
|
||
|
||
pTemp = pMemHdr;
|
||
pMemHdr = pMemHdr->pNext;
|
||
EngFreeMem(pTemp);
|
||
}
|
||
|
||
gMemListHdr.pPrev = gMemListHdr.pNext = &gMemListHdr;
|
||
EngDeleteSemaphore(ghDebugMemSemaphore);
|
||
}
|
||
|
||
|
||
|
||
PVOID
|
||
MemDebugAlloc(
|
||
IN DWORD dwFlags,
|
||
IN DWORD dwSize,
|
||
IN DWORD dwTag,
|
||
IN PCSTR pstrFilename,
|
||
IN INT iLineNumber
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Allocate a memory block of the specified size, and
|
||
save necessary information for debugging purposes
|
||
|
||
Arguments:
|
||
|
||
dwFlags - Memory allocation flags
|
||
dwSize - Number of bytes to be allocated
|
||
dwSag - Specifies the pool tag to be used
|
||
pstrFilename, iLineNumber - Caller information: source filename and line number
|
||
|
||
Return Value:
|
||
|
||
Pointer to newly allocated memory block, NULL if there is an error
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEBUGMEMHDR pMemHdr;
|
||
INT iPadding;
|
||
PBYTE pub;
|
||
BYTE ubPattern;
|
||
|
||
if (dwSize == 0)
|
||
{
|
||
WARNING(("Zero-size memory allocation: %s (%d)\n",
|
||
StripDirPrefixA(pstrFilename),
|
||
iLineNumber));
|
||
}
|
||
|
||
gdwNumMemAllocs++;
|
||
|
||
//
|
||
// Determine if we need to simulate memory allocation failure
|
||
//
|
||
|
||
if (gdwMemErrFreq && (gdwNumMemAllocs % gdwMemErrFreq) == 0)
|
||
{
|
||
WARNING(("Simulated memory error - %s (%d), %d bytes\n",
|
||
pstrFilename,
|
||
iLineNumber,
|
||
dwSize));
|
||
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Calculate the total amount of memory to allocate
|
||
//
|
||
|
||
iPadding = sizeof(DWORD) - dwSize % sizeof(DWORD);
|
||
|
||
if (pMemHdr = EngAllocMem(dwFlags, dwSize + sizeof(DEBUGMEMHDR) + iPadding, dwTag))
|
||
{
|
||
//
|
||
// Save debug information in the memory block header
|
||
//
|
||
|
||
pMemHdr->pvSignature = pMemHdr;
|
||
pMemHdr->dwSize = dwSize;
|
||
pMemHdr->iLineNumber = iLineNumber;
|
||
CopyStringA(pMemHdr->strFilename, StripDirPrefixA(pstrFilename), MAX_DEBUGMEM_FILENAME);
|
||
|
||
//
|
||
// If no flag bit is set, fill the newly allocated memory
|
||
// with a predefined pattern.
|
||
//
|
||
|
||
if (dwFlags == 0)
|
||
{
|
||
PDWORD pdwFill = (LPDWORD) ((PBYTE) pMemHdr + sizeof(DEBUGMEMHDR));
|
||
DWORD dwFill = (dwSize + iPadding) / sizeof(DWORD);
|
||
|
||
while (dwFill--)
|
||
*pdwFill++ = 0xdeadbeef;
|
||
}
|
||
|
||
//
|
||
// Save patterns after the memory block to detect corruption
|
||
//
|
||
|
||
pub = (PBYTE) pMemHdr + sizeof(DEBUGMEMHDR) + dwSize;
|
||
ubPattern = (BYTE) pMemHdr;
|
||
|
||
while (iPadding--)
|
||
*pub++ = ubPattern++;
|
||
|
||
//
|
||
// Maintain all currently allocated memory blocks in a linked list
|
||
//
|
||
|
||
EngAcquireSemaphore(ghDebugMemSemaphore);
|
||
|
||
gMemListHdr.pNext->pPrev = pMemHdr;
|
||
pMemHdr->pNext = gMemListHdr.pNext;
|
||
pMemHdr->pPrev = &gMemListHdr;
|
||
gMemListHdr.pNext = pMemHdr;
|
||
|
||
if (dwSize > gdwMaxMemChunk)
|
||
gdwMaxMemChunk = dwSize;
|
||
|
||
if ((gdwCurMemUsage += dwSize) > gdwMaxMemUsage)
|
||
gdwMaxMemUsage = gdwCurMemUsage;
|
||
|
||
EngReleaseSemaphore(ghDebugMemSemaphore);
|
||
|
||
return (PBYTE) pMemHdr + sizeof(DEBUGMEMHDR);
|
||
}
|
||
else
|
||
{
|
||
WARNING(("Memory allocation failed: %d\n", GetLastError()));
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
MemDebugFree(
|
||
IN PVOID pv
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Free a memory block previously allocated using DebugAllocMem
|
||
|
||
Arguments:
|
||
|
||
pv - Points to the memory block to be freed
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
if (pv != NULL)
|
||
{
|
||
PDEBUGMEMHDR pMemHdr, pPrev, pNext;
|
||
|
||
//
|
||
// Map the caller pointer to a pointer to memory block header
|
||
//
|
||
|
||
pMemHdr = (PDEBUGMEMHDR) ((PBYTE) pv - sizeof(DEBUGMEMHDR));
|
||
|
||
//
|
||
// Make sure the specified pointer is in the list of currently allocated blocks
|
||
//
|
||
|
||
EngAcquireSemaphore(ghDebugMemSemaphore);
|
||
|
||
if ((pMemHdr->pNext == NULL) ||
|
||
(pMemHdr->pNext->pPrev != pMemHdr) ||
|
||
(pMemHdr->pPrev == NULL) ||
|
||
(pMemHdr->pPrev->pNext != pMemHdr))
|
||
{
|
||
RIP(("Freeing non-existent memory: 0x%x\n", pMemHdr));
|
||
}
|
||
else if (BCheckDebugMemory(pMemHdr))
|
||
{
|
||
pMemHdr->pNext->pPrev = pMemHdr->pPrev;
|
||
pMemHdr->pPrev->pNext = pMemHdr->pNext;
|
||
|
||
gdwCurMemUsage -= pMemHdr->dwSize;
|
||
EngFreeMem(pMemHdr);
|
||
}
|
||
|
||
EngReleaseSemaphore(ghDebugMemSemaphore);
|
||
}
|
||
}
|
||
|
||
#endif // MEMDEBUG
|
||
|