|
|
//
// Copyright (c) 2000 Microsoft Corporation
//
// Module Name
//
// heapwalk.c
//
// Abstract
//
// Contains functions that create/modify/update the datastructure
// HEAP_ENTRY_LIST. HEAP_ENTRY_LIST maintains miminum amount of data
// for a HEAP Object.
//
// Author
//
// Narayana Batchu (nbatchu) [May 11, 2001]
//
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
#include "heapwalk.h"
//
// Initialize
//
// Initializes and allocates memory for the private member
// variables of the HEAP_ENTRY_LIST datastructure.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST whose member variables
// to be initialized.
//
// Return Value
//
VOID Initialize(LPHEAP_ENTRY_LIST pList) { if (!pList) return;
pList->HeapEntryCount = 0; pList->ListSorted = TRUE; pList->PresentCapacity = INITIAL_CAPACITY;
pList->pHeapEntries = (LPHEAP_ENTRY_INFO)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HEAP_ENTRY_INFO) * pList->PresentCapacity );
if (!pList->pHeapEntries) pList->PresentCapacity = 0; }
//
// DestroyList
//
// Cleans up the datastructure HEAP_ENTRY_LIST and frees up the
// memory associated with the pHeapEntries member.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST whose member variables
// to be cleaned up.
//
// Return Value
//
VOID DestroyList(LPHEAP_ENTRY_LIST pList) { if (!pList) return;
pList->HeapEntryCount = 0; pList->ListSorted = TRUE; pList->PresentCapacity = 0;
if (NULL != pList->pHeapEntries) { HeapFree(GetProcessHeap(), 0, pList->pHeapEntries); pList->pHeapEntries = NULL; } }
//
// GetMaxBlockSize
//
// This function searches through the HEAP_ENTRY_LIST to find out
// the maximum block size whose status is defined by 'State'.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST.
//
// State Specifies the status to search for the maximum size.
// State of any block can be 0 (FREE) and 1 (BUSY).
// There are other valid status values also,
// but we dont maintain those entries.
//
// Return Value
//
// DWORD Returns the maximum size of the block with status 'State'.
//
ULONG GetMaxBlockSize(LPHEAP_ENTRY_LIST pList, BLOCK_STATE State) { ULONG MaxBlockSize = 0; UINT Index;
if (!pList) goto ERROR1;
if (FALSE == pList->ListSorted) { SortHeapEntries(pList); }
for (Index=0; Index < pList->HeapEntryCount; Index++) { if (State == pList->pHeapEntries[Index].BlockState) { MaxBlockSize = pList->pHeapEntries[Index].BlockSize; break; } }
ERROR1: return MaxBlockSize; }
//
// GetMaxFreeBlockSize
//
// This function searches through the HEAP_ENTRY_LIST to find out
// the maximum free block size.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST.
//
// Return Value
//
// DWORD Returns the maximum size of the available block
//
ULONG GetMaxFreeBlockSize(LPHEAP_ENTRY_LIST pList) { return GetMaxBlockSize(pList, HEAP_BLOCK_FREE); }
//
// GetMaxAllocBlockSize
//
// This function searches through the HEAP_ENTRY_LIST to find out
// the maximum allocated block size.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST.
//
// Return Value
//
// DWORD Returns the maximum size of the allocated block.
//
ULONG GetMaxAllocBlockSize(LPHEAP_ENTRY_LIST pList) { return GetMaxBlockSize(pList, HEAP_BLOCK_BUSY); }
//
// GetTopNfreeEntries
//
// This function scans through the entry list to find the top
// n free entries in the list.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST.
//
// pArray Array of HEAP_ENTRY_INFO structures. This holds the
// top n free block sizes available for the process.
//
// Entries Specifies the top number of entries to be read from
// the list.
//
// Return Value
//
// BOOL Returns TRUE if successful.
//
BOOL GetTopNfreeEntries( LPHEAP_ENTRY_LIST pList, LPHEAP_ENTRY_INFO pArray, UINT EntriesToRead) { return GetTopNentries( HEAP_BLOCK_FREE, pList, pArray, EntriesToRead ); }
//
// GetTopNallocEntries
//
// This function scans through the entry list to find the top
// n allocated entries in the list.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST.
//
// pArray Array of HEAP_ENTRY_INFO structures. This holds the
// top n allocated block sizes available for the process.
//
// Entries Specifies the top number of entries to be read from
// the list.
//
// Return Value
//
// BOOL Returns TRUE if successful.
//
BOOL GetTopNallocEntries( LPHEAP_ENTRY_LIST pList, LPHEAP_ENTRY_INFO pArray, UINT EntriesToRead ) { return GetTopNentries( HEAP_BLOCK_BUSY, pList, pArray, EntriesToRead ); }
//
// GetTopNallocEntries
//
// This function scans through the entry list to find the top
// n entries in the list, whose staus matches 'State'.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST.
//
// pArray Array of HEAP_ENTRY_INFO structures. This holds the
// top n block sizes available for the process, whose status
// matches 'State'.
//
// Entries Specifies the top number of entries to be read from
// the list.
//
// Return Value
//
// BOOL Returns TRUE if successful.
//
BOOL GetTopNentries( BLOCK_STATE State, LPHEAP_ENTRY_LIST pList, LPHEAP_ENTRY_INFO pArray, UINT EntriesToRead ) { BOOL fSuccess = FALSE; UINT EntriesRead = 0; UINT Index; if (!pArray || !pList) goto ERROR2; if (FALSE == pList->ListSorted) { SortHeapEntries(pList); } for (Index=0; Index < pList->HeapEntryCount; Index++) { if (EntriesRead == EntriesToRead) break;
if (State == pList->pHeapEntries[Index].BlockState) { pArray[EntriesRead].BlockSize = pList->pHeapEntries[Index].BlockSize;
pArray[EntriesRead].BlockCount = pList->pHeapEntries[Index].BlockCount;
pArray[EntriesRead].BlockState = pList->pHeapEntries[Index].BlockState;
EntriesRead++; } }
if (EntriesRead == EntriesToRead) fSuccess = TRUE;
ERROR2: return fSuccess; }
//
// IncreaseCapacity
//
// Increases the array capacity by double. This function is called
// when tried to insert at the end of the array which is full.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST.
//
// Return Value
//
// BOOL Returns TRUE if successful in increasing the capacity.
//
BOOL IncreaseCapacity(LPHEAP_ENTRY_LIST pList) { BOOL fSuccess = FALSE; UINT NewCapacity = 0; PVOID pvTemp = NULL;
if (NULL == pList) {
goto Exit; }
if (0 == pList->PresentCapacity) {
NewCapacity = INITIAL_CAPACITY;
pvTemp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, NewCapacity * sizeof(HEAP_ENTRY_INFO)); } else {
NewCapacity = pList->PresentCapacity * 2;
pvTemp = HeapReAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, pList->pHeapEntries, NewCapacity * sizeof(HEAP_ENTRY_INFO)); }
if (NULL != pvTemp) {
pList->pHeapEntries = pvTemp; pList->PresentCapacity = NewCapacity; fSuccess = TRUE; }
Exit:
return fSuccess;
/*
BOOL fSuccess = FALSE; UINT NewCapacity;
if (!pList) goto ERROR3; NewCapacity = pList->PresentCapacity * 2;
if (0 == NewCapacity) NewCapacity = INITIAL_CAPACITY;
__try { pList->pHeapEntries = (LPHEAP_ENTRY_INFO)HeapReAlloc( GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, pList->pHeapEntries, NewCapacity * sizeof(HEAP_ENTRY_INFO) );
pList->PresentCapacity = NewCapacity; fSuccess = TRUE; } __except(GetExceptionCode() == STATUS_NO_MEMORY || GetExceptionCode() == STATUS_ACCESS_VIOLATION) { //
// Ignoring the exceptions raised by HeapReAlloc().
//
}
ERROR3: return fSuccess; */ }
//
// FindMatch
//
// Finds an entry in the HEAP_ENTRY_LIST that matches the size and
// status of pHeapEntry.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST.
//
// pHeapEntry Pointer to HEAP_ENTRY_INFO to be serached for in 'pList'.
//
// Return Value
//
// DWORD Index of the heap entry that matched the input heap entry
// 'pHeapEntry'
//
//
UINT FindMatch(LPHEAP_ENTRY_LIST pList, LPHEAP_ENTRY_INFO pHeapEntry) { UINT MatchedEntry = NO_MATCH; UINT Index; if (!pList || !pHeapEntry) goto ERROR4;
for (Index = 0; Index < pList->HeapEntryCount; Index++) { if (pList->pHeapEntries[Index].BlockSize == pHeapEntry->BlockSize && pList->pHeapEntries[Index].BlockState == pHeapEntry->BlockState) { MatchedEntry = Index; break; } }
ERROR4: return MatchedEntry; }
//
// InsertHeapEntry
//
// Inserts a new heap entry to the list. It updates the block count if
// a match is found else a new entry is made at the end of the HEAP_
// ENTRY_INFO array.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST.
//
// pHeapEntry Pointer to HEAP_ENTRY_INFO that is to be added to 'pList'.
//
// Return Value
//
// DWORD Returns the index at which it is added to the array. If
// for any reason, it is not added to the list, then it
// returns NO_MATCH value.
//
UINT InsertHeapEntry(LPHEAP_ENTRY_LIST pList, LPHEAP_ENTRY_INFO pHeapEntry) { UINT MatchedEntry = NO_MATCH; if (!pList || !pHeapEntry) goto ERROR5; MatchedEntry = FindMatch(pList, pHeapEntry); if (NO_MATCH != MatchedEntry) pList->pHeapEntries[MatchedEntry].BlockCount++; else { UINT Index = pList->HeapEntryCount; if (Index == pList->PresentCapacity && !IncreaseCapacity(pList)) goto ERROR5;
pList->pHeapEntries[Index].BlockSize = pHeapEntry->BlockSize; pList->pHeapEntries[Index].BlockState = pHeapEntry->BlockState; pList->pHeapEntries[Index].BlockCount = 1;
MatchedEntry = Index; pList->HeapEntryCount++; pList->ListSorted = FALSE; }
ERROR5: return MatchedEntry;
}
VOID SetHeapEntry( LPHEAP_ENTRY_INFO HeapEntryInfo, USHORT Status, ULONG Size ) { if (NULL == HeapEntryInfo) { return; }
HeapEntryInfo->BlockState = Status; HeapEntryInfo->BlockSize = Size; HeapEntryInfo->BlockCount = 1; }
//
// DeleteHeapEntry
//
// Deletes a new heap entry to the list. It decrements the block count
// if a match is found.
//
// Its possible that the block size is zero and still the heap entry
// exits. In such cases we dont decrement the block count (which would
// make it negative) and return a NO_MATCH.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST
//
// pHeapEntry Pointer to HEAP_ENTRY_INFO that is to be removed from 'pList'.
//
// Return Value
//
// DWORD Returns the index at which it is removed from the array. If for
// any reason (Count==0), it is not removed to the list, then it
// returns NO_MATCH value.
//
UINT DeleteHeapEntry(LPHEAP_ENTRY_LIST pList, LPHEAP_ENTRY_INFO pHeapEntry) { UINT MatchedEntry = NO_MATCH; if (!pList || !pHeapEntry) goto ERROR6;
MatchedEntry = FindMatch(pList, pHeapEntry); if (NO_MATCH != MatchedEntry && 0 != pList->pHeapEntries[MatchedEntry].BlockCount) { pList->pHeapEntries[MatchedEntry].BlockCount--; } else MatchedEntry = NO_MATCH;
ERROR6: return MatchedEntry; }
//
// SortByBlockSize
//
// Compare function required by qsort (uses quick sort to sort
// the elements in the array).
//
// More info about the arguments and the return values could be
// found in MSDN.
//
int __cdecl SortByBlockSize(const void * arg1, const void *arg2) { int iCompare; LPHEAP_ENTRY_INFO hpEntry1 = (LPHEAP_ENTRY_INFO)arg1; LPHEAP_ENTRY_INFO hpEntry2 = (LPHEAP_ENTRY_INFO)arg2;
iCompare = (hpEntry2->BlockSize - hpEntry1->BlockSize); return iCompare; }
//
// DisplayHeapFragStatistics
//
// Sorts and displays the fragmentation statistics. It displays
// two tables one for free blocks and another for allocated blocks.
//
// Arguments
//
// File Pointer to C FILE structure, to which the heap frag-
// mentation statistics have to be dumped.
//
// pList Pointer to HEAP_ENTRY_LIST, to be sorted and
// dumped to 'File'.
//
// Return Value
//
VOID DisplayHeapFragStatistics( FILE * File, PVOID HeapAddress, LPHEAP_ENTRY_LIST pList ) { if (!pList) return;
fprintf( File, "\n*- - - - - - - - - - Heap %p Fragmentation Statistics - - - - - - - - - -\n\n", HeapAddress ); SortHeapEntries(pList); PrintList(File, pList, HEAP_BLOCK_BUSY); PrintList(File, pList, HEAP_BLOCK_FREE); }
//
// SortHeapEntries
//
// Sorts the heap entries based on their sizes. The top most entry
// would be having the maximun block size.
//
// Also, removes those heap entries from the array whose block count
// has dropped to zero, making available more space.
//
// Arguments
//
// pList Pointer to HEAP_ENTRY_LIST, whose entries to be sorted by
// their sizes.
//
// Return Value
//
VOID SortHeapEntries(LPHEAP_ENTRY_LIST pList) { UINT Index; if (!pList) return;
if (FALSE == pList->ListSorted) { qsort( pList->pHeapEntries, pList->HeapEntryCount, sizeof(HEAP_ENTRY_INFO), &SortByBlockSize );
for (Index = pList->HeapEntryCount-1; Index > 0; Index--) { if (0 != pList->pHeapEntries[Index].BlockCount) break; } pList->HeapEntryCount = Index + 1; pList->ListSorted = TRUE; } }
//
// PrintList
//
// Utility function that prints out the heap entries to the stdout/
// file, whose status is equal to "State".
//
// Arguments
//
// File Pointer to C FILE structure, to which the heap frag-
// mentation statistics have to be dumped.
//
// pList Pointer to HEAP_ENTRY_LIST, to be sorted and
// dumped to 'File'.
//
// State State of the blocks to be displayed.
//
// Return Value
//
VOID PrintList(FILE * File, LPHEAP_ENTRY_LIST pList, BLOCK_STATE State) { UINT Index;
if (!pList) return;
if (HEAP_BLOCK_FREE == State) fprintf(File, "\nTable of Free Blocks\n\n"); else if (HEAP_BLOCK_BUSY == State) fprintf(File, "\nTable of Allocated Blocks\n\n");
fprintf(File, " SIZE | COUNT\n"); fprintf(File, " ------------------------\n"); for (Index = 0; Index < pList->HeapEntryCount; Index++) { if (State == pList->pHeapEntries[Index].BlockState) { fprintf( File, " 0x%08x | 0x%08x\n", pList->pHeapEntries[Index].BlockSize, pList->pHeapEntries[Index].BlockCount ); } } fprintf(File, "\n"); }
|