434 lines
9.1 KiB
C++
434 lines
9.1 KiB
C++
/*++
|
||
|
||
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 )
|
||
|