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