694 lines
17 KiB
C
694 lines
17 KiB
C
/*++
|
||
|
||
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;
|
||
}
|
||
|
||
|