windows-nt/Source/XPSP1/NT/base/screg/winreg/perfdlls/process/perfheap.c

694 lines
17 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
perfheap.c
Abstract:
This file implements an Performance Object that presents
Heap performance object data
Created:
Adrian Marinescu 9-Mar-2000
Revision History:
--*/
//
// Include Files
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <assert.h>
#include <winperf.h>
#include <ntprfctr.h>
#include <perfutil.h>
#include "perfsprc.h"
#include "perfmsg.h"
#include "dataheap.h"
//
// Redefinition for heap data
//
#define MAX_HEAP_COUNT 200
#define HEAP_MAXIMUM_FREELISTS 128
#define HEAP_MAXIMUM_SEGMENTS 64
#define HEAP_OP_COUNT 2
#define HEAP_OP_ALLOC 0
#define HEAP_OP_FREE 1
typedef struct _HEAP_ENTRY {
USHORT Size;
USHORT PreviousSize;
UCHAR SegmentIndex;
UCHAR Flags;
UCHAR UnusedBytes;
UCHAR SmallTagIndex;
#if defined(_WIN64)
ULONGLONG Reserved1;
#endif
} HEAP_ENTRY, *PHEAP_ENTRY;
typedef struct _HEAP_SEGMENT {
HEAP_ENTRY Entry;
ULONG Signature;
ULONG Flags;
struct _HEAP *Heap;
SIZE_T LargestUnCommittedRange;
PVOID BaseAddress;
ULONG NumberOfPages;
PHEAP_ENTRY FirstEntry;
PHEAP_ENTRY LastValidEntry;
ULONG NumberOfUnCommittedPages;
ULONG NumberOfUnCommittedRanges;
PVOID UnCommittedRanges;
USHORT AllocatorBackTraceIndex;
USHORT Reserved;
PHEAP_ENTRY LastEntryInSegment;
} HEAP_SEGMENT, *PHEAP_SEGMENT;
typedef struct _HEAP {
HEAP_ENTRY Entry;
ULONG Signature;
ULONG Flags;
ULONG ForceFlags;
ULONG VirtualMemoryThreshold;
SIZE_T SegmentReserve;
SIZE_T SegmentCommit;
SIZE_T DeCommitFreeBlockThreshold;
SIZE_T DeCommitTotalFreeThreshold;
SIZE_T TotalFreeSize;
SIZE_T MaximumAllocationSize;
USHORT ProcessHeapsListIndex;
USHORT HeaderValidateLength;
PVOID HeaderValidateCopy;
USHORT NextAvailableTagIndex;
USHORT MaximumTagIndex;
PVOID TagEntries;
PVOID UCRSegments;
PVOID UnusedUnCommittedRanges;
ULONG AlignRound;
ULONG AlignMask;
LIST_ENTRY VirtualAllocdBlocks;
PHEAP_SEGMENT Segments[ HEAP_MAXIMUM_SEGMENTS ];
union {
ULONG FreeListsInUseUlong[ HEAP_MAXIMUM_FREELISTS / 32 ];
UCHAR FreeListsInUseBytes[ HEAP_MAXIMUM_FREELISTS / 8 ];
} u;
USHORT FreeListsInUseTerminate;
USHORT AllocatorBackTraceIndex;
ULONG NonDedicatedListLength;
PVOID LargeBlocksIndex;
PVOID PseudoTagEntries;
LIST_ENTRY FreeLists[ HEAP_MAXIMUM_FREELISTS ];
PVOID LockVariable;
PVOID CommitRoutine;
PVOID Lookaside;
ULONG LookasideLockCount;
} HEAP, *PHEAP;
typedef struct _HEAP_PERF_DATA {
UINT64 CountFrequence;
UINT64 OperationTime[HEAP_OP_COUNT];
//
// The data bellow are only for sampling
//
ULONG Sequence;
UINT64 TempTime[HEAP_OP_COUNT];
ULONG TempCount[HEAP_OP_COUNT];
} HEAP_PERF_DATA, *PHEAP_PERF_DATA;
//
// The heap index structure
//
typedef struct _HEAP_INDEX {
ULONG ArraySize;
ULONG VirtualMemorySize;
HEAP_PERF_DATA PerfData;
union {
PULONG FreeListsInUseUlong;
PUCHAR FreeListsInUseBytes;
} u;
PVOID *FreeListHints;
} HEAP_INDEX, *PHEAP_INDEX;
typedef struct _HEAP_LOOKASIDE {
SLIST_HEADER ListHead;
USHORT Depth;
USHORT MaximumDepth;
ULONG TotalAllocates;
ULONG AllocateMisses;
ULONG TotalFrees;
ULONG FreeMisses;
ULONG LastTotalAllocates;
ULONG LastAllocateMisses;
ULONG Counters[2];
} HEAP_LOOKASIDE, *PHEAP_LOOKASIDE;
//
// Local variables
//
static HEAP_LOOKASIDE LookasideBuffer[HEAP_MAXIMUM_FREELISTS];
static DWORD PageSize = 0;
//
// Implementation for heap query function
//
BOOLEAN
ReadHeapData (
IN HANDLE hProcess,
IN ULONG HeapNumber,
IN PHEAP Heap,
OUT PHEAP_COUNTER_DATA pHCD
)
/*++
Routine Description:
The routine loads into the given heap couter structure the
data from the heap structure
Arguments:
hProcess - The process containing the heap
Heap - the heap address
pPerfInstanceDefinition - Performance instance definition data
pHCD - Counter data
Returns:
Returns TRUE if query succeeds.
--*/
{
HEAP_SEGMENT CrtSegment;
HEAP CrtHeap;
ULONG SegmentIndex;
RTL_CRITICAL_SECTION CriticalSection;
HEAP_INDEX HeapIndex;
ULONG i;
//
// Read the heap structure from the process address space
//
if (!ReadProcessMemory(hProcess, Heap, &CrtHeap, sizeof(CrtHeap), NULL)) {
return FALSE;
}
//
// We won't display data for heaps w/o index.
//
if ((CrtHeap.LargeBlocksIndex == NULL)
&&
(HeapNumber != 0)) {
//
// We are not handling small heaps
//
return FALSE;
}
pHCD->FreeSpace = CrtHeap.TotalFreeSize;
pHCD->FreeListLength = CrtHeap.NonDedicatedListLength;
pHCD->CommittedBytes = 0;
pHCD->ReservedBytes = 0;
pHCD->VirtualBytes = 0;
pHCD->UncommitedRangesLength = 0;
//
// Walking the heap segments and get the virtual address counters
//
for (SegmentIndex = 0; SegmentIndex < HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
if ((CrtHeap.Segments[SegmentIndex] == NULL) ||
!ReadProcessMemory(hProcess, CrtHeap.Segments[SegmentIndex], &CrtSegment, sizeof(CrtSegment), NULL)) {
break;
}
pHCD->ReservedBytes += CrtSegment.NumberOfPages * PageSize;
pHCD->CommittedBytes += (CrtSegment.NumberOfPages - CrtSegment.NumberOfUnCommittedPages) * PageSize;
pHCD->VirtualBytes += CrtSegment.NumberOfPages * PageSize - CrtSegment.LargestUnCommittedRange;
pHCD->UncommitedRangesLength += CrtSegment.NumberOfUnCommittedRanges;
}
if (pHCD->CommittedBytes == 0) {
pHCD->CommittedBytes = 1;
}
if (pHCD->VirtualBytes == 0) {
pHCD->VirtualBytes = 1;
}
//
// Compute the heap fragmentation counters
//
pHCD->BlockFragmentation = (ULONG)(pHCD->FreeSpace * 100 / pHCD->CommittedBytes);
pHCD->VAFragmentation =(ULONG)(((pHCD->VirtualBytes - pHCD->CommittedBytes)*100)/pHCD->VirtualBytes);
//
// Read the lock contention
//
pHCD->LockContention = 0;
if (ReadProcessMemory(hProcess, CrtHeap.LockVariable, &CriticalSection, sizeof(CriticalSection), NULL)) {
RTL_CRITICAL_SECTION_DEBUG DebugInfo;
if (ReadProcessMemory(hProcess, CriticalSection.DebugInfo, &DebugInfo, sizeof(DebugInfo), NULL)) {
pHCD->LockContention = DebugInfo.ContentionCount;
}
}
//
// Walk the lookaside to count the blocks
//
pHCD->LookasideAllocs = 0;
pHCD->LookasideFrees = 0;
pHCD->LookasideBlocks = 0;
pHCD->LargestLookasideDepth = 0;
pHCD->SmallAllocs = 0;
pHCD->SmallFrees = 0;
pHCD->MedAllocs = 0;
pHCD->MedFrees = 0;
pHCD->LargeAllocs = 0;
pHCD->LargeFrees = 0;
if (ReadProcessMemory(hProcess, CrtHeap.Lookaside, &LookasideBuffer, sizeof(LookasideBuffer), NULL)) {
for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i++) {
pHCD->SmallAllocs += LookasideBuffer[i].TotalAllocates;
pHCD->SmallFrees += LookasideBuffer[i].TotalFrees;
pHCD->LookasideAllocs += LookasideBuffer[i].TotalAllocates - LookasideBuffer[i].AllocateMisses;
pHCD->LookasideFrees += LookasideBuffer[i].TotalFrees - LookasideBuffer[i].FreeMisses;
if (LookasideBuffer[i].Depth > pHCD->LargestLookasideDepth) {
pHCD->LargestLookasideDepth = LookasideBuffer[i].Depth;
}
if (i == 0) {
} else if (i < 8) {
pHCD->MedAllocs += LookasideBuffer[i].Counters[0];
pHCD->MedFrees += LookasideBuffer[i].Counters[1];
} else {
pHCD->LargeAllocs += LookasideBuffer[i].Counters[0];
pHCD->LargeFrees += LookasideBuffer[i].Counters[1];
}
}
}
pHCD->LookasideBlocks = pHCD->LookasideFrees - pHCD->LookasideAllocs;
//
// Calculate the totals
//
pHCD->TotalAllocs = pHCD->SmallAllocs + pHCD->MedAllocs + pHCD->LargeAllocs;
pHCD->TotalFrees = pHCD->SmallFrees + pHCD->MedFrees + pHCD->LargeFrees;
//
// Set the difference between allocs and frees
//
pHCD->DiffOperations = pHCD->TotalAllocs - pHCD->TotalFrees;
pHCD->AllocTime = 0;
pHCD->AllocTime = 0;
//
// Determine the alloc/free rates
//
if (ReadProcessMemory(hProcess, CrtHeap.LargeBlocksIndex, &HeapIndex, sizeof(HeapIndex), NULL)) {
if (HeapIndex.PerfData.OperationTime[0]) {
pHCD->AllocTime = HeapIndex.PerfData.CountFrequence / HeapIndex.PerfData.OperationTime[0];
}
if (HeapIndex.PerfData.OperationTime[1]) {
pHCD->FreeTime = HeapIndex.PerfData.CountFrequence / HeapIndex.PerfData.OperationTime[1];
}
}
return TRUE;
}
DWORD APIENTRY
CollectHeapObjectData (
IN OUT LPVOID *lppData,
IN OUT LPDWORD lpcbTotalBytes,
IN OUT LPDWORD lpNumObjectTypes
)
/*++
Routine Description:
This routine will return the data for the heap object
Arguments:
IN OUT LPVOID *lppData
IN: pointer to the address of the buffer to receive the completed
PerfDataBlock and subordinate structures. This routine will
append its data to the buffer starting at the point referenced
by *lppData.
OUT: points to the first byte after the data structure added by this
routine. This routine updated the value at lppdata after appending
its data.
IN OUT LPDWORD lpcbTotalBytes
IN: the address of the DWORD that tells the size in bytes of the
buffer referenced by the lppData argument
OUT: the number of bytes added by this routine is writted to the
DWORD pointed to by this argument
IN OUT LPDWORD NumObjectTypes
IN: the address of the DWORD to receive the number of objects added
by this routine
OUT: the number of objects added by this routine is writted to the
DWORD pointed to by this argument
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
LONG lReturn = ERROR_SUCCESS;
DWORD TotalLen; // Length of the total return block
PHEAP_DATA_DEFINITION pHeapDataDefinition;
PPERF_INSTANCE_DEFINITION pPerfInstanceDefinition;
PHEAP_COUNTER_DATA pHCD;
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
ULONG ProcessNumber;
ULONG NumHeapInstances;
ULONG HeapNumber;
ULONG ProcessBufferOffset;
UNICODE_STRING HeapName;
WCHAR HeapNameBuffer[MAX_THREAD_NAME_LENGTH+1];
BOOL bMoreProcesses = FALSE;
HeapName.Length =
HeapName.MaximumLength = (MAX_THREAD_NAME_LENGTH + 1) * sizeof(WCHAR);
HeapName.Buffer = HeapNameBuffer;
pHeapDataDefinition = (HEAP_DATA_DEFINITION *) *lppData;
//
// Get the page size from the system
//
if (!PageSize) {
SYSTEM_INFO SystemInfo;
GetSystemInfo(&SystemInfo);
PageSize = SystemInfo.dwPageSize;
}
//
// Check for sufficient space for Thread object type definition
//
TotalLen = sizeof(HEAP_DATA_DEFINITION) +
sizeof(PERF_INSTANCE_DEFINITION) +
sizeof(HEAP_COUNTER_DATA);
if ( *lpcbTotalBytes < TotalLen ) {
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_MORE_DATA;
}
//
// Define the heap data block
//
memcpy(pHeapDataDefinition,
&HeapDataDefinition,
sizeof(HEAP_DATA_DEFINITION));
pHeapDataDefinition->HeapObjectType.PerfTime = PerfTime;
ProcessBufferOffset = 0;
//
// Now collect data for each process
//
ProcessNumber = 0;
NumHeapInstances = 0;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pProcessBuffer;
pPerfInstanceDefinition =
(PPERF_INSTANCE_DEFINITION)&pHeapDataDefinition[1];
TotalLen = sizeof(HEAP_DATA_DEFINITION);
if (ProcessInfo) {
if (ProcessInfo->NextEntryOffset != 0) {
bMoreProcesses = TRUE;
}
}
while ( bMoreProcesses && (ProcessInfo != NULL)) {
HANDLE hProcess;
NTSTATUS Status;
PROCESS_BASIC_INFORMATION BasicInfo;
//
// Get a handle to the process.
//
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, (DWORD)(ULONGLONG)ProcessInfo->UniqueProcessId );
if ( hProcess ) {
//
// Get the process PEB
//
Status = NtQueryInformationProcess(
hProcess,
ProcessBasicInformation,
&BasicInfo,
sizeof(BasicInfo),
NULL
);
if ( NT_SUCCESS(Status) ) {
ULONG NumberOfHeaps;
PVOID ProcessHeaps[MAX_HEAP_COUNT];
PVOID HeapBuffer;
PPEB Peb;
Peb = BasicInfo.PebBaseAddress;
//
// Read the heaps from the process PEB
//
if (!ReadProcessMemory(hProcess, &Peb->NumberOfHeaps, &NumberOfHeaps, sizeof(NumberOfHeaps), NULL)) {
goto READERROR;
}
//
// Limit the number of heaps to be read
//
if (NumberOfHeaps > MAX_HEAP_COUNT) {
NumberOfHeaps = MAX_HEAP_COUNT;
}
if (!ReadProcessMemory(hProcess, &Peb->ProcessHeaps, &HeapBuffer, sizeof(HeapBuffer), NULL)) {
goto READERROR;
}
if (!ReadProcessMemory(hProcess, HeapBuffer, &ProcessHeaps, NumberOfHeaps * sizeof(PVOID), NULL)) {
goto READERROR;
}
//
// Loop through the heaps and retireve the data
//
for (HeapNumber = 0; HeapNumber < NumberOfHeaps; HeapNumber++) {
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
(MAX_THREAD_NAME_LENGTH+1+sizeof(DWORD))*
sizeof(WCHAR) +
sizeof (HEAP_COUNTER_DATA);
if ( *lpcbTotalBytes < TotalLen ) {
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
CloseHandle( hProcess );
return ERROR_MORE_DATA;
}
//
// Build the monitor instance based on the process name and
// heap address
//
RtlIntegerToUnicodeString( (ULONG)(ULONGLONG)ProcessHeaps[HeapNumber],
16,
&HeapName);
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pHCD,
PROCESS_OBJECT_TITLE_INDEX,
ProcessNumber,
(DWORD)-1,
HeapName.Buffer);
pHCD->CounterBlock.ByteLength = sizeof(HEAP_COUNTER_DATA);
//
// Get the data from the heap
//
if (ReadHeapData ( hProcess,
HeapNumber,
(PHEAP)ProcessHeaps[HeapNumber],
pHCD) ) {
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pHCD[1];
NumHeapInstances++;
}
}
}
READERROR:
CloseHandle( hProcess );
}
ProcessNumber++;
//
// Move to the next process, if any
//
if (ProcessInfo->NextEntryOffset == 0) {
bMoreProcesses = FALSE;
continue;
}
ProcessBufferOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
&pProcessBuffer[ProcessBufferOffset];
}
// Note number of heap instances
pHeapDataDefinition->HeapObjectType.NumInstances =
NumHeapInstances;
//
// Now we know how large an area we used for the
// heap definition, so we can update the offset
// to the next object definition
//
*lpcbTotalBytes =
pHeapDataDefinition->HeapObjectType.TotalByteLength =
(DWORD)((PCHAR) pPerfInstanceDefinition -
(PCHAR) pHeapDataDefinition);
#if DBG
if (*lpcbTotalBytes > TotalLen ) {
DbgPrint ("\nPERFPROC: Heap Perf Ctr. Instance Size Underestimated:");
DbgPrint ("\nPERFPROC: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
}
#endif
*lppData = (LPVOID)pPerfInstanceDefinition;
*lpNumObjectTypes = 1;
return lReturn;
}