|
|
/*++
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; }
|