Querying Job Statistics

[Previous] [Next]

We've already discussed how to use the QueryInformationJobObject function to get the current restrictions on a job. You can also use it to get statistical information about a job. For example, to get basic accounting information, you call QueryInformationJobObject, passing JobObjectBasicAccountingInformation for the second parameter and the address of a JOBOBJECT_BASIC_ACCOUNTING_INFORMATION structure:

 typedef struct _JOBOBJECT_BASIC_ACCOUNTING_INFORMATION { LARGE_INTEGER TotalUserTime; LARGE_INTEGER TotalKernelTime; LARGE_INTEGER ThisPeriodTotalUserTime; LARGE_INTEGER ThisPeriodTotalKernelTime; DWORD TotalPageFaultCount; DWORD TotalProcesses; DWORD ActiveProcesses; DWORD TotalTerminatedProcesses; } JOBOBJECT_BASIC_ACCOUNTING_INFORMATION, *PJOBOBJECT_BASIC_ACCOUNTING_INFORMATION; 

Table 5-3 briefly describes the members.

Table 5-3. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION members

Member Description
TotalUserTime Specifies how much user-mode CPU time processes in the job have used
TotalKernelTime Specifies how much kernel-mode CPU time processes in the job have used
ThisPeriodTotalUserTime Like TotalUserTime, except this value is reset to 0 when SetInformationJobObject is called to change basic limit information and the JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME limit flag is not used
ThisPeriodTotalKernelTime Like ThisPeriodTotalUserTime, except this value shows kernel-mode time
TotalPageFaultCount Specifies the total number of page faults that processes in the job have accrued
TotalProcesses Specifies the total number of processes that have ever been part of the job
ActiveProcesses Specifies the number of processes that are currently part of the job
TotalTerminatedProcesses Specifies the number of processes that have been killed because they have exceeded their allotted CPU time limit

In addition to querying this basic accounting information, you can make a single call to query both basic accounting and I/O accounting information. To do this, you pass JobObjectBasicAndIoAccountingInformation for the second parameter and the address of a JOBOBJECT_BASIC_AND_IO_ ACCOUNTING_INFORMATION structure:

 typedef struct JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION { JOBOBJECT_BASIC_ACCOUNTING_INFORMATION BasicInfo; IO_COUNTERS IoInfo; } JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION; 

As you can see, this structure simply returns a JOBOBJECT_BASIC_ ACCOUNTING_INFORMATION and an IO_COUNTERS structure:

 typedef struct _IO_COUNTERS { ULONGLONG ReadOperationCount; ULONGLONG WriteOperationCount; ULONGLONG OtherOperationCount; ULONGLONG ReadTransferCount; ULONGLONG WriteTransferCount; ULONGLONG OtherTransferCount; } IO_COUNTERS; 

This structure tells you the number of read, write, and non-read/write operations (as well as total bytes transferred during those operations) that have been performed by processes in the job. By the way, you can use the new GetProcessIoCounters function to obtain this information for processes that are not in jobs:

 BOOL GetProcessIoCounters( HANDLE hProcess, PIO_COUNTERS pIoCounters); 

You can also call QueryInformationJobObject at any time to get the set of process IDs for processes that are currently running in the job. To do this, you must first guess how many processes you expect to see in the job, and then you have to allocate a block of memory large enough to hold an array of these process IDs plus the size of a JOBOBJECT_BASIC_PROCESS_ID_LIST structure:

 typedef struct _JOBOBJECT_BASIC_PROCESS_ID_LIST { DWORD NumberOfAssignedProcesses; DWORD NumberOfProcessIdsInList; DWORD ProcessIdList[1]; } JOBOBJECT_BASIC_PROCESS_ID_LIST, *PJOBOBJECT_BASIC_PROCESS_ID_LIST; 

So, to get the set of Process IDs currently in a job, you must execute code similar to the following:

 void EnumProcessIdsInJob(HANDLE hjob) { // I assume that there will never be more // than 10 processes in this job. #define MAX_PROCESS_IDS     10 // Calculate the number of bytes needed for structure & process IDs. DWORD cb = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) + (MAX_PROCESS_IDS _ 1) * sizeof(DWORD); // Allocate the block of memory. PJOBOBJECT_BASIC_PROCESS_ID_LIST pjobpil = _alloca(cb); // Tell the function the maximum number of processes // that we allocated space for. pjobpil->NumberOfAssignedProcesses = MAX_PROCESS_IDS; // Request the current set of process IDs. QueryInformationJobObject(hjob, JobObjectBasicProcessIdList, pjobpil, cb, &cb); // Enumerate the process IDs. for (int x = 0; x < pjobpil->NumberOfProcessIdsInList; x++) { // Use pjobpil->ProcessIdList[x]... } // Since _alloca was used to allocate the memory, // we don't need to free it here. } 

This is all the information you get using these functions, but the operating system actually keeps a lot more information about jobs. It does this using performance counters; you can retrieve the information using the functions in the Performance Data Helper function library (PDH.dll). You can also use the Microsoft Management Console (MMC) Performance Monitor Snap-In to view the job information. The dialog box in Figure 5-3 shows some of the counters available for job objects in the system. Figure 5-4 shows some of the available job object details counters. You can also see that Jeff's job has four processes in it: calc, cmd, notepad, and wordpad.

Note that you can obtain performance counter information only for jobs that were assigned names when CreateJobObject was called. For this reason, you might want to create job objects with names even though you do not intend to share these objects across process boundaries by name.

Figure 5-3. MMC Performance Monitor: job object counters

Figure 5-4. MMC Performance Monitor: job object details counters



Programming Applications for Microsoft Windows
Programming Applications for Microsoft Windows (Microsoft Programming Series)
ISBN: 1572319968
EAN: 2147483647
Year: 1999
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net