windows-nt/Source/XPSP1/NT/printscan/print/spooler/dbglib/dbgheap.cxx
2020-09-26 16:20:57 +08:00

651 lines
10 KiB
C++

/*++
Copyright (c) 1999-1999 Microsoft Corporation
All rights reserved.
Module Name:
dbgheap.cxx
Abstract:
Debug heap
Author:
Steve Kiraly (SteveKi) 6-Feb-1999
Revision History:
--*/
#include "precomp.hxx"
#pragma hdrstop
#include "dbgheap.hxx"
/*++
Title:
Constructor
Routine Description:
This routine only initialize class variables, you must
call Initialize before the class is in a usable state.
Arguments:
None.
Return Value:
None.
--*/
TDebugHeap::
TDebugHeap(
VOID
)
{
}
/*++
Title:
Destructor
Routine Description:
Class destructor, you must call Destroy to relase this class.
Arguments:
None
Return Value:
None
--*/
TDebugHeap::
~TDebugHeap(
VOID
)
{
}
/*++
Title:
bValid
Routine Description:
Use this method to determin if the heap is usable.
Arguments:
None.
Return Value:
TRUE class is valid i.e. usable, FALSE class not usable.
--*/
BOOL
TDebugHeap::
Valid(
VOID
) const
{
return m_bValid;
}
/*++
Title:
Initialize
Routine Description:
Initialize the heap, the bValid method should be called
after this method to determine if the heap is in a usable
state.
Arguments:
None
Return Value:
None
--*/
VOID
TDebugHeap::
Initialize(
VOID
)
{
//
// Initalize the call members.
//
InitalizeClassMembers();
//
// Create the debug heap.
//
m_hHeap = HeapCreate( HEAP_NO_SERIALIZE, m_uSize, 0 );
if (m_hHeap)
{
//
// Allocation the initial heap.
//
m_pHeap = reinterpret_cast<BlockHeader *>( HeapAlloc( m_hHeap, 0, m_uSize ) );
if (m_pHeap)
{
//
// Initialize any needed heap variables.
//
m_pHeap->pNext = NULL;
m_pHeap->uSize = m_uSize - sizeof( BlockHeader );
m_pHeap->eStatus = kFree;
m_bValid = TRUE;
}
else
{
//
// Error occurred cleanup.
//
Destroy();
}
}
}
/*++
Title:
Destroy
Routine Description:
Release any resources for the heap.
Arguments:
None
Return Value:
None
--*/
VOID
TDebugHeap::
Destroy(
VOID
)
{
//
// Destroy the heap if it was allocated.
//
if (m_hHeap)
{
//
// Destroy the heap data.
//
HeapDestroy( m_hHeap );
}
//
// Clear the heap variables.
//
InitalizeClassMembers();
}
/*++
Title:
Malloc
Routine Description:
Allocate a new block of memory.
Arguments:
Size - size in bytes of the requested block to allocate
Return Value:
Pointer to newly allocated block is success, NULL on failure
--*/
PVOID
TDebugHeap::
Malloc(
IN SIZE_T uSize
)
{
BlockHeader *pBlock = NULL;
//
// Ignore zero size requests.
//
if (uSize)
{
//
// Round up to some reasonable even value.
//
uSize = RoundUpToGranularity( uSize );
//
// Find first block that can hold the required size. Coalesce the
// blocks as the chain is traversed.
//
for (pBlock = reinterpret_cast<BlockHeader *>( m_pHeap ); pBlock; pBlock = pBlock->pNext)
{
if (pBlock->eStatus == kFree)
{
//
// Coalesce the blocks as we look for an appropriate block.
//
Coalesce( pBlock );
//
// Found a big enough block
//
if (pBlock->uSize >= uSize)
{
break;
}
}
}
//
// Check for failure
//
if (pBlock)
{
//
// Split the block, if possible
//
SplitBlock( pBlock, uSize );
//
// Mark the block as in use.
//
pBlock->eStatus = kInUse;
//
// Return the appropriate pointer
//
pBlock++;
}
else
{
ErrorText( _T("Error: Unabled to allocate memory, size %d.\n"), uSize );
}
}
return pBlock;
}
/*++
Title:
Free
Routine Description:
Delete the block of memory.
Arguments:
pData - pointer to data to free
Return Value:
None
--*/
VOID
TDebugHeap::
Free(
IN PVOID pData
)
{
//
// Ignore null pointer.
//
if (pData)
{
//
// Back up to start of the header
//
BlockHeader *pBlock = reinterpret_cast<BlockHeader *>( pData ) - 1;
//
// Free the block if not already free.
//
if (pBlock >= m_pHeap && pBlock <= m_pHeap + m_uSize && pBlock->eStatus == kInUse)
{
//
// Mark the block as freed.
//
pBlock->eStatus = kFree;
}
else
{
ErrorText( _T("Error: Invalid or free block passed to free 0x%lx.\n"), pBlock );
}
}
}
/********************************************************************
Private member functions.
********************************************************************/
/*++
Title:
SplitBlock
Routine Description:
Take the current block and attempt to split it into two.
Arguments:
pBlockHeader - pointer to memory block header
Size - size of requested block
Return Value:
None
--*/
VOID
TDebugHeap::
SplitBlock(
IN BlockHeader *pBlock,
IN SIZE_T uSize
)
{
if (pBlock->uSize >= (uSize + sizeof(BlockHeader)))
{
BlockHeader *pNext;
//
// Split the block into two the size requested and the remainder.
//
pNext = reinterpret_cast<BlockHeader *>( (reinterpret_cast<PBYTE>( pBlock ) + uSize + sizeof(BlockHeader)) );
pNext->pNext = pBlock->pNext;
pNext->uSize = pBlock->uSize - uSize - sizeof(BlockHeader);
//
// Can only split off FREE blocks
//
pNext->eStatus = kFree;
pBlock->pNext = pNext;
pBlock->uSize = uSize;
}
}
/*++
Title:
Coalesce
Routine Description:
Take and collapse any adjacent blocks.
Arguments:
pBlock - pointer to memory block header
Return Value:
None
--*/
VOID
TDebugHeap::
Coalesce(
IN BlockHeader *pBlock
)
{
//
// Check for null pointers
//
if (pBlock)
{
//
// The next block can be tacked onto the end of the current
//
for (BlockHeader *pNext = pBlock->pNext; pNext && pNext->eStatus == kFree; pNext = pBlock->pNext)
{
//
// Remove from the chain
//
pBlock->pNext = pNext->pNext;
//
// Absorb its storage
//
pBlock->uSize += pNext->uSize + sizeof( BlockHeader );
}
}
}
/*++
Title:
WalkDebugHeap
Routine Description:
Walk the heap list.
Arguments:
pEnumProc - pointer function to call at each block
pRefDate - caller defined reference data
Return Value:
Always returns TRUE success.
--*/
BOOL
TDebugHeap::
Walk(
IN pfHeapEnumProc pEnumProc,
IN PVOID pRefData
)
{
//
// If an enumerator was not passed then use the default.
//
if (!pEnumProc)
{
pEnumProc = DefaultHeapEnumProc;
}
BlockHeader *pBlock = reinterpret_cast<BlockHeader *>( m_pHeap );
//
// Coalesce before we walk to decrease free block spew.
//
Coalesce( pBlock );
//
// If we only have one free bock then the heap is empty
// then just skip the walk.
//
if( pBlock->pNext || pBlock->eStatus != kFree )
{
//
// Display the interal heap summary information
//
ErrorText( _T("Internal Heap Information:\n") );
ErrorText( _T("\tHandle : 0x%lx\n"), m_hHeap );
ErrorText( _T("\tStarting Block : 0x%lx\n"), m_pHeap );
ErrorText( _T("\tHeap Size : %d bytes\n"), m_uSize );
ErrorText( _T("\tGranularity : %d bytes\n"), m_uGranularity );
ErrorText( _T("Internal Heap Entries:\n") );
//
// Point to the first block in the heap.
//
pBlock = reinterpret_cast<BlockHeader *>( m_pHeap );
//
// Walk the chain, calling the enumerator at each free node.
//
for ( ; pBlock; pBlock = pBlock->pNext)
{
if( pBlock->eStatus == kFree )
{
if (!pEnumProc( pBlock, pRefData ))
{
break;
}
}
}
//
// Point to the first block in the heap.
//
pBlock = reinterpret_cast<BlockHeader *>( m_pHeap );
//
// Walk the chain, calling the enumerator at each busy node.
//
for ( ; pBlock; pBlock = pBlock->pNext)
{
if( pBlock->eStatus != kFree )
{
if (!pEnumProc( pBlock, pRefData ))
{
break;
}
}
}
}
return TRUE;
}
/*++
Title:
DefaultHeapEnumProc
Routine Description:
Display the heap node data.
Arguments:
pBlockHeader - pointer to memory block
pRefDate - caller defined reference data
Return Value:
Always returns TRUE success.
--*/
BOOL
TDebugHeap::
DefaultHeapEnumProc(
IN BlockHeader *pBlockHeader,
IN PVOID pRefData
)
{
LPCTSTR pszStatus = pBlockHeader->eStatus == kFree ? _T("free") : _T("busy");
ErrorText( _T("\t0x%lx : %s (%d)\n"), pBlockHeader, pszStatus, pBlockHeader->uSize );
return TRUE;
}
/*++
Title:
RoundUpToGranularity
Routine Description:
Rounds up the specified value to the next granular value.
Arguments:
uValue - Value to round up.
Return Value:
Returns rounded up value.
--*/
SIZE_T
TDebugHeap::
RoundUpToGranularity(
IN SIZE_T uValue
) const
{
return (uValue + m_uGranularity - 1) & ~(m_uGranularity - 1);
}
/*++
Title:
InitalizeClassMembers
Routine Description:
Initalizes the class members.
Arguments:
None.
Return Value:
Nothing.
--*/
VOID
TDebugHeap::
InitalizeClassMembers(
VOID
)
{
m_bValid = FALSE;
m_pHeap = NULL;
m_hHeap = NULL;
m_uSize = kDefaultHeapSize;
m_uGranularity = kDefaultHeapGranularity;
}