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.

782 lines
22 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. perfos.c
  5. Abstract:
  6. Author:
  7. Bob Watson (a-robw) Aug 95
  8. Revision History:
  9. --*/
  10. #include <nt.h>
  11. #include <ntrtl.h>
  12. #include <nturtl.h>
  13. #include <windows.h>
  14. #include <winperf.h>
  15. #include <ntprfctr.h>
  16. #include <assert.h>
  17. #define PERF_HEAP hLibHeap
  18. #include <perfutil.h>
  19. #include "perfos.h"
  20. #include "perfosmc.h"
  21. // bit field definitions for collect function flags
  22. #define POS_GET_SYS_PERF_INFO ((DWORD)0x00010000)
  23. #define POS_COLLECT_CACHE_DATA ((DWORD)0x00010001)
  24. #define POS_COLLECT_CPU_DATA ((DWORD)0x00000002)
  25. #define POS_COLLECT_MEMORY_DATA ((DWORD)0x00010004)
  26. #define POS_COLLECT_OBJECTS_DATA ((DWORD)0x00000008)
  27. #define POS_COLLECT_PAGEFILE_DATA ((DWORD)0x00000010)
  28. #define POS_COLLECT_SYSTEM_DATA ((DWORD)0x00010020)
  29. #define POS_COLLECT_FUNCTION_MASK ((DWORD)0x0000003F)
  30. #define POS_COLLECT_GLOBAL_DATA ((DWORD)0x0001003F)
  31. #define POS_COLLECT_FOREIGN_DATA ((DWORD)0)
  32. #define POS_COLLECT_COSTLY_DATA ((DWORD)0)
  33. // global variables to this DLL
  34. HANDLE ThisDLLHandle = NULL;
  35. HANDLE hEventLog = NULL;
  36. HANDLE hLibHeap = NULL;
  37. SYSTEM_BASIC_INFORMATION BasicInfo;
  38. SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
  39. PM_OPEN_PROC OpenOSObject;
  40. PM_COLLECT_PROC CollectOSObjectData;
  41. PM_CLOSE_PROC CloseOSObject;
  42. LPWSTR wszTotal = NULL;
  43. // variables local to this module
  44. POS_FUNCTION_INFO posDataFuncInfo[] = {
  45. {CACHE_OBJECT_TITLE_INDEX, POS_COLLECT_CACHE_DATA, 0, CollectCacheObjectData},
  46. {PROCESSOR_OBJECT_TITLE_INDEX, POS_COLLECT_CPU_DATA, 0, CollectProcessorObjectData},
  47. {MEMORY_OBJECT_TITLE_INDEX, POS_COLLECT_MEMORY_DATA, 0, CollectMemoryObjectData},
  48. {OBJECT_OBJECT_TITLE_INDEX, POS_COLLECT_OBJECTS_DATA, 0, CollectObjectsObjectData},
  49. {PAGEFILE_OBJECT_TITLE_INDEX, POS_COLLECT_PAGEFILE_DATA, 0, CollectPageFileObjectData},
  50. {SYSTEM_OBJECT_TITLE_INDEX, POS_COLLECT_SYSTEM_DATA, 0, CollectSystemObjectData}
  51. };
  52. #define POS_NUM_FUNCS (sizeof(posDataFuncInfo) / sizeof(posDataFuncInfo[1]))
  53. BOOL bInitOk = FALSE;
  54. BOOL bReportedNotOpen = FALSE;
  55. #ifdef DBG
  56. LONG64 clock0, clock1, freq, diff;
  57. #endif
  58. BOOL
  59. DllProcessAttach (
  60. IN HANDLE DllHandle
  61. )
  62. /*++
  63. Description:
  64. perform any initialization function that apply to all object
  65. modules
  66. --*/
  67. {
  68. BOOL bReturn = TRUE;
  69. NTSTATUS status;
  70. WCHAR wszTempBuffer[MAX_PATH];
  71. LONG lStatus;
  72. DWORD dwBufferSize;
  73. UNREFERENCED_PARAMETER (DllHandle);
  74. if (hLibHeap == NULL) {
  75. hLibHeap = HeapCreate (0, 1, 0);
  76. }
  77. assert (hLibHeap != NULL);
  78. if (hLibHeap == NULL) {
  79. return FALSE;
  80. }
  81. // open handle to the event log
  82. if (hEventLog == NULL) {
  83. hEventLog = MonOpenEventLog((LPWSTR)L"PerfOS");
  84. //
  85. // collect basic and static processor data
  86. //
  87. status = NtQuerySystemInformation(
  88. SystemBasicInformation,
  89. &BasicInfo,
  90. sizeof(SYSTEM_BASIC_INFORMATION),
  91. NULL
  92. );
  93. if (!NT_SUCCESS(status)) {
  94. BasicInfo.PageSize = 0;
  95. status = (LONG)RtlNtStatusToDosError(status);
  96. if (hEventLog != NULL) {
  97. ReportEvent (hEventLog,
  98. EVENTLOG_ERROR_TYPE,
  99. 0,
  100. PERFOS_UNABLE_QUERY_BASIC_INFO,
  101. NULL,
  102. 0,
  103. sizeof(DWORD),
  104. NULL,
  105. (LPVOID)&status);
  106. }
  107. bReturn = FALSE;
  108. }
  109. }
  110. wszTempBuffer[0] = UNICODE_NULL;
  111. wszTempBuffer[MAX_PATH-1] = UNICODE_NULL;
  112. lStatus = GetPerflibKeyValue (
  113. szTotalValue,
  114. REG_SZ,
  115. sizeof(wszTempBuffer) - sizeof(WCHAR),
  116. (LPVOID)&wszTempBuffer[0],
  117. DEFAULT_TOTAL_STRING_LEN,
  118. (LPVOID)&szDefaultTotalString[0]);
  119. if (lStatus == ERROR_SUCCESS) {
  120. // then a string was returned in the temp buffer
  121. dwBufferSize = lstrlenW (wszTempBuffer) + 1;
  122. dwBufferSize *= sizeof (WCHAR);
  123. wszTotal = ALLOCMEM (dwBufferSize);
  124. if (wszTotal == NULL) {
  125. // unable to allocate buffer so use static buffer
  126. wszTotal = (LPWSTR)&szDefaultTotalString[0];
  127. } else {
  128. memcpy (wszTotal, wszTempBuffer, dwBufferSize);
  129. }
  130. } else {
  131. // unable to get string from registry so just use static buffer
  132. wszTotal = (LPWSTR)&szDefaultTotalString[0];
  133. }
  134. return bReturn;
  135. }
  136. BOOL
  137. DllProcessDetach (
  138. IN HANDLE DllHandle
  139. )
  140. {
  141. UNREFERENCED_PARAMETER (DllHandle);
  142. if ((dwCpuOpenCount + dwPageOpenCount + dwObjOpenCount) != 0) {
  143. // close the objects now sinc this is the last chance
  144. // as the DLL is in the process of being unloaded
  145. // if any of the open counters are > 1, then set them to 1
  146. // to insure the object is closed on this call
  147. if (dwCpuOpenCount > 1) dwCpuOpenCount = 1;
  148. if (dwPageOpenCount > 1) dwPageOpenCount = 1;
  149. if (dwObjOpenCount > 1) dwObjOpenCount = 1;
  150. CloseOSObject();
  151. }
  152. assert ((dwCpuOpenCount + dwPageOpenCount + dwObjOpenCount) == 0);
  153. if ((wszTotal != NULL) && (wszTotal != &szDefaultTotalString[0])) {
  154. FREEMEM (wszTotal);
  155. wszTotal = NULL;
  156. }
  157. if (HeapDestroy (hLibHeap)) hLibHeap = NULL;
  158. if (hEventLog != NULL) {
  159. MonCloseEventLog ();
  160. hEventLog = NULL;
  161. }
  162. return TRUE;
  163. }
  164. BOOL
  165. __stdcall
  166. DllInit(
  167. IN HANDLE DLLHandle,
  168. IN DWORD Reason,
  169. IN LPVOID ReservedAndUnused
  170. )
  171. {
  172. ReservedAndUnused;
  173. // this will prevent the DLL from getting
  174. // the DLL_THREAD_* messages
  175. DisableThreadLibraryCalls (DLLHandle);
  176. switch(Reason) {
  177. case DLL_PROCESS_ATTACH:
  178. return DllProcessAttach (DLLHandle);
  179. case DLL_PROCESS_DETACH:
  180. return DllProcessDetach (DLLHandle);
  181. case DLL_THREAD_ATTACH:
  182. case DLL_THREAD_DETACH:
  183. default:
  184. return TRUE;
  185. }
  186. }
  187. DWORD APIENTRY
  188. OpenOSObject (
  189. LPWSTR lpDeviceNames
  190. )
  191. /*++
  192. Routine Description:
  193. This routine will initialize the data structures used to pass
  194. data back to the registry
  195. Arguments:
  196. Pointer to object ID of each device to be opened (PerfGen)
  197. Return Value:
  198. None.
  199. --*/
  200. {
  201. DWORD status;
  202. // cache object does not need to be opened
  203. // open Processor Object
  204. status = OpenProcessorObject (lpDeviceNames);
  205. // memory object does not need to be opened
  206. // open Objects object
  207. if (status == ERROR_SUCCESS) {
  208. status = OpenObjectsObject (lpDeviceNames);
  209. // open Pagefile object
  210. if (status == ERROR_SUCCESS) {
  211. status = OpenPageFileObject (lpDeviceNames);
  212. if (status != ERROR_SUCCESS) {
  213. // processor & Objects opened & page file did not
  214. // close the open objects
  215. CloseProcessorObject ();
  216. CloseObjectsObject();
  217. }
  218. } else {
  219. // processor Open and Objects did not
  220. // close the open objects
  221. CloseProcessorObject();
  222. }
  223. } else {
  224. // nothing opened
  225. }
  226. if (status == ERROR_SUCCESS) {
  227. status = OpenSystemObject(lpDeviceNames);
  228. if (status != ERROR_SUCCESS) {
  229. CloseProcessorObject();
  230. CloseObjectsObject();
  231. CloseSystemObject();
  232. }
  233. }
  234. if (status == ERROR_SUCCESS) {
  235. bInitOk = TRUE;
  236. } else if (hEventLog != NULL) {
  237. ReportEvent (hEventLog,
  238. EVENTLOG_ERROR_TYPE,
  239. 0,
  240. PERFOS_UNABLE_OPEN,
  241. NULL,
  242. 0,
  243. sizeof(DWORD),
  244. NULL,
  245. (LPVOID)&status);
  246. }
  247. #ifdef DBG
  248. DbgPrint("PERFOS: OpenOsObject() status = %d\n", status);
  249. #endif
  250. return status;
  251. }
  252. DWORD APIENTRY
  253. ReadOSObjectData (
  254. IN DWORD FunctionCallMask,
  255. IN OUT LPVOID *lppData,
  256. IN OUT LPDWORD lpcbTotalBytes,
  257. IN OUT LPDWORD lpNumObjectTypes
  258. )
  259. /*++
  260. Routine Description:
  261. This routine will return the data for the OS object
  262. Arguments:
  263. IN DWORD FunctionCallMask
  264. bit mask of functions to call
  265. IN OUT LPVOID *lppData
  266. IN: pointer to the address of the buffer to receive the completed
  267. data structure. In the case of an item list, Global or Costly
  268. query, this will be a collection of one or more perf data objects.
  269. In the case of a PERF_QUERY_OBJECTS request, this will be an array
  270. of DWORDs listing the object ID's of the perf data objects
  271. supported by this DLL.
  272. OUT: points to the first byte after the data structure added by this
  273. routine. This routine updated the value at lppdata after appending
  274. its data.
  275. IN OUT LPDWORD lpcbTotalBytes
  276. IN: the address of the DWORD that tells the size in bytes of the
  277. buffer referenced by the lppData argument
  278. OUT: the number of bytes added by this routine is writted to the
  279. DWORD pointed to by this argument
  280. IN OUT LPDWORD NumObjectTypes
  281. IN: the number of objects listed in the array of DWORDs referenced
  282. by the pObjList argument
  283. OUT: the number of objects returned by this routine is writted to the
  284. DWORD pointed to by this argument
  285. Returns:
  286. 0 if successful, else Win 32 error code of failure
  287. --*/
  288. {
  289. NTSTATUS Status;
  290. DWORD lReturn = ERROR_SUCCESS;
  291. DWORD FunctionIndex;
  292. DWORD dwNumObjectsFromFunction;
  293. DWORD dwOrigBuffSize;
  294. DWORD dwByteSize;
  295. DWORD dwReturnedBufferSize;
  296. // collect data
  297. if (FunctionCallMask & POS_GET_SYS_PERF_INFO) {
  298. #ifdef DBG
  299. STARTTIMING;
  300. #endif
  301. Status = NtQuerySystemInformation(
  302. SystemPerformanceInformation,
  303. &SysPerfInfo,
  304. sizeof(SysPerfInfo),
  305. &dwReturnedBufferSize
  306. );
  307. if (!NT_SUCCESS(Status)) {
  308. if (hEventLog != NULL) {
  309. ReportEvent (hEventLog,
  310. EVENTLOG_ERROR_TYPE,
  311. 0,
  312. PERFOS_UNABLE_QUERY_SYS_PERF_INFO,
  313. NULL,
  314. 0,
  315. sizeof(DWORD),
  316. NULL,
  317. (LPVOID)&Status);
  318. }
  319. memset (&SysPerfInfo, 0, sizeof(SysPerfInfo));
  320. }
  321. #ifdef DBG
  322. ENDTIMING(("PERFOS: %d takes %I64d ms\n", __LINE__, diff));
  323. #endif
  324. } else {
  325. memset (&SysPerfInfo, 0, sizeof(SysPerfInfo));
  326. }
  327. *lpNumObjectTypes = 0;
  328. dwOrigBuffSize = dwByteSize = *lpcbTotalBytes;
  329. *lpcbTotalBytes = 0;
  330. // remove query bits
  331. FunctionCallMask &= POS_COLLECT_FUNCTION_MASK;
  332. for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
  333. if (posDataFuncInfo[FunctionIndex].dwCollectFunctionBit &
  334. FunctionCallMask) {
  335. dwNumObjectsFromFunction = 0;
  336. // check for QUADWORD alignment of data buffer
  337. assert (((ULONG_PTR)(*lppData) & 0x00000007) == 0);
  338. #ifdef DBG
  339. STARTTIMING;
  340. #endif
  341. lReturn = (*posDataFuncInfo[FunctionIndex].pCollectFunction) (
  342. lppData,
  343. &dwByteSize,
  344. &dwNumObjectsFromFunction);
  345. if (lReturn == ERROR_SUCCESS) {
  346. *lpNumObjectTypes += dwNumObjectsFromFunction;
  347. *lpcbTotalBytes += dwByteSize;
  348. dwOrigBuffSize -= dwByteSize;
  349. dwByteSize = dwOrigBuffSize;
  350. } else {
  351. break;
  352. }
  353. #ifdef DBG
  354. ENDTIMING(("PERFOS: %d POS %d takes %I64d ms\n", __LINE__, FunctionIndex, diff));
  355. #endif
  356. }
  357. // *lppData is updated by each function
  358. // *lpcbTotalBytes is updated after each successful function
  359. // *lpNumObjects is updated after each successful function
  360. }
  361. return lReturn;
  362. }
  363. /*
  364. DWORD APIENTRY
  365. QueryOSObjectData (
  366. IN LPDWORD pObjList,
  367. IN OUT LPVOID *lppData,
  368. IN OUT LPDWORD lpcbTotalBytes,
  369. IN OUT LPDWORD lpNumObjectTypes
  370. )
  371. */
  372. /*++
  373. Routine Description:
  374. This routine will return the data for the processor object
  375. Arguments:
  376. IN LPDWORD *pObjList
  377. pointer to an array of Performance Objects that are
  378. to be returned to the caller. Each object is referenced by its
  379. DWORD value. If the first element in the array is one of the
  380. following then only the first item is read and the following
  381. data is returned:
  382. PERF_QUERY_OBJECTS an array of object id's supported
  383. by this function is returned in the data
  384. PERF_QUERY_GLOBAL all perf objects supported by this
  385. function are returned (Except COSTLY objects)
  386. PERF_QUERY_COSTLY all COSTLY perf objects supported
  387. by this function are returned
  388. Foreign objects are not supported by this API
  389. IN OUT LPVOID *lppData
  390. IN: pointer to the address of the buffer to receive the completed
  391. data structure. In the case of an item list, Global or Costly
  392. query, this will be a collection of one or more perf data objects.
  393. In the case of a PERF_QUERY_OBJECTS request, this will be an array
  394. of DWORDs listing the object ID's of the perf data objects
  395. supported by this DLL.
  396. OUT: points to the first byte after the data structure added by this
  397. routine. This routine updated the value at lppdata after appending
  398. its data.
  399. IN OUT LPDWORD lpcbTotalBytes
  400. IN: the address of the DWORD that tells the size in bytes of the
  401. buffer referenced by the lppData argument
  402. OUT: the number of bytes added by this routine is writted to the
  403. DWORD pointed to by this argument
  404. IN OUT LPDWORD NumObjectTypes
  405. IN: the number of objects listed in the array of DWORDs referenced
  406. by the pObjList argument
  407. OUT: the number of objects returned by this routine is writted to the
  408. DWORD pointed to by this argument
  409. Returns:
  410. 0 if successful, else Win 32 error code of failure
  411. --*/
  412. /*
  413. {
  414. LONG lReturn = ERROR_SUCCESS;
  415. DWORD FunctionCallMask = 0;
  416. DWORD FunctionIndex;
  417. LPDWORD pdwRetBuffer;
  418. DWORD ObjectIndex;
  419. if (!bInitOk) {
  420. if (hEventLog != NULL)) {
  421. ReportEvent (hEventLog,
  422. EVENTLOG_ERROR_TYPE,
  423. 0,
  424. PERFOS_NOT_OPEN,
  425. NULL,
  426. 0,
  427. 0,
  428. NULL,
  429. NULL);
  430. }
  431. *lpcbTotalBytes = (DWORD) 0;
  432. *lpNumObjectTypes = (DWORD) 0;
  433. lReturn = ERROR_SUCCESS;
  434. goto QUERY_BAIL_OUT;
  435. }
  436. // evaluate the object list
  437. if (*lpNumObjectTypes == 1) {
  438. // then see if it's a predefined request value
  439. if (pObjList[0] == PERF_QUERY_GLOBAL) {
  440. FunctionCallMask = POS_COLLECT_GLOBAL_DATA;
  441. } else if (pObjList[0] == PERF_QUERY_COSTLY) {
  442. FunctionCallMask = POS_COLLECT_COSTLY_DATA;
  443. } else if (pObjList[0] == PERF_QUERY_OBJECTS) {
  444. if (*lpcbTotalBytes < (POS_NUM_FUNCS * sizeof(DWORD))) {
  445. lReturn = ERROR_MORE_DATA;
  446. } else {
  447. pdwRetBuffer = (LPDWORD)*lppData;
  448. for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
  449. pdwRetBuffer[FunctionIndex] =
  450. posDataFuncInfo[FunctionIndex].dwObjectId;
  451. }
  452. *lppData = &pdwRetBuffer[FunctionIndex];
  453. *lpcbTotalBytes = (POS_NUM_FUNCS * sizeof(DWORD));
  454. *lpNumObjectTypes = FunctionIndex;
  455. lReturn = ERROR_SUCCESS;
  456. goto QUERY_BAIL_OUT;
  457. }
  458. }
  459. }
  460. if (FunctionCallMask == 0) {
  461. // it's not a predfined value so run through the list
  462. // read the object list and build the call mask
  463. ObjectIndex = 0;
  464. while (ObjectIndex < *lpNumObjectTypes) {
  465. // search for this object in the list of object id's
  466. // supported by this DLL
  467. for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
  468. if (pObjList[ObjectIndex] ==
  469. posDataFuncInfo[FunctionIndex].dwObjectId) {
  470. FunctionCallMask |=
  471. posDataFuncInfo[FunctionIndex].dwCollectFunctionBit;
  472. break; // out of inner loop
  473. }
  474. }
  475. ObjectIndex++;
  476. }
  477. }
  478. if (FunctionCallMask != 0) {
  479. lReturn = ReadOSObjectData (FunctionCallMask,
  480. lppData,
  481. lpcbTotalBytes,
  482. lpNumObjectTypes);
  483. } else {
  484. *lpcbTotalBytes = (DWORD) 0;
  485. *lpNumObjectTypes = (DWORD) 0;
  486. lReturn = ERROR_SUCCESS;
  487. }
  488. QUERY_BAIL_OUT:
  489. return lReturn;
  490. }
  491. */
  492. DWORD APIENTRY
  493. CollectOSObjectData (
  494. IN LPWSTR lpValueName,
  495. IN OUT LPVOID *lppData,
  496. IN OUT LPDWORD lpcbTotalBytes,
  497. IN OUT LPDWORD lpNumObjectTypes
  498. )
  499. /*++
  500. Routine Description:
  501. This routine will return the data for the processor object
  502. Arguments:
  503. IN LPWSTR lpValueName
  504. pointer to a wide character string passed by registry.
  505. IN OUT LPVOID *lppData
  506. IN: pointer to the address of the buffer to receive the completed
  507. PerfDataBlock and subordinate structures. This routine will
  508. append its data to the buffer starting at the point referenced
  509. by *lppData.
  510. OUT: points to the first byte after the data structure added by this
  511. routine. This routine updated the value at lppdata after appending
  512. its data.
  513. IN OUT LPDWORD lpcbTotalBytes
  514. IN: the address of the DWORD that tells the size in bytes of the
  515. buffer referenced by the lppData argument
  516. OUT: the number of bytes added by this routine is writted to the
  517. DWORD pointed to by this argument
  518. IN OUT LPDWORD NumObjectTypes
  519. IN: the address of the DWORD to receive the number of objects added
  520. by this routine
  521. OUT: the number of objects added by this routine is writted to the
  522. DWORD pointed to by this argument
  523. Returns:
  524. 0 if successful, else Win 32 error code of failure
  525. --*/
  526. {
  527. LONG lReturn = ERROR_SUCCESS;
  528. // build bit mask of functions to call
  529. DWORD dwQueryType;
  530. DWORD FunctionCallMask = 0;
  531. DWORD FunctionIndex;
  532. #ifdef DBG
  533. STARTTIMING;
  534. #endif
  535. if (!bInitOk) {
  536. if (!bReportedNotOpen) {
  537. if (hEventLog != NULL) {
  538. bReportedNotOpen = ReportEvent (hEventLog,
  539. EVENTLOG_ERROR_TYPE,
  540. 0,
  541. PERFOS_NOT_OPEN,
  542. NULL,
  543. 0,
  544. 0,
  545. NULL,
  546. NULL);
  547. }
  548. }
  549. *lpcbTotalBytes = (DWORD) 0;
  550. *lpNumObjectTypes = (DWORD) 0;
  551. lReturn = ERROR_SUCCESS;
  552. goto COLLECT_BAIL_OUT;
  553. }
  554. dwQueryType = GetQueryType (lpValueName);
  555. switch (dwQueryType) {
  556. case QUERY_ITEMS:
  557. for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
  558. if (IsNumberInUnicodeList (
  559. posDataFuncInfo[FunctionIndex].dwObjectId, lpValueName)) {
  560. FunctionCallMask |=
  561. posDataFuncInfo[FunctionIndex].dwCollectFunctionBit;
  562. }
  563. }
  564. break;
  565. case QUERY_GLOBAL:
  566. FunctionCallMask = POS_COLLECT_GLOBAL_DATA;
  567. break;
  568. case QUERY_FOREIGN:
  569. FunctionCallMask = POS_COLLECT_FOREIGN_DATA;
  570. break;
  571. case QUERY_COSTLY:
  572. FunctionCallMask = POS_COLLECT_COSTLY_DATA;
  573. break;
  574. default:
  575. FunctionCallMask = POS_COLLECT_COSTLY_DATA;
  576. break;
  577. }
  578. if (FunctionCallMask != 0) {
  579. lReturn = ReadOSObjectData (FunctionCallMask,
  580. lppData,
  581. lpcbTotalBytes,
  582. lpNumObjectTypes);
  583. } else {
  584. *lpcbTotalBytes = (DWORD) 0;
  585. *lpNumObjectTypes = (DWORD) 0;
  586. lReturn = ERROR_SUCCESS;
  587. }
  588. COLLECT_BAIL_OUT:
  589. #ifdef DBG
  590. ENDTIMING (("PERFOS: %d Collect takes %I64d ms\n", __LINE__, diff));
  591. #endif
  592. return lReturn;
  593. }
  594. DWORD APIENTRY
  595. CloseOSObject (
  596. )
  597. /*++
  598. Routine Description:
  599. This routine closes the open handles to the Signal Gen counters.
  600. Arguments:
  601. None.
  602. Return Value:
  603. ERROR_SUCCESS
  604. --*/
  605. {
  606. DWORD status;
  607. DWORD dwReturn = ERROR_SUCCESS;
  608. // cache object does not need to be closeed
  609. // close Processor Object
  610. status = CloseProcessorObject ();
  611. assert (status == ERROR_SUCCESS);
  612. if (status != ERROR_SUCCESS) dwReturn = status;
  613. // memory object does not need to be closeed
  614. // close Objects object
  615. status = CloseObjectsObject ();
  616. assert (status == ERROR_SUCCESS);
  617. if (status != ERROR_SUCCESS) dwReturn = status;
  618. // close Pagefile object
  619. status = ClosePageFileObject ();
  620. assert (status == ERROR_SUCCESS);
  621. if (status != ERROR_SUCCESS) dwReturn = status;
  622. // close System object
  623. status = CloseSystemObject();
  624. if (status != ERROR_SUCCESS) dwReturn = status;
  625. return dwReturn;
  626. }