mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
693 lines
17 KiB
693 lines
17 KiB
/*++
|
|
|
|
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;
|
|
}
|
|
|
|
|