Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1257 lines
50 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
perfjob.c
Abstract:
This file implements an Performance Job Object that presents
information on the Job Object
Created:
Bob Watson 8-Oct-1997
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 "procmsg.h"
#include "datajob.h"
#define MAX_STR_CHAR 1024
#define MAX_STR_SIZE ((DWORD)((MAX_STR_CHAR - 1)* sizeof(WCHAR)))
#define MAX_NAME_LENGTH MAX_PATH
#define BUFFERSIZE 1024
DWORD dwBufferSize = BUFFERSIZE;
const WCHAR szJob[] = L"Job";
const WCHAR szObjDirName[] = L"\\BaseNamedObjects";
#define MAX_EVENT_STRINGS 4
WORD wEvtStringCount;
LPWSTR szEvtStringArray[MAX_EVENT_STRINGS];
UNICODE_STRING DirectoryName = {(sizeof(szObjDirName) - sizeof(WCHAR)), // name len - NULL
sizeof(szObjDirName), // size of buffer
(PWCHAR)szObjDirName}; // address of buffer
BOOL bOpenJobErrorLogged = FALSE;
PSYSTEM_PROCESS_INFORMATION APIENTRY
GetProcessPointerFromProcessId (
IN ULONG_PTR dwPid
)
{
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
ULONG ProcessBufferOffset = 0;
BOOLEAN NullProcess;
DWORD dwIndex = 0;
BOOL bNotFound = TRUE;
BOOL bMoreProcesses = FALSE;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) pProcessBuffer;
if (ProcessInfo) {
if (ProcessInfo->NextEntryOffset != 0) {
bMoreProcesses = TRUE;
}
}
while ( bMoreProcesses && bNotFound &&
(ProcessInfo != NULL)) {
// check for Live processes
// (i.e. name or threads)
if ((ProcessInfo->ImageName.Buffer != NULL) ||
(ProcessInfo->NumberOfThreads > 0)){
// thread is not Dead
NullProcess = FALSE;
} else {
// thread is dead
NullProcess = TRUE;
}
if (( !NullProcess ) && (dwPid == (HandleToUlong(ProcessInfo->UniqueProcessId)))) {
// found it so return current value
bNotFound = FALSE;
continue;
} else {
dwIndex++;
}
// exit if this was the last process in list
if (ProcessInfo->NextEntryOffset == 0) {
bMoreProcesses = FALSE;
continue;
}
// point to next buffer in list
ProcessBufferOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
&pProcessBuffer[ProcessBufferOffset];
}
if (bNotFound) {
return NULL;
} else {
return ProcessInfo;
}
}
DWORD APIENTRY
CollectJobObjectData (
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
PPERF_INSTANCE_DEFINITION pPerfInstanceDefinition;
PJOB_DATA_DEFINITION pJobDataDefinition;
PJOB_COUNTER_DATA pJCD;
JOB_COUNTER_DATA jcdTotal;
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS tmpStatus = STATUS_SUCCESS;
HANDLE DirectoryHandle, JobHandle;
ULONG ReturnedLength;
POBJECT_DIRECTORY_INFORMATION DirInfo;
POBJECT_NAME_INFORMATION NameInfo;
OBJECT_ATTRIBUTES Attributes;
WCHAR wszNameBuffer[MAX_STR_CHAR];
DWORD dwSize;
PUCHAR Buffer;
BOOL bStatus;
JOBOBJECT_BASIC_ACCOUNTING_INFORMATION JobAcctInfo;
DWORD dwWin32Status = ERROR_SUCCESS;
ACCESS_MASK ExtraAccess = 0;
ULONG Context = 0;
DWORD NumJobInstances = 0;
// get size of a data block that has 1 instance
TotalLen = sizeof(JOB_DATA_DEFINITION) + // object def + counter defs
sizeof (PERF_INSTANCE_DEFINITION) + // 1 instance def
MAX_VALUE_NAME_LENGTH + // 1 instance name
sizeof(JOB_COUNTER_DATA); // 1 instance data block
if ( *lpcbTotalBytes < TotalLen ) {
*lpcbTotalBytes = 0;
*lpNumObjectTypes = 0;
return ERROR_MORE_DATA;
}
// cast callers buffer to the object data definition type
pJobDataDefinition = (JOB_DATA_DEFINITION *) *lppData;
//
// Define Job Object data block
//
memcpy(pJobDataDefinition,
&JobDataDefinition,
sizeof(JOB_DATA_DEFINITION));
// set timestamp of this object
pJobDataDefinition->JobObjectType.PerfTime = PerfTime;
// Now collect data for each job object found in system
//
// Perform initial setup
//
Buffer = ALLOCMEM(hLibHeap, HEAP_ZERO_MEMORY, dwBufferSize);
if ((Buffer == NULL)) {
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFPROC_UNABLE_ALLOCATE_JOB_DATA,
NULL,
0,
0,
NULL,
NULL);
*lpcbTotalBytes = 0;
*lpNumObjectTypes = 0;
return ERROR_SUCCESS;
}
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pJobDataDefinition[1];
// adjust TotalLen to be the size of the buffer already in use
TotalLen = sizeof (JOB_DATA_DEFINITION);
// zero the total instance buffer
memset (&jcdTotal, 0, sizeof (jcdTotal));
//
// Open the directory for list directory access
//
// this should always succeed since it's a system name we
// will be querying
//
InitializeObjectAttributes( &Attributes,
&DirectoryName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
Status = NtOpenDirectoryObject( &DirectoryHandle,
DIRECTORY_QUERY | ExtraAccess,
&Attributes
);
if (NT_SUCCESS( Status )) {
//
// Get the actual name of the object directory object.
//
NameInfo = (POBJECT_NAME_INFORMATION) &Buffer[0];
Status = NtQueryObject( DirectoryHandle,
ObjectNameInformation,
NameInfo,
dwBufferSize,
(PULONG) NULL );
}
if (NT_SUCCESS( Status )) {
//
// Query the entire directory in one sweep
//
for (Status = NtQueryDirectoryObject( DirectoryHandle,
Buffer,
dwBufferSize,
FALSE,
FALSE,
&Context,
&ReturnedLength );
NT_SUCCESS( Status );
Status = NtQueryDirectoryObject( DirectoryHandle,
Buffer,
dwBufferSize,
FALSE,
FALSE,
&Context,
&ReturnedLength ) ) {
//
// Check the status of the operation.
//
if (!NT_SUCCESS( Status )) {
break;
}
//
// For every record in the buffer type out the directory information
//
//
// Point to the first record in the buffer, we are guaranteed to have
// one otherwise Status would have been No More Files
//
DirInfo = (POBJECT_DIRECTORY_INFORMATION) &Buffer[0];
//
// Continue while there's a valid record.
//
while (DirInfo->Name.Length != 0) {
//
// Print out information about the Job
//
if (wcsncmp ( DirInfo->TypeName.Buffer, &szJob[0], ((sizeof(szJob)/sizeof(WCHAR)) - 1)) == 0) {
ULONG len;
UNICODE_STRING JobName;
NTSTATUS Status;
// this is really a job, so list the name
dwSize = DirInfo->Name.Length;
if (dwSize > (MAX_STR_SIZE - sizeof(szObjDirName))) {
dwSize = MAX_STR_SIZE - sizeof(szObjDirName);
}
len = wcslen(szObjDirName);
wcscpy(wszNameBuffer, szObjDirName);
wszNameBuffer[len] = L'\\';
len++;
memcpy (&wszNameBuffer[len], DirInfo->Name.Buffer, dwSize);
wszNameBuffer[dwSize/sizeof(WCHAR)+len] = 0;
// now query the process ID's for this job
RtlInitUnicodeString(&JobName, wszNameBuffer);
InitializeObjectAttributes(
&Attributes,
&JobName,
0,
NULL, NULL);
Status = NtOpenJobObject(
&JobHandle,
JOB_OBJECT_QUERY,
&Attributes);
if (NT_SUCCESS(Status)) {
// strip Job name prefix for instance name
memcpy (wszNameBuffer, DirInfo->Name.Buffer, dwSize);
wszNameBuffer[dwSize/sizeof(WCHAR)] = 0;
bStatus = QueryInformationJobObject (
JobHandle,
JobObjectBasicAccountingInformation,
&JobAcctInfo,
sizeof(JobAcctInfo),
&ReturnedLength);
ASSERT (ReturnedLength == sizeof(JobAcctInfo));
if (bStatus) {
// *** create and initialize perf data instance here ***
// see if this instance will fit
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
DWORD_MULTIPLE ((DirInfo->Name.Length + sizeof(WCHAR))) +
sizeof (JOB_COUNTER_DATA);
if ( *lpcbTotalBytes < TotalLen ) {
*lpcbTotalBytes = 0;
*lpNumObjectTypes = 0;
Status = STATUS_NO_MEMORY;
dwWin32Status = ERROR_MORE_DATA;
break;
}
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pJCD,
0,
0,
(DWORD)-1,
wszNameBuffer);
// test structure for Quadword Alignment
assert (((DWORD)(pJCD) & 0x00000007) == 0);
//
// Format and collect Process data
//
pJCD->CounterBlock.ByteLength = sizeof (JOB_COUNTER_DATA);
jcdTotal.CurrentProcessorTime +=
pJCD->CurrentProcessorTime =
JobAcctInfo.TotalUserTime.QuadPart +
JobAcctInfo.TotalKernelTime.QuadPart;
jcdTotal.CurrentUserTime +=
pJCD->CurrentUserTime = JobAcctInfo.TotalUserTime.QuadPart;
jcdTotal.CurrentKernelTime +=
pJCD->CurrentKernelTime = JobAcctInfo.TotalKernelTime.QuadPart;
#ifdef _DATAJOB_INCLUDE_TOTAL_COUNTERS
// convert these times from 100 ns Time base to 1 mS time base
jcdTotal.TotalProcessorTime +=
pJCD->TotalProcessorTime =
(JobAcctInfo.ThisPeriodTotalUserTime.QuadPart +
JobAcctInfo.ThisPeriodTotalKernelTime.QuadPart) / 10000;
jcdTotal.TotalUserTime +=
pJCD->TotalUserTime =
JobAcctInfo.ThisPeriodTotalUserTime.QuadPart / 10000;
jcdTotal.TotalKernelTime +=
pJCD->TotalKernelTime =
JobAcctInfo.ThisPeriodTotalKernelTime.QuadPart / 1000;
jcdTotal.CurrentProcessorUsage +=
pJCD->CurrentProcessorUsage =
(JobAcctInfo.TotalUserTime.QuadPart +
JobAcctInfo.TotalKernelTime.QuadPart) / 10000;
jcdTotal.CurrentUserUsage +=
pJCD->CurrentUserUsage =
JobAcctInfo.TotalUserTime.QuadPart / 10000;
jcdTotal.CurrentKernelUsage +=
pJCD->CurrentKernelUsage =
JobAcctInfo.TotalKernelTime.QuadPart / 10000;
#endif
jcdTotal.PageFaults +=
pJCD->PageFaults = JobAcctInfo.TotalPageFaultCount;
jcdTotal.TotalProcessCount +=
pJCD->TotalProcessCount = JobAcctInfo.TotalProcesses;
jcdTotal.ActiveProcessCount +=
pJCD->ActiveProcessCount = JobAcctInfo.ActiveProcesses;
jcdTotal.TerminatedProcessCount +=
pJCD->TerminatedProcessCount = JobAcctInfo.TotalTerminatedProcesses;
NumJobInstances++;
CloseHandle (JobHandle);
// set perfdata pointer to next byte
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pJCD[1];
} else {
// unable to query job accounting info
dwWin32Status = GetLastError();
tmpStatus = Status;
Status = STATUS_SUCCESS;
if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
wEvtStringCount = 0;
szEvtStringArray[wEvtStringCount++] = wszNameBuffer;
// unable to open this Job
ReportEventW (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
PERFPROC_UNABLE_QUERY_JOB_ACCT,
NULL,
wEvtStringCount,
sizeof(DWORD),
szEvtStringArray,
(LPVOID) & dwWin32Status);
bOpenJobErrorLogged = TRUE;
}
}
} else {
dwWin32Status = GetLastError();
tmpStatus = Status;
Status = STATUS_SUCCESS;
if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
wEvtStringCount = 0;
szEvtStringArray[wEvtStringCount++] = wszNameBuffer;
// unable to open this Job
ReportEventW (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
PERFPROC_UNABLE_OPEN_JOB,
NULL,
wEvtStringCount,
sizeof(DWORD),
szEvtStringArray,
(LPVOID) & dwWin32Status);
bOpenJobErrorLogged = TRUE;
}
}
}
//
// There is another record so advance DirInfo to the next entry
//
DirInfo = (POBJECT_DIRECTORY_INFORMATION) (((PUCHAR) DirInfo) +
sizeof( OBJECT_DIRECTORY_INFORMATION ) );
}
RtlZeroMemory( Buffer, dwBufferSize );
}
if ((Status == STATUS_NO_MORE_FILES) ||
(Status == STATUS_NO_MORE_ENTRIES)) {
// this is OK
Status = STATUS_SUCCESS;
}
if (Status == STATUS_SUCCESS && NumJobInstances == 0
&& bOpenJobErrorLogged == TRUE
&& dwWin32Status != ERROR_SUCCESS) {
Status = tmpStatus;
}
if (Buffer) FREEMEM(hLibHeap, 0, Buffer);
//
// Now close the directory object
//
(VOID) NtClose( DirectoryHandle );
}
if (NT_SUCCESS(Status)) {
if (NumJobInstances > 0) {
// see if the total instance will fit
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
(MAX_NAME_LENGTH+1+sizeof(DWORD))*
sizeof(WCHAR) +
sizeof (JOB_COUNTER_DATA);
if ( *lpcbTotalBytes < TotalLen ) {
*lpcbTotalBytes = 0;
*lpNumObjectTypes = 0;
return ERROR_MORE_DATA;
}
// it looks like it will fit so create "total" instance
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pJCD,
0,
0,
(DWORD)-1,
wszTotal);
// test structure for Quadword Alignment
assert (((DWORD)(pJCD) & 0x00000007) == 0);
//
// transfer total info
//
memcpy (pJCD, &jcdTotal, sizeof (jcdTotal));
pJCD->CounterBlock.ByteLength = sizeof (JOB_COUNTER_DATA);
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pJCD[1];
NumJobInstances++;
}
pJobDataDefinition->JobObjectType.NumInstances =
NumJobInstances;
//
// Now we know how large an area we used for the
// data, so we can update the offset
// to the next object definition
//
*lpcbTotalBytes =
pJobDataDefinition->JobObjectType.TotalByteLength =
(DWORD)((PCHAR) pPerfInstanceDefinition -
(PCHAR) pJobDataDefinition);
#if DBG
if (*lpcbTotalBytes > TotalLen ) {
DbgPrint ("\nPERFPROC: Job Perf Ctr. Instance Size Underestimated:");
DbgPrint ("\nPERFPROC: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
}
#endif
*lppData = (LPVOID) pPerfInstanceDefinition;
*lpNumObjectTypes = 1;
} else {
*lpcbTotalBytes = 0;
*lpNumObjectTypes = 0;
if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
wEvtStringCount = 0;
szEvtStringArray[wEvtStringCount++] = DirectoryName.Buffer;
// unable to query the object directory
ReportEventW (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
PERFPROC_UNABLE_QUERY_OBJECT_DIR,
NULL,
wEvtStringCount,
sizeof(DWORD),
szEvtStringArray,
(LPVOID)&Status);
bOpenJobErrorLogged = TRUE;
}
}
return ERROR_SUCCESS;
}
DWORD APIENTRY
CollectJobDetailData (
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
--*/
{
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
PUNICODE_STRING pProcessName;
DWORD TotalLen; // Length of the total return block
PPERF_INSTANCE_DEFINITION pPerfInstanceDefinition;
PJOB_DETAILS_DATA_DEFINITION pJobDetailsDataDefinition;
PJOB_DETAILS_COUNTER_DATA pJDCD;
JOB_DETAILS_COUNTER_DATA jdcdTotal;
JOB_DETAILS_COUNTER_DATA jdcdGrandTotal;
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS tmpStatus = STATUS_SUCCESS;
HANDLE DirectoryHandle, JobHandle;
ULONG ReturnedLength;
POBJECT_DIRECTORY_INFORMATION DirInfo;
POBJECT_NAME_INFORMATION NameInfo;
OBJECT_ATTRIBUTES Attributes;
WCHAR wszNameBuffer[MAX_STR_CHAR];
DWORD i, dwSize;
PUCHAR Buffer;
BOOL bStatus;
PJOBOBJECT_BASIC_PROCESS_ID_LIST pJobPidList;
DWORD dwWin32Status = ERROR_SUCCESS;
ACCESS_MASK ExtraAccess = 0;
ULONG Context = 0;
DWORD NumJobObjects = 0;
DWORD NumJobDetailInstances = 0;
// get size of a data block that has 1 instance
TotalLen = sizeof(JOB_DETAILS_DATA_DEFINITION) + // object def + counter defs
sizeof (PERF_INSTANCE_DEFINITION) + // 1 instance def
MAX_VALUE_NAME_LENGTH + // 1 instance name
sizeof(JOB_DETAILS_COUNTER_DATA); // 1 instance data block
if ( *lpcbTotalBytes < TotalLen ) {
*lpcbTotalBytes = 0;
*lpNumObjectTypes = 0;
return ERROR_MORE_DATA;
}
// cast callers buffer to the object data definition type
pJobDetailsDataDefinition = (JOB_DETAILS_DATA_DEFINITION *) *lppData;
//
// Define Job Details Object data block
//
memcpy(pJobDetailsDataDefinition,
&JobDetailsDataDefinition,
sizeof(JOB_DETAILS_DATA_DEFINITION));
// set timestamp of this object
pJobDetailsDataDefinition->JobDetailsObjectType.PerfTime = PerfTime;
// Now collect data for each job object found in system
//
// Perform initial setup
//
Buffer = NULL;
pJobPidList = NULL;
if (hLibHeap) {
Buffer = ALLOCMEM(hLibHeap, HEAP_ZERO_MEMORY, dwBufferSize);
pJobPidList = ALLOCMEM(hLibHeap, HEAP_ZERO_MEMORY, dwBufferSize);
}
if ((Buffer == NULL) || (pJobPidList == NULL)) {
*lpcbTotalBytes = 0;
*lpNumObjectTypes = 0;
// free the one that got allocated (if any)
if (Buffer != NULL) FREEMEM(hLibHeap, 0, Buffer);
if (pJobPidList != NULL) FREEMEM(hLibHeap, 0, pJobPidList);
ReportEventW(hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFPROC_UNABLE_ALLOCATE_JOB_DATA,
NULL,
0,
0,
szEvtStringArray,
NULL);
return ERROR_SUCCESS;
}
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pJobDetailsDataDefinition[1];
// adjust TotalLen to be the size of the buffer already in use
TotalLen = sizeof (JOB_DETAILS_DATA_DEFINITION);
// zero the total instance buffer
memset (&jdcdGrandTotal, 0, sizeof (jdcdGrandTotal));
//
// Open the directory for list directory access
//
// this should always succeed since it's a system name we
// will be querying
//
InitializeObjectAttributes( &Attributes,
&DirectoryName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
Status = NtOpenDirectoryObject( &DirectoryHandle,
DIRECTORY_QUERY | ExtraAccess,
&Attributes
);
if (NT_SUCCESS( Status )) {
//
// Get the actual name of the object directory object.
//
NameInfo = (POBJECT_NAME_INFORMATION) &Buffer[0];
Status = NtQueryObject( DirectoryHandle,
ObjectNameInformation,
NameInfo,
dwBufferSize,
(PULONG) NULL );
}
if (NT_SUCCESS( Status )) {
//
// Query the entire directory in one sweep
//
for (Status = NtQueryDirectoryObject( DirectoryHandle,
Buffer,
dwBufferSize,
FALSE,
FALSE,
&Context,
&ReturnedLength );
NT_SUCCESS( Status );
Status = NtQueryDirectoryObject( DirectoryHandle,
Buffer,
dwBufferSize,
FALSE,
FALSE,
&Context,
&ReturnedLength ) ) {
//
// Check the status of the operation.
//
if (!NT_SUCCESS( Status )) {
break;
}
//
// For every record in the buffer type out the directory information
//
//
// Point to the first record in the buffer, we are guaranteed to have
// one otherwise Status would have been No More Files
//
DirInfo = (POBJECT_DIRECTORY_INFORMATION) &Buffer[0];
//
// contine while there's a valid record
//
while (DirInfo->Name.Length != 0) {
//
// Print out information about the Job
//
if (wcsncmp ( DirInfo->TypeName.Buffer, &szJob[0], ((sizeof(szJob)/sizeof(WCHAR)) - 1)) == 0) {
ULONG len;
UNICODE_STRING JobName;
NTSTATUS Status;
// this is really a job, so list the name
dwSize = DirInfo->Name.Length;
if (dwSize > (MAX_STR_SIZE - sizeof(szObjDirName))) {
dwSize = MAX_STR_SIZE - sizeof(szObjDirName);
}
len = wcslen(szObjDirName);
wcscpy(wszNameBuffer, szObjDirName);
wszNameBuffer[len] = L'\\';
len++;
memcpy (&wszNameBuffer[len], DirInfo->Name.Buffer, dwSize);
wszNameBuffer[dwSize/sizeof(WCHAR)+len] = 0;
// now query the process ID's for this job
RtlInitUnicodeString(&JobName, wszNameBuffer);
InitializeObjectAttributes(
&Attributes,
&JobName,
0,
NULL, NULL);
Status = NtOpenJobObject(
&JobHandle,
JOB_OBJECT_QUERY,
&Attributes);
// clear the Job total counter block
memset (&jdcdTotal, 0, sizeof (jdcdTotal));
if (NT_SUCCESS(Status)) {
// strip Job name prefix for instance name
memcpy (wszNameBuffer, DirInfo->Name.Buffer, dwSize);
wszNameBuffer[dwSize/sizeof(WCHAR)] = 0;
// now query the process ID's for this job
bStatus = QueryInformationJobObject (
JobHandle,
JobObjectBasicProcessIdList,
pJobPidList,
dwBufferSize,
&ReturnedLength);
// ASSERT (bStatus == TRUE);
ASSERT (ReturnedLength <= BUFFERSIZE);
ASSERT (pJobPidList->NumberOfAssignedProcesses ==
pJobPidList->NumberOfProcessIdsInList);
// test to see if there was enough room in the first buffer
// for everything, if not, expand the buffer and retry
if ((bStatus) && (pJobPidList->NumberOfAssignedProcesses >
pJobPidList->NumberOfProcessIdsInList)) {
PJOBOBJECT_BASIC_PROCESS_ID_LIST pOldBuffer;
dwBufferSize +=
(pJobPidList->NumberOfAssignedProcesses -
pJobPidList->NumberOfProcessIdsInList) *
sizeof (DWORD);
pOldBuffer = pJobPidList;
pJobPidList = REALLOCMEM (hLibHeap, 0,
pJobPidList, dwBufferSize);
// ASSERT (pJobPidList != NULL);
if (pJobPidList != NULL) {
bStatus = QueryInformationJobObject (
JobHandle,
JobObjectBasicProcessIdList,
pJobPidList,
dwBufferSize,
&ReturnedLength);
} else {
bStatus = FALSE;
FREEMEM(hLibHeap, 0, pOldBuffer);
SetLastError ( ERROR_OUTOFMEMORY );
}
}
if (bStatus) {
for (i=0;i < pJobPidList->NumberOfProcessIdsInList; i++) {
// *** create and initialize perf data instance here ***
// get process data object from ID
ProcessInfo = GetProcessPointerFromProcessId (pJobPidList->ProcessIdList[i]);
// ASSERT (ProcessInfo != NULL);
if (ProcessInfo != NULL) {
// get process name
if (lProcessNameCollectionMethod == PNCM_MODULE_FILE) {
pProcessName = GetProcessSlowName (ProcessInfo);
} else {
pProcessName = GetProcessShortName (ProcessInfo);
}
ReturnedLength = pProcessName->Length + sizeof(WCHAR);
// see if this instance will fit
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
DWORD_MULTIPLE (ReturnedLength) +
sizeof (JOB_DETAILS_COUNTER_DATA);
if ( *lpcbTotalBytes < TotalLen ) {
*lpcbTotalBytes = 0;
*lpNumObjectTypes = 0;
Status = STATUS_NO_MEMORY;
dwWin32Status = ERROR_MORE_DATA;
break;
}
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pJDCD,
JOB_OBJECT_TITLE_INDEX,
NumJobObjects,
(DWORD)-1,
pProcessName->Buffer);
// test structure for Quadword Alignment
assert (((DWORD)(pJDCD) & 0x00000007) == 0);
//
// Format and collect Process data
//
pJDCD->CounterBlock.ByteLength = sizeof (JOB_DETAILS_COUNTER_DATA);
//
// Convert User time from 100 nsec units to counter frequency.
//
jdcdTotal.ProcessorTime +=
pJDCD->ProcessorTime = ProcessInfo->KernelTime.QuadPart +
ProcessInfo->UserTime.QuadPart;
jdcdTotal.UserTime +=
pJDCD->UserTime = ProcessInfo->UserTime.QuadPart;
jdcdTotal.KernelTime +=
pJDCD->KernelTime = ProcessInfo->KernelTime.QuadPart;
jdcdTotal.PeakVirtualSize +=
pJDCD->PeakVirtualSize = ProcessInfo->PeakVirtualSize;
jdcdTotal.VirtualSize +=
pJDCD->VirtualSize = ProcessInfo->VirtualSize;
jdcdTotal.PageFaults +=
pJDCD->PageFaults = ProcessInfo->PageFaultCount;
jdcdTotal.PeakWorkingSet +=
pJDCD->PeakWorkingSet = ProcessInfo->PeakWorkingSetSize;
jdcdTotal.TotalWorkingSet +=
pJDCD->TotalWorkingSet = ProcessInfo->WorkingSetSize;
#ifdef _DATAPROC_PRIVATE_WS_
jdcdTotal.PrivateWorkingSet +=
pJDCD->PrivateWorkingSet = ProcessInfo->PrivateWorkingSetSize;
jdcdTotal.SharedWorkingSet +=
pJDCD->SharedWorkingSet =
ProcessInfo->WorkingSetSize -
ProcessInfo->PrivateWorkingSetSize;
#endif
jdcdTotal.PeakPageFile +=
pJDCD->PeakPageFile = ProcessInfo->PeakPagefileUsage;
jdcdTotal.PageFile +=
pJDCD->PageFile = ProcessInfo->PagefileUsage;
jdcdTotal.PrivatePages +=
pJDCD->PrivatePages = ProcessInfo->PrivatePageCount;
jdcdTotal.ThreadCount +=
pJDCD->ThreadCount = ProcessInfo->NumberOfThreads;
// base priority is not totaled
pJDCD->BasePriority = ProcessInfo->BasePriority;
// elpased time is not totaled
pJDCD->ElapsedTime = ProcessInfo->CreateTime.QuadPart;
pJDCD->ProcessId = HandleToUlong(ProcessInfo->UniqueProcessId);
pJDCD->CreatorProcessId = HandleToUlong(ProcessInfo->InheritedFromUniqueProcessId);
jdcdTotal.PagedPool +=
pJDCD->PagedPool = (DWORD)ProcessInfo->QuotaPagedPoolUsage;
jdcdTotal.NonPagedPool +=
pJDCD->NonPagedPool = (DWORD)ProcessInfo->QuotaNonPagedPoolUsage;
jdcdTotal.HandleCount +=
pJDCD->HandleCount = (DWORD)ProcessInfo->HandleCount;
// update I/O counters
jdcdTotal.ReadOperationCount +=
pJDCD->ReadOperationCount = ProcessInfo->ReadOperationCount.QuadPart;
jdcdTotal.DataOperationCount +=
pJDCD->DataOperationCount = ProcessInfo->ReadOperationCount.QuadPart;
jdcdTotal.WriteOperationCount +=
pJDCD->WriteOperationCount = ProcessInfo->WriteOperationCount.QuadPart;
jdcdTotal.DataOperationCount += ProcessInfo->WriteOperationCount.QuadPart;
pJDCD->DataOperationCount += ProcessInfo->WriteOperationCount.QuadPart;
jdcdTotal.OtherOperationCount +=
pJDCD->OtherOperationCount = ProcessInfo->OtherOperationCount.QuadPart;
jdcdTotal.ReadTransferCount +=
pJDCD->ReadTransferCount = ProcessInfo->ReadTransferCount.QuadPart;
jdcdTotal.DataTransferCount +=
pJDCD->DataTransferCount = ProcessInfo->ReadTransferCount.QuadPart;
jdcdTotal.WriteTransferCount +=
pJDCD->WriteTransferCount = ProcessInfo->WriteTransferCount.QuadPart;
jdcdTotal.DataTransferCount += ProcessInfo->WriteTransferCount.QuadPart;
pJDCD->DataTransferCount += ProcessInfo->WriteTransferCount.QuadPart;
jdcdTotal.OtherTransferCount +=
pJDCD->OtherTransferCount = ProcessInfo->OtherTransferCount.QuadPart;
// set perfdata pointer to next byte
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pJDCD[1];
NumJobDetailInstances++;
} else {
// unable to locate info on this process
// for now, we'll ignore this...
}
}
CloseHandle (JobHandle);
// see if this instance will fit
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
DWORD_MULTIPLE (MAX_STR_SIZE) +
sizeof (JOB_DETAILS_COUNTER_DATA);
if ( *lpcbTotalBytes < TotalLen ) {
*lpcbTotalBytes = 0;
*lpNumObjectTypes = 0;
Status = STATUS_NO_MEMORY;
dwWin32Status = ERROR_MORE_DATA;
break;
}
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pJDCD,
JOB_OBJECT_TITLE_INDEX,
NumJobObjects,
(DWORD)-1,
wszTotal);
// test structure for Quadword Alignment
assert (((DWORD)(pJDCD) & 0x00000007) == 0);
// copy total data to caller's buffer
memcpy (pJDCD, &jdcdTotal, sizeof (jdcdTotal));
pJDCD->CounterBlock.ByteLength = sizeof (JOB_DETAILS_COUNTER_DATA);
// update grand total instance
//
jdcdGrandTotal.ProcessorTime += jdcdTotal.ProcessorTime;
jdcdGrandTotal.UserTime += jdcdTotal.UserTime;
jdcdGrandTotal.KernelTime += jdcdTotal. KernelTime;
jdcdGrandTotal.PeakVirtualSize += jdcdTotal.PeakVirtualSize;
jdcdGrandTotal.VirtualSize += jdcdTotal.VirtualSize;
jdcdGrandTotal.PageFaults += jdcdTotal.PageFaults;
jdcdGrandTotal.PeakWorkingSet += jdcdTotal.PeakWorkingSet;
jdcdGrandTotal.TotalWorkingSet += jdcdTotal.TotalWorkingSet;
#ifdef _DATAPROC_PRIVATE_WS_
jdcdGrandTotal.PrivateWorkingSet += jdcdTotal.PrivateWorkingSet;
jdcdGrandTotal.SharedWorkingSet += jdcdTotal.SharedWorkingSet;
#endif
jdcdGrandTotal.PeakPageFile += jdcdTotal.PeakPageFile;
jdcdGrandTotal.PageFile += jdcdTotal.PageFile;
jdcdGrandTotal.PrivatePages += jdcdTotal.PrivatePages;
jdcdGrandTotal.ThreadCount += jdcdTotal.ThreadCount;
jdcdGrandTotal.PagedPool += jdcdTotal.PagedPool;
jdcdGrandTotal.NonPagedPool += jdcdTotal.NonPagedPool;
jdcdGrandTotal.HandleCount += jdcdTotal.HandleCount;
// set perfdata pointer to next byte
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pJDCD[1];
NumJobDetailInstances++;
NumJobObjects++;
} else {
// unable to read PID list from Job
dwWin32Status = GetLastError();
tmpStatus = Status;
Status = STATUS_SUCCESS;
if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
wEvtStringCount = 0;
szEvtStringArray[wEvtStringCount++] = wszNameBuffer;
// unable to open this Job
ReportEventW (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
PERFPROC_UNABLE_QUERY_JOB_PIDS,
NULL,
wEvtStringCount,
sizeof(DWORD),
szEvtStringArray,
(LPVOID) & dwWin32Status);
bOpenJobErrorLogged = TRUE;
}
}
} else {
dwWin32Status = GetLastError();
tmpStatus = Status;
Status = STATUS_SUCCESS;
if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
wEvtStringCount = 0;
szEvtStringArray[wEvtStringCount++] = wszNameBuffer;
// unable to open this Job
ReportEventW (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
PERFPROC_UNABLE_OPEN_JOB,
NULL,
wEvtStringCount,
sizeof(DWORD),
szEvtStringArray,
(LPVOID) & dwWin32Status);
bOpenJobErrorLogged = TRUE;
}
}
}
//
// There is another record so advance DirInfo to the next entry
//
DirInfo = (POBJECT_DIRECTORY_INFORMATION) (((PUCHAR) DirInfo) +
sizeof( OBJECT_DIRECTORY_INFORMATION ) );
}
RtlZeroMemory( Buffer, dwBufferSize );
}
if ((Status == STATUS_NO_MORE_FILES) ||
(Status == STATUS_NO_MORE_ENTRIES)) {
// this is OK
Status = STATUS_SUCCESS;
}
if (Status == STATUS_SUCCESS && NumJobDetailInstances == 0
&& bOpenJobErrorLogged == TRUE
&& dwWin32Status != ERROR_SUCCESS) {
Status = tmpStatus;
}
if (Buffer) FREEMEM(hLibHeap, 0, Buffer);
if (pJobPidList) FREEMEM(hLibHeap, 0, pJobPidList);
//
// Now close the directory object
//
(VOID) NtClose( DirectoryHandle );
if (NumJobDetailInstances > 0) {
// see if this instance will fit
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
DWORD_MULTIPLE (MAX_STR_SIZE) +
sizeof (JOB_DETAILS_COUNTER_DATA);
if ( *lpcbTotalBytes < TotalLen ) {
*lpcbTotalBytes = 0;
*lpNumObjectTypes = 0;
Status = STATUS_NO_MEMORY;
dwWin32Status = ERROR_MORE_DATA;
} else {
// set the Total Elapsed Time to be the current time so that it will
// show up as 0 when displayed.
jdcdGrandTotal.ElapsedTime = pJobDetailsDataDefinition->JobDetailsObjectType.PerfTime.QuadPart;
// build the grand total instance
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pJDCD,
JOB_OBJECT_TITLE_INDEX,
NumJobObjects,
(DWORD)-1,
wszTotal);
// test structure for Quadword Alignment
ASSERT (((ULONG_PTR)(pJDCD) & 0x00000007) == 0);
// copy total data to caller's buffer
memcpy (pJDCD, &jdcdGrandTotal, sizeof (jdcdGrandTotal));
pJDCD->CounterBlock.ByteLength = sizeof (JOB_DETAILS_COUNTER_DATA);
// update pointers
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pJDCD[1];
NumJobDetailInstances++;
}
}
pJobDetailsDataDefinition->JobDetailsObjectType.NumInstances =
NumJobDetailInstances;
//
// 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 =
pJobDetailsDataDefinition->JobDetailsObjectType.TotalByteLength =
(DWORD)((PCHAR) pPerfInstanceDefinition -
(PCHAR) pJobDetailsDataDefinition);
#if DBG
if (*lpcbTotalBytes > TotalLen ) {
DbgPrint ("\nPERFPROC: Job Perf Ctr. Instance Size Underestimated:");
DbgPrint ("\nPERFPROC: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
}
#endif
*lppData = (LPVOID) pPerfInstanceDefinition;
*lpNumObjectTypes = 1;
} else {
*lpcbTotalBytes = 0;
*lpNumObjectTypes = 0;
if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
wEvtStringCount = 0;
szEvtStringArray[wEvtStringCount++] = DirectoryName.Buffer;
// unable to query the object directory
ReportEventW (hEventLog,
EVENTLOG_WARNING_TYPE,
0,
PERFPROC_UNABLE_QUERY_OBJECT_DIR,
NULL,
wEvtStringCount,
sizeof(DWORD),
szEvtStringArray,
(LPVOID)&Status);
bOpenJobErrorLogged = TRUE;
}
}
return ERROR_SUCCESS;
}