Source code of Windows XP (NT5)
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.

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