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.

1918 lines
62 KiB

  1. /*++ BUILD Version: 0001 // Increment this if a change has global effects
  2. Copyright (c) 1992-1994 Microsoft Corporation
  3. Module Name:
  4. perflib.c
  5. Abstract:
  6. This file implements the Configuration Registry
  7. for the purposes of the Performance Monitor.
  8. This file contains the code which implements the Performance part
  9. of the Configuration Registry.
  10. Author:
  11. Russ Blake 11/15/91
  12. Revision History:
  13. 04/20/91 - russbl - Converted to lib in Registry
  14. from stand-alone .dll form.
  15. 11/04/92 - a-robw - added pagefile and image counter routines
  16. 11/01/96 - bobw - revamped to support dynamic loading and
  17. unloading of performance modules
  18. --*/
  19. #define UNICODE
  20. //
  21. // Include files
  22. //
  23. #pragma warning(disable:4306)
  24. #include <nt.h>
  25. #include <ntrtl.h>
  26. #include <nturtl.h>
  27. #include <ntregapi.h>
  28. #include <ntprfctr.h>
  29. #include <windows.h>
  30. #include <string.h>
  31. #include <stdlib.h>
  32. #include <winperf.h>
  33. #include <rpc.h>
  34. #include "regrpc.h"
  35. #include "ntconreg.h"
  36. #include "prflbmsg.h" // event log messages
  37. #include <initguid.h>
  38. #include <guiddef.h>
  39. #define _INIT_WINPERFP_
  40. #include "perflib.h"
  41. #pragma warning (default:4306)
  42. #define NUM_VALUES 2
  43. //
  44. // performance gathering thead priority
  45. //
  46. #define DEFAULT_THREAD_PRIORITY THREAD_BASE_PRIORITY_LOWRT
  47. //
  48. // constants
  49. //
  50. const WCHAR DLLValue[] = L"Library";
  51. const CHAR OpenValue[] = "Open";
  52. const CHAR CloseValue[] = "Close";
  53. const CHAR CollectValue[] = "Collect";
  54. const CHAR QueryValue[] = "Query";
  55. const WCHAR ObjListValue[] = L"Object List";
  56. const WCHAR LinkageKey[] = L"\\Linkage";
  57. const WCHAR ExportValue[] = L"Export";
  58. const WCHAR PerflibKey[] = L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib";
  59. const WCHAR HKLMPerflibKey[] = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib";
  60. const WCHAR CounterValue[] = L"Counter";
  61. const WCHAR HelpValue[] = L"Help";
  62. const WCHAR PerfSubKey[] = L"\\Performance";
  63. const WCHAR ExtPath[] = L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services";
  64. const WCHAR OpenTimeout[] = L"Open Timeout";
  65. const WCHAR CollectTimeout[] = L"Collect Timeout";
  66. const WCHAR EventLogLevel[] = L"EventLogLevel";
  67. const WCHAR ExtCounterTestLevel[] = L"ExtCounterTestLevel";
  68. const WCHAR OpenProcedureWaitTime[] = L"OpenProcedureWaitTime";
  69. const WCHAR TotalInstanceName[] = L"TotalInstanceName";
  70. const WCHAR LibraryUnloadTime[] = L"Library Unload Time";
  71. const WCHAR KeepResident[] = L"Keep Library Resident";
  72. const WCHAR NULL_STRING[] = L"\0"; // pointer to null string
  73. const WCHAR UseCollectionThread[] = L"UseCollectionThread";
  74. const WCHAR cszLibraryValidationData[] = L"Library Validation Code";
  75. const WCHAR cszSuccessfulFileData[] = L"Successful File Date";
  76. const WCHAR cszPerflibFlags[] = L"Configuration Flags";
  77. const WCHAR FirstCounter[] = L"First Counter";
  78. const WCHAR LastCounter[] = L"Last Counter";
  79. const WCHAR FirstHelp[] = L"First Help";
  80. const WCHAR LastHelp[] = L"Last Help";
  81. const WCHAR cszFailureCount[] = L"Error Count";
  82. const WCHAR cszFailureLimit[] = L"Error Count Limit";
  83. //
  84. // external variables defined in perfname.c
  85. //
  86. extern WCHAR DefaultLangId[];
  87. WCHAR NativeLangId[8] = L"\0";
  88. //
  89. // Data collection thread variables
  90. //
  91. #define COLLECTION_WAIT_TIME 10000L // 10 seconds to get all the data
  92. HANDLE hCollectThread = NULL;
  93. #define COLLECT_THREAD_PROCESS_EVENT 0
  94. #define COLLECT_THREAD_EXIT_EVENT 1
  95. #define COLLECT_THREAD_LOOP_EVENT_COUNT 2
  96. #define COLLECT_THREAD_DONE_EVENT 2
  97. #define COLLECT_THREAD_EVENT_COUNT 3
  98. static HANDLE hCollectEvents[COLLECT_THREAD_EVENT_COUNT];
  99. static BOOL bThreadHung = FALSE;
  100. static DWORD CollectThreadFunction (LPVOID dwArg);
  101. #define COLL_FLAG_USE_SEPARATE_THREAD 1
  102. DWORD dwCollectionFlags = 0;
  103. //
  104. // Global variable Definitions
  105. //
  106. // event log handle for perflib generated errors
  107. //
  108. HANDLE hEventLog = NULL;
  109. //
  110. // used to count concurrent opens.
  111. //
  112. DWORD NumberOfOpens = 0;
  113. //
  114. // Synchronization objects for Multi-threaded access
  115. //
  116. HANDLE hGlobalDataMutex = NULL; // sync for ctr object list
  117. //
  118. // computer name cache buffers. Initialized in predefh.c
  119. //
  120. DWORD ComputerNameLength;
  121. LPWSTR pComputerName = NULL;
  122. // The next pointer is used to point to an array of addresses of
  123. // Open/Collect/Close routines found by searching the Configuration Registry.
  124. // object list head
  125. PEXT_OBJECT ExtensibleObjects = NULL;
  126. //
  127. // count of active list users (threads)
  128. DWORD dwExtObjListRefCount = 0;
  129. //
  130. // event to indicate the object list is not in use
  131. HANDLE hExtObjListIsNotInUse = NULL;
  132. //
  133. // Number of Extensible Objects found during the "open" call
  134. DWORD NumExtensibleObjects = 0;
  135. //
  136. // see if the perflib data is restricted to ADMIN's ONLY or just anyone
  137. //
  138. static LONG lCheckProfileSystemRight = CPSR_NOT_DEFINED;
  139. //
  140. // flag to see if the ProfileSystemPerformance priv should be set.
  141. // if it is attempted and the caller does not have permission to use this priv.
  142. // it won't be set. This is only attempted once.
  143. //
  144. static BOOL bEnableProfileSystemPerfPriv = FALSE;
  145. //
  146. // timeout value (in mS) for timing threads & libraries
  147. //
  148. DWORD dwThreadAndLibraryTimeout = PERFLIB_TIMING_THREAD_TIMEOUT;
  149. // global key for access to HKLM\Software\....\Perflib
  150. //
  151. HKEY ghKeyPerflib = NULL;
  152. //
  153. // Error report frequency
  154. DWORD dwErrorFrequency = 1;
  155. LONG lEventLogLevel = LOG_NONE;
  156. LONG lPerflibConfigFlags = PLCF_DEFAULT;
  157. // performance data block entries
  158. WCHAR szPerflibSectionFile[MAX_PATH];
  159. WCHAR szPerflibSectionName[MAX_PATH];
  160. HANDLE hPerflibSectionFile = NULL;
  161. HANDLE hPerflibSectionMap = NULL;
  162. LPVOID lpPerflibSectionAddr = NULL;
  163. DWORD dwBoostPriority = 1;
  164. #define dwPerflibSectionMaxEntries 127L
  165. const DWORD dwPerflibSectionSize = (sizeof(PERFDATA_SECTION_HEADER) + \
  166. (sizeof(PERFDATA_SECTION_RECORD) * dwPerflibSectionMaxEntries));
  167. // forward function references
  168. LONG
  169. PerfEnumTextValue (
  170. IN HKEY hKey,
  171. IN DWORD dwIndex,
  172. OUT PUNICODE_STRING lpValueName,
  173. OUT LPDWORD lpReserved OPTIONAL,
  174. OUT LPDWORD lpType OPTIONAL,
  175. OUT LPBYTE lpData,
  176. IN OUT LPDWORD lpcbData,
  177. OUT LPDWORD lpcbLen OPTIONAL
  178. );
  179. #if 0 // collection thread functions are not supported
  180. DWORD
  181. OpenCollectionThread (
  182. )
  183. {
  184. BOOL bError = FALSE;
  185. DWORD dwThreadID;
  186. assert (hCollectThread == NULL);
  187. // if it's already created, then just return
  188. if (hCollectThread != NULL) return ERROR_SUCCESS;
  189. bThreadHung = FALSE;
  190. hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] = CreateEvent (
  191. NULL, // default security
  192. FALSE, // auto reset
  193. FALSE, // non-signaled
  194. NULL); // no name
  195. bError = hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] == NULL;
  196. assert (hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] != NULL);
  197. hCollectEvents[COLLECT_THREAD_EXIT_EVENT] = CreateEvent (
  198. NULL, // default security
  199. FALSE, // auto reset
  200. FALSE, // non-signaled
  201. NULL); // no name
  202. bError = (hCollectEvents[COLLECT_THREAD_EXIT_EVENT] == NULL) | bError;
  203. assert (hCollectEvents[COLLECT_THREAD_EXIT_EVENT] != NULL);
  204. hCollectEvents[COLLECT_THREAD_DONE_EVENT] = CreateEvent (
  205. NULL, // default security
  206. FALSE, // auto reset
  207. FALSE, // non-signaled
  208. NULL); // no name
  209. bError = (hCollectEvents[COLLECT_THREAD_DONE_EVENT] == NULL) | bError;
  210. assert (hCollectEvents[COLLECT_THREAD_DONE_EVENT] != NULL);
  211. if (!bError) {
  212. // create data collection thread
  213. hCollectThread = CreateThread (
  214. NULL, // default security
  215. 0, // default stack size
  216. (LPTHREAD_START_ROUTINE)CollectThreadFunction,
  217. NULL, // no argument
  218. 0, // no flags
  219. &dwThreadID); // we don't need the ID so it's in an automatic variable
  220. if (hCollectThread == NULL) {
  221. bError = TRUE;
  222. }
  223. assert (hCollectThread != NULL);
  224. }
  225. if (bError) {
  226. if (hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] != NULL) {
  227. CloseHandle (hCollectEvents[COLLECT_THREAD_PROCESS_EVENT]);
  228. hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] = NULL;
  229. }
  230. if (hCollectEvents[COLLECT_THREAD_EXIT_EVENT] != NULL) {
  231. CloseHandle (hCollectEvents[COLLECT_THREAD_EXIT_EVENT]);
  232. hCollectEvents[COLLECT_THREAD_EXIT_EVENT] = NULL;
  233. }
  234. if (hCollectEvents[COLLECT_THREAD_DONE_EVENT] != NULL) {
  235. CloseHandle (hCollectEvents[COLLECT_THREAD_DONE_EVENT] = NULL);
  236. hCollectEvents[COLLECT_THREAD_DONE_EVENT] = NULL;
  237. }
  238. if (hCollectThread != NULL) {
  239. CloseHandle (hCollectThread);
  240. hCollectThread = NULL;
  241. }
  242. return (GetLastError());
  243. } else {
  244. return ERROR_SUCCESS;
  245. }
  246. }
  247. DWORD
  248. CloseCollectionThread (
  249. )
  250. {
  251. if (hCollectThread != NULL) {
  252. // close the data collection thread
  253. if (bThreadHung) {
  254. // then kill it the hard way
  255. // this might cause problems, but it's better than
  256. // a thread leak
  257. TerminateThread (hCollectThread, ERROR_TIMEOUT);
  258. } else {
  259. // then ask it to leave
  260. SetEvent (hCollectEvents[COLLECT_THREAD_EXIT_EVENT]);
  261. }
  262. // wait for thread to leave
  263. WaitForSingleObject (hCollectThread, COLLECTION_WAIT_TIME);
  264. // close the handles and clear the variables
  265. CloseHandle (hCollectThread);
  266. hCollectThread = NULL;
  267. CloseHandle (hCollectEvents[COLLECT_THREAD_PROCESS_EVENT]);
  268. hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] = NULL;
  269. CloseHandle (hCollectEvents[COLLECT_THREAD_EXIT_EVENT]);
  270. hCollectEvents[COLLECT_THREAD_EXIT_EVENT] = NULL;
  271. CloseHandle (hCollectEvents[COLLECT_THREAD_DONE_EVENT]);
  272. hCollectEvents[COLLECT_THREAD_DONE_EVENT] = NULL;
  273. } else {
  274. // nothing was opened
  275. }
  276. return ERROR_SUCCESS;
  277. }
  278. #endif
  279. DWORD
  280. PerfOpenKey (
  281. )
  282. {
  283. LARGE_INTEGER liPerfDataWaitTime;
  284. PLARGE_INTEGER pTimeout;
  285. NTSTATUS status = STATUS_SUCCESS;
  286. DWORD dwFnStatus = ERROR_SUCCESS;
  287. DWORD dwType, dwSize, dwValue;
  288. HANDLE hDataMutex;
  289. DWORD bMutexHeld = FALSE;
  290. OSVERSIONINFOEXW OsVersion;
  291. if (hGlobalDataMutex == NULL) {
  292. hDataMutex = CreateMutex(NULL, TRUE, NULL);
  293. if (hDataMutex == NULL) {
  294. DebugPrint((0, "Perf Data Mutex Not Initialized\n"));
  295. goto OPD_Error_Exit_NoSemaphore;
  296. }
  297. if (InterlockedCompareExchangePointer(
  298. &hGlobalDataMutex,
  299. hDataMutex,
  300. NULL) != NULL) {
  301. CloseHandle(hDataMutex); // mutex just got created by another thread
  302. hDataMutex = NULL;
  303. }
  304. else {
  305. hGlobalDataMutex = hDataMutex;
  306. bMutexHeld = TRUE;
  307. }
  308. }
  309. if (!bMutexHeld) {
  310. if ((dwThreadAndLibraryTimeout == 0) ||
  311. (dwThreadAndLibraryTimeout == INFINITE)) {
  312. pTimeout = NULL;
  313. }
  314. else {
  315. liPerfDataWaitTime.QuadPart = MakeTimeOutValue(dwThreadAndLibraryTimeout);
  316. pTimeout = &liPerfDataWaitTime;
  317. }
  318. status = NtWaitForSingleObject (
  319. hGlobalDataMutex, // Mutex
  320. FALSE, // not alertable
  321. pTimeout); // wait time
  322. if (status != STATUS_SUCCESS) {
  323. // unable to contine, return error;
  324. dwFnStatus = PerfpDosError(status);
  325. DebugPrint((0, "Status=%X in waiting for global mutex",
  326. status));
  327. goto OPD_Error_Exit_NoSemaphore;
  328. }
  329. }
  330. // if here, then the data semaphore has been acquired by this thread
  331. if (!NumberOfOpens++) {
  332. if (ghKeyPerflib == NULL) {
  333. dwFnStatus = (DWORD)RegOpenKeyExW (
  334. HKEY_LOCAL_MACHINE,
  335. HKLMPerflibKey,
  336. 0L,
  337. KEY_READ,
  338. &ghKeyPerflib);
  339. }
  340. assert (ghKeyPerflib != NULL);
  341. dwSize = sizeof(dwValue);
  342. dwValue = dwType = 0;
  343. dwFnStatus = PrivateRegQueryValueExW (
  344. ghKeyPerflib,
  345. DisablePerformanceCounters,
  346. NULL,
  347. &dwType,
  348. (LPBYTE)&dwValue,
  349. &dwSize);
  350. if ((dwFnStatus == ERROR_SUCCESS) &&
  351. (dwType == REG_DWORD) &&
  352. (dwValue == 1)) {
  353. // then DON'T Load any libraries and unload any that have been
  354. // loaded
  355. NumberOfOpens--; // since it didn't open.
  356. dwFnStatus = ERROR_SERVICE_DISABLED;
  357. } else {
  358. ComputerNameLength = 0;
  359. GetComputerNameW(pComputerName, &ComputerNameLength);
  360. ComputerNameLength++; // account for the NULL terminator
  361. pComputerName = ALLOCMEM(ComputerNameLength * sizeof(WCHAR));
  362. if (pComputerName == NULL) {
  363. ComputerNameLength = 0;
  364. }
  365. else {
  366. if ( !GetComputerNameW(pComputerName, &ComputerNameLength) ) {
  367. //
  368. // Signal failure to data collection routine
  369. //
  370. ComputerNameLength = 0;
  371. } else {
  372. pComputerName[ComputerNameLength] = UNICODE_NULL;
  373. ComputerNameLength = (ComputerNameLength+1) * sizeof(WCHAR);
  374. }
  375. }
  376. WinPerfStartTrace(ghKeyPerflib);
  377. // create event and indicate the list is busy
  378. hExtObjListIsNotInUse = CreateEvent (NULL, TRUE, FALSE, NULL);
  379. // read collection thread flag
  380. dwType = 0;
  381. dwSize = sizeof(DWORD);
  382. dwFnStatus = PrivateRegQueryValueExW (ghKeyPerflib,
  383. cszPerflibFlags,
  384. NULL,
  385. &dwType,
  386. (LPBYTE)&lPerflibConfigFlags,
  387. &dwSize);
  388. if ((dwFnStatus == ERROR_SUCCESS) && (dwType == REG_DWORD)) {
  389. // then keep it
  390. } else {
  391. // apply default value
  392. lPerflibConfigFlags = PLCF_DEFAULT;
  393. }
  394. // create global section for perf data on perflibs
  395. if ((hPerflibSectionFile == NULL) && (lPerflibConfigFlags & PLCF_ENABLE_PERF_SECTION)) {
  396. WCHAR szTmpFileName[MAX_PATH];
  397. PPERFDATA_SECTION_HEADER pHead;
  398. WCHAR szPID[32];
  399. // create section name
  400. lstrcpyW (szPerflibSectionName, (LPCWSTR)L"Perflib_Perfdata_");
  401. _ultow ((ULONG)GetCurrentProcessId(), szPID, 16);
  402. lstrcatW (szPerflibSectionName, szPID);
  403. // create filename
  404. lstrcpyW (szTmpFileName, (LPCWSTR)L"%TEMP%\\");
  405. lstrcatW (szTmpFileName, szPerflibSectionName);
  406. lstrcatW (szTmpFileName, (LPCWSTR)L".dat");
  407. ExpandEnvironmentStrings (szTmpFileName, szPerflibSectionFile, MAX_PATH);
  408. hPerflibSectionFile = CreateFile (szPerflibSectionFile,
  409. GENERIC_READ | GENERIC_WRITE,
  410. FILE_SHARE_READ | FILE_SHARE_WRITE,
  411. NULL,
  412. OPEN_ALWAYS,
  413. FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_TEMPORARY,
  414. NULL);
  415. if (hPerflibSectionFile != INVALID_HANDLE_VALUE) {
  416. // create file mapping object
  417. hPerflibSectionMap = CreateFileMapping (
  418. hPerflibSectionFile,
  419. NULL,
  420. PAGE_READWRITE,
  421. 0, dwPerflibSectionSize,
  422. szPerflibSectionName);
  423. if (hPerflibSectionMap != NULL) {
  424. // map view of file
  425. lpPerflibSectionAddr = MapViewOfFile (
  426. hPerflibSectionMap,
  427. FILE_MAP_WRITE,
  428. 0,0, dwPerflibSectionSize);
  429. if (lpPerflibSectionAddr != NULL) {
  430. // init section if not already
  431. pHead = (PPERFDATA_SECTION_HEADER)lpPerflibSectionAddr;
  432. if (pHead->dwInitSignature != PDSH_INIT_SIG) {
  433. // then init
  434. // clear file to 0
  435. memset (pHead, 0, dwPerflibSectionSize);
  436. pHead->dwEntriesInUse = 0;
  437. pHead->dwMaxEntries = dwPerflibSectionMaxEntries;
  438. pHead->dwMissingEntries = 0;
  439. pHead->dwInitSignature = PDSH_INIT_SIG;
  440. } else {
  441. // already initialized so leave it
  442. }
  443. } else {
  444. // unable to map file so close
  445. TRACE((WINPERF_DBG_TRACE_WARNING),
  446. (&PerflibGuid, __LINE__, PERF_OPEN_KEY, 0, 0, NULL));
  447. CloseHandle (hPerflibSectionMap);
  448. hPerflibSectionMap = NULL;
  449. CloseHandle (hPerflibSectionFile);
  450. hPerflibSectionFile = NULL;
  451. }
  452. } else {
  453. // unable to create file mapping so close file
  454. TRACE((WINPERF_DBG_TRACE_WARNING),
  455. (&PerflibGuid, __LINE__, PERF_OPEN_KEY, 0, 0, NULL));
  456. CloseHandle (hPerflibSectionFile);
  457. hPerflibSectionFile = NULL;
  458. }
  459. } else {
  460. // unable to open file so no perf stats available
  461. TRACE((WINPERF_DBG_TRACE_WARNING),
  462. (&PerflibGuid, __LINE__, PERF_OPEN_KEY, 0, 0, NULL));
  463. hPerflibSectionFile = NULL;
  464. }
  465. }
  466. // find and open perf counters
  467. OpenExtensibleObjects();
  468. dwExtObjListRefCount = 0;
  469. SetEvent (hExtObjListIsNotInUse); // indicate the list is not busy
  470. // read collection thread flag
  471. dwType = 0;
  472. dwSize = sizeof(DWORD);
  473. dwFnStatus = PrivateRegQueryValueExW (ghKeyPerflib,
  474. UseCollectionThread,
  475. NULL,
  476. &dwType,
  477. (LPBYTE)&dwCollectionFlags,
  478. &dwSize);
  479. if ((dwFnStatus == ERROR_SUCCESS) && (dwType == REG_DWORD)) {
  480. // validate the answer
  481. switch (dwCollectionFlags) {
  482. case 0:
  483. // this is a valid value
  484. break;
  485. case COLL_FLAG_USE_SEPARATE_THREAD:
  486. // this feature is not supported so skip through
  487. default:
  488. // this is for invalid values
  489. dwCollectionFlags = 0;
  490. // dwCollectionFlags = COLL_FLAG_USE_SEPARATE_THREAD;
  491. break;
  492. }
  493. }
  494. if (dwFnStatus != ERROR_SUCCESS) {
  495. dwCollectionFlags = 0;
  496. // dwCollectionFlags = COLL_FLAG_USE_SEPARATE_THREAD;
  497. }
  498. if (dwCollectionFlags == COLL_FLAG_USE_SEPARATE_THREAD) {
  499. // create data collection thread
  500. // a seperate thread is required for COM/OLE compatibity as some
  501. // client threads may be COM initialized incorrectly for the
  502. // extensible counter DLL's that may be called
  503. // status = OpenCollectionThread ();
  504. } else {
  505. hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] = NULL;
  506. hCollectEvents[COLLECT_THREAD_EXIT_EVENT] = NULL;
  507. hCollectEvents[COLLECT_THREAD_DONE_EVENT] = NULL;
  508. hCollectThread = NULL;
  509. }
  510. dwFnStatus = ERROR_SUCCESS;
  511. }
  512. RtlZeroMemory(&OsVersion, sizeof(OsVersion));
  513. OsVersion.dwOSVersionInfoSize = sizeof(OsVersion);
  514. status = RtlGetVersion((POSVERSIONINFOW) &OsVersion);
  515. if (NT_SUCCESS(status)) {
  516. if (OsVersion.wProductType == VER_NT_WORKSTATION) {
  517. dwBoostPriority = 0;
  518. }
  519. }
  520. }
  521. // KdPrint(("PERFLIB: [Open] Pid: %d, Number Of PerflibHandles: %d\n",
  522. // GetCurrentProcessId(), NumberOfOpens));
  523. if (hGlobalDataMutex != NULL) ReleaseMutex (hGlobalDataMutex);
  524. OPD_Error_Exit_NoSemaphore:
  525. TRACE((WINPERF_DBG_TRACE_INFO),
  526. (&PerflibGuid, __LINE__, PERF_OPEN_KEY, 0, status,
  527. &NumberOfOpens, sizeof(NumberOfOpens), NULL));
  528. return dwFnStatus;
  529. }
  530. LONG
  531. PerfRegQueryValue (
  532. IN HKEY hKey,
  533. IN PUNICODE_STRING lpValueName,
  534. OUT LPDWORD lpReserved OPTIONAL,
  535. OUT LPDWORD lpType OPTIONAL,
  536. OUT LPBYTE lpData,
  537. OUT LPDWORD lpcbData,
  538. OUT LPDWORD lpcbLen OPTIONAL
  539. )
  540. /*++
  541. PerfRegQueryValue - Get data
  542. Inputs:
  543. hKey - Predefined handle to open remote
  544. machine
  545. lpValueName - Name of the value to be returned;
  546. could be "ForeignComputer:<computername>
  547. or perhaps some other objects, separated
  548. by ~; must be Unicode string
  549. lpReserved - should be omitted (NULL)
  550. lpType - should be omitted (NULL)
  551. lpData - pointer to a buffer to receive the
  552. performance data
  553. lpcbData - pointer to a variable containing the
  554. size in bytes of the output buffer;
  555. on output, will receive the number
  556. of bytes actually returned
  557. lpcbLen - Return the number of bytes to transmit to
  558. the client (used by RPC) (optional).
  559. Return Value:
  560. DOS error code indicating status of call or
  561. ERROR_SUCCESS if all ok
  562. --*/
  563. {
  564. DWORD dwQueryType; // type of request
  565. DWORD TotalLen; // Length of the total return block
  566. DWORD Win32Error; // Failure code
  567. DWORD lFnStatus = ERROR_SUCCESS; // Win32 status to return to caller
  568. LPVOID pDataDefinition; // Pointer to next object definition
  569. UNICODE_STRING usLocalValue = {0,0, NULL};
  570. PERF_DATA_BLOCK *pPerfDataBlock = (PERF_DATA_BLOCK *)lpData;
  571. LARGE_INTEGER liQueryWaitTime ;
  572. THREAD_BASIC_INFORMATION tbiData;
  573. LONG lOldPriority, lNewPriority;
  574. NTSTATUS status = STATUS_SUCCESS;
  575. LPWSTR lpLangId = NULL;
  576. DBG_UNREFERENCED_PARAMETER(lpReserved);
  577. HEAP_PROBE();
  578. lOldPriority = lNewPriority = -1;
  579. // make a local copy of the value string if the arg references
  580. // the static buffer since it can be overwritten by
  581. // some of the RegistryEventSource call made by this routine
  582. pDataDefinition = NULL;
  583. if (lpValueName != NULL) {
  584. if (lpValueName == &NtCurrentTeb( )->StaticUnicodeString) {
  585. if (RtlCreateUnicodeString (
  586. &usLocalValue, lpValueName->Buffer)) {
  587. lFnStatus = ERROR_SUCCESS;
  588. } else {
  589. // unable to create string
  590. lFnStatus = ERROR_INVALID_PARAMETER;
  591. }
  592. } else {
  593. // copy the arg to the local structure
  594. memcpy (&usLocalValue, lpValueName, sizeof(UNICODE_STRING));
  595. }
  596. } else {
  597. lFnStatus = ERROR_INVALID_PARAMETER;
  598. goto PRQV_ErrorExit1;
  599. }
  600. if (lFnStatus != ERROR_SUCCESS) {
  601. goto PRQV_ErrorExit1;
  602. }
  603. if (hGlobalDataMutex == NULL) {
  604. // if a Mutex was not allocated then the key needs to be opened.
  605. // Without synchronization, it's too easy for threads to get
  606. // tangled up
  607. lFnStatus = PerfOpenKey ();
  608. if (lFnStatus == ERROR_SUCCESS) {
  609. if (!TestClientForAccess ()) {
  610. if (lEventLogLevel >= LOG_USER) {
  611. LPTSTR szMessageArray[2];
  612. TCHAR szUserName[128];
  613. TCHAR szModuleName[MAX_PATH];
  614. DWORD dwUserNameLength;
  615. dwUserNameLength = sizeof(szUserName)/sizeof(TCHAR);
  616. GetUserName (szUserName, &dwUserNameLength);
  617. GetModuleFileName (NULL, szModuleName,
  618. sizeof(szModuleName)/sizeof(TCHAR));
  619. szMessageArray[0] = szUserName;
  620. szMessageArray[1] = szModuleName;
  621. ReportEvent (hEventLog,
  622. EVENTLOG_ERROR_TYPE, // error type
  623. 0, // category (not used)
  624. (DWORD)PERFLIB_ACCESS_DENIED, // event,
  625. NULL, // SID (not used),
  626. 2, // number of strings
  627. 0, // sizeof raw data
  628. szMessageArray, // message text array
  629. NULL); // raw data
  630. }
  631. lFnStatus = ERROR_ACCESS_DENIED;
  632. TRACE((WINPERF_DBG_TRACE_FATAL),
  633. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, lFnStatus, NULL));
  634. }
  635. }
  636. }
  637. if (lFnStatus != ERROR_SUCCESS) {
  638. // goto the exit point
  639. goto PRQV_ErrorExit1;
  640. }
  641. if (dwBoostPriority != 0) {
  642. status = NtQueryInformationThread (
  643. NtCurrentThread(),
  644. ThreadBasicInformation,
  645. &tbiData,
  646. sizeof(tbiData),
  647. NULL);
  648. if (NT_SUCCESS(status)) {
  649. lOldPriority = tbiData.Priority;
  650. } else {
  651. TRACE((WINPERF_DBG_TRACE_WARNING),
  652. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0,
  653. status, NULL));
  654. lOldPriority = -1;
  655. }
  656. lNewPriority = DEFAULT_THREAD_PRIORITY; // perfmon's favorite priority
  657. //
  658. // Only RAISE the priority here. Don't lower it if it's high
  659. //
  660. if ((lOldPriority > 0) && (lOldPriority < lNewPriority)) {
  661. status = NtSetInformationThread(
  662. NtCurrentThread(),
  663. ThreadPriority,
  664. &lNewPriority,
  665. sizeof(lNewPriority)
  666. );
  667. if (!NT_SUCCESS(status)) {
  668. TRACE((WINPERF_DBG_TRACE_WARNING),
  669. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0,
  670. status, NULL));
  671. lOldPriority = -1;
  672. }
  673. } else {
  674. lOldPriority = -1; // to save resetting at the end
  675. }
  676. }
  677. //
  678. // Set the length parameter to zero so that in case of an error,
  679. // nothing will be transmitted back to the client and the client won't
  680. // attempt to unmarshall anything.
  681. //
  682. if( ARGUMENT_PRESENT( lpcbLen )) {
  683. *lpcbLen = 0;
  684. }
  685. // if here, then assume the caller has the necessary access
  686. /*
  687. determine query type, can be one of the following
  688. Global
  689. get all objects
  690. List
  691. get objects in list (usLocalValue)
  692. Foreign Computer
  693. call extensible Counter Routine only
  694. Costly
  695. costly object items
  696. Counter
  697. get counter names for the specified language Id
  698. Help
  699. get help names for the specified language Id
  700. */
  701. dwQueryType = GetQueryType (usLocalValue.Buffer);
  702. TRACE((WINPERF_DBG_TRACE_INFO),
  703. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, dwQueryType, NULL));
  704. if (dwQueryType == QUERY_COUNTER || dwQueryType == QUERY_HELP ||
  705. dwQueryType == QUERY_ADDCOUNTER || dwQueryType == QUERY_ADDHELP ) {
  706. liQueryWaitTime.QuadPart = MakeTimeOutValue(QUERY_WAIT_TIME);
  707. status = NtWaitForSingleObject (
  708. hGlobalDataMutex, // semaphore
  709. FALSE, // not alertable
  710. &liQueryWaitTime); // wait 'til timeout
  711. if (status != STATUS_SUCCESS) {
  712. lFnStatus = ERROR_BUSY;
  713. Win32Error = ERROR_BUSY;
  714. TotalLen = *lpcbData;
  715. TRACE((WINPERF_DBG_TRACE_FATAL),
  716. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL));
  717. } else {
  718. TRACE((WINPERF_DBG_TRACE_INFO),
  719. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL));
  720. if (hKey == HKEY_PERFORMANCE_DATA) {
  721. lpLangId = NULL;
  722. } else if (hKey == HKEY_PERFORMANCE_TEXT) {
  723. lpLangId = DefaultLangId;
  724. } else if (hKey == HKEY_PERFORMANCE_NLSTEXT) {
  725. RtlZeroMemory(NativeLangId, 8 * sizeof(WCHAR));
  726. lpLangId = &NativeLangId[0];
  727. PerfGetLangId(NativeLangId);
  728. }
  729. status = PerfGetNames (
  730. dwQueryType,
  731. &usLocalValue,
  732. lpData,
  733. lpcbData,
  734. lpcbLen,
  735. lpLangId);
  736. TRACE((WINPERF_DBG_TRACE_INFO),
  737. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status,
  738. &hKey, sizeof(hKey), NULL));
  739. if (!NT_SUCCESS(status) && (hKey == HKEY_PERFORMANCE_NLSTEXT)) {
  740. // Sublanguage doesn't exist, so try the real one
  741. //
  742. TRACE((WINPERF_DBG_TRACE_WARNING),
  743. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL));
  744. RtlZeroMemory(NativeLangId, 8 * sizeof(WCHAR));
  745. PerfGetPrimaryLangId(GetUserDefaultUILanguage(), NativeLangId);
  746. status = PerfGetNames (
  747. dwQueryType,
  748. &usLocalValue,
  749. lpData,
  750. lpcbData,
  751. lpcbLen,
  752. lpLangId);
  753. }
  754. if (!NT_SUCCESS(status)) {
  755. TRACE((WINPERF_DBG_TRACE_FATAL),
  756. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL));
  757. // convert error to win32 for return
  758. }
  759. lFnStatus = PerfpDosError(status);
  760. if (ARGUMENT_PRESENT (lpType)) {
  761. // test for optional value
  762. *lpType = REG_MULTI_SZ;
  763. }
  764. ReleaseMutex (hGlobalDataMutex);
  765. }
  766. } else {
  767. // define info block for data collection
  768. COLLECT_THREAD_DATA CollectThreadData = {0, NULL, NULL, NULL, NULL, NULL, 0, 0};
  769. liQueryWaitTime.QuadPart = MakeTimeOutValue(QUERY_WAIT_TIME);
  770. status = NtWaitForSingleObject (
  771. hGlobalDataMutex, // semaphore
  772. FALSE, // not alertable
  773. &liQueryWaitTime); // wait 'til timeout
  774. if (status != STATUS_SUCCESS) {
  775. lFnStatus = ERROR_BUSY;
  776. Win32Error = ERROR_BUSY;
  777. TotalLen = *lpcbData;
  778. TRACE((WINPERF_DBG_TRACE_FATAL),
  779. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL));
  780. } else {
  781. TRACE((WINPERF_DBG_TRACE_INFO),
  782. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL));
  783. //
  784. // Format Return Buffer: start with basic data block
  785. //
  786. TotalLen = sizeof(PERF_DATA_BLOCK) +
  787. ((CNLEN+sizeof(UNICODE_NULL))*sizeof(WCHAR));
  788. if ( *lpcbData < TotalLen ) {
  789. Win32Error = ERROR_MORE_DATA;
  790. TRACE((WINPERF_DBG_TRACE_ERROR),
  791. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, TotalLen,
  792. lpcbData, sizeof(DWORD), NULL));
  793. } else {
  794. // foreign data provider will return the perf data header
  795. if (dwQueryType == QUERY_FOREIGN) {
  796. // reset the values to avoid confusion
  797. // *lpcbData = 0; // 0 bytes (removed to enable foreign computers)
  798. pDataDefinition = (LPVOID)lpData;
  799. memset (lpData, 0, sizeof (PERF_DATA_BLOCK)); // clear out header
  800. } else {
  801. MonBuildPerfDataBlock(pPerfDataBlock,
  802. (PVOID *) &pDataDefinition,
  803. 0,
  804. PROCESSOR_OBJECT_TITLE_INDEX);
  805. }
  806. CollectThreadData.dwQueryType = dwQueryType;
  807. CollectThreadData.lpValueName = usLocalValue.Buffer,
  808. CollectThreadData.lpData = lpData;
  809. CollectThreadData.lpcbData = lpcbData;
  810. CollectThreadData.lppDataDefinition = &pDataDefinition;
  811. CollectThreadData.pCurrentExtObject = NULL;
  812. CollectThreadData.lReturnValue = ERROR_SUCCESS;
  813. CollectThreadData.dwActionFlags = CTD_AF_NO_ACTION;
  814. if (hCollectThread == NULL) {
  815. // then call the function directly and hope for the best
  816. Win32Error = QueryExtensibleData (
  817. &CollectThreadData);
  818. } else {
  819. // collect the data in a separate thread
  820. // load the args
  821. // set event to get things going
  822. SetEvent (hCollectEvents[COLLECT_THREAD_PROCESS_EVENT]);
  823. // now wait for the thread to return
  824. Win32Error = WaitForSingleObject (
  825. hCollectEvents[COLLECT_THREAD_DONE_EVENT],
  826. COLLECTION_WAIT_TIME);
  827. if (Win32Error == WAIT_TIMEOUT) {
  828. bThreadHung = TRUE;
  829. // log error
  830. TRACE((WINPERF_DBG_TRACE_FATAL),
  831. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, Win32Error, NULL));
  832. if (lEventLogLevel >= LOG_USER) {
  833. LPSTR szMessageArray[2];
  834. WORD wStringIndex;
  835. // load data for eventlog message
  836. wStringIndex = 0;
  837. if (CollectThreadData.pCurrentExtObject != NULL) {
  838. szMessageArray[wStringIndex++] =
  839. CollectThreadData.pCurrentExtObject->szCollectProcName;
  840. } else {
  841. szMessageArray[wStringIndex++] = "Unknown";
  842. }
  843. ReportEventA (hEventLog,
  844. EVENTLOG_ERROR_TYPE, // error type
  845. 0, // category (not used)
  846. (DWORD)PERFLIB_COLLECTION_HUNG, // event,
  847. NULL, // SID (not used),
  848. wStringIndex, // number of strings
  849. 0, // sizeof raw data
  850. szMessageArray, // message text array
  851. NULL); // raw data
  852. }
  853. DisablePerfLibrary (CollectThreadData.pCurrentExtObject);
  854. // DebugPrint((0, "Collection thread is hung in %s\n",
  855. // CollectThreadData.pCurrentExtObject->szCollectProcName != NULL ?
  856. // CollectThreadData.pCurrentExtObject->szCollectProcName : "Unknown"));
  857. // and then wait forever for the thread to return
  858. // this is done to prevent the function from returning
  859. // while the collection thread is using the buffer
  860. // passed in by the calling function and causing
  861. // all kind of havoc should the buffer be changed and/or
  862. // deleted and then have the thread continue for some reason
  863. Win32Error = WaitForSingleObject (
  864. hCollectEvents[COLLECT_THREAD_DONE_EVENT],
  865. INFINITE);
  866. }
  867. bThreadHung = FALSE; // in case it was true, but came out
  868. // here the thread has returned so continue on
  869. Win32Error = CollectThreadData.lReturnValue;
  870. }
  871. #if 0
  872. if (CollectThreadData.dwActionFlags != CTD_AF_NO_ACTION) {
  873. if (CollectThreadData.dwActionFlags == CTD_AF_OPEN_THREAD) {
  874. OpenCollectionThread();
  875. } else if (CollectThreadData.dwActionFlags == CTD_AF_CLOSE_THREAD) {
  876. CloseCollectionThread();
  877. } else {
  878. assert (CollectThreadData.dwActionFlags != 0);
  879. }
  880. }
  881. #endif
  882. }
  883. ReleaseMutex (hGlobalDataMutex);
  884. }
  885. // if an error was encountered, return it
  886. if (Win32Error != ERROR_SUCCESS) {
  887. lFnStatus = Win32Error;
  888. } else {
  889. //
  890. // Final housekeeping for data return: note data size
  891. //
  892. TotalLen = (DWORD) ((PCHAR) pDataDefinition - (PCHAR) lpData);
  893. *lpcbData = TotalLen;
  894. pPerfDataBlock->TotalByteLength = TotalLen;
  895. lFnStatus = ERROR_SUCCESS;
  896. }
  897. if (ARGUMENT_PRESENT (lpcbLen)) { // test for optional parameter
  898. *lpcbLen = TotalLen;
  899. }
  900. if (ARGUMENT_PRESENT (lpType)) { // test for optional value
  901. *lpType = REG_BINARY;
  902. }
  903. }
  904. PRQV_ErrorExit1:
  905. if (dwBoostPriority != 0) {
  906. // reset thread to original priority
  907. if ((lOldPriority > 0) && (lOldPriority != lNewPriority)) {
  908. NtSetInformationThread(
  909. NtCurrentThread(),
  910. ThreadPriority,
  911. &lOldPriority,
  912. sizeof(lOldPriority)
  913. );
  914. }
  915. }
  916. if (usLocalValue.Buffer != NULL) {
  917. // restore the value string if it was from the local static buffer
  918. // then free the local buffer
  919. if (lpValueName == &NtCurrentTeb( )->StaticUnicodeString) {
  920. memcpy (lpValueName->Buffer, usLocalValue.Buffer, usLocalValue.MaximumLength);
  921. RtlFreeUnicodeString (&usLocalValue);
  922. }
  923. }
  924. HEAP_PROBE();
  925. TRACE((WINPERF_DBG_TRACE_INFO),
  926. (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, lFnStatus, NULL));
  927. return (LONG) lFnStatus;
  928. }
  929. LONG
  930. PerfRegCloseKey
  931. (
  932. IN OUT PHKEY phKey
  933. )
  934. /*++
  935. Routine Description:
  936. Closes all performance handles when the usage count drops to 0.
  937. Arguments:
  938. phKey - Supplies a handle to an open key to be closed.
  939. Return Value:
  940. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  941. --*/
  942. {
  943. NTSTATUS status;
  944. LARGE_INTEGER liQueryWaitTime ;
  945. HANDLE hObjMutex;
  946. LONG lReturn = ERROR_SUCCESS;
  947. HKEY hKey;
  948. PEXT_OBJECT pThisExtObj, pNextExtObj;
  949. //
  950. // Set the handle to NULL so that RPC knows that it has been closed.
  951. //
  952. hKey = *phKey;
  953. *phKey = NULL;
  954. if (hKey != HKEY_PERFORMANCE_DATA) {
  955. return ERROR_SUCCESS;
  956. }
  957. if (NumberOfOpens == 0) {
  958. // KdPrint(("PERFLIB: [Close] Pid: %d, Number Of PerflibHandles: %d\n",
  959. // GetCurrentProcessId(), NumberOfOpens));
  960. return ERROR_SUCCESS;
  961. }
  962. // wait for ext obj list to be "un"-busy
  963. liQueryWaitTime.QuadPart = MakeTimeOutValue (CLOSE_WAIT_TIME);
  964. status = NtWaitForSingleObject (
  965. hExtObjListIsNotInUse,
  966. FALSE,
  967. &liQueryWaitTime);
  968. if (status == STATUS_SUCCESS) {
  969. TRACE((WINPERF_DBG_TRACE_INFO),
  970. (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status, NULL));
  971. // then the list is inactive so continue
  972. if (hGlobalDataMutex != NULL) { // if a mutex was allocated, then use it
  973. // if here, then assume a mutex is ready
  974. liQueryWaitTime.QuadPart = MakeTimeOutValue(CLOSE_WAIT_TIME);
  975. status = NtWaitForSingleObject (
  976. hGlobalDataMutex, // semaphore
  977. FALSE, // not alertable
  978. &liQueryWaitTime); // wait forever
  979. if (status == STATUS_SUCCESS) {
  980. TRACE((WINPERF_DBG_TRACE_INFO),
  981. (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status, NULL));
  982. // now we have a lock on the global data, so continue
  983. NumberOfOpens--;
  984. if (!NumberOfOpens && (hKey == HKEY_PERFORMANCE_DATA)) {
  985. // walk down list of known objects and close and delete each one
  986. pNextExtObj = ExtensibleObjects;
  987. while (pNextExtObj != NULL) {
  988. // close and destroy each entry in the list
  989. pThisExtObj = pNextExtObj;
  990. hObjMutex = pThisExtObj->hMutex;
  991. status = NtWaitForSingleObject (
  992. hObjMutex,
  993. FALSE,
  994. &liQueryWaitTime);
  995. if (status == STATUS_SUCCESS) {
  996. TRACE((WINPERF_DBG_TRACE_INFO),
  997. (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status,
  998. pThisExtObj->szServiceName,
  999. WSTRSIZE(pThisExtObj->szServiceName),
  1000. NULL));
  1001. InterlockedIncrement((LONG *)&pThisExtObj->dwLockoutCount);
  1002. status = CloseExtObjectLibrary(pThisExtObj, TRUE);
  1003. // close the handle to the perf subkey
  1004. NtClose (pThisExtObj->hPerfKey);
  1005. ReleaseMutex (hObjMutex); // release
  1006. CloseHandle (hObjMutex); // and free
  1007. pNextExtObj = pThisExtObj->pNext;
  1008. // toss the memory for this object
  1009. FREEMEM (pThisExtObj);
  1010. } else {
  1011. TRACE((WINPERF_DBG_TRACE_INFO),
  1012. (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status,
  1013. pThisExtObj->szServiceName,
  1014. WSTRSIZE(pThisExtObj->szServiceName),
  1015. NULL));
  1016. // this shouldn't happen since we've locked the
  1017. // list of objects
  1018. pNextExtObj = pThisExtObj->pNext;
  1019. }
  1020. }
  1021. // close the global objects
  1022. FREEMEM(pComputerName);
  1023. ComputerNameLength = 0;
  1024. pComputerName = NULL;
  1025. ExtensibleObjects = NULL;
  1026. NumExtensibleObjects = 0;
  1027. // close the timer thread
  1028. DestroyPerflibFunctionTimer ();
  1029. if (hEventLog != NULL) {
  1030. DeregisterEventSource (hEventLog);
  1031. hEventLog = NULL;
  1032. } // else the event log has already been closed
  1033. // release event handle
  1034. CloseHandle (hExtObjListIsNotInUse);
  1035. hExtObjListIsNotInUse = NULL;
  1036. // CloseCollectionThread();
  1037. if (ghKeyPerflib != NULL) {
  1038. RegCloseKey(ghKeyPerflib);
  1039. ghKeyPerflib = NULL;
  1040. }
  1041. if (lpPerflibSectionAddr != NULL) {
  1042. UnmapViewOfFile (lpPerflibSectionAddr);
  1043. lpPerflibSectionAddr = NULL;
  1044. CloseHandle (hPerflibSectionMap);
  1045. hPerflibSectionMap = NULL;
  1046. CloseHandle (hPerflibSectionFile);
  1047. hPerflibSectionFile = NULL;
  1048. }
  1049. ReleaseMutex(hGlobalDataMutex);
  1050. } else { // this isn't the last open call so return success
  1051. assert(NumberOfOpens != 0);
  1052. ReleaseMutex (hGlobalDataMutex);
  1053. }
  1054. } else {
  1055. TRACE((WINPERF_DBG_TRACE_FATAL),
  1056. (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status, NULL));
  1057. // unable to lock the global data mutex in a timely fashion
  1058. // so return
  1059. lReturn = ERROR_BUSY;
  1060. }
  1061. } else {
  1062. // if there's no mutex then something's fishy. It probably hasn't
  1063. // been opened, yet.
  1064. lReturn = ERROR_NOT_READY;
  1065. }
  1066. } else {
  1067. TRACE((WINPERF_DBG_TRACE_FATAL),
  1068. (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status, NULL));
  1069. // the object list is still in use so return and let the
  1070. // caller try again later
  1071. lReturn = WAIT_TIMEOUT;
  1072. }
  1073. // KdPrint(("PERFLIB: [Close] Pid: %d, Number Of PerflibHandles: %d\n",
  1074. // GetCurrentProcessId(), NumberOfOpens));
  1075. TRACE((WINPERF_DBG_TRACE_INFO),
  1076. (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, lReturn,
  1077. &NumberOfOpens, sizeof(NumberOfOpens), NULL));
  1078. return lReturn;
  1079. }
  1080. LONG
  1081. PerfRegSetValue (
  1082. IN HKEY hKey,
  1083. IN LPWSTR lpValueName,
  1084. IN DWORD Reserved,
  1085. IN DWORD dwType,
  1086. IN LPBYTE lpData,
  1087. IN DWORD cbData
  1088. )
  1089. /*++
  1090. PerfRegSetValue - Set data
  1091. Inputs:
  1092. hKey - Predefined handle to open remote
  1093. machine
  1094. lpValueName - Name of the value to be returned;
  1095. could be "ForeignComputer:<computername>
  1096. or perhaps some other objects, separated
  1097. by ~; must be Unicode string
  1098. lpReserved - should be omitted (NULL)
  1099. lpType - should be REG_MULTI_SZ
  1100. lpData - pointer to a buffer containing the
  1101. performance name
  1102. lpcbData - pointer to a variable containing the
  1103. size in bytes of the input buffer;
  1104. Return Value:
  1105. DOS error code indicating status of call or
  1106. ERROR_SUCCESS if all ok
  1107. --*/
  1108. {
  1109. DWORD dwQueryType; // type of request
  1110. LPWSTR lpLangId = NULL;
  1111. NTSTATUS status;
  1112. UNICODE_STRING String;
  1113. LONG lReturn = ERROR_SUCCESS;
  1114. UNREFERENCED_PARAMETER(dwType);
  1115. UNREFERENCED_PARAMETER(Reserved);
  1116. dwQueryType = GetQueryType (lpValueName);
  1117. TRACE((WINPERF_DBG_TRACE_INFO),
  1118. (&PerflibGuid, __LINE__, PERF_REG_SET_VALUE, 0, dwQueryType,
  1119. &hKey, sizeof(hKey), NULL));
  1120. // convert the query to set commands
  1121. if ((dwQueryType == QUERY_COUNTER) ||
  1122. (dwQueryType == QUERY_ADDCOUNTER)) {
  1123. dwQueryType = QUERY_ADDCOUNTER;
  1124. } else if ((dwQueryType == QUERY_HELP) ||
  1125. (dwQueryType == QUERY_ADDHELP)) {
  1126. dwQueryType = QUERY_ADDHELP;
  1127. } else {
  1128. lReturn = ERROR_BADKEY;
  1129. goto Error_exit;
  1130. }
  1131. if (hKey == HKEY_PERFORMANCE_TEXT) {
  1132. lpLangId = DefaultLangId;
  1133. } else if (hKey == HKEY_PERFORMANCE_NLSTEXT) {
  1134. lpLangId = &NativeLangId[0];
  1135. PerfGetLangId(NativeLangId);
  1136. } else {
  1137. lReturn = ERROR_BADKEY;
  1138. goto Error_exit;
  1139. }
  1140. RtlInitUnicodeString(&String, lpValueName);
  1141. status = PerfGetNames (
  1142. dwQueryType,
  1143. &String,
  1144. lpData,
  1145. &cbData,
  1146. NULL,
  1147. lpLangId);
  1148. if (!NT_SUCCESS(status) && (hKey == HKEY_PERFORMANCE_NLSTEXT)) {
  1149. TRACE((WINPERF_DBG_TRACE_INFO),
  1150. (&PerflibGuid, __LINE__, PERF_REG_SET_VALUE, 0, status, NULL));
  1151. // Sublanguage doesn't exist, so try the real one
  1152. //
  1153. PerfGetPrimaryLangId(GetUserDefaultUILanguage(), NativeLangId);
  1154. status = PerfGetNames (
  1155. dwQueryType,
  1156. &String,
  1157. lpData,
  1158. &cbData,
  1159. NULL,
  1160. lpLangId);
  1161. }
  1162. if (!NT_SUCCESS(status)) {
  1163. TRACE((WINPERF_DBG_TRACE_FATAL),
  1164. (&PerflibGuid, __LINE__, PERF_REG_SET_VALUE, 0, status, NULL));
  1165. lReturn = (error_status_t)PerfpDosError(status);
  1166. }
  1167. Error_exit:
  1168. TRACE((WINPERF_DBG_TRACE_INFO),
  1169. (&PerflibGuid, __LINE__, PERF_REG_SET_VALUE, 0, lReturn, NULL));
  1170. return (lReturn);
  1171. }
  1172. LONG
  1173. PerfRegEnumKey (
  1174. IN HKEY hKey,
  1175. IN DWORD dwIndex,
  1176. OUT PUNICODE_STRING lpName,
  1177. OUT LPDWORD lpReserved OPTIONAL,
  1178. OUT PUNICODE_STRING lpClass OPTIONAL,
  1179. OUT PFILETIME lpftLastWriteTime OPTIONAL
  1180. )
  1181. /*++
  1182. Routine Description:
  1183. Enumerates keys under HKEY_PERFORMANCE_DATA.
  1184. Arguments:
  1185. Same as RegEnumKeyEx. Returns that there are no such keys.
  1186. Return Value:
  1187. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  1188. --*/
  1189. {
  1190. UNREFERENCED_PARAMETER(hKey);
  1191. UNREFERENCED_PARAMETER(dwIndex);
  1192. UNREFERENCED_PARAMETER(lpReserved);
  1193. lpName->Length = 0;
  1194. if (ARGUMENT_PRESENT (lpClass)) {
  1195. lpClass->Length = 0;
  1196. }
  1197. if ( ARGUMENT_PRESENT(lpftLastWriteTime) ) {
  1198. lpftLastWriteTime->dwLowDateTime = 0;
  1199. lpftLastWriteTime->dwHighDateTime = 0;
  1200. }
  1201. return ERROR_NO_MORE_ITEMS;
  1202. }
  1203. LONG
  1204. PerfRegQueryInfoKey (
  1205. IN HKEY hKey,
  1206. OUT PUNICODE_STRING lpClass,
  1207. OUT LPDWORD lpReserved OPTIONAL,
  1208. OUT LPDWORD lpcSubKeys,
  1209. OUT LPDWORD lpcbMaxSubKeyLen,
  1210. OUT LPDWORD lpcbMaxClassLen,
  1211. OUT LPDWORD lpcValues,
  1212. OUT LPDWORD lpcbMaxValueNameLen,
  1213. OUT LPDWORD lpcbMaxValueLen,
  1214. OUT LPDWORD lpcbSecurityDescriptor,
  1215. OUT PFILETIME lpftLastWriteTime
  1216. )
  1217. /*++
  1218. Routine Description:
  1219. This returns information concerning the predefined handle
  1220. HKEY_PERFORMANCE_DATA
  1221. Arguments:
  1222. Same as RegQueryInfoKey.
  1223. Return Value:
  1224. Returns ERROR_SUCCESS (0) for success.
  1225. --*/
  1226. {
  1227. DWORD TempLength=0;
  1228. DWORD MaxValueLen=0;
  1229. UNICODE_STRING Null;
  1230. SECURITY_DESCRIPTOR SecurityDescriptor;
  1231. HKEY hPerflibKey;
  1232. OBJECT_ATTRIBUTES Obja;
  1233. NTSTATUS Status;
  1234. DWORD PerfStatus = ERROR_SUCCESS;
  1235. UNICODE_STRING PerflibSubKeyString;
  1236. BOOL bGetSACL = TRUE;
  1237. UNREFERENCED_PARAMETER(lpReserved);
  1238. if (lpClass->MaximumLength >= sizeof(UNICODE_NULL)) {
  1239. lpClass->Length = 0;
  1240. *lpClass->Buffer = UNICODE_NULL;
  1241. }
  1242. *lpcSubKeys = 0;
  1243. *lpcbMaxSubKeyLen = 0;
  1244. *lpcbMaxClassLen = 0;
  1245. *lpcValues = NUM_VALUES;
  1246. *lpcbMaxValueNameLen = VALUE_NAME_LENGTH;
  1247. *lpcbMaxValueLen = 0;
  1248. if ( ARGUMENT_PRESENT(lpftLastWriteTime) ) {
  1249. lpftLastWriteTime->dwLowDateTime = 0;
  1250. lpftLastWriteTime->dwHighDateTime = 0;
  1251. }
  1252. if ((hKey == HKEY_PERFORMANCE_TEXT) ||
  1253. (hKey == HKEY_PERFORMANCE_NLSTEXT)) {
  1254. //
  1255. // We have to go enumerate the values to determine the answer for
  1256. // the MaxValueLen parameter.
  1257. //
  1258. Null.Buffer = NULL;
  1259. Null.Length = 0;
  1260. Null.MaximumLength = 0;
  1261. PerfStatus = PerfEnumTextValue(hKey,
  1262. 0,
  1263. &Null,
  1264. NULL,
  1265. NULL,
  1266. NULL,
  1267. &MaxValueLen,
  1268. NULL);
  1269. if (PerfStatus == ERROR_SUCCESS) {
  1270. PerfStatus = PerfEnumTextValue(hKey,
  1271. 1,
  1272. &Null,
  1273. NULL,
  1274. NULL,
  1275. NULL,
  1276. &TempLength,
  1277. NULL);
  1278. }
  1279. if (PerfStatus == ERROR_SUCCESS) {
  1280. if (TempLength > MaxValueLen) {
  1281. MaxValueLen = TempLength;
  1282. }
  1283. *lpcbMaxValueLen = MaxValueLen;
  1284. } else {
  1285. TRACE((WINPERF_DBG_TRACE_FATAL),
  1286. (&PerflibGuid, __LINE__, PERF_REG_QUERY_INFO_KEY, 0, PerfStatus, NULL));
  1287. // unable to successfully enum text values for this
  1288. // key so return 0's and the error code
  1289. *lpcValues = 0;
  1290. *lpcbMaxValueNameLen = 0;
  1291. }
  1292. }
  1293. if (PerfStatus == ERROR_SUCCESS) {
  1294. // continune if all is OK
  1295. // now get the size of SecurityDescriptor for Perflib key
  1296. RtlInitUnicodeString (
  1297. &PerflibSubKeyString,
  1298. PerflibKey);
  1299. //
  1300. // Initialize the OBJECT_ATTRIBUTES structure and open the key.
  1301. //
  1302. InitializeObjectAttributes(
  1303. &Obja,
  1304. &PerflibSubKeyString,
  1305. OBJ_CASE_INSENSITIVE,
  1306. NULL,
  1307. NULL
  1308. );
  1309. Status = NtOpenKey(
  1310. &hPerflibKey,
  1311. MAXIMUM_ALLOWED | ACCESS_SYSTEM_SECURITY,
  1312. &Obja
  1313. );
  1314. if ( ! NT_SUCCESS( Status )) {
  1315. Status = NtOpenKey(
  1316. &hPerflibKey,
  1317. MAXIMUM_ALLOWED,
  1318. &Obja
  1319. );
  1320. bGetSACL = FALSE;
  1321. }
  1322. if ( ! NT_SUCCESS( Status )) {
  1323. TRACE((WINPERF_DBG_TRACE_FATAL),
  1324. (&PerflibGuid, __LINE__, PERF_REG_QUERY_INFO_KEY, 0, Status, NULL));
  1325. } else {
  1326. *lpcbSecurityDescriptor = 0;
  1327. if (bGetSACL == FALSE) {
  1328. //
  1329. // Get the size of the key's SECURITY_DESCRIPTOR for OWNER, GROUP
  1330. // and DACL. These three are always accessible (or inaccesible)
  1331. // as a set.
  1332. //
  1333. Status = NtQuerySecurityObject(
  1334. hPerflibKey,
  1335. OWNER_SECURITY_INFORMATION
  1336. | GROUP_SECURITY_INFORMATION
  1337. | DACL_SECURITY_INFORMATION,
  1338. &SecurityDescriptor,
  1339. 0,
  1340. lpcbSecurityDescriptor
  1341. );
  1342. } else {
  1343. //
  1344. // Get the size of the key's SECURITY_DESCRIPTOR for OWNER, GROUP,
  1345. // DACL, and SACL.
  1346. //
  1347. Status = NtQuerySecurityObject(
  1348. hPerflibKey,
  1349. OWNER_SECURITY_INFORMATION
  1350. | GROUP_SECURITY_INFORMATION
  1351. | DACL_SECURITY_INFORMATION
  1352. | SACL_SECURITY_INFORMATION,
  1353. &SecurityDescriptor,
  1354. 0,
  1355. lpcbSecurityDescriptor
  1356. );
  1357. }
  1358. if( Status != STATUS_BUFFER_TOO_SMALL ) {
  1359. *lpcbSecurityDescriptor = 0;
  1360. } else {
  1361. // this is expected so set status to success
  1362. Status = STATUS_SUCCESS;
  1363. }
  1364. NtClose(hPerflibKey);
  1365. }
  1366. if (NT_SUCCESS( Status )) {
  1367. PerfStatus = ERROR_SUCCESS;
  1368. } else {
  1369. TRACE((WINPERF_DBG_TRACE_FATAL),
  1370. (&PerflibGuid, __LINE__, PERF_REG_QUERY_INFO_KEY, 0, Status, NULL));
  1371. // return error
  1372. PerfStatus = PerfpDosError(Status);
  1373. }
  1374. } // else return status
  1375. return (LONG) PerfStatus;
  1376. }
  1377. LONG
  1378. PerfRegEnumValue (
  1379. IN HKEY hKey,
  1380. IN DWORD dwIndex,
  1381. OUT PUNICODE_STRING lpValueName,
  1382. OUT LPDWORD lpReserved OPTIONAL,
  1383. OUT LPDWORD lpType OPTIONAL,
  1384. OUT LPBYTE lpData,
  1385. IN OUT LPDWORD lpcbData,
  1386. OUT LPDWORD lpcbLen OPTIONAL
  1387. )
  1388. /*++
  1389. Routine Description:
  1390. Enumerates Values under HKEY_PERFORMANCE_DATA.
  1391. Arguments:
  1392. Same as RegEnumValue. Returns the values.
  1393. Return Value:
  1394. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  1395. --*/
  1396. {
  1397. USHORT cbNameSize;
  1398. // table of names used by enum values
  1399. UNICODE_STRING ValueNames[NUM_VALUES];
  1400. ValueNames [0].Length = (WORD)(lstrlenW (GLOBAL_STRING) * sizeof(WCHAR));
  1401. ValueNames [0].MaximumLength = (WORD)(ValueNames [0].Length + sizeof(UNICODE_NULL));
  1402. ValueNames [0].Buffer = (LPWSTR)GLOBAL_STRING;
  1403. ValueNames [1].Length = (WORD)(lstrlenW(COSTLY_STRING) * sizeof(WCHAR));
  1404. ValueNames [1].MaximumLength = (WORD)(ValueNames [1].Length + sizeof(UNICODE_NULL));
  1405. ValueNames [1].Buffer = (LPWSTR)COSTLY_STRING;
  1406. if ((hKey == HKEY_PERFORMANCE_TEXT) ||
  1407. (hKey == HKEY_PERFORMANCE_NLSTEXT)) {
  1408. return(PerfEnumTextValue(hKey,
  1409. dwIndex,
  1410. lpValueName,
  1411. lpReserved,
  1412. lpType,
  1413. lpData,
  1414. lpcbData,
  1415. lpcbLen));
  1416. }
  1417. if ( dwIndex >= NUM_VALUES ) {
  1418. //
  1419. // This is a request for data from a non-existent value name
  1420. //
  1421. *lpcbData = 0;
  1422. return ERROR_NO_MORE_ITEMS;
  1423. }
  1424. cbNameSize = ValueNames[dwIndex].Length;
  1425. if ( lpValueName->MaximumLength < cbNameSize ) {
  1426. return ERROR_MORE_DATA;
  1427. } else {
  1428. lpValueName->Length = cbNameSize;
  1429. RtlCopyUnicodeString(lpValueName, &ValueNames[dwIndex]);
  1430. if (ARGUMENT_PRESENT (lpType)) {
  1431. *lpType = REG_BINARY;
  1432. }
  1433. return PerfRegQueryValue(hKey,
  1434. lpValueName,
  1435. NULL,
  1436. lpType,
  1437. lpData,
  1438. lpcbData,
  1439. lpcbLen);
  1440. }
  1441. }
  1442. LONG
  1443. PerfEnumTextValue (
  1444. IN HKEY hKey,
  1445. IN DWORD dwIndex,
  1446. OUT PUNICODE_STRING lpValueName,
  1447. OUT LPDWORD lpReserved OPTIONAL,
  1448. OUT LPDWORD lpType OPTIONAL,
  1449. OUT LPBYTE lpData,
  1450. IN OUT LPDWORD lpcbData,
  1451. OUT LPDWORD lpcbLen OPTIONAL
  1452. )
  1453. /*++
  1454. Routine Description:
  1455. Enumerates Values under Perflib\lang
  1456. Arguments:
  1457. Same as RegEnumValue. Returns the values.
  1458. Return Value:
  1459. Returns ERROR_SUCCESS (0) for success; error-code for failure.
  1460. --*/
  1461. {
  1462. UNICODE_STRING FullValueName;
  1463. LONG lReturn = ERROR_SUCCESS;
  1464. //
  1465. // Only two values, "Counter" and "Help"
  1466. //
  1467. if (dwIndex==0) {
  1468. lpValueName->Length = 0;
  1469. RtlInitUnicodeString(&FullValueName, CounterValue);
  1470. } else if (dwIndex==1) {
  1471. lpValueName->Length = 0;
  1472. RtlInitUnicodeString(&FullValueName, HelpValue);
  1473. } else {
  1474. return(ERROR_NO_MORE_ITEMS);
  1475. }
  1476. RtlCopyUnicodeString(lpValueName, &FullValueName);
  1477. //
  1478. // We need to NULL terminate the name to make RPC happy.
  1479. //
  1480. if (lpValueName->Length+sizeof(WCHAR) <= lpValueName->MaximumLength) {
  1481. lpValueName->Buffer[lpValueName->Length / sizeof(WCHAR)] = UNICODE_NULL;
  1482. lpValueName->Length += sizeof(UNICODE_NULL);
  1483. }
  1484. lReturn = PerfRegQueryValue(hKey,
  1485. &FullValueName,
  1486. lpReserved,
  1487. lpType,
  1488. lpData,
  1489. lpcbData,
  1490. lpcbLen);
  1491. return lReturn;
  1492. }
  1493. #if 0
  1494. DWORD
  1495. CollectThreadFunction (
  1496. LPDWORD dwArg
  1497. )
  1498. {
  1499. DWORD dwWaitStatus = 0;
  1500. BOOL bExit = FALSE;
  1501. NTSTATUS status = STATUS_SUCCESS;
  1502. THREAD_BASIC_INFORMATION tbiData;
  1503. LONG lOldPriority, lNewPriority;
  1504. LONG lStatus;
  1505. UNREFERENCED_PARAMETER (dwArg);
  1506. // KdPrint(("PERFLIB: Entering Data Collection Thread: PID: %d, TID: %d\n",
  1507. // GetCurrentProcessId(), GetCurrentThreadId()));
  1508. // raise the priority of this thread
  1509. status = NtQueryInformationThread (
  1510. NtCurrentThread(),
  1511. ThreadBasicInformation,
  1512. &tbiData,
  1513. sizeof(tbiData),
  1514. NULL);
  1515. if (NT_SUCCESS(status)) {
  1516. lOldPriority = tbiData.Priority;
  1517. lNewPriority = DEFAULT_THREAD_PRIORITY; // perfmon's favorite priority
  1518. //
  1519. // Only RAISE the priority here. Don't lower it if it's high
  1520. //
  1521. if (lOldPriority < lNewPriority) {
  1522. status = NtSetInformationThread(
  1523. NtCurrentThread(),
  1524. ThreadPriority,
  1525. &lNewPriority,
  1526. sizeof(lNewPriority)
  1527. );
  1528. if (status != STATUS_SUCCESS) {
  1529. DebugPrint((0, "Set Thread Priority failed: 0x%8.8x\n", status));
  1530. }
  1531. }
  1532. }
  1533. // wait for flags
  1534. while (!bExit) {
  1535. dwWaitStatus = WaitForMultipleObjects (
  1536. COLLECT_THREAD_LOOP_EVENT_COUNT,
  1537. hCollectEvents,
  1538. FALSE, // wait for ANY event to go
  1539. INFINITE); // wait for ever
  1540. // see why the wait returned:
  1541. if (dwWaitStatus == (WAIT_OBJECT_0 + COLLECT_THREAD_PROCESS_EVENT)) {
  1542. // the event is cleared automatically
  1543. // collect data
  1544. lStatus = QueryExtensibleData (
  1545. &CollectThreadData);
  1546. CollectThreadData.lReturnValue = lStatus;
  1547. SetEvent (hCollectEvents[COLLECT_THREAD_DONE_EVENT]);
  1548. } else if (dwWaitStatus == (WAIT_OBJECT_0 + COLLECT_THREAD_EXIT_EVENT)) {
  1549. bExit = TRUE;
  1550. continue; // go up and bail out
  1551. } else {
  1552. // who knows, so output message
  1553. KdPrint(("\nPERFLILB: Collect Thread wait returned unknown value: 0x%8.8x",dwWaitStatus));
  1554. bExit = TRUE;
  1555. continue;
  1556. }
  1557. }
  1558. // KdPrint(("PERFLIB: Leaving Data Collection Thread: PID: %d, TID: %d\n",
  1559. // GetCurrentProcessId(), GetCurrentThreadId()));
  1560. return ERROR_SUCCESS;
  1561. }
  1562. #endif
  1563. BOOL
  1564. PerfRegCleanup()
  1565. /*++
  1566. Routine Description:
  1567. Cleans up anything that perflib uses before it unloads. Assumes
  1568. that there are queries or perf reg opens outstanding.
  1569. Arguments:
  1570. None
  1571. Return Value:
  1572. Returns TRUE if succeed. FALSE otherwise.
  1573. --*/
  1574. {
  1575. if (hGlobalDataMutex != NULL) {
  1576. if (NumberOfOpens != 0)
  1577. return FALSE;
  1578. CloseHandle(hGlobalDataMutex);
  1579. hGlobalDataMutex = NULL;
  1580. }
  1581. return TRUE;
  1582. }