385 lines
12 KiB
C
385 lines
12 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
perfproc.c
|
||
|
||
Abstract:
|
||
|
||
This file implements an Performance Object that presents
|
||
Image details performance object data
|
||
|
||
Created:
|
||
|
||
Bob Watson 22-Oct-1996
|
||
|
||
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 "dataproc.h"
|
||
|
||
static BOOL bOldestProcessTime = FALSE;
|
||
static LARGE_INTEGER OldestProcessTime = {0,0};
|
||
|
||
|
||
DWORD APIENTRY
|
||
CollectProcessObjectData (
|
||
IN OUT LPVOID *lppData,
|
||
IN OUT LPDWORD lpcbTotalBytes,
|
||
IN OUT LPDWORD lpNumObjectTypes
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will return the data for the processor 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
|
||
|
||
--*/
|
||
{
|
||
DWORD TotalLen; // Length of the total return block
|
||
|
||
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
|
||
|
||
PPERF_INSTANCE_DEFINITION pPerfInstanceDefinition;
|
||
PPROCESS_DATA_DEFINITION pProcessDataDefinition;
|
||
PPROCESS_COUNTER_DATA pPCD;
|
||
PROCESS_COUNTER_DATA pcdTotal;
|
||
|
||
ULONG NumProcessInstances;
|
||
BOOLEAN NullProcess;
|
||
|
||
PUNICODE_STRING pProcessName;
|
||
ULONG ProcessBufferOffset;
|
||
|
||
pProcessDataDefinition = (PROCESS_DATA_DEFINITION *) *lppData;
|
||
|
||
//
|
||
// Check for sufficient space for Process object type definition
|
||
//
|
||
|
||
TotalLen = sizeof(PROCESS_DATA_DEFINITION) +
|
||
sizeof (PERF_INSTANCE_DEFINITION) +
|
||
MAX_VALUE_NAME_LENGTH +
|
||
sizeof(PROCESS_COUNTER_DATA);
|
||
|
||
if ( *lpcbTotalBytes < TotalLen ) {
|
||
*lpcbTotalBytes = 0;
|
||
*lpNumObjectTypes = 0;
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
//
|
||
// Define Process data block
|
||
//
|
||
|
||
memcpy(pProcessDataDefinition,
|
||
&ProcessDataDefinition,
|
||
sizeof(PROCESS_DATA_DEFINITION));
|
||
|
||
pProcessDataDefinition->ProcessObjectType.PerfTime = PerfTime;
|
||
|
||
ProcessBufferOffset = 0;
|
||
|
||
// Now collect data for each process
|
||
|
||
NumProcessInstances = 0;
|
||
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) pProcessBuffer;
|
||
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
|
||
&pProcessDataDefinition[1];
|
||
|
||
// adjust TotalLen to be the size of the buffer already in use
|
||
TotalLen = sizeof (PROCESS_DATA_DEFINITION);
|
||
|
||
// zero the total instance buffer
|
||
memset (&pcdTotal, 0, sizeof (pcdTotal));
|
||
|
||
while ( ProcessInfo != NULL ) {
|
||
|
||
// see if this instance will fit
|
||
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
|
||
((MAX_PROCESS_NAME_LENGTH+1+sizeof(DWORD)) * sizeof(WCHAR)) +
|
||
sizeof (PROCESS_COUNTER_DATA);
|
||
|
||
if ( *lpcbTotalBytes < TotalLen ) {
|
||
*lpcbTotalBytes = 0;
|
||
*lpNumObjectTypes = 0;
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
// check for Live processes
|
||
// (i.e. name or threads)
|
||
|
||
pProcessName = NULL;
|
||
|
||
if ((ProcessInfo->ImageName.Buffer != NULL) ||
|
||
(ProcessInfo->NumberOfThreads > 0)){
|
||
// thread is not Dead
|
||
// get process name
|
||
if (lProcessNameCollectionMethod == PNCM_MODULE_FILE) {
|
||
pProcessName = GetProcessSlowName (ProcessInfo);
|
||
} else {
|
||
pProcessName = GetProcessShortName (ProcessInfo);
|
||
}
|
||
NullProcess = FALSE;
|
||
} else {
|
||
// thread is dead
|
||
NullProcess = TRUE;
|
||
}
|
||
|
||
if ( !NullProcess ) {
|
||
|
||
// get the old process creation time the first time we are in
|
||
// this routine
|
||
if (!bOldestProcessTime) {
|
||
if (OldestProcessTime.QuadPart <= 0) {
|
||
OldestProcessTime = ProcessInfo->CreateTime;
|
||
} else if (ProcessInfo->CreateTime.QuadPart > 0) {
|
||
// both time values are not zero, see which one is smaller
|
||
if (OldestProcessTime.QuadPart >
|
||
ProcessInfo->CreateTime.QuadPart) {
|
||
OldestProcessTime = ProcessInfo->CreateTime;
|
||
}
|
||
}
|
||
}
|
||
|
||
// get Pool usage for this process
|
||
|
||
NumProcessInstances++;
|
||
|
||
MonBuildInstanceDefinition(pPerfInstanceDefinition,
|
||
(PVOID *) &pPCD,
|
||
0,
|
||
0,
|
||
(DWORD)-1,
|
||
(pProcessName ? pProcessName->Buffer : NULL)
|
||
);
|
||
|
||
// test structure for Quadword Alignment
|
||
assert (((DWORD)(pPCD) & 0x00000007) == 0);
|
||
|
||
//
|
||
// Format and collect Process data
|
||
//
|
||
|
||
pPCD->CounterBlock.ByteLength = sizeof (PROCESS_COUNTER_DATA);
|
||
//
|
||
// Convert User time from 100 nsec units to counter frequency.
|
||
//
|
||
pcdTotal.ProcessorTime +=
|
||
pPCD->ProcessorTime = ProcessInfo->KernelTime.QuadPart +
|
||
ProcessInfo->UserTime.QuadPart;
|
||
pcdTotal.UserTime +=
|
||
pPCD->UserTime = ProcessInfo->UserTime.QuadPart;
|
||
pcdTotal.KernelTime +=
|
||
pPCD->KernelTime = ProcessInfo->KernelTime.QuadPart;
|
||
|
||
pcdTotal.PeakVirtualSize +=
|
||
pPCD->PeakVirtualSize = ProcessInfo->PeakVirtualSize;
|
||
pcdTotal.VirtualSize +=
|
||
pPCD->VirtualSize = ProcessInfo->VirtualSize;
|
||
|
||
pcdTotal.PageFaults +=
|
||
pPCD->PageFaults = ProcessInfo->PageFaultCount;
|
||
pcdTotal.PeakWorkingSet +=
|
||
pPCD->PeakWorkingSet = ProcessInfo->PeakWorkingSetSize;
|
||
pcdTotal.TotalWorkingSet +=
|
||
pPCD->TotalWorkingSet = ProcessInfo->WorkingSetSize;
|
||
|
||
#ifdef _DATAPROC_PRIVATE_WS_
|
||
pcdTotal.PrivateWorkingSet +=
|
||
pPCD->PrivateWorkingSet = ProcessInfo->PrivateWorkingSetSize;
|
||
pcdTotal.SharedWorkingSet +=
|
||
pPCD->SharedWorkingSet =
|
||
ProcessInfo->WorkingSetSize -
|
||
ProcessInfo->PrivateWorkingSetSize;
|
||
#endif //_DATAPROC_PRIVATE_WS_
|
||
|
||
pcdTotal.PeakPageFile +=
|
||
pPCD->PeakPageFile = ProcessInfo->PeakPagefileUsage;
|
||
pcdTotal.PageFile +=
|
||
pPCD->PageFile = ProcessInfo->PagefileUsage;
|
||
|
||
pcdTotal.PrivatePages +=
|
||
pPCD->PrivatePages = ProcessInfo->PrivatePageCount;
|
||
|
||
pcdTotal.ThreadCount +=
|
||
pPCD->ThreadCount = ProcessInfo->NumberOfThreads;
|
||
|
||
// base priority is not totaled
|
||
pPCD->BasePriority = ProcessInfo->BasePriority;
|
||
|
||
// elpased time is not totaled
|
||
if (bOldestProcessTime &&
|
||
(ProcessInfo->CreateTime.QuadPart <= 0)) {
|
||
pPCD->ElapsedTime = OldestProcessTime.QuadPart;
|
||
} else {
|
||
pPCD->ElapsedTime = ProcessInfo->CreateTime.QuadPart;
|
||
}
|
||
|
||
pPCD->ProcessId = HandleToUlong(ProcessInfo->UniqueProcessId);
|
||
pPCD->CreatorProcessId = HandleToUlong(ProcessInfo->InheritedFromUniqueProcessId);
|
||
|
||
pcdTotal.PagedPool +=
|
||
pPCD->PagedPool = (DWORD)ProcessInfo->QuotaPagedPoolUsage;
|
||
pcdTotal.NonPagedPool +=
|
||
pPCD->NonPagedPool = (DWORD)ProcessInfo->QuotaNonPagedPoolUsage;
|
||
pcdTotal.HandleCount +=
|
||
pPCD->HandleCount = (DWORD)ProcessInfo->HandleCount;
|
||
|
||
|
||
// update I/O counters
|
||
pcdTotal.ReadOperationCount +=
|
||
pPCD->ReadOperationCount = ProcessInfo->ReadOperationCount.QuadPart;
|
||
pcdTotal.DataOperationCount +=
|
||
pPCD->DataOperationCount = ProcessInfo->ReadOperationCount.QuadPart;
|
||
pcdTotal.WriteOperationCount +=
|
||
pPCD->WriteOperationCount = ProcessInfo->WriteOperationCount.QuadPart;
|
||
pcdTotal.DataOperationCount += ProcessInfo->WriteOperationCount.QuadPart;
|
||
pPCD->DataOperationCount += ProcessInfo->WriteOperationCount.QuadPart;
|
||
pcdTotal.OtherOperationCount +=
|
||
pPCD->OtherOperationCount = ProcessInfo->OtherOperationCount.QuadPart;
|
||
|
||
pcdTotal.ReadTransferCount +=
|
||
pPCD->ReadTransferCount = ProcessInfo->ReadTransferCount.QuadPart;
|
||
pcdTotal.DataTransferCount +=
|
||
pPCD->DataTransferCount = ProcessInfo->ReadTransferCount.QuadPart;
|
||
pcdTotal.WriteTransferCount +=
|
||
pPCD->WriteTransferCount = ProcessInfo->WriteTransferCount.QuadPart;
|
||
pcdTotal.DataTransferCount += ProcessInfo->WriteTransferCount.QuadPart;
|
||
pPCD->DataTransferCount += ProcessInfo->WriteTransferCount.QuadPart;
|
||
pcdTotal.OtherTransferCount +=
|
||
pPCD->OtherTransferCount = ProcessInfo->OtherTransferCount.QuadPart;
|
||
|
||
// set perfdata pointer to next byte
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pPCD[1];
|
||
}
|
||
// exit if this was the last process in list
|
||
if (ProcessInfo->NextEntryOffset == 0) {
|
||
break;
|
||
}
|
||
|
||
// point to next buffer in list
|
||
ProcessBufferOffset += ProcessInfo->NextEntryOffset;
|
||
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
|
||
&pProcessBuffer[ProcessBufferOffset];
|
||
|
||
}
|
||
|
||
if (NumProcessInstances > 0) {
|
||
|
||
// see if the total instance will fit
|
||
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
|
||
(MAX_PROCESS_NAME_LENGTH+1+sizeof(DWORD))*
|
||
sizeof(WCHAR) +
|
||
sizeof (PROCESS_COUNTER_DATA);
|
||
|
||
if ( *lpcbTotalBytes < TotalLen ) {
|
||
*lpcbTotalBytes = 0;
|
||
*lpNumObjectTypes = 0;
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
// it looks like it will fit so create "total" instance
|
||
|
||
NumProcessInstances++;
|
||
|
||
// set the Total Elapsed Time to be the current time so that it will
|
||
// show up as 0 when displayed.
|
||
pcdTotal.ElapsedTime = pProcessDataDefinition->ProcessObjectType.PerfTime.QuadPart;
|
||
|
||
MonBuildInstanceDefinition(pPerfInstanceDefinition,
|
||
(PVOID *) &pPCD,
|
||
0,
|
||
0,
|
||
(DWORD)-1,
|
||
wszTotal);
|
||
|
||
// test structure for Quadword Alignment
|
||
assert (((DWORD)(pPCD) & 0x00000007) == 0);
|
||
|
||
//
|
||
// Format and collect Process data
|
||
//
|
||
memcpy (pPCD, &pcdTotal, sizeof (pcdTotal));
|
||
pPCD->CounterBlock.ByteLength = sizeof (PROCESS_COUNTER_DATA);
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pPCD[1];
|
||
|
||
}
|
||
|
||
// flag so we don't have to get the oldest Process Creation time again.
|
||
bOldestProcessTime = TRUE;
|
||
|
||
// Note number of process instances
|
||
|
||
pProcessDataDefinition->ProcessObjectType.NumInstances =
|
||
NumProcessInstances;
|
||
|
||
//
|
||
// Now we know how large an area we used for the
|
||
// Process definition, so we can update the offset
|
||
// to the next object definition
|
||
//
|
||
|
||
*lpcbTotalBytes =
|
||
pProcessDataDefinition->ProcessObjectType.TotalByteLength =
|
||
(DWORD)((PCHAR) pPerfInstanceDefinition -
|
||
(PCHAR) pProcessDataDefinition);
|
||
|
||
#if DBG
|
||
if (*lpcbTotalBytes > TotalLen ) {
|
||
DbgPrint ("\nPERFPROC: Process Perf Ctr. Instance Size Underestimated:");
|
||
DbgPrint ("\nPERFPROC: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
|
||
}
|
||
#endif
|
||
|
||
*lppData = (LPVOID) pPerfInstanceDefinition;
|
||
|
||
*lpNumObjectTypes = 1;
|
||
|
||
return ERROR_SUCCESS;
|
||
}
|