|
|
/*++
Copyright (c) 1995-1999 Microsoft Corporation
Module Name:
heapstat.cxx
Abstract:
This module contains an NTSD debugger extension for dumping various heap statistics.
Author:
Keith Moore (keithmo) 01-Nov-1997
Revision History:
--*/
#include "inetdbgp.h"
#define MAX_SIZE 65536
// Large busy block (size exceeds MAX_SIZE)
#define MAX_LBBSIZE 1024
typedef struct _ENUM_CONTEXT {
BOOLEAN ContinueEnum; ULONG FreeJumbo; ULONG BusyJumbo; ULONG FreeJumboBytes; ULONG BusyJumboBytes; ULONG BusyOverhead; ULONG FreeOverhead; ULONG FreeCounters[MAX_SIZE]; ULONG BusyCounters[MAX_SIZE]; ULONG LargeBusyBlock[MAX_LBBSIZE];
} ENUM_CONTEXT, *PENUM_CONTEXT;
#define BYTES_TO_K(cb) ( ( (cb) + 512 ) / 1024 )
/************************************************************
* Dump Heap Info ************************************************************/
BOOLEAN CALLBACK HspEnumHeapSegmentEntriesProc( IN PVOID Param, IN PHEAP_ENTRY LocalHeapEntry, IN PHEAP_ENTRY RemoteHeapEntry )
/*++
Routine Description:
Callback invoked for each heap entry within a heap segment.
Arguments:
Param - An uninterpreted parameter passed to the enumerator.
LocalHeapEntry - Pointer to a local copy of the HEAP_ENTRY structure.
RemoteHeapEntry - The remote address of the HEAP_ENTRY structure in the debugee.
Return Value:
BOOLEAN - TRUE if the enumeration should continue, FALSE if it should be terminated.
--*/
{
PENUM_CONTEXT context = (PENUM_CONTEXT)Param; ULONG entryLength; ULONG allocLength;
//
// Calculate the total length of this entry, including the heap
// header and any "slop" at the end of the block.
//
entryLength = (ULONG)LocalHeapEntry->Size << HEAP_GRANULARITY_SHIFT;
//
// From that, compute the number of bytes in use. This is the size
// of the allocation request as received from the application.
//
allocLength = entryLength - (ULONG)LocalHeapEntry->UnusedBytes;
//
// Adjust the appropriate accumulators.
//
if( LocalHeapEntry->Flags & HEAP_ENTRY_BUSY ) {
context->BusyOverhead += entryLength;
if( allocLength < MAX_SIZE ) { context->BusyCounters[allocLength] += 1; } else { context->BusyJumbo += 1; context->BusyJumboBytes += allocLength;
if (context->LargeBusyBlock[MAX_LBBSIZE-1] == 0) { BOOL fFound = FALSE; UINT i = 0; for (; context->LargeBusyBlock[i] != 0 && i < MAX_LBBSIZE; i++) { if( CheckControlC() ) { context->ContinueEnum = FALSE; return FALSE; }
if (allocLength == context->LargeBusyBlock[i]) { fFound = TRUE; break; } } if (!fFound && i < MAX_LBBSIZE-1) { context->LargeBusyBlock[i] = allocLength; } } }
} else {
context->FreeOverhead += entryLength;
if( allocLength < MAX_SIZE ) { context->FreeCounters[allocLength] += 1; } else { context->FreeJumbo += 1; context->FreeJumboBytes += allocLength; }
}
return TRUE;
} // HspEnumHeapSegmentEntriesProc
BOOLEAN CALLBACK HspEnumHeapSegmentsProc( IN PVOID Param, IN PHEAP_SEGMENT LocalHeapSegment, IN PHEAP_SEGMENT RemoteHeapSegment, IN ULONG HeapSegmentIndex )
/*++
Routine Description:
Callback invoked for each heap segment within a heap.
Arguments:
Param - An uninterpreted parameter passed to the enumerator.
LocalHeapSegment - Pointer to a local copy of the HEAP_SEGMENT structure.
RemoteHeapSegment - The remote address of the HEAP_SEGMENT structure in the debugee.
Return Value:
BOOLEAN - TRUE if the enumeration should continue, FALSE if it should be terminated.
--*/
{
//
// Enumerate the entries for the specified segment.
//
if( !EnumHeapSegmentEntries( LocalHeapSegment, RemoteHeapSegment, HspEnumHeapSegmentEntriesProc, Param ) ) { dprintf( "error retrieving heap segment entries\n" ); return FALSE; }
return TRUE;
} // HspEnumHeapSegmentsProc
BOOLEAN CALLBACK HspEnumHeapsProc( IN PVOID Param, IN PHEAP LocalHeap, IN PHEAP RemoteHeap, IN ULONG HeapIndex )
/*++
Routine Description:
Callback invoked for each heap within a process.
Arguments:
Param - An uninterpreted parameter passed to the enumerator.
LocalHeap - Pointer to a local copy of the HEAP structure.
RemoteHeap - The remote address of the HEAP structure in the debugee.
Return Value:
BOOLEAN - TRUE if the enumeration should continue, FALSE if it should be terminated.
--*/
{
//
// Enumerate the segments for the specified heap.
//
if( !EnumHeapSegments( LocalHeap, RemoteHeap, HspEnumHeapSegmentsProc, Param ) ) { dprintf( "error retrieving heap segments\n" ); return FALSE; }
return TRUE;
} // HspEnumHeapsProc
DECLARE_API( heapstat )
/*++
Routine Description:
This function is called as an NTSD extension to format and dump heap statistics.
Arguments:
hCurrentProcess - Supplies a handle to the current process (at the time the extension was called).
hCurrentThread - Supplies a handle to the current thread (at the time the extension was called).
CurrentPc - Supplies the current pc at the time the extension is called.
lpExtensionApis - Supplies the address of the functions callable by this extension.
lpArgumentString - Supplies the asciiz string that describes the ansi string to be dumped.
Return Value:
None.
--*/
{
PENUM_CONTEXT context; ULONG i; ULONG busyBytes; ULONG totalBusy; ULONG totalFree; ULONG totalBusyBytes; ULONG lowerNoiseBound;
INIT_API();
//
// Setup.
//
context = (PENUM_CONTEXT)malloc( sizeof(*context) );
if( context == NULL ) { dprintf( "out of memory\n" ); return; }
RtlZeroMemory( context, sizeof(*context) );
context->ContinueEnum = TRUE;
//
// Skip leading blanks.
//
while( *lpArgumentString == ' ' || *lpArgumentString == '\t' ) { lpArgumentString++; }
if( *lpArgumentString == '\0' ) { lowerNoiseBound = 1; } else { lowerNoiseBound = strtoul( lpArgumentString, NULL, 16 ); }
//
// Enumerate the heaps, which will enumerate the segments, which
// will enumerate the entries, which will accumulate the statistics.
//
if( !EnumProcessHeaps( HspEnumHeapsProc, (PVOID)context ) ) { dprintf( "error retrieving process heaps\n" ); free( context ); return; }
//
// Dump 'em.
//
dprintf( " Size : NumBusy : NumFree : BusyBytes\n" );
totalBusy = 0; totalFree = 0; totalBusyBytes = 0;
for( i = 0 ; i < MAX_SIZE ; i++ ) {
if (CheckControlC()) goto cleanup;
busyBytes = i * context->BusyCounters[i];
if( context->BusyCounters[i] >= lowerNoiseBound || context->FreeCounters[i] >= lowerNoiseBound ) {
dprintf( " %5lx : %8lx : %8lx : %8lx (%10ldK)\n", i, context->BusyCounters[i], context->FreeCounters[i], busyBytes, BYTES_TO_K( busyBytes ) );
}
totalBusy += context->BusyCounters[i]; totalBusyBytes += busyBytes; totalFree += context->FreeCounters[i];
}
if( context->BusyJumbo >= lowerNoiseBound || context->FreeJumbo >= lowerNoiseBound ) {
dprintf( ">%5lx : %8lx : %8lx : %8lx (%10ldK)\n", MAX_SIZE, context->BusyJumbo, context->FreeJumbo, context->BusyJumboBytes, BYTES_TO_K( context->BusyJumboBytes ) );
totalBusy += context->BusyJumbo; totalFree += context->FreeJumbo; totalBusyBytes += context->BusyJumboBytes;
}
if (context->LargeBusyBlock[0] != 0) { for (i = 0; i < MAX_LBBSIZE && context->LargeBusyBlock[i] != 0; i++) { dprintf("%8lx : \n", context->LargeBusyBlock[i]); } }
dprintf( " Total : %8lx : %8lx : %8lx (%10ldK)\n" "\n" " Total Heap Impact from Busy Blocks = %8lx (%10ldK)\n" " Total Heap Impact from Free Blocks = %8lx (%10ldK)\n", totalBusy, totalFree, totalBusyBytes, BYTES_TO_K( totalBusyBytes ), context->BusyOverhead, BYTES_TO_K( context->BusyOverhead ), context->FreeOverhead, BYTES_TO_K( context->FreeOverhead ) );
cleanup: free( context );
} // DECLARE_API( heapstat )
|