Leaked source code of windows server 2003
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.

1256 lines
51 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. perfjob.c
  5. Abstract:
  6. This file implements an Performance Job Object that presents
  7. information on the Job Object
  8. Created:
  9. Bob Watson 8-Oct-1997
  10. Revision History
  11. --*/
  12. //
  13. // Include Files
  14. //
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #include <windows.h>
  19. #include <assert.h>
  20. #include <winperf.h>
  21. #include <ntprfctr.h>
  22. #define PERF_HEAP hLibHeap
  23. #include <perfutil.h>
  24. #include "perfsprc.h"
  25. #include "perfmsg.h"
  26. #include "procmsg.h"
  27. #include "datajob.h"
  28. #define MAX_STR_CHAR 1024
  29. #define MAX_STR_SIZE ((DWORD)((MAX_STR_CHAR - 1)* sizeof(WCHAR)))
  30. #define MAX_NAME_LENGTH MAX_PATH
  31. #define BUFFERSIZE 1024
  32. DWORD dwBufferSize = BUFFERSIZE;
  33. const WCHAR szJob[] = L"Job";
  34. const WCHAR szObjDirName[] = L"\\BaseNamedObjects";
  35. #define MAX_EVENT_STRINGS 4
  36. WORD wEvtStringCount;
  37. LPWSTR szEvtStringArray[MAX_EVENT_STRINGS];
  38. UNICODE_STRING DirectoryName = {(sizeof(szObjDirName) - sizeof(WCHAR)), // name len - NULL
  39. sizeof(szObjDirName), // size of buffer
  40. (PWCHAR)szObjDirName}; // address of buffer
  41. BOOL bOpenJobErrorLogged = FALSE;
  42. PSYSTEM_PROCESS_INFORMATION APIENTRY
  43. GetProcessPointerFromProcessId (
  44. IN ULONG_PTR dwPid
  45. )
  46. {
  47. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  48. ULONG ProcessBufferOffset = 0;
  49. BOOLEAN NullProcess;
  50. DWORD dwIndex = 0;
  51. BOOL bNotFound = TRUE;
  52. BOOL bMoreProcesses = FALSE;
  53. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) pProcessBuffer;
  54. if (ProcessInfo) {
  55. if (ProcessInfo->NextEntryOffset != 0) {
  56. bMoreProcesses = TRUE;
  57. }
  58. }
  59. while ( bMoreProcesses && bNotFound &&
  60. (ProcessInfo != NULL)) {
  61. // check for Live processes
  62. // (i.e. name or threads)
  63. if ((ProcessInfo->ImageName.Buffer != NULL) ||
  64. (ProcessInfo->NumberOfThreads > 0)){
  65. // thread is not Dead
  66. NullProcess = FALSE;
  67. } else {
  68. // thread is dead
  69. NullProcess = TRUE;
  70. }
  71. if (( !NullProcess ) && (dwPid == (HandleToUlong(ProcessInfo->UniqueProcessId)))) {
  72. // found it so return current value
  73. bNotFound = FALSE;
  74. continue;
  75. } else {
  76. dwIndex++;
  77. }
  78. // exit if this was the last process in list
  79. if (ProcessInfo->NextEntryOffset == 0) {
  80. bMoreProcesses = FALSE;
  81. continue;
  82. }
  83. // point to next buffer in list
  84. ProcessBufferOffset += ProcessInfo->NextEntryOffset;
  85. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
  86. &pProcessBuffer[ProcessBufferOffset];
  87. }
  88. if (bNotFound) {
  89. return NULL;
  90. } else {
  91. return ProcessInfo;
  92. }
  93. }
  94. DWORD APIENTRY
  95. CollectJobObjectData (
  96. IN OUT LPVOID *lppData,
  97. IN OUT LPDWORD lpcbTotalBytes,
  98. IN OUT LPDWORD lpNumObjectTypes
  99. )
  100. /*++
  101. Routine Description:
  102. This routine will return the data for the processor object
  103. Arguments:
  104. IN OUT LPVOID *lppData
  105. IN: pointer to the address of the buffer to receive the completed
  106. PerfDataBlock and subordinate structures. This routine will
  107. append its data to the buffer starting at the point referenced
  108. by *lppData.
  109. OUT: points to the first byte after the data structure added by this
  110. routine. This routine updated the value at lppdata after appending
  111. its data.
  112. IN OUT LPDWORD lpcbTotalBytes
  113. IN: the address of the DWORD that tells the size in bytes of the
  114. buffer referenced by the lppData argument
  115. OUT: the number of bytes added by this routine is writted to the
  116. DWORD pointed to by this argument
  117. IN OUT LPDWORD NumObjectTypes
  118. IN: the address of the DWORD to receive the number of objects added
  119. by this routine
  120. OUT: the number of objects added by this routine is writted to the
  121. DWORD pointed to by this argument
  122. Returns:
  123. 0 if successful, else Win 32 error code of failure
  124. --*/
  125. {
  126. DWORD TotalLen; // Length of the total return block
  127. PPERF_INSTANCE_DEFINITION pPerfInstanceDefinition;
  128. PJOB_DATA_DEFINITION pJobDataDefinition;
  129. PJOB_COUNTER_DATA pJCD;
  130. JOB_COUNTER_DATA jcdTotal;
  131. NTSTATUS Status = STATUS_SUCCESS;
  132. NTSTATUS tmpStatus = STATUS_SUCCESS;
  133. HANDLE DirectoryHandle, JobHandle;
  134. ULONG ReturnedLength;
  135. POBJECT_DIRECTORY_INFORMATION DirInfo;
  136. POBJECT_NAME_INFORMATION NameInfo;
  137. OBJECT_ATTRIBUTES Attributes;
  138. WCHAR wszNameBuffer[MAX_STR_CHAR];
  139. DWORD dwSize;
  140. PUCHAR Buffer;
  141. BOOL bStatus;
  142. JOBOBJECT_BASIC_ACCOUNTING_INFORMATION JobAcctInfo;
  143. DWORD dwWin32Status = ERROR_SUCCESS;
  144. ACCESS_MASK ExtraAccess = 0;
  145. ULONG Context = 0;
  146. DWORD NumJobInstances = 0;
  147. // get size of a data block that has 1 instance
  148. TotalLen = sizeof(JOB_DATA_DEFINITION) + // object def + counter defs
  149. sizeof (PERF_INSTANCE_DEFINITION) + // 1 instance def
  150. MAX_VALUE_NAME_LENGTH + // 1 instance name
  151. sizeof(JOB_COUNTER_DATA); // 1 instance data block
  152. if ( *lpcbTotalBytes < TotalLen ) {
  153. *lpcbTotalBytes = 0;
  154. *lpNumObjectTypes = 0;
  155. return ERROR_MORE_DATA;
  156. }
  157. // cast callers buffer to the object data definition type
  158. pJobDataDefinition = (JOB_DATA_DEFINITION *) *lppData;
  159. //
  160. // Define Job Object data block
  161. //
  162. memcpy(pJobDataDefinition,
  163. &JobDataDefinition,
  164. sizeof(JOB_DATA_DEFINITION));
  165. // set timestamp of this object
  166. pJobDataDefinition->JobObjectType.PerfTime = PerfTime;
  167. // Now collect data for each job object found in system
  168. //
  169. // Perform initial setup
  170. //
  171. Buffer = ALLOCMEM(dwBufferSize);
  172. if ((Buffer == NULL)) {
  173. ReportEvent (hEventLog,
  174. EVENTLOG_ERROR_TYPE,
  175. 0,
  176. PERFPROC_UNABLE_ALLOCATE_JOB_DATA,
  177. NULL,
  178. 0,
  179. 0,
  180. NULL,
  181. NULL);
  182. *lpcbTotalBytes = 0;
  183. *lpNumObjectTypes = 0;
  184. return ERROR_SUCCESS;
  185. }
  186. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  187. &pJobDataDefinition[1];
  188. // adjust TotalLen to be the size of the buffer already in use
  189. TotalLen = sizeof (JOB_DATA_DEFINITION);
  190. // zero the total instance buffer
  191. memset (&jcdTotal, 0, sizeof (jcdTotal));
  192. //
  193. // Open the directory for list directory access
  194. //
  195. // this should always succeed since it's a system name we
  196. // will be querying
  197. //
  198. InitializeObjectAttributes( &Attributes,
  199. &DirectoryName,
  200. OBJ_CASE_INSENSITIVE,
  201. NULL,
  202. NULL );
  203. Status = NtOpenDirectoryObject( &DirectoryHandle,
  204. DIRECTORY_QUERY | ExtraAccess,
  205. &Attributes
  206. );
  207. if (NT_SUCCESS( Status )) {
  208. //
  209. // Get the actual name of the object directory object.
  210. //
  211. NameInfo = (POBJECT_NAME_INFORMATION) &Buffer[0];
  212. Status = NtQueryObject( DirectoryHandle,
  213. ObjectNameInformation,
  214. NameInfo,
  215. dwBufferSize,
  216. (PULONG) NULL );
  217. }
  218. if (NT_SUCCESS( Status )) {
  219. //
  220. // Query the entire directory in one sweep
  221. //
  222. for (Status = NtQueryDirectoryObject( DirectoryHandle,
  223. Buffer,
  224. dwBufferSize,
  225. FALSE,
  226. FALSE,
  227. &Context,
  228. &ReturnedLength );
  229. NT_SUCCESS( Status );
  230. Status = NtQueryDirectoryObject( DirectoryHandle,
  231. Buffer,
  232. dwBufferSize,
  233. FALSE,
  234. FALSE,
  235. &Context,
  236. &ReturnedLength ) ) {
  237. //
  238. // Check the status of the operation.
  239. //
  240. if (!NT_SUCCESS( Status )) {
  241. break;
  242. }
  243. //
  244. // For every record in the buffer type out the directory information
  245. //
  246. //
  247. // Point to the first record in the buffer, we are guaranteed to have
  248. // one otherwise Status would have been No More Files
  249. //
  250. DirInfo = (POBJECT_DIRECTORY_INFORMATION) &Buffer[0];
  251. //
  252. // Continue while there's a valid record.
  253. //
  254. while (DirInfo->Name.Length != 0) {
  255. //
  256. // Print out information about the Job
  257. //
  258. if (wcsncmp ( DirInfo->TypeName.Buffer, &szJob[0], ((sizeof(szJob)/sizeof(WCHAR)) - 1)) == 0) {
  259. SIZE_T len;
  260. UNICODE_STRING JobName;
  261. // this is really a job, so list the name
  262. dwSize = DirInfo->Name.Length;
  263. if (dwSize >= (MAX_STR_SIZE - sizeof(szObjDirName))) {
  264. dwSize = MAX_STR_SIZE - sizeof(szObjDirName) - 1;
  265. }
  266. len = wcslen(szObjDirName);
  267. wcscpy(wszNameBuffer, szObjDirName);
  268. wszNameBuffer[len] = L'\\';
  269. len++;
  270. memcpy (&wszNameBuffer[len], DirInfo->Name.Buffer, dwSize);
  271. wszNameBuffer[dwSize/sizeof(WCHAR)+len] = 0;
  272. // now query the process ID's for this job
  273. RtlInitUnicodeString(&JobName, wszNameBuffer);
  274. InitializeObjectAttributes(
  275. &Attributes,
  276. &JobName,
  277. 0,
  278. NULL, NULL);
  279. Status = NtOpenJobObject(
  280. &JobHandle,
  281. JOB_OBJECT_QUERY,
  282. &Attributes);
  283. if (NT_SUCCESS(Status)) {
  284. // strip Job name prefix for instance name
  285. memcpy (wszNameBuffer, DirInfo->Name.Buffer, dwSize);
  286. wszNameBuffer[dwSize/sizeof(WCHAR)] = 0;
  287. bStatus = QueryInformationJobObject (
  288. JobHandle,
  289. JobObjectBasicAccountingInformation,
  290. &JobAcctInfo,
  291. sizeof(JobAcctInfo),
  292. &ReturnedLength);
  293. ASSERT (ReturnedLength == sizeof(JobAcctInfo));
  294. if (bStatus) {
  295. // *** create and initialize perf data instance here ***
  296. // see if this instance will fit
  297. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  298. QWORD_MULTIPLE ((DirInfo->Name.Length + sizeof(WCHAR))) +
  299. sizeof (JOB_COUNTER_DATA);
  300. if ( *lpcbTotalBytes < TotalLen ) {
  301. *lpcbTotalBytes = 0;
  302. *lpNumObjectTypes = 0;
  303. Status = STATUS_NO_MEMORY;
  304. dwWin32Status = ERROR_MORE_DATA;
  305. break;
  306. }
  307. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  308. (PVOID *) &pJCD,
  309. 0,
  310. 0,
  311. (DWORD)-1,
  312. wszNameBuffer);
  313. // test structure for Quadword Alignment
  314. assert (((DWORD)(pJCD) & 0x00000007) == 0);
  315. //
  316. // Format and collect Process data
  317. //
  318. pJCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof (JOB_COUNTER_DATA));
  319. jcdTotal.CurrentProcessorTime +=
  320. pJCD->CurrentProcessorTime =
  321. JobAcctInfo.TotalUserTime.QuadPart +
  322. JobAcctInfo.TotalKernelTime.QuadPart;
  323. jcdTotal.CurrentUserTime +=
  324. pJCD->CurrentUserTime = JobAcctInfo.TotalUserTime.QuadPart;
  325. jcdTotal.CurrentKernelTime +=
  326. pJCD->CurrentKernelTime = JobAcctInfo.TotalKernelTime.QuadPart;
  327. #ifdef _DATAJOB_INCLUDE_TOTAL_COUNTERS
  328. // convert these times from 100 ns Time base to 1 mS time base
  329. jcdTotal.TotalProcessorTime +=
  330. pJCD->TotalProcessorTime =
  331. (JobAcctInfo.ThisPeriodTotalUserTime.QuadPart +
  332. JobAcctInfo.ThisPeriodTotalKernelTime.QuadPart) / 10000;
  333. jcdTotal.TotalUserTime +=
  334. pJCD->TotalUserTime =
  335. JobAcctInfo.ThisPeriodTotalUserTime.QuadPart / 10000;
  336. jcdTotal.TotalKernelTime +=
  337. pJCD->TotalKernelTime =
  338. JobAcctInfo.ThisPeriodTotalKernelTime.QuadPart / 1000;
  339. jcdTotal.CurrentProcessorUsage +=
  340. pJCD->CurrentProcessorUsage =
  341. (JobAcctInfo.TotalUserTime.QuadPart +
  342. JobAcctInfo.TotalKernelTime.QuadPart) / 10000;
  343. jcdTotal.CurrentUserUsage +=
  344. pJCD->CurrentUserUsage =
  345. JobAcctInfo.TotalUserTime.QuadPart / 10000;
  346. jcdTotal.CurrentKernelUsage +=
  347. pJCD->CurrentKernelUsage =
  348. JobAcctInfo.TotalKernelTime.QuadPart / 10000;
  349. #endif
  350. jcdTotal.PageFaults +=
  351. pJCD->PageFaults = JobAcctInfo.TotalPageFaultCount;
  352. jcdTotal.TotalProcessCount +=
  353. pJCD->TotalProcessCount = JobAcctInfo.TotalProcesses;
  354. jcdTotal.ActiveProcessCount +=
  355. pJCD->ActiveProcessCount = JobAcctInfo.ActiveProcesses;
  356. jcdTotal.TerminatedProcessCount +=
  357. pJCD->TerminatedProcessCount = JobAcctInfo.TotalTerminatedProcesses;
  358. NumJobInstances++;
  359. CloseHandle (JobHandle);
  360. // set perfdata pointer to next byte
  361. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pJCD[1];
  362. } else {
  363. // unable to query job accounting info
  364. dwWin32Status = GetLastError();
  365. tmpStatus = Status;
  366. Status = STATUS_SUCCESS;
  367. if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
  368. wEvtStringCount = 0;
  369. szEvtStringArray[wEvtStringCount++] = wszNameBuffer;
  370. // unable to open this Job
  371. ReportEventW (hEventLog,
  372. EVENTLOG_WARNING_TYPE,
  373. 0,
  374. PERFPROC_UNABLE_QUERY_JOB_ACCT,
  375. NULL,
  376. wEvtStringCount,
  377. sizeof(DWORD),
  378. szEvtStringArray,
  379. (LPVOID) & dwWin32Status);
  380. bOpenJobErrorLogged = TRUE;
  381. }
  382. }
  383. } else {
  384. dwWin32Status = GetLastError();
  385. tmpStatus = Status;
  386. Status = STATUS_SUCCESS;
  387. if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
  388. wEvtStringCount = 0;
  389. szEvtStringArray[wEvtStringCount++] = wszNameBuffer;
  390. // unable to open this Job
  391. ReportEventW (hEventLog,
  392. EVENTLOG_WARNING_TYPE,
  393. 0,
  394. PERFPROC_UNABLE_OPEN_JOB,
  395. NULL,
  396. wEvtStringCount,
  397. sizeof(DWORD),
  398. szEvtStringArray,
  399. (LPVOID) & dwWin32Status);
  400. bOpenJobErrorLogged = TRUE;
  401. }
  402. }
  403. }
  404. //
  405. // There is another record so advance DirInfo to the next entry
  406. //
  407. DirInfo = (POBJECT_DIRECTORY_INFORMATION) (((PUCHAR) DirInfo) +
  408. sizeof( OBJECT_DIRECTORY_INFORMATION ) );
  409. }
  410. RtlZeroMemory( Buffer, dwBufferSize );
  411. }
  412. if ((Status == STATUS_NO_MORE_FILES) ||
  413. (Status == STATUS_NO_MORE_ENTRIES)) {
  414. // this is OK
  415. Status = STATUS_SUCCESS;
  416. }
  417. if (Status == STATUS_SUCCESS && NumJobInstances == 0
  418. && bOpenJobErrorLogged == TRUE
  419. && dwWin32Status != ERROR_SUCCESS) {
  420. Status = tmpStatus;
  421. }
  422. //
  423. // Now close the directory object
  424. //
  425. (VOID) NtClose( DirectoryHandle );
  426. }
  427. if (Buffer) {
  428. FREEMEM(Buffer);
  429. }
  430. if (NT_SUCCESS(Status)) {
  431. if (NumJobInstances > 0) {
  432. // see if the total instance will fit
  433. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  434. QWORD_MULTIPLE((MAX_NAME_LENGTH+1+sizeof(DWORD))*
  435. sizeof(WCHAR) +
  436. sizeof (JOB_COUNTER_DATA));
  437. if ( *lpcbTotalBytes < TotalLen ) {
  438. *lpcbTotalBytes = 0;
  439. *lpNumObjectTypes = 0;
  440. return ERROR_MORE_DATA;
  441. }
  442. // it looks like it will fit so create "total" instance
  443. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  444. (PVOID *) &pJCD,
  445. 0,
  446. 0,
  447. (DWORD)-1,
  448. wszTotal);
  449. // test structure for Quadword Alignment
  450. assert (((DWORD)(pJCD) & 0x00000007) == 0);
  451. //
  452. // transfer total info
  453. //
  454. memcpy (pJCD, &jcdTotal, sizeof (jcdTotal));
  455. pJCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof (JOB_COUNTER_DATA));
  456. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pJCD[1];
  457. NumJobInstances++;
  458. }
  459. pJobDataDefinition->JobObjectType.NumInstances =
  460. NumJobInstances;
  461. //
  462. // Now we know how large an area we used for the
  463. // data, so we can update the offset
  464. // to the next object definition
  465. //
  466. *lpcbTotalBytes =
  467. pJobDataDefinition->JobObjectType.TotalByteLength =
  468. QWORD_MULTIPLE(
  469. (DWORD)((PCHAR) pPerfInstanceDefinition -
  470. (PCHAR) pJobDataDefinition));
  471. #if DBG
  472. if (*lpcbTotalBytes > TotalLen ) {
  473. DbgPrint ("\nPERFPROC: Job Perf Ctr. Instance Size Underestimated:");
  474. DbgPrint ("\nPERFPROC: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
  475. }
  476. #endif
  477. *lppData = (LPVOID) ((PCHAR) pJobDataDefinition + *lpcbTotalBytes);
  478. *lpNumObjectTypes = 1;
  479. } else {
  480. *lpcbTotalBytes = 0;
  481. *lpNumObjectTypes = 0;
  482. if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
  483. wEvtStringCount = 0;
  484. szEvtStringArray[wEvtStringCount++] = DirectoryName.Buffer;
  485. // unable to query the object directory
  486. ReportEventW (hEventLog,
  487. EVENTLOG_WARNING_TYPE,
  488. 0,
  489. PERFPROC_UNABLE_QUERY_OBJECT_DIR,
  490. NULL,
  491. wEvtStringCount,
  492. sizeof(DWORD),
  493. szEvtStringArray,
  494. (LPVOID)&Status);
  495. bOpenJobErrorLogged = TRUE;
  496. }
  497. }
  498. return ERROR_SUCCESS;
  499. }
  500. DWORD APIENTRY
  501. CollectJobDetailData (
  502. IN OUT LPVOID *lppData,
  503. IN OUT LPDWORD lpcbTotalBytes,
  504. IN OUT LPDWORD lpNumObjectTypes
  505. )
  506. /*++
  507. Routine Description:
  508. This routine will return the data for the processor object
  509. Arguments:
  510. IN OUT LPVOID *lppData
  511. IN: pointer to the address of the buffer to receive the completed
  512. PerfDataBlock and subordinate structures. This routine will
  513. append its data to the buffer starting at the point referenced
  514. by *lppData.
  515. OUT: points to the first byte after the data structure added by this
  516. routine. This routine updated the value at lppdata after appending
  517. its data.
  518. IN OUT LPDWORD lpcbTotalBytes
  519. IN: the address of the DWORD that tells the size in bytes of the
  520. buffer referenced by the lppData argument
  521. OUT: the number of bytes added by this routine is writted to the
  522. DWORD pointed to by this argument
  523. IN OUT LPDWORD NumObjectTypes
  524. IN: the address of the DWORD to receive the number of objects added
  525. by this routine
  526. OUT: the number of objects added by this routine is writted to the
  527. DWORD pointed to by this argument
  528. Returns:
  529. 0 if successful, else Win 32 error code of failure
  530. --*/
  531. {
  532. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  533. PUNICODE_STRING pProcessName;
  534. DWORD TotalLen; // Length of the total return block
  535. PPERF_INSTANCE_DEFINITION pPerfInstanceDefinition;
  536. PJOB_DETAILS_DATA_DEFINITION pJobDetailsDataDefinition;
  537. PJOB_DETAILS_COUNTER_DATA pJDCD;
  538. JOB_DETAILS_COUNTER_DATA jdcdTotal;
  539. JOB_DETAILS_COUNTER_DATA jdcdGrandTotal;
  540. NTSTATUS Status = STATUS_SUCCESS;
  541. NTSTATUS tmpStatus = STATUS_SUCCESS;
  542. HANDLE DirectoryHandle, JobHandle;
  543. ULONG ReturnedLength;
  544. POBJECT_DIRECTORY_INFORMATION DirInfo;
  545. POBJECT_NAME_INFORMATION NameInfo;
  546. OBJECT_ATTRIBUTES Attributes;
  547. WCHAR wszNameBuffer[MAX_STR_CHAR];
  548. DWORD i, dwSize;
  549. PUCHAR Buffer;
  550. BOOL bStatus;
  551. PJOBOBJECT_BASIC_PROCESS_ID_LIST pJobPidList;
  552. DWORD dwWin32Status = ERROR_SUCCESS;
  553. ACCESS_MASK ExtraAccess = 0;
  554. ULONG Context = 0;
  555. DWORD NumJobObjects = 0;
  556. DWORD NumJobDetailInstances = 0;
  557. // get size of a data block that has 1 instance
  558. TotalLen = sizeof(JOB_DETAILS_DATA_DEFINITION) + // object def + counter defs
  559. sizeof (PERF_INSTANCE_DEFINITION) + // 1 instance def
  560. MAX_VALUE_NAME_LENGTH + // 1 instance name
  561. sizeof(JOB_DETAILS_COUNTER_DATA); // 1 instance data block
  562. if ( *lpcbTotalBytes < TotalLen ) {
  563. *lpcbTotalBytes = 0;
  564. *lpNumObjectTypes = 0;
  565. return ERROR_MORE_DATA;
  566. }
  567. // cast callers buffer to the object data definition type
  568. pJobDetailsDataDefinition = (JOB_DETAILS_DATA_DEFINITION *) *lppData;
  569. //
  570. // Define Job Details Object data block
  571. //
  572. memcpy(pJobDetailsDataDefinition,
  573. &JobDetailsDataDefinition,
  574. sizeof(JOB_DETAILS_DATA_DEFINITION));
  575. // set timestamp of this object
  576. pJobDetailsDataDefinition->JobDetailsObjectType.PerfTime = PerfTime;
  577. // Now collect data for each job object found in system
  578. //
  579. // Perform initial setup
  580. //
  581. Buffer = NULL;
  582. pJobPidList = NULL;
  583. if (hLibHeap) {
  584. Buffer = ALLOCMEM(dwBufferSize);
  585. pJobPidList = ALLOCMEM(dwBufferSize);
  586. }
  587. if ((Buffer == NULL) || (pJobPidList == NULL)) {
  588. *lpcbTotalBytes = 0;
  589. *lpNumObjectTypes = 0;
  590. // free the one that got allocated (if any)
  591. if (Buffer != NULL) FREEMEM(Buffer);
  592. if (pJobPidList != NULL) FREEMEM(pJobPidList);
  593. ReportEventW(hEventLog,
  594. EVENTLOG_ERROR_TYPE,
  595. 0,
  596. PERFPROC_UNABLE_ALLOCATE_JOB_DATA,
  597. NULL,
  598. 0,
  599. 0,
  600. szEvtStringArray,
  601. NULL);
  602. return ERROR_SUCCESS;
  603. }
  604. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  605. &pJobDetailsDataDefinition[1];
  606. // adjust TotalLen to be the size of the buffer already in use
  607. TotalLen = sizeof (JOB_DETAILS_DATA_DEFINITION);
  608. // zero the total instance buffer
  609. memset (&jdcdGrandTotal, 0, sizeof (jdcdGrandTotal));
  610. //
  611. // Open the directory for list directory access
  612. //
  613. // this should always succeed since it's a system name we
  614. // will be querying
  615. //
  616. InitializeObjectAttributes( &Attributes,
  617. &DirectoryName,
  618. OBJ_CASE_INSENSITIVE,
  619. NULL,
  620. NULL );
  621. Status = NtOpenDirectoryObject( &DirectoryHandle,
  622. DIRECTORY_QUERY | ExtraAccess,
  623. &Attributes
  624. );
  625. if (NT_SUCCESS( Status )) {
  626. //
  627. // Get the actual name of the object directory object.
  628. //
  629. NameInfo = (POBJECT_NAME_INFORMATION) &Buffer[0];
  630. Status = NtQueryObject( DirectoryHandle,
  631. ObjectNameInformation,
  632. NameInfo,
  633. dwBufferSize,
  634. (PULONG) NULL );
  635. }
  636. if (NT_SUCCESS( Status )) {
  637. //
  638. // Query the entire directory in one sweep
  639. //
  640. for (Status = NtQueryDirectoryObject( DirectoryHandle,
  641. Buffer,
  642. dwBufferSize,
  643. FALSE,
  644. FALSE,
  645. &Context,
  646. &ReturnedLength );
  647. NT_SUCCESS( Status );
  648. Status = NtQueryDirectoryObject( DirectoryHandle,
  649. Buffer,
  650. dwBufferSize,
  651. FALSE,
  652. FALSE,
  653. &Context,
  654. &ReturnedLength ) ) {
  655. //
  656. // Check the status of the operation.
  657. //
  658. if (!NT_SUCCESS( Status )) {
  659. break;
  660. }
  661. //
  662. // For every record in the buffer type out the directory information
  663. //
  664. //
  665. // Point to the first record in the buffer, we are guaranteed to have
  666. // one otherwise Status would have been No More Files
  667. //
  668. DirInfo = (POBJECT_DIRECTORY_INFORMATION) &Buffer[0];
  669. //
  670. // contine while there's a valid record
  671. //
  672. while (DirInfo->Name.Length != 0) {
  673. //
  674. // Print out information about the Job
  675. //
  676. if (wcsncmp ( DirInfo->TypeName.Buffer, &szJob[0], ((sizeof(szJob)/sizeof(WCHAR)) - 1)) == 0) {
  677. SIZE_T len;
  678. UNICODE_STRING JobName;
  679. // this is really a job, so list the name
  680. dwSize = DirInfo->Name.Length;
  681. if (dwSize > (MAX_STR_SIZE - sizeof(szObjDirName))) {
  682. dwSize = MAX_STR_SIZE - sizeof(szObjDirName);
  683. }
  684. len = wcslen(szObjDirName);
  685. wcscpy(wszNameBuffer, szObjDirName);
  686. wszNameBuffer[len] = L'\\';
  687. len++;
  688. memcpy (&wszNameBuffer[len], DirInfo->Name.Buffer, dwSize);
  689. wszNameBuffer[dwSize/sizeof(WCHAR)+len] = 0;
  690. // now query the process ID's for this job
  691. RtlInitUnicodeString(&JobName, wszNameBuffer);
  692. InitializeObjectAttributes(
  693. &Attributes,
  694. &JobName,
  695. 0,
  696. NULL, NULL);
  697. Status = NtOpenJobObject(
  698. &JobHandle,
  699. JOB_OBJECT_QUERY,
  700. &Attributes);
  701. // clear the Job total counter block
  702. memset (&jdcdTotal, 0, sizeof (jdcdTotal));
  703. if (NT_SUCCESS(Status)) {
  704. // strip Job name prefix for instance name
  705. memcpy (wszNameBuffer, DirInfo->Name.Buffer, dwSize);
  706. wszNameBuffer[dwSize/sizeof(WCHAR)] = 0;
  707. // now query the process ID's for this job
  708. bStatus = QueryInformationJobObject (
  709. JobHandle,
  710. JobObjectBasicProcessIdList,
  711. pJobPidList,
  712. dwBufferSize,
  713. &ReturnedLength);
  714. // ASSERT (bStatus == TRUE);
  715. ASSERT (ReturnedLength <= BUFFERSIZE);
  716. ASSERT (pJobPidList->NumberOfAssignedProcesses ==
  717. pJobPidList->NumberOfProcessIdsInList);
  718. // test to see if there was enough room in the first buffer
  719. // for everything, if not, expand the buffer and retry
  720. if ((bStatus) && (pJobPidList->NumberOfAssignedProcesses >
  721. pJobPidList->NumberOfProcessIdsInList)) {
  722. dwBufferSize +=
  723. (pJobPidList->NumberOfAssignedProcesses -
  724. pJobPidList->NumberOfProcessIdsInList) *
  725. sizeof (DWORD);
  726. FREEMEM(pJobPidList);
  727. pJobPidList = ALLOCMEM (dwBufferSize);
  728. if (pJobPidList != NULL) {
  729. bStatus = QueryInformationJobObject (
  730. JobHandle,
  731. JobObjectBasicProcessIdList,
  732. pJobPidList,
  733. dwBufferSize,
  734. &ReturnedLength);
  735. } else {
  736. bStatus = FALSE;
  737. SetLastError ( ERROR_OUTOFMEMORY );
  738. }
  739. }
  740. if (bStatus) {
  741. for (i=0;i < pJobPidList->NumberOfProcessIdsInList; i++) {
  742. // *** create and initialize perf data instance here ***
  743. // get process data object from ID
  744. ProcessInfo = GetProcessPointerFromProcessId (pJobPidList->ProcessIdList[i]);
  745. // ASSERT (ProcessInfo != NULL);
  746. if (ProcessInfo != NULL) {
  747. // get process name
  748. pProcessName = GetProcessShortName (ProcessInfo);
  749. ReturnedLength = pProcessName->Length + sizeof(WCHAR);
  750. // see if this instance will fit
  751. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  752. QWORD_MULTIPLE (ReturnedLength) +
  753. sizeof (JOB_DETAILS_COUNTER_DATA);
  754. if ( *lpcbTotalBytes < TotalLen ) {
  755. *lpcbTotalBytes = 0;
  756. *lpNumObjectTypes = 0;
  757. Status = STATUS_NO_MEMORY;
  758. dwWin32Status = ERROR_MORE_DATA;
  759. break;
  760. }
  761. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  762. (PVOID *) &pJDCD,
  763. JOB_OBJECT_TITLE_INDEX,
  764. NumJobObjects,
  765. (DWORD)-1,
  766. pProcessName->Buffer);
  767. // test structure for Quadword Alignment
  768. assert (((DWORD)(pJDCD) & 0x00000007) == 0);
  769. //
  770. // Format and collect Process data
  771. //
  772. pJDCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof (JOB_DETAILS_COUNTER_DATA));
  773. //
  774. // Convert User time from 100 nsec units to counter frequency.
  775. //
  776. jdcdTotal.ProcessorTime +=
  777. pJDCD->ProcessorTime = ProcessInfo->KernelTime.QuadPart +
  778. ProcessInfo->UserTime.QuadPart;
  779. jdcdTotal.UserTime +=
  780. pJDCD->UserTime = ProcessInfo->UserTime.QuadPart;
  781. jdcdTotal.KernelTime +=
  782. pJDCD->KernelTime = ProcessInfo->KernelTime.QuadPart;
  783. jdcdTotal.PeakVirtualSize +=
  784. pJDCD->PeakVirtualSize = ProcessInfo->PeakVirtualSize;
  785. jdcdTotal.VirtualSize +=
  786. pJDCD->VirtualSize = ProcessInfo->VirtualSize;
  787. jdcdTotal.PageFaults +=
  788. pJDCD->PageFaults = ProcessInfo->PageFaultCount;
  789. jdcdTotal.PeakWorkingSet +=
  790. pJDCD->PeakWorkingSet = ProcessInfo->PeakWorkingSetSize;
  791. jdcdTotal.TotalWorkingSet +=
  792. pJDCD->TotalWorkingSet = ProcessInfo->WorkingSetSize;
  793. #ifdef _DATAPROC_PRIVATE_WS_
  794. jdcdTotal.PrivateWorkingSet +=
  795. pJDCD->PrivateWorkingSet = ProcessInfo->PrivateWorkingSetSize;
  796. jdcdTotal.SharedWorkingSet +=
  797. pJDCD->SharedWorkingSet =
  798. ProcessInfo->WorkingSetSize -
  799. ProcessInfo->PrivateWorkingSetSize;
  800. #endif
  801. jdcdTotal.PeakPageFile +=
  802. pJDCD->PeakPageFile = ProcessInfo->PeakPagefileUsage;
  803. jdcdTotal.PageFile +=
  804. pJDCD->PageFile = ProcessInfo->PagefileUsage;
  805. jdcdTotal.PrivatePages +=
  806. pJDCD->PrivatePages = ProcessInfo->PrivatePageCount;
  807. jdcdTotal.ThreadCount +=
  808. pJDCD->ThreadCount = ProcessInfo->NumberOfThreads;
  809. // base priority is not totaled
  810. pJDCD->BasePriority = ProcessInfo->BasePriority;
  811. // elpased time is not totaled
  812. pJDCD->ElapsedTime = ProcessInfo->CreateTime.QuadPart;
  813. pJDCD->ProcessId = HandleToUlong(ProcessInfo->UniqueProcessId);
  814. pJDCD->CreatorProcessId = HandleToUlong(ProcessInfo->InheritedFromUniqueProcessId);
  815. jdcdTotal.PagedPool +=
  816. pJDCD->PagedPool = (DWORD)ProcessInfo->QuotaPagedPoolUsage;
  817. jdcdTotal.NonPagedPool +=
  818. pJDCD->NonPagedPool = (DWORD)ProcessInfo->QuotaNonPagedPoolUsage;
  819. jdcdTotal.HandleCount +=
  820. pJDCD->HandleCount = (DWORD)ProcessInfo->HandleCount;
  821. // update I/O counters
  822. jdcdTotal.ReadOperationCount +=
  823. pJDCD->ReadOperationCount = ProcessInfo->ReadOperationCount.QuadPart;
  824. jdcdTotal.DataOperationCount +=
  825. pJDCD->DataOperationCount = ProcessInfo->ReadOperationCount.QuadPart;
  826. jdcdTotal.WriteOperationCount +=
  827. pJDCD->WriteOperationCount = ProcessInfo->WriteOperationCount.QuadPart;
  828. jdcdTotal.DataOperationCount += ProcessInfo->WriteOperationCount.QuadPart;
  829. pJDCD->DataOperationCount += ProcessInfo->WriteOperationCount.QuadPart;
  830. jdcdTotal.OtherOperationCount +=
  831. pJDCD->OtherOperationCount = ProcessInfo->OtherOperationCount.QuadPart;
  832. jdcdTotal.ReadTransferCount +=
  833. pJDCD->ReadTransferCount = ProcessInfo->ReadTransferCount.QuadPart;
  834. jdcdTotal.DataTransferCount +=
  835. pJDCD->DataTransferCount = ProcessInfo->ReadTransferCount.QuadPart;
  836. jdcdTotal.WriteTransferCount +=
  837. pJDCD->WriteTransferCount = ProcessInfo->WriteTransferCount.QuadPart;
  838. jdcdTotal.DataTransferCount += ProcessInfo->WriteTransferCount.QuadPart;
  839. pJDCD->DataTransferCount += ProcessInfo->WriteTransferCount.QuadPart;
  840. jdcdTotal.OtherTransferCount +=
  841. pJDCD->OtherTransferCount = ProcessInfo->OtherTransferCount.QuadPart;
  842. // set perfdata pointer to next byte
  843. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pJDCD[1];
  844. NumJobDetailInstances++;
  845. } else {
  846. // unable to locate info on this process
  847. // for now, we'll ignore this...
  848. }
  849. }
  850. CloseHandle (JobHandle);
  851. // see if this instance will fit
  852. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  853. QWORD_MULTIPLE (MAX_STR_SIZE) +
  854. sizeof (JOB_DETAILS_COUNTER_DATA);
  855. if ( *lpcbTotalBytes < TotalLen ) {
  856. *lpcbTotalBytes = 0;
  857. *lpNumObjectTypes = 0;
  858. Status = STATUS_NO_MEMORY;
  859. dwWin32Status = ERROR_MORE_DATA;
  860. break;
  861. }
  862. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  863. (PVOID *) &pJDCD,
  864. JOB_OBJECT_TITLE_INDEX,
  865. NumJobObjects,
  866. (DWORD)-1,
  867. wszTotal);
  868. // test structure for Quadword Alignment
  869. assert (((DWORD)(pJDCD) & 0x00000007) == 0);
  870. // copy total data to caller's buffer
  871. memcpy (pJDCD, &jdcdTotal, sizeof (jdcdTotal));
  872. pJDCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof (JOB_DETAILS_COUNTER_DATA));
  873. // update grand total instance
  874. //
  875. jdcdGrandTotal.ProcessorTime += jdcdTotal.ProcessorTime;
  876. jdcdGrandTotal.UserTime += jdcdTotal.UserTime;
  877. jdcdGrandTotal.KernelTime += jdcdTotal. KernelTime;
  878. jdcdGrandTotal.PeakVirtualSize += jdcdTotal.PeakVirtualSize;
  879. jdcdGrandTotal.VirtualSize += jdcdTotal.VirtualSize;
  880. jdcdGrandTotal.PageFaults += jdcdTotal.PageFaults;
  881. jdcdGrandTotal.PeakWorkingSet += jdcdTotal.PeakWorkingSet;
  882. jdcdGrandTotal.TotalWorkingSet += jdcdTotal.TotalWorkingSet;
  883. #ifdef _DATAPROC_PRIVATE_WS_
  884. jdcdGrandTotal.PrivateWorkingSet += jdcdTotal.PrivateWorkingSet;
  885. jdcdGrandTotal.SharedWorkingSet += jdcdTotal.SharedWorkingSet;
  886. #endif
  887. jdcdGrandTotal.PeakPageFile += jdcdTotal.PeakPageFile;
  888. jdcdGrandTotal.PageFile += jdcdTotal.PageFile;
  889. jdcdGrandTotal.PrivatePages += jdcdTotal.PrivatePages;
  890. jdcdGrandTotal.ThreadCount += jdcdTotal.ThreadCount;
  891. jdcdGrandTotal.PagedPool += jdcdTotal.PagedPool;
  892. jdcdGrandTotal.NonPagedPool += jdcdTotal.NonPagedPool;
  893. jdcdGrandTotal.HandleCount += jdcdTotal.HandleCount;
  894. // set perfdata pointer to next byte
  895. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pJDCD[1];
  896. NumJobDetailInstances++;
  897. NumJobObjects++;
  898. } else {
  899. // unable to read PID list from Job
  900. dwWin32Status = GetLastError();
  901. tmpStatus = Status;
  902. Status = STATUS_SUCCESS;
  903. if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
  904. wEvtStringCount = 0;
  905. szEvtStringArray[wEvtStringCount++] = wszNameBuffer;
  906. // unable to open this Job
  907. ReportEventW (hEventLog,
  908. EVENTLOG_WARNING_TYPE,
  909. 0,
  910. PERFPROC_UNABLE_QUERY_JOB_PIDS,
  911. NULL,
  912. wEvtStringCount,
  913. sizeof(DWORD),
  914. szEvtStringArray,
  915. (LPVOID) & dwWin32Status);
  916. bOpenJobErrorLogged = TRUE;
  917. }
  918. }
  919. } else {
  920. dwWin32Status = GetLastError();
  921. tmpStatus = Status;
  922. Status = STATUS_SUCCESS;
  923. if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
  924. wEvtStringCount = 0;
  925. szEvtStringArray[wEvtStringCount++] = wszNameBuffer;
  926. // unable to open this Job
  927. ReportEventW (hEventLog,
  928. EVENTLOG_WARNING_TYPE,
  929. 0,
  930. PERFPROC_UNABLE_OPEN_JOB,
  931. NULL,
  932. wEvtStringCount,
  933. sizeof(DWORD),
  934. szEvtStringArray,
  935. (LPVOID) & dwWin32Status);
  936. bOpenJobErrorLogged = TRUE;
  937. }
  938. }
  939. }
  940. //
  941. // There is another record so advance DirInfo to the next entry
  942. //
  943. DirInfo = (POBJECT_DIRECTORY_INFORMATION) (((PUCHAR) DirInfo) +
  944. sizeof( OBJECT_DIRECTORY_INFORMATION ) );
  945. }
  946. RtlZeroMemory( Buffer, dwBufferSize );
  947. }
  948. if ((Status == STATUS_NO_MORE_FILES) ||
  949. (Status == STATUS_NO_MORE_ENTRIES)) {
  950. // this is OK
  951. Status = STATUS_SUCCESS;
  952. }
  953. if (Status == STATUS_SUCCESS && NumJobDetailInstances == 0
  954. && bOpenJobErrorLogged == TRUE
  955. && dwWin32Status != ERROR_SUCCESS) {
  956. Status = tmpStatus;
  957. }
  958. //
  959. // Now close the directory object
  960. //
  961. (VOID) NtClose( DirectoryHandle );
  962. if (NumJobDetailInstances > 0) {
  963. // see if this instance will fit
  964. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  965. QWORD_MULTIPLE (MAX_STR_SIZE) +
  966. sizeof (JOB_DETAILS_COUNTER_DATA);
  967. if ( *lpcbTotalBytes < TotalLen ) {
  968. *lpcbTotalBytes = 0;
  969. *lpNumObjectTypes = 0;
  970. Status = STATUS_NO_MEMORY;
  971. dwWin32Status = ERROR_MORE_DATA;
  972. } else {
  973. // set the Total Elapsed Time to be the current time so that it will
  974. // show up as 0 when displayed.
  975. jdcdGrandTotal.ElapsedTime = pJobDetailsDataDefinition->JobDetailsObjectType.PerfTime.QuadPart;
  976. // build the grand total instance
  977. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  978. (PVOID *) &pJDCD,
  979. JOB_OBJECT_TITLE_INDEX,
  980. NumJobObjects,
  981. (DWORD)-1,
  982. wszTotal);
  983. // test structure for Quadword Alignment
  984. //ASSERT (((ULONG_PTR)(pJDCD) & 0x00000007) == 0);
  985. // copy total data to caller's buffer
  986. memcpy (pJDCD, &jdcdGrandTotal, sizeof (jdcdGrandTotal));
  987. pJDCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof (JOB_DETAILS_COUNTER_DATA));
  988. // update pointers
  989. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pJDCD[1];
  990. NumJobDetailInstances++;
  991. }
  992. }
  993. pJobDetailsDataDefinition->JobDetailsObjectType.NumInstances =
  994. NumJobDetailInstances;
  995. //
  996. // Now we know how large an area we used for the
  997. // Process definition, so we can update the offset
  998. // to the next object definition
  999. //
  1000. *lpcbTotalBytes =
  1001. pJobDetailsDataDefinition->JobDetailsObjectType.TotalByteLength =
  1002. QWORD_MULTIPLE(
  1003. (DWORD)((PCHAR) pPerfInstanceDefinition -
  1004. (PCHAR) pJobDetailsDataDefinition));
  1005. #if DBG
  1006. if (*lpcbTotalBytes > TotalLen ) {
  1007. DbgPrint ("\nPERFPROC: Job Perf Ctr. Instance Size Underestimated:");
  1008. DbgPrint ("\nPERFPROC: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
  1009. }
  1010. #endif
  1011. *lppData = (LPVOID) ((PCHAR) pJobDetailsDataDefinition + *lpcbTotalBytes);
  1012. *lpNumObjectTypes = 1;
  1013. } else {
  1014. *lpcbTotalBytes = 0;
  1015. *lpNumObjectTypes = 0;
  1016. if (bOpenJobErrorLogged == FALSE && MESSAGE_LEVEL >= LOG_VERBOSE) {
  1017. wEvtStringCount = 0;
  1018. szEvtStringArray[wEvtStringCount++] = DirectoryName.Buffer;
  1019. // unable to query the object directory
  1020. ReportEventW (hEventLog,
  1021. EVENTLOG_WARNING_TYPE,
  1022. 0,
  1023. PERFPROC_UNABLE_QUERY_OBJECT_DIR,
  1024. NULL,
  1025. wEvtStringCount,
  1026. sizeof(DWORD),
  1027. szEvtStringArray,
  1028. (LPVOID)&Status);
  1029. bOpenJobErrorLogged = TRUE;
  1030. }
  1031. }
  1032. if (Buffer) {
  1033. FREEMEM(Buffer);
  1034. }
  1035. if (pJobPidList) {
  1036. FREEMEM(pJobPidList);
  1037. }
  1038. return ERROR_SUCCESS;
  1039. }