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.

1819 lines
58 KiB

  1. /*++ BUILD Version: 0001 // Increment this if a change has global effects
  2. Copyright (c) 1994-1997 Microsoft Corporation
  3. Module Name:
  4. utils.c
  5. Abstract:
  6. Utility functions used by the performance library functions
  7. Author:
  8. Russ Blake 11/15/91
  9. Revision History:
  10. --*/
  11. #define UNICODE
  12. //
  13. // Include files
  14. //
  15. #pragma warning(disable:4306)
  16. #include <nt.h>
  17. #include <ntrtl.h>
  18. #include <nturtl.h>
  19. #include <windows.h>
  20. #include <winperf.h>
  21. #include <prflbmsg.h>
  22. #include <regrpc.h>
  23. #include "ntconreg.h"
  24. #include "perflib.h"
  25. #pragma warning(default:4306)
  26. // test for delimiter, end of line and non-digit characters
  27. // used by IsNumberInUnicodeList routine
  28. //
  29. #define DIGIT 1
  30. #define DELIMITER 2
  31. #define INVALID 3
  32. #define EvalThisChar(c,d) ( \
  33. (c == d) ? DELIMITER : \
  34. (c == 0) ? DELIMITER : \
  35. (c < '0') ? INVALID : \
  36. (c > '9') ? INVALID : \
  37. DIGIT)
  38. #define MAX_KEYWORD_LEN (sizeof (ADDHELP_STRING) / sizeof(WCHAR))
  39. const WCHAR GLOBAL_STRING[] = L"GLOBAL";
  40. const WCHAR FOREIGN_STRING[] = L"FOREIGN";
  41. const WCHAR COSTLY_STRING[] = L"COSTLY";
  42. const WCHAR COUNTER_STRING[] = L"COUNTER";
  43. const WCHAR HELP_STRING[] = L"EXPLAIN";
  44. const WCHAR HELP_STRING2[] = L"HELP";
  45. const WCHAR ADDCOUNTER_STRING[] = L"ADDCOUNTER";
  46. const WCHAR ADDHELP_STRING[] = L"ADDEXPLAIN";
  47. const WCHAR ONLY_STRING[] = L"ONLY";
  48. const WCHAR DisablePerformanceCounters[] = L"Disable Performance Counters";
  49. // minimum length to hold a value name understood by Perflib
  50. const DWORD VALUE_NAME_LENGTH = ((sizeof(COSTLY_STRING) * 2) + sizeof(UNICODE_NULL));
  51. #define PL_TIMER_START_EVENT 0
  52. #define PL_TIMER_EXIT_EVENT 1
  53. #define PL_TIMER_NUM_OBJECTS 2
  54. static HANDLE hTimerHandles[PL_TIMER_NUM_OBJECTS] = {NULL,NULL};
  55. static HANDLE hTimerDataMutex = NULL;
  56. static HANDLE hPerflibTimingThread = NULL;
  57. static LPOPEN_PROC_WAIT_INFO pTimerItemListHead = NULL;
  58. #define PERFLIB_TIMER_INTERVAL 200 // 200 ms Timer
  59. #define PERFLIB_TIMEOUT_COUNT 64
  60. extern HANDLE hEventLog;
  61. #ifdef DBG
  62. #include <stdio.h> // for _vsnprintf
  63. #define DEBUG_BUFFER_LENGTH MAX_PATH*2
  64. ULONG PerfLibDebug = 0;
  65. UCHAR PerfLibDebugBuffer[DEBUG_BUFFER_LENGTH];
  66. #endif
  67. RTL_CRITICAL_SECTION PerfpCritSect;
  68. //
  69. // Perflib functions:
  70. //
  71. NTSTATUS
  72. GetPerflibKeyValue (
  73. IN LPCWSTR szItem,
  74. IN DWORD dwRegType,
  75. IN DWORD dwMaxSize, // ... of pReturnBuffer in bytes
  76. OUT LPVOID pReturnBuffer,
  77. IN DWORD dwDefaultSize, // ... of pDefault in bytes
  78. IN LPVOID pDefault,
  79. IN OUT PHKEY pKey
  80. )
  81. /*++
  82. read and return the current value of the specified value
  83. under the Perflib registry key. If unable to read the value
  84. return the default value from the argument list.
  85. the value is returned in the pReturnBuffer.
  86. NOTE: if pKey is NULL, this routine will open and close a local key
  87. if pKey is not NULL, this routine will return the handle to the
  88. Perflib regkey and the caller is responsible for closing it.
  89. --*/
  90. {
  91. HKEY hPerflibKey;
  92. OBJECT_ATTRIBUTES Obja;
  93. NTSTATUS Status;
  94. UNICODE_STRING PerflibSubKeyString;
  95. UNICODE_STRING ValueNameString;
  96. LONG lReturn = STATUS_SUCCESS;
  97. PKEY_VALUE_PARTIAL_INFORMATION pValueInformation;
  98. ULONG ValueBufferLength;
  99. ULONG ResultLength;
  100. BOOL bUseDefault = TRUE;
  101. Status = STATUS_SUCCESS;
  102. hPerflibKey = NULL;
  103. if (pKey != NULL) {
  104. hPerflibKey = *pKey;
  105. }
  106. if (hPerflibKey == NULL) {
  107. // initialize UNICODE_STRING structures used in this function
  108. RtlInitUnicodeString (
  109. &PerflibSubKeyString,
  110. (LPCWSTR)L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");
  111. RtlInitUnicodeString (
  112. &ValueNameString,
  113. (LPWSTR)szItem);
  114. //
  115. // Initialize the OBJECT_ATTRIBUTES structure and open the key.
  116. //
  117. InitializeObjectAttributes(
  118. &Obja,
  119. &PerflibSubKeyString,
  120. OBJ_CASE_INSENSITIVE,
  121. NULL,
  122. NULL
  123. );
  124. Status = NtOpenKey(
  125. &hPerflibKey,
  126. KEY_READ,
  127. &Obja
  128. );
  129. }
  130. if (NT_SUCCESS( Status )) {
  131. // read value of desired entry
  132. ValueBufferLength = ResultLength = 1024;
  133. pValueInformation = ALLOCMEM(ResultLength);
  134. if (pValueInformation != NULL) {
  135. while ( (Status = NtQueryValueKey(hPerflibKey,
  136. &ValueNameString,
  137. KeyValuePartialInformation,
  138. pValueInformation,
  139. ValueBufferLength,
  140. &ResultLength))
  141. == STATUS_BUFFER_OVERFLOW ) {
  142. FREEMEM(pValueInformation);
  143. pValueInformation = ALLOCMEM(ResultLength);
  144. if ( pValueInformation == NULL) {
  145. ValueBufferLength = 0;
  146. Status = STATUS_NO_MEMORY;
  147. break;
  148. } else {
  149. ValueBufferLength = ResultLength;
  150. }
  151. }
  152. if (NT_SUCCESS(Status)) {
  153. // check to see if it's the desired type
  154. if (pValueInformation->Type == dwRegType) {
  155. // see if it will fit
  156. if (pValueInformation->DataLength <= dwMaxSize) {
  157. memcpy (pReturnBuffer, &pValueInformation->Data[0],
  158. pValueInformation->DataLength);
  159. bUseDefault = FALSE;
  160. lReturn = STATUS_SUCCESS;
  161. }
  162. }
  163. } else {
  164. // return the default value
  165. lReturn = Status;
  166. }
  167. // release temp buffer
  168. if (pValueInformation) {
  169. FREEMEM (pValueInformation);
  170. }
  171. } else {
  172. // unable to allocate memory for this operation so
  173. // just return the default value
  174. }
  175. if (pKey == NULL) {
  176. // close the local registry key
  177. NtClose(hPerflibKey);
  178. }
  179. else {
  180. *pKey = hPerflibKey;
  181. }
  182. } else {
  183. // return default value
  184. }
  185. if (bUseDefault) {
  186. memcpy (pReturnBuffer, pDefault, dwDefaultSize);
  187. lReturn = STATUS_SUCCESS;
  188. }
  189. return lReturn;
  190. }
  191. BOOL
  192. MatchString (
  193. IN LPCWSTR lpValueArg,
  194. IN LPCWSTR lpNameArg
  195. )
  196. /*++
  197. MatchString
  198. return TRUE if lpName is in lpValue. Otherwise return FALSE
  199. Arguments
  200. IN lpValue
  201. string passed to PerfRegQuery Value for processing
  202. IN lpName
  203. string for one of the keyword names
  204. Return TRUE | FALSE
  205. --*/
  206. {
  207. BOOL bFound = TRUE; // assume found until contradicted
  208. LPWSTR lpValue = (LPWSTR)lpValueArg;
  209. LPWSTR lpName = (LPWSTR)lpNameArg;
  210. // check to the length of the shortest string
  211. while ((*lpValue != 0) && (*lpName != 0)) {
  212. if (*lpValue++ != *lpName++) {
  213. bFound = FALSE; // no match
  214. break; // bail out now
  215. }
  216. }
  217. return (bFound);
  218. }
  219. DWORD
  220. GetQueryType (
  221. IN LPWSTR lpValue
  222. )
  223. /*++
  224. GetQueryType
  225. returns the type of query described in the lpValue string so that
  226. the appropriate processing method may be used
  227. Arguments
  228. IN lpValue
  229. string passed to PerfRegQuery Value for processing
  230. Return Value
  231. QUERY_GLOBAL
  232. if lpValue == 0 (null pointer)
  233. lpValue == pointer to Null string
  234. lpValue == pointer to "Global" string
  235. QUERY_FOREIGN
  236. if lpValue == pointer to "Foriegn" string
  237. QUERY_COSTLY
  238. if lpValue == pointer to "Costly" string
  239. QUERY_COUNTER
  240. if lpValue == pointer to "Counter" string
  241. QUERY_HELP
  242. if lpValue == pointer to "Explain" string
  243. QUERY_ADDCOUNTER
  244. if lpValue == pointer to "Addcounter" string
  245. QUERY_ADDHELP
  246. if lpValue == pointer to "Addexplain" string
  247. otherwise:
  248. QUERY_ITEMS
  249. --*/
  250. {
  251. WCHAR LocalBuff[MAX_KEYWORD_LEN+1];
  252. WORD i;
  253. if (lpValue == 0 || *lpValue == 0)
  254. return QUERY_GLOBAL;
  255. // convert the input string to Upper case before matching
  256. for (i=0; i < MAX_KEYWORD_LEN; i++) {
  257. if (*lpValue == TEXT(' ') || *lpValue == TEXT('\0')) {
  258. break;
  259. }
  260. LocalBuff[i] = *lpValue ;
  261. if (*lpValue >= TEXT('a') && *lpValue <= TEXT('z')) {
  262. LocalBuff[i] = LocalBuff[i] - TEXT('a') + TEXT('A');
  263. }
  264. lpValue++ ;
  265. }
  266. LocalBuff[i] = TEXT('\0');
  267. // check for "Global" request
  268. if (MatchString (LocalBuff, GLOBAL_STRING))
  269. return QUERY_GLOBAL ;
  270. // check for "Foreign" request
  271. if (MatchString (LocalBuff, FOREIGN_STRING))
  272. return QUERY_FOREIGN ;
  273. // check for "Costly" request
  274. if (MatchString (LocalBuff, COSTLY_STRING))
  275. return QUERY_COSTLY;
  276. // check for "Counter" request
  277. if (MatchString (LocalBuff, COUNTER_STRING))
  278. return QUERY_COUNTER;
  279. // check for "Help" request
  280. if (MatchString (LocalBuff, HELP_STRING))
  281. return QUERY_HELP;
  282. if (MatchString (LocalBuff, HELP_STRING2))
  283. return QUERY_HELP;
  284. // check for "AddCounter" request
  285. if (MatchString (LocalBuff, ADDCOUNTER_STRING))
  286. return QUERY_ADDCOUNTER;
  287. // check for "AddHelp" request
  288. if (MatchString (LocalBuff, ADDHELP_STRING))
  289. return QUERY_ADDHELP;
  290. // None of the above, then it must be an item list
  291. return QUERY_ITEMS;
  292. }
  293. DWORD
  294. GetNextNumberFromList (
  295. IN LPWSTR szStartChar,
  296. IN LPWSTR *szNextChar
  297. )
  298. /*++
  299. Reads a character string from the szStartChar to the next
  300. delimiting space character or the end of the string and returns
  301. the value of the decimal number found. If no valid number is found
  302. then 0 is returned. The pointer to the next character in the
  303. string is returned in the szNextChar parameter. If the character
  304. referenced by this pointer is 0, then the end of the string has
  305. been reached.
  306. --*/
  307. {
  308. DWORD dwThisNumber = 0;
  309. WCHAR *pwcThisChar = szStartChar;
  310. WCHAR wcDelimiter = L' ';
  311. BOOL bValidNumber = FALSE;
  312. if (szStartChar != 0) {
  313. do {
  314. switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
  315. case DIGIT:
  316. // if this is the first digit after a delimiter, then
  317. // set flags to start computing the new number
  318. bValidNumber = TRUE;
  319. dwThisNumber *= 10;
  320. dwThisNumber += (*pwcThisChar - (WCHAR)'0');
  321. break;
  322. case DELIMITER:
  323. // a delimter is either the delimiter character or the
  324. // end of the string ('\0') if when the delimiter has been
  325. // reached a valid number was found, then return it
  326. //
  327. if (bValidNumber || (*pwcThisChar == 0)) {
  328. *szNextChar = pwcThisChar;
  329. return dwThisNumber;
  330. } else {
  331. // continue until a non-delimiter char or the
  332. // end of the file is found
  333. }
  334. break;
  335. case INVALID:
  336. // if an invalid character was encountered, ignore all
  337. // characters up to the next delimiter and then start fresh.
  338. // the invalid number is not compared.
  339. bValidNumber = FALSE;
  340. break;
  341. default:
  342. break;
  343. }
  344. pwcThisChar++;
  345. } while (pwcThisChar != NULL); // always TRUE - avoid W4 warning
  346. return 0;
  347. } else {
  348. *szNextChar = szStartChar;
  349. return 0;
  350. }
  351. }
  352. BOOL
  353. IsNumberInUnicodeList (
  354. IN DWORD dwNumber,
  355. IN LPWSTR lpwszUnicodeList
  356. )
  357. /*++
  358. IsNumberInUnicodeList
  359. Arguments:
  360. IN dwNumber
  361. DWORD number to find in list
  362. IN lpwszUnicodeList
  363. Null terminated, Space delimited list of decimal numbers
  364. Return Value:
  365. TRUE:
  366. dwNumber was found in the list of unicode number strings
  367. FALSE:
  368. dwNumber was not found in the list.
  369. --*/
  370. {
  371. DWORD dwThisNumber;
  372. WCHAR *pwcThisChar;
  373. if (lpwszUnicodeList == 0) return FALSE; // null pointer, # not founde
  374. pwcThisChar = lpwszUnicodeList;
  375. dwThisNumber = 0;
  376. while (*pwcThisChar != 0) {
  377. dwThisNumber = GetNextNumberFromList (
  378. pwcThisChar, &pwcThisChar);
  379. if (dwNumber == dwThisNumber) return TRUE;
  380. }
  381. // if here, then the number wasn't found
  382. return FALSE;
  383. } // IsNumberInUnicodeList
  384. BOOL
  385. MonBuildPerfDataBlock(
  386. PERF_DATA_BLOCK *pBuffer,
  387. PVOID *pBufferNext,
  388. DWORD NumObjectTypes,
  389. DWORD DefaultObject
  390. )
  391. /*++
  392. MonBuildPerfDataBlock - build the PERF_DATA_BLOCK structure
  393. Inputs:
  394. pBuffer - where the data block should be placed
  395. pBufferNext - where pointer to next byte of data block
  396. is to begin; DWORD aligned
  397. NumObjectTypes - number of types of objects being reported
  398. DefaultObject - object to display by default when
  399. this system is selected; this is the
  400. object type title index
  401. --*/
  402. {
  403. // Initialize Signature and version ID for this data structure
  404. pBuffer->Signature[0] = L'P';
  405. pBuffer->Signature[1] = L'E';
  406. pBuffer->Signature[2] = L'R';
  407. pBuffer->Signature[3] = L'F';
  408. pBuffer->LittleEndian = TRUE;
  409. pBuffer->Version = PERF_DATA_VERSION;
  410. pBuffer->Revision = PERF_DATA_REVISION;
  411. //
  412. // The next field will be filled in at the end when the length
  413. // of the return data is known
  414. //
  415. pBuffer->TotalByteLength = 0;
  416. pBuffer->NumObjectTypes = NumObjectTypes;
  417. pBuffer->DefaultObject = DefaultObject;
  418. GetSystemTime(&pBuffer->SystemTime);
  419. NtQueryPerformanceCounter(&pBuffer->PerfTime,&pBuffer->PerfFreq);
  420. GetSystemTimeAsFileTime ((FILETIME *)&pBuffer->PerfTime100nSec.QuadPart);
  421. if ( ComputerNameLength ) {
  422. // There is a Computer name: i.e., the network is installed
  423. pBuffer->SystemNameLength = ComputerNameLength;
  424. pBuffer->SystemNameOffset = sizeof(PERF_DATA_BLOCK);
  425. RtlMoveMemory(&pBuffer[1],
  426. pComputerName,
  427. ComputerNameLength);
  428. *pBufferNext = (PVOID) ((PCHAR) &pBuffer[1] +
  429. QWORD_MULTIPLE(ComputerNameLength));
  430. pBuffer->HeaderLength = (DWORD)((PCHAR) *pBufferNext - (PCHAR) pBuffer);
  431. } else {
  432. // Member of Computers Anonymous
  433. pBuffer->SystemNameLength = 0;
  434. pBuffer->SystemNameOffset = 0;
  435. *pBufferNext = &pBuffer[1];
  436. pBuffer->HeaderLength = sizeof(PERF_DATA_BLOCK);
  437. }
  438. return 0;
  439. }
  440. //
  441. // Timer functions
  442. //
  443. DWORD
  444. PerflibTimerFunction (
  445. LPDWORD dwArg
  446. )
  447. /*++
  448. PerflibTimerFunction
  449. Timing thread used to write an event log message if the timer expires.
  450. This thread runs until the Exit event is set or the wait for the
  451. Exit event times out.
  452. While the start event is set, then the timer checks the current events
  453. to be timed and reports on any that have expired. It then sleeps for
  454. the duration of the timing interval after which it checks the status
  455. of the start & exit events to begin the next cycle.
  456. The timing events are added and deleted from the list only by the
  457. StartPerflibFunctionTimer and KillPerflibFunctionTimer functions.
  458. Arguments
  459. dwArg -- Not Used
  460. --*/
  461. {
  462. NTSTATUS NtStatus = STATUS_SUCCESS;
  463. BOOL bKeepTiming = TRUE;
  464. LPOPEN_PROC_WAIT_INFO pLocalInfo;
  465. LPWSTR szMessageArray[2];
  466. LARGE_INTEGER liWaitTime;
  467. UNREFERENCED_PARAMETER (dwArg);
  468. DebugPrint ((5, "\nPERFLIB: Entering Timing Thread: PID: %d, TID: %d",
  469. GetCurrentProcessId(), GetCurrentThreadId()));
  470. TRACE((WINPERF_DBG_TRACE_INFO),
  471. (& PerflibGuid,
  472. __LINE__,
  473. PERF_TIMERFUNCTION,
  474. 0,
  475. STATUS_SUCCESS,
  476. NULL));
  477. while (bKeepTiming) {
  478. liWaitTime.QuadPart =
  479. MakeTimeOutValue((PERFLIB_TIMING_THREAD_TIMEOUT));
  480. // wait for either the start or exit event flags to be set
  481. NtStatus = NtWaitForMultipleObjects (
  482. PL_TIMER_NUM_OBJECTS,
  483. &hTimerHandles[0],
  484. WaitAny, //wait for either one to be set
  485. FALSE, // not alertable
  486. &liWaitTime);
  487. if ((NtStatus != STATUS_TIMEOUT) &&
  488. (NtStatus <= STATUS_WAIT_3)) {
  489. if ((NtStatus - STATUS_WAIT_0) == PL_TIMER_EXIT_EVENT ) {
  490. DebugPrint ((5, "\nPERFLIB: Timing Thread received Exit Event (1): PID: %d, TID: %d",
  491. GetCurrentProcessId(), GetCurrentThreadId()));
  492. // then that's all
  493. bKeepTiming = FALSE;
  494. NtStatus = STATUS_SUCCESS;
  495. break;
  496. } else if ((NtStatus - STATUS_WAIT_0) == PL_TIMER_START_EVENT) {
  497. DebugPrint ((5, "\nPERFLIB: Timing Thread received Start Event: PID: %d, TID: %d",
  498. GetCurrentProcessId(), GetCurrentThreadId()));
  499. // then the timer is running so wait the interval period
  500. // wait on exit event here to prevent hanging
  501. liWaitTime.QuadPart =
  502. MakeTimeOutValue((PERFLIB_TIMER_INTERVAL));
  503. NtStatus = NtWaitForSingleObject (
  504. hTimerHandles[PL_TIMER_EXIT_EVENT],
  505. FALSE,
  506. &liWaitTime);
  507. if (NtStatus == STATUS_TIMEOUT) {
  508. // then the wait time expired without being told
  509. // to terminate the thread so
  510. // now evaluate the list of timed events
  511. // lock the data mutex
  512. DWORD dwTimeOut = 0;
  513. DebugPrint ((5, "\nPERFLIB: Timing Thread Evaluating Entries: PID: %d, TID: %d",
  514. GetCurrentProcessId(), GetCurrentThreadId()));
  515. liWaitTime.QuadPart =
  516. MakeTimeOutValue((PERFLIB_TIMER_INTERVAL * 2));
  517. NtStatus = STATUS_TIMEOUT;
  518. while ( NtStatus == STATUS_TIMEOUT
  519. && dwTimeOut < PERFLIB_TIMEOUT_COUNT) {
  520. NtStatus = NtWaitForSingleObject (
  521. hTimerDataMutex,
  522. FALSE,
  523. & liWaitTime);
  524. if (NtStatus == STATUS_TIMEOUT) {
  525. dwTimeOut ++;
  526. DebugPrint((5, "\nPERFLIB:NtWaitForSingleObject(TimerDataMutex,%d) time out for the %dth time. PID: %d, TID: %d",
  527. liWaitTime, dwTimeOut,
  528. GetCurrentProcessId(),
  529. GetCurrentThreadId()));
  530. TRACE((WINPERF_DBG_TRACE_WARNING),
  531. (& PerflibGuid,
  532. __LINE__,
  533. PERF_TIMERFUNCTION,
  534. 0,
  535. STATUS_TIMEOUT,
  536. & dwTimeOut, sizeof(dwTimeOut),
  537. NULL));
  538. }
  539. }
  540. if (NtStatus != STATUS_WAIT_0) {
  541. // cannot grab hTimerDataMutex, there is no guarantee
  542. // that this is the exclusive one to work on
  543. // pTimerItemListHead list, so just bail out.
  544. //
  545. bKeepTiming = FALSE;
  546. NtStatus = STATUS_SUCCESS;
  547. TRACE((WINPERF_DBG_TRACE_WARNING),
  548. (& PerflibGuid,
  549. __LINE__,
  550. PERF_TIMERFUNCTION,
  551. 0,
  552. NtStatus,
  553. NULL));
  554. break;
  555. }
  556. else {
  557. for (pLocalInfo = pTimerItemListHead;
  558. pLocalInfo != NULL;
  559. pLocalInfo = pLocalInfo->pNext) {
  560. DebugPrint ((5, "\nPERFLIB: Timing Thread Entry %X. count %d: PID: %d, TID: %d",
  561. pLocalInfo, pLocalInfo->dwWaitTime,
  562. GetCurrentProcessId(), GetCurrentThreadId()));
  563. if (pLocalInfo->dwWaitTime > 0) {
  564. if (pLocalInfo->dwWaitTime == 1) {
  565. if (THROTTLE_PERFLIB(pLocalInfo->dwEventMsg)) {
  566. // then this is the last interval so log error
  567. // if this DLL hasn't already been disabled
  568. szMessageArray[0] = pLocalInfo->szServiceName;
  569. szMessageArray[1] = pLocalInfo->szLibraryName;
  570. ReportEvent (hEventLog,
  571. EVENTLOG_ERROR_TYPE, // error type
  572. 0, // category (not used)
  573. (DWORD)pLocalInfo->dwEventMsg, // event,
  574. NULL, // SID (not used),
  575. 2, // number of strings
  576. 0, // sizeof raw data
  577. szMessageArray, // message text array
  578. NULL); // raw data
  579. }
  580. if (pLocalInfo->pData != NULL) {
  581. if (lPerflibConfigFlags & PLCF_ENABLE_TIMEOUT_DISABLE) {
  582. if (!(((PEXT_OBJECT)pLocalInfo->pData)->dwFlags & PERF_EO_DISABLED)) {
  583. // then pData is an extensible counter data block
  584. // disable the ext. counter
  585. DisablePerfLibrary((PEXT_OBJECT) pLocalInfo->pData,
  586. PERFLIB_DISABLE_ALL);
  587. } // end if not already disabled
  588. } // end if disable DLL on Timeouts is enabled
  589. } // data is NULL so skip
  590. }
  591. pLocalInfo->dwWaitTime--;
  592. }
  593. }
  594. ReleaseMutex (hTimerDataMutex);
  595. }
  596. } else {
  597. DebugPrint ((5, "\nPERFLIB: Timing Thread received Exit Event (2): PID: %d, TID: %d",
  598. GetCurrentProcessId(), GetCurrentThreadId()));
  599. // we've been told to exit so
  600. NtStatus = STATUS_SUCCESS;
  601. bKeepTiming = FALSE;
  602. break;
  603. }
  604. } else {
  605. // some unexpected error was returned
  606. assert (FALSE);
  607. }
  608. } else {
  609. DebugPrint ((5, "\nPERFLIB: Timing Thread Timed out: PID: %d, TID: %d",
  610. GetCurrentProcessId(), GetCurrentThreadId()));
  611. // the wait timed out so it's time to go
  612. NtStatus = STATUS_SUCCESS;
  613. bKeepTiming = FALSE;
  614. break;
  615. }
  616. }
  617. DebugPrint ((5, "\nPERFLIB: Leaving Timing Thread: PID: %d, TID: %d",
  618. GetCurrentProcessId(), GetCurrentThreadId()));
  619. return PerfpDosError(NtStatus);
  620. }
  621. HANDLE
  622. StartPerflibFunctionTimer (
  623. IN LPOPEN_PROC_WAIT_INFO pInfo
  624. )
  625. /*++
  626. Starts a timing event by adding it to the list of timing events.
  627. If the timer thread is not running, then the is started as well.
  628. If this is the first event in the list then the Start Event is
  629. set indicating that the timing thread can begin processing timing
  630. event(s).
  631. --*/
  632. {
  633. LONG Status = ERROR_SUCCESS;
  634. LPOPEN_PROC_WAIT_INFO pLocalInfo = NULL;
  635. DWORD dwLibNameLen = 0;
  636. DWORD dwBufferLength = sizeof (OPEN_PROC_WAIT_INFO);
  637. LARGE_INTEGER liWaitTime;
  638. HANDLE hReturn = NULL;
  639. HANDLE hDataMutex;
  640. if (pInfo == NULL) {
  641. // no required argument
  642. Status = ERROR_INVALID_PARAMETER;
  643. } else {
  644. // check on or create sync objects
  645. // allocate timing events for the timing thread
  646. if (hTimerHandles[PL_TIMER_START_EVENT] == NULL) {
  647. // create the event as NOT signaled since we're not ready to start
  648. hTimerHandles[PL_TIMER_START_EVENT] = CreateEvent (NULL, TRUE, FALSE, NULL);
  649. if (hTimerHandles[PL_TIMER_START_EVENT] == NULL) {
  650. Status = GetLastError();
  651. }
  652. }
  653. if (hTimerHandles[PL_TIMER_EXIT_EVENT] == NULL) {
  654. hTimerHandles[PL_TIMER_EXIT_EVENT] = CreateEvent (NULL, TRUE, FALSE, NULL);
  655. if (hTimerHandles[PL_TIMER_EXIT_EVENT] == NULL) {
  656. Status = GetLastError();
  657. }
  658. }
  659. // create data sync mutex if it hasn't already been created
  660. if (hTimerDataMutex == NULL) {
  661. hDataMutex = CreateMutex(NULL, FALSE, NULL);
  662. if (hDataMutex == NULL) {
  663. Status = GetLastError();
  664. }
  665. else {
  666. if (InterlockedCompareExchangePointer(& hTimerDataMutex,
  667. hDataMutex,
  668. NULL) != NULL) {
  669. CloseHandle(hDataMutex);
  670. hDataMutex = NULL;
  671. }
  672. else {
  673. hTimerDataMutex = hDataMutex;
  674. }
  675. }
  676. }
  677. }
  678. if (Status == ERROR_SUCCESS) {
  679. // continue creating timer entry
  680. if (hPerflibTimingThread != NULL) {
  681. // see if the handle is valid (i.e the thread is alive)
  682. Status = WaitForSingleObject (hPerflibTimingThread, 0);
  683. if (Status == WAIT_OBJECT_0) {
  684. // the thread has terminated so close the handle
  685. CloseHandle (hPerflibTimingThread);
  686. hPerflibTimingThread = NULL;
  687. Status = ERROR_SUCCESS;
  688. } else if (Status == WAIT_TIMEOUT) {
  689. // the thread is still running so continue
  690. Status = ERROR_SUCCESS;
  691. } else {
  692. // some other, probably serious, error
  693. // so pass it on through
  694. }
  695. } else {
  696. // the thread has never been created yet so continue
  697. }
  698. if (hPerflibTimingThread == NULL) {
  699. // create the timing thread
  700. assert (pTimerItemListHead == NULL); // there should be no entries, yet
  701. // everything is ready for the timer thread
  702. hPerflibTimingThread = CreateThread (
  703. NULL, 0,
  704. (LPTHREAD_START_ROUTINE)PerflibTimerFunction,
  705. NULL, 0, NULL);
  706. assert (hPerflibTimingThread != NULL);
  707. if (hPerflibTimingThread == NULL) {
  708. Status = GetLastError();
  709. }
  710. }
  711. if (Status == ERROR_SUCCESS) {
  712. // compute the length of the required buffer;
  713. dwLibNameLen = (lstrlenW (pInfo->szLibraryName) + 1) * sizeof(WCHAR);
  714. dwBufferLength += dwLibNameLen;
  715. dwBufferLength += (lstrlenW (pInfo->szServiceName) + 1) * sizeof(WCHAR);
  716. dwBufferLength = QWORD_MULTIPLE (dwBufferLength);
  717. pLocalInfo = ALLOCMEM (dwBufferLength);
  718. if (pLocalInfo == NULL)
  719. Status = ERROR_OUTOFMEMORY;
  720. }
  721. if ((Status == ERROR_SUCCESS) && (pLocalInfo != NULL)) {
  722. // copy the arg buffer to the local list
  723. pLocalInfo->szLibraryName = (LPWSTR)&pLocalInfo[1];
  724. lstrcpyW (pLocalInfo->szLibraryName, pInfo->szLibraryName);
  725. pLocalInfo->szServiceName = (LPWSTR)
  726. ((LPBYTE)pLocalInfo->szLibraryName + dwLibNameLen);
  727. lstrcpyW (pLocalInfo->szServiceName, pInfo->szServiceName);
  728. // convert wait time in milliseconds to the number of "loops"
  729. pLocalInfo->dwWaitTime = pInfo->dwWaitTime / PERFLIB_TIMER_INTERVAL;
  730. if (pLocalInfo->dwWaitTime == 0) pLocalInfo->dwWaitTime =1; // have at least 1 loop
  731. pLocalInfo->dwEventMsg = pInfo->dwEventMsg;
  732. pLocalInfo->pData = pInfo->pData;
  733. // wait for access to the data
  734. if (hTimerDataMutex != NULL) {
  735. NTSTATUS NtStatus;
  736. liWaitTime.QuadPart =
  737. MakeTimeOutValue((PERFLIB_TIMER_INTERVAL * 2));
  738. NtStatus = NtWaitForSingleObject (
  739. hTimerDataMutex,
  740. FALSE,
  741. &liWaitTime);
  742. Status = PerfpDosError(NtStatus);
  743. } else {
  744. Status = ERROR_NOT_READY;
  745. }
  746. if (Status == WAIT_OBJECT_0) {
  747. DebugPrint ((5, "\nPERFLIB: Timing Thread Adding Entry: %X (%d) PID: %d, TID: %d",
  748. pLocalInfo, pLocalInfo->dwWaitTime,
  749. GetCurrentProcessId(), GetCurrentThreadId()));
  750. // we have access to the data so add this item to the front of the list
  751. pLocalInfo->pNext = pTimerItemListHead;
  752. pTimerItemListHead = pLocalInfo;
  753. ReleaseMutex (hTimerDataMutex);
  754. if (pLocalInfo->pNext == NULL) {
  755. // then the list was empty before this call so start the timer
  756. // going
  757. SetEvent (hTimerHandles[PL_TIMER_START_EVENT]);
  758. }
  759. hReturn = (HANDLE)pLocalInfo;
  760. } else {
  761. SetLastError (Status);
  762. }
  763. } else {
  764. // unable to create thread
  765. SetLastError (Status);
  766. }
  767. } else {
  768. // unable to start timer
  769. SetLastError (Status);
  770. }
  771. return hReturn;
  772. }
  773. DWORD
  774. KillPerflibFunctionTimer (
  775. IN HANDLE hPerflibTimer
  776. )
  777. /*++
  778. Terminates a timing event by removing it from the list. When the last
  779. item is removed from the list the Start event is reset so the timing
  780. thread will wait for either the next start event, exit event or it's
  781. timeout to expire.
  782. --*/
  783. {
  784. NTSTATUS Status;
  785. LPOPEN_PROC_WAIT_INFO pArg = (LPOPEN_PROC_WAIT_INFO)hPerflibTimer;
  786. LPOPEN_PROC_WAIT_INFO pLocalInfo;
  787. BOOL bFound = FALSE;
  788. LARGE_INTEGER liWaitTime;
  789. DWORD dwReturn = ERROR_SUCCESS;
  790. if (hTimerDataMutex == NULL) {
  791. dwReturn = ERROR_NOT_READY;
  792. } else if (pArg == NULL) {
  793. dwReturn = ERROR_INVALID_HANDLE;
  794. } else {
  795. // so far so good
  796. // wait for access to the data
  797. liWaitTime.QuadPart =
  798. MakeTimeOutValue((PERFLIB_TIMER_INTERVAL * 2));
  799. Status = NtWaitForSingleObject (
  800. hTimerDataMutex,
  801. FALSE,
  802. &liWaitTime);
  803. if (Status == STATUS_WAIT_0) {
  804. // we have access to the list so walk down the list and remove the
  805. // specified item
  806. // see if it's the first one in the list
  807. DebugPrint ((5, "\nPERFLIB: Timing Thread Removing Entry: %X (%d) PID: %d, TID: %d",
  808. pArg, pArg->dwWaitTime,
  809. GetCurrentProcessId(), GetCurrentThreadId()));
  810. if (pArg == pTimerItemListHead) {
  811. // then remove it
  812. pTimerItemListHead = pArg->pNext;
  813. bFound = TRUE;
  814. } else {
  815. for (pLocalInfo = pTimerItemListHead;
  816. pLocalInfo != NULL;
  817. pLocalInfo = pLocalInfo->pNext) {
  818. if (pLocalInfo->pNext == pArg) {
  819. pLocalInfo->pNext = pArg->pNext;
  820. bFound = TRUE;
  821. break;
  822. }
  823. }
  824. }
  825. assert (bFound);
  826. if (bFound) {
  827. // it's out of the list so release the lock
  828. ReleaseMutex (hTimerDataMutex);
  829. if (pTimerItemListHead == NULL) {
  830. // then the list is empty now so stop timing
  831. // going
  832. ResetEvent (hTimerHandles[PL_TIMER_START_EVENT]);
  833. }
  834. // free memory
  835. FREEMEM (pArg);
  836. dwReturn = ERROR_SUCCESS;
  837. } else {
  838. dwReturn = ERROR_NOT_FOUND;
  839. }
  840. } else {
  841. dwReturn = ERROR_TIMEOUT;
  842. }
  843. }
  844. return dwReturn;
  845. }
  846. DWORD
  847. DestroyPerflibFunctionTimer (
  848. )
  849. /*++
  850. Terminates the timing thread and cancels any current timer events.
  851. NOTE: This routine can be called even if timer thread is not started!
  852. --*/
  853. {
  854. NTSTATUS Status = STATUS_WAIT_0;
  855. LPOPEN_PROC_WAIT_INFO pThisItem;
  856. LPOPEN_PROC_WAIT_INFO pNextItem;
  857. LARGE_INTEGER liWaitTime;
  858. HANDLE hTemp;
  859. if (hTimerDataMutex != NULL) {
  860. DWORD dwTimeOut = 0;
  861. LONG dwStatus = ERROR_SUCCESS;
  862. // wait for data mutex
  863. liWaitTime.QuadPart =
  864. MakeTimeOutValue((PERFLIB_TIMER_INTERVAL * 5));
  865. Status = STATUS_TIMEOUT;
  866. while (Status == STATUS_TIMEOUT && dwTimeOut < PERFLIB_TIMEOUT_COUNT) {
  867. Status = NtWaitForSingleObject (
  868. hTimerDataMutex,
  869. FALSE,
  870. & liWaitTime);
  871. if (Status == STATUS_TIMEOUT) {
  872. if (hPerflibTimingThread != NULL) {
  873. // see if the handle is valid (i.e the thread is alive)
  874. dwStatus = WaitForSingleObject(hPerflibTimingThread,
  875. liWaitTime.LowPart);
  876. if (dwStatus == WAIT_OBJECT_0) {
  877. // the thread has terminated so close the handle
  878. Status = STATUS_WAIT_0;
  879. }
  880. }
  881. }
  882. if (Status == STATUS_TIMEOUT) {
  883. dwTimeOut ++;
  884. DebugPrint((5, "\nPERFLIB:NtWaitForSingleObject(TimerDataMutex,%d) time out for the %dth time in DestroyPErflibFunctionTimer(). PID: %d, TID: %d",
  885. liWaitTime, dwTimeOut,
  886. GetCurrentProcessId(),
  887. GetCurrentThreadId()));
  888. TRACE((WINPERF_DBG_TRACE_WARNING),
  889. (& PerflibGuid,
  890. __LINE__,
  891. PERF_DESTROYFUNCTIONTIMER,
  892. 0,
  893. STATUS_TIMEOUT,
  894. & dwTimeOut, sizeof(dwTimeOut),
  895. NULL));
  896. }
  897. }
  898. assert (Status != STATUS_TIMEOUT);
  899. }
  900. // free all entries in the list
  901. if (Status == STATUS_WAIT_0) {
  902. for (pNextItem = pTimerItemListHead;
  903. pNextItem != NULL;) {
  904. pThisItem = pNextItem;
  905. pNextItem = pThisItem->pNext;
  906. FREEMEM (pThisItem);
  907. }
  908. }
  909. else {
  910. TRACE((WINPERF_DBG_TRACE_WARNING),
  911. (& PerflibGuid,
  912. __LINE__,
  913. PERF_DESTROYFUNCTIONTIMER,
  914. 0,
  915. Status,
  916. NULL));
  917. }
  918. // all items have been freed so clear header
  919. pTimerItemListHead = NULL;
  920. // set exit event
  921. if (hTimerHandles[PL_TIMER_EXIT_EVENT] != NULL) {
  922. SetEvent (hTimerHandles[PL_TIMER_EXIT_EVENT]);
  923. }
  924. if (hPerflibTimingThread != NULL) {
  925. // wait for thread to terminate
  926. liWaitTime.QuadPart =
  927. MakeTimeOutValue((PERFLIB_TIMER_INTERVAL * 5));
  928. Status = NtWaitForSingleObject (
  929. hPerflibTimingThread,
  930. FALSE,
  931. &liWaitTime);
  932. assert (Status != STATUS_TIMEOUT);
  933. hTemp = hPerflibTimingThread;
  934. hPerflibTimingThread = NULL;
  935. CloseHandle (hTemp);
  936. }
  937. if (hTimerDataMutex != NULL) {
  938. hTemp = hTimerDataMutex;
  939. hTimerDataMutex = NULL;
  940. // close handles and leave
  941. ReleaseMutex (hTemp);
  942. CloseHandle (hTemp);
  943. }
  944. if (hTimerHandles[PL_TIMER_START_EVENT] != NULL) {
  945. CloseHandle (hTimerHandles[PL_TIMER_START_EVENT]);
  946. hTimerHandles[PL_TIMER_START_EVENT] = NULL;
  947. }
  948. if (hTimerHandles[PL_TIMER_EXIT_EVENT] != NULL) {
  949. CloseHandle (hTimerHandles[PL_TIMER_EXIT_EVENT]);
  950. hTimerHandles[PL_TIMER_EXIT_EVENT] = NULL;
  951. }
  952. return ERROR_SUCCESS;
  953. }
  954. LONG
  955. PrivateRegQueryValueExT (
  956. HKEY hKey,
  957. LPVOID lpValueName,
  958. LPDWORD lpReserved,
  959. LPDWORD lpType,
  960. LPBYTE lpData,
  961. LPDWORD lpcbData,
  962. BOOL bUnicode
  963. )
  964. /*
  965. wrapper function to allow RegQueryValues while inside a RegQueryValue
  966. */
  967. {
  968. LONG ReturnStatus;
  969. NTSTATUS ntStatus = STATUS_SUCCESS;
  970. BOOL bStatus;
  971. UNICODE_STRING usLocal = {0,0,NULL};
  972. PSTR AnsiValueBuffer;
  973. ULONG AnsiValueLength;
  974. PWSTR UnicodeValueBuffer;
  975. ULONG UnicodeValueLength;
  976. ULONG Index;
  977. PKEY_VALUE_PARTIAL_INFORMATION pValueInformation;
  978. LONG ValueBufferLength;
  979. ULONG ResultLength;
  980. UNREFERENCED_PARAMETER (lpReserved);
  981. if (hKey == NULL || hKey == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
  982. if (bUnicode) {
  983. bStatus = RtlCreateUnicodeString (&usLocal, (LPCWSTR)lpValueName);
  984. } else {
  985. bStatus = RtlCreateUnicodeStringFromAsciiz (&usLocal, (LPCSTR)lpValueName);
  986. }
  987. if (bStatus) {
  988. ValueBufferLength =
  989. ResultLength =
  990. sizeof(KEY_VALUE_PARTIAL_INFORMATION) + *lpcbData;
  991. pValueInformation = ALLOCMEM(ResultLength);
  992. if (pValueInformation != NULL) {
  993. ntStatus = NtQueryValueKey(
  994. hKey,
  995. &usLocal,
  996. KeyValuePartialInformation,
  997. pValueInformation,
  998. ValueBufferLength,
  999. &ResultLength);
  1000. if ((NT_SUCCESS(ntStatus) || ntStatus == STATUS_BUFFER_OVERFLOW)) {
  1001. // return data
  1002. if (ARGUMENT_PRESENT(lpType)) {
  1003. *lpType = pValueInformation->Type;
  1004. }
  1005. if (ARGUMENT_PRESENT(lpcbData)) {
  1006. *lpcbData = pValueInformation->DataLength;
  1007. }
  1008. if (NT_SUCCESS(ntStatus)) {
  1009. if (ARGUMENT_PRESENT(lpData)) {
  1010. if (!bUnicode &&
  1011. (pValueInformation->Type == REG_SZ ||
  1012. pValueInformation->Type == REG_EXPAND_SZ ||
  1013. pValueInformation->Type == REG_MULTI_SZ)
  1014. ) {
  1015. // then convert the unicode return to an
  1016. // ANSI string before returning
  1017. // the local wide buffer used
  1018. UnicodeValueLength = ResultLength;
  1019. UnicodeValueBuffer = (LPWSTR)&pValueInformation->Data[0];
  1020. AnsiValueBuffer = (LPSTR)lpData;
  1021. AnsiValueLength = ARGUMENT_PRESENT( lpcbData )?
  1022. *lpcbData : 0;
  1023. Index = 0;
  1024. ntStatus = RtlUnicodeToMultiByteN(
  1025. AnsiValueBuffer,
  1026. AnsiValueLength,
  1027. &Index,
  1028. UnicodeValueBuffer,
  1029. UnicodeValueLength);
  1030. if (NT_SUCCESS( ntStatus ) &&
  1031. (ARGUMENT_PRESENT( lpcbData ))) {
  1032. *lpcbData = Index;
  1033. }
  1034. } else {
  1035. if (pValueInformation->DataLength <= *lpcbData) {
  1036. // copy the buffer to the user's buffer
  1037. memcpy (lpData, &pValueInformation->Data[0],
  1038. pValueInformation->DataLength);
  1039. ntStatus = STATUS_SUCCESS;
  1040. } else {
  1041. ntStatus = STATUS_BUFFER_OVERFLOW;
  1042. }
  1043. *lpcbData = pValueInformation->DataLength;
  1044. }
  1045. }
  1046. }
  1047. }
  1048. if (pValueInformation != NULL) {
  1049. // release temp buffer
  1050. FREEMEM (pValueInformation);
  1051. }
  1052. } else {
  1053. // unable to allocate memory for this operation so
  1054. ntStatus = STATUS_NO_MEMORY;
  1055. }
  1056. RtlFreeUnicodeString (&usLocal);
  1057. } else {
  1058. // this is a guess at the most likely cause for the string
  1059. // creation to fail.
  1060. ntStatus = STATUS_NO_MEMORY;
  1061. }
  1062. ReturnStatus = PerfpDosError(ntStatus);
  1063. return ReturnStatus;
  1064. }
  1065. LONG
  1066. GetPerfDllFileInfo (
  1067. LPCWSTR szFileName,
  1068. PDLL_VALIDATION_DATA pDllData
  1069. )
  1070. {
  1071. WCHAR szFullPath[MAX_PATH*2];
  1072. DWORD dwStatus = ERROR_FILE_NOT_FOUND;
  1073. DWORD dwRetValue;
  1074. HANDLE hFile;
  1075. BOOL bStatus;
  1076. LARGE_INTEGER liSize;
  1077. szFullPath[0] = UNICODE_NULL;
  1078. dwRetValue = SearchPathW (
  1079. NULL,
  1080. szFileName,
  1081. NULL,
  1082. sizeof(szFullPath) / sizeof(szFullPath[0]),
  1083. szFullPath,
  1084. NULL);
  1085. if (dwRetValue > 0) {
  1086. //then the file was found so open it.
  1087. hFile = CreateFileW (
  1088. szFullPath,
  1089. GENERIC_READ,
  1090. FILE_SHARE_READ,
  1091. NULL,
  1092. OPEN_EXISTING,
  1093. FILE_ATTRIBUTE_NORMAL,
  1094. NULL);
  1095. if (hFile != INVALID_HANDLE_VALUE) {
  1096. // get file creation date/time
  1097. bStatus = GetFileTime (
  1098. hFile,
  1099. &pDllData->CreationDate,
  1100. NULL, NULL);
  1101. if (bStatus) {
  1102. // get file size
  1103. liSize.LowPart = GetFileSize (
  1104. hFile, (PULONG)&liSize.HighPart);
  1105. if (liSize.LowPart != 0xFFFFFFFF) {
  1106. pDllData->FileSize = liSize.QuadPart;
  1107. dwStatus = ERROR_SUCCESS;
  1108. } else {
  1109. dwStatus = GetLastError();
  1110. }
  1111. } else {
  1112. dwStatus = GetLastError();
  1113. }
  1114. CloseHandle (hFile);
  1115. } else {
  1116. dwStatus = GetLastError();
  1117. }
  1118. } else {
  1119. dwStatus = GetLastError();
  1120. }
  1121. return dwStatus;
  1122. }
  1123. DWORD
  1124. DisablePerfLibrary (
  1125. PEXT_OBJECT pObj,
  1126. DWORD dwValue
  1127. )
  1128. {
  1129. // continue only if the "Disable" feature is enabled and
  1130. // if this library hasn't already been disabled.
  1131. if ((!(lPerflibConfigFlags & PLCF_NO_DISABLE_DLLS)) &&
  1132. (!(pObj->dwFlags & PERF_EO_DISABLED))) {
  1133. // set the disabled bit in the info
  1134. pObj->dwFlags |= PERF_EO_DISABLED;
  1135. return DisableLibrary(pObj->hPerfKey, pObj->szServiceName, dwValue);
  1136. }
  1137. return ERROR_SUCCESS;
  1138. }
  1139. DWORD
  1140. DisableLibrary(
  1141. IN HKEY hPerfKey,
  1142. IN LPWSTR szServiceName,
  1143. IN DWORD dwValue
  1144. )
  1145. {
  1146. //
  1147. // This routine will disable regardless of settings
  1148. //
  1149. DWORD dwLocalValue, dwSize;
  1150. DWORD dwFnStatus = ERROR_SUCCESS;
  1151. WORD wStringIndex = 0;
  1152. LPWSTR szMessageArray[2];
  1153. dwLocalValue = dwValue;
  1154. if (dwLocalValue != 1 && dwLocalValue != 2 && dwLocalValue != 4) dwLocalValue = 1;
  1155. // disable perf library entry in the service key
  1156. dwSize = sizeof(dwLocalValue);
  1157. dwFnStatus = RegSetValueExW (
  1158. hPerfKey,
  1159. DisablePerformanceCounters,
  1160. 0L,
  1161. REG_DWORD,
  1162. (LPBYTE) & dwLocalValue,
  1163. dwSize);
  1164. // report error
  1165. if (dwFnStatus == ERROR_SUCCESS) {
  1166. // system disabled
  1167. szMessageArray[wStringIndex++] =
  1168. szServiceName;
  1169. ReportEvent (hEventLog,
  1170. EVENTLOG_ERROR_TYPE, // error type
  1171. 0, // category (not used)
  1172. (DWORD)PERFLIB_LIBRARY_DISABLED, // event,
  1173. NULL, // SID (not used),
  1174. wStringIndex, // number of strings
  1175. 0, // sizeof raw data
  1176. szMessageArray, // message text array
  1177. NULL); // raw data
  1178. } else {
  1179. // local disable only
  1180. szMessageArray[wStringIndex++] =
  1181. szServiceName;
  1182. ReportEvent (hEventLog,
  1183. EVENTLOG_ERROR_TYPE, // error type
  1184. 0, // category (not used)
  1185. (DWORD)PERFLIB_LIBRARY_TEMP_DISABLED, // event,
  1186. NULL, // SID (not used),
  1187. wStringIndex, // number of strings
  1188. 0, // sizeof raw data
  1189. szMessageArray, // message text array
  1190. NULL); // raw data
  1191. }
  1192. return ERROR_SUCCESS;
  1193. }
  1194. /*
  1195. DWORD
  1196. PerfCheckRegistry(
  1197. IN HKEY hPerfKey,
  1198. IN LPCWSTR szServiceName
  1199. )
  1200. {
  1201. DWORD dwType = 0;
  1202. DWORD dwSize = sizeof(DWORD);
  1203. DWORD dwData = 0;
  1204. DWORD status;
  1205. WORD wStringIndex;
  1206. LPWSTR szMessageArray[2];
  1207. status = PrivateRegQueryValueExA(
  1208. hPerfKey,
  1209. FirstCounter,
  1210. NULL,
  1211. &dwType,
  1212. (LPBYTE)&dwData,
  1213. &dwSize);
  1214. if ((status != ERROR_SUCCESS) || (dwType != REG_DWORD) ||
  1215. (dwData < LAST_BASE_INDEX)) {
  1216. wStringIndex = 0;
  1217. szMessageArray[wStringIndex++] = (LPWSTR) FirstCounter;
  1218. szMessageArray[wStringIndex++] = (LPWSTR) szServiceName;
  1219. ReportEvent(hEventLog,
  1220. EVENTLOG_ERROR_TYPE,
  1221. 0,
  1222. (DWORD)PERFLIB_REGVALUE_NOT_FOUND,
  1223. NULL,
  1224. wStringIndex,
  1225. 0,
  1226. szMessageArray,
  1227. NULL);
  1228. return FALSE;
  1229. }
  1230. status = PrivateRegQueryValueExA(
  1231. hPerfKey,
  1232. LastCounter,
  1233. NULL,
  1234. &dwType,
  1235. (LPBYTE)&dwData,
  1236. &dwSize);
  1237. if ((status != ERROR_SUCCESS) || (dwType != REG_DWORD) ||
  1238. (dwData <= LAST_BASE_INDEX)) {
  1239. wStringIndex = 0;
  1240. szMessageArray[wStringIndex++] = (LPWSTR) LastCounter;
  1241. szMessageArray[wStringIndex++] = (LPWSTR) szServiceName;
  1242. ReportEvent(hEventLog,
  1243. EVENTLOG_ERROR_TYPE,
  1244. 0,
  1245. (DWORD)PERFLIB_REGVALUE_NOT_FOUND,
  1246. NULL,
  1247. wStringIndex,
  1248. 0,
  1249. szMessageArray,
  1250. NULL);
  1251. return FALSE;
  1252. }
  1253. status = PrivateRegQueryValueExA(
  1254. hPerfKey,
  1255. FirstHelp,
  1256. NULL,
  1257. &dwType,
  1258. (LPBYTE)&dwData,
  1259. &dwSize);
  1260. if ((status != ERROR_SUCCESS) || (dwType != REG_DWORD) ||
  1261. (dwData < LAST_BASE_INDEX)) {
  1262. wStringIndex = 0;
  1263. szMessageArray[wStringIndex++] = (LPWSTR) FirstHelp;
  1264. szMessageArray[wStringIndex++] = (LPWSTR) szServiceName;
  1265. ReportEvent(hEventLog,
  1266. EVENTLOG_ERROR_TYPE,
  1267. 0,
  1268. (DWORD)PERFLIB_REGVALUE_NOT_FOUND,
  1269. NULL,
  1270. wStringIndex,
  1271. 0,
  1272. szMessageArray,
  1273. NULL);
  1274. return FALSE;
  1275. }
  1276. status = PrivateRegQueryValueExA(
  1277. hPerfKey,
  1278. LastHelp,
  1279. NULL,
  1280. &dwType,
  1281. (LPBYTE)&dwData,
  1282. &dwSize);
  1283. if ((status != ERROR_SUCCESS) || (dwType != REG_DWORD) ||
  1284. (dwData <= LAST_BASE_INDEX)) {
  1285. wStringIndex = 0;
  1286. szMessageArray[wStringIndex++] = (LPWSTR) LastHelp;
  1287. szMessageArray[wStringIndex++] = (LPWSTR) szServiceName;
  1288. ReportEvent(hEventLog,
  1289. EVENTLOG_ERROR_TYPE,
  1290. 0,
  1291. (DWORD)PERFLIB_REGVALUE_NOT_FOUND,
  1292. NULL,
  1293. wStringIndex,
  1294. 0,
  1295. szMessageArray,
  1296. NULL);
  1297. return FALSE;
  1298. }
  1299. return TRUE;
  1300. }
  1301. */
  1302. DWORD
  1303. PerfpDosError(
  1304. IN NTSTATUS Status
  1305. )
  1306. // Need to convert NtStatus that we generate to DosError
  1307. {
  1308. if (Status == STATUS_SUCCESS)
  1309. return ERROR_SUCCESS;
  1310. if (Status == STATUS_BUFFER_OVERFLOW)
  1311. return ERROR_MORE_DATA;
  1312. if (Status == STATUS_TIMEOUT)
  1313. return WAIT_TIMEOUT;
  1314. if (Status == STATUS_WAIT_63)
  1315. return (DWORD) ERROR_SUCCESS;
  1316. if ((Status >= STATUS_ABANDONED) && (Status <= STATUS_ABANDONED_WAIT_63))
  1317. return (DWORD) ERROR_CANCELLED;
  1318. return RtlNtStatusToDosError(Status);
  1319. }
  1320. PERROR_LOG
  1321. PerfpFindError(
  1322. IN ULONG ErrorNumber,
  1323. IN PERROR_LOG ErrorLog
  1324. )
  1325. {
  1326. PLIST_ENTRY entry, head;
  1327. PERROR_LOG pError;
  1328. DebugPrint((3, "PERFLIB:FindError Entering critsec for %d\n", ErrorNumber));
  1329. RtlEnterCriticalSection(&PerfpCritSect);
  1330. head = (PLIST_ENTRY) ErrorLog;
  1331. entry = head->Flink;
  1332. while (entry != head) {
  1333. pError = (PERROR_LOG) entry;
  1334. DebugPrint((4, "PERFLIB:FindError Comparing entry %X/%X %d\n",
  1335. entry, ErrorLog, pError->ErrorNumber));
  1336. if (pError->ErrorNumber == ErrorNumber)
  1337. break;
  1338. entry = entry->Flink;
  1339. }
  1340. if (entry == head) {
  1341. pError = ALLOCMEM(sizeof(ERROR_LOG));
  1342. if (pError == NULL) {
  1343. DebugPrint((3, "PERFLIB:FindError Leaving critsec1\n"));
  1344. RtlLeaveCriticalSection(&PerfpCritSect);
  1345. return NULL;
  1346. }
  1347. pError->ErrorNumber = ErrorNumber;
  1348. pError->ErrorCount = 0;
  1349. pError->LastTime = 0;
  1350. entry = (PLIST_ENTRY) pError;
  1351. DebugPrint((3, "PERFLIB:FindError Added entry %X to %X\n", entry, ErrorLog));
  1352. InsertHeadList(head, entry);
  1353. }
  1354. else {
  1355. RemoveEntryList(entry);
  1356. InsertHeadList(head, entry);
  1357. pError = (PERROR_LOG) entry;
  1358. DebugPrint((3, "PERFLIB:FindError Found entry %X in %X\n", entry, ErrorLog));
  1359. }
  1360. DebugPrint((3, "PERFLIB:FindError Leaving critsec\n"));
  1361. RtlLeaveCriticalSection(&PerfpCritSect);
  1362. return (PERROR_LOG) entry;
  1363. }
  1364. ULONG
  1365. PerfpCheckErrorTime(
  1366. IN PERROR_LOG ErrorLog,
  1367. IN LONG64 TimeLimit,
  1368. IN HKEY hKey
  1369. )
  1370. {
  1371. WCHAR wstr[32];
  1372. LONG64 timeStamp;
  1373. DWORD status, dwType, dwSize;
  1374. ULONG bUpdate = FALSE;
  1375. UNICODE_STRING uString;
  1376. GetSystemTimeAsFileTime((PFILETIME) &timeStamp);
  1377. DebugPrint((3, "PERFLIB: CheckErrorTime Error %d time %I64d last %I64d limit %I64d\n",
  1378. ErrorLog->ErrorNumber, timeStamp, ErrorLog->LastTime, TimeLimit));
  1379. if ((timeStamp - ErrorLog->LastTime) < TimeLimit)
  1380. return FALSE;
  1381. wstr[0] = UNICODE_NULL;
  1382. if (hKey != NULL) {
  1383. uString.Buffer = &wstr[0];
  1384. uString.Length = 0;
  1385. uString.MaximumLength = sizeof(wstr);
  1386. RtlIntegerToUnicodeString(ErrorLog->ErrorNumber, 10, &uString);
  1387. DebugPrint((3, "Err %d string %ws\n", ErrorLog->ErrorNumber, uString.Buffer));
  1388. if (ErrorLog->LastTime == 0) {
  1389. dwType = REG_DWORD;
  1390. dwSize = sizeof(timeStamp);
  1391. status = PrivateRegQueryValueExW(
  1392. hKey,
  1393. wstr, // Need to be string
  1394. NULL,
  1395. &dwType,
  1396. (LPBYTE) &ErrorLog->LastTime,
  1397. &dwSize
  1398. );
  1399. if ((status == ERROR_SUCCESS) &&
  1400. (dwType == REG_QWORD)) {
  1401. if ((timeStamp - ErrorLog->LastTime) >= TimeLimit) {
  1402. ErrorLog->LastTime = 0;
  1403. }
  1404. }
  1405. }
  1406. if (ErrorLog->LastTime == 0) {
  1407. bUpdate = TRUE;
  1408. ErrorLog->LastTime = timeStamp;
  1409. dwSize = sizeof(timeStamp);
  1410. status = RegSetValueExW(
  1411. hKey,
  1412. wstr,
  1413. 0L,
  1414. REG_QWORD,
  1415. (LPBYTE) &timeStamp,
  1416. dwSize);
  1417. }
  1418. }
  1419. if ((timeStamp - ErrorLog->LastTime) >= TimeLimit) {
  1420. // Get here if we no write access to regkey
  1421. ErrorLog->LastTime = timeStamp;
  1422. bUpdate = TRUE;
  1423. }
  1424. return bUpdate;
  1425. }
  1426. VOID
  1427. PerfpDeleteErrorLogs(
  1428. IN PERROR_LOG ErrorLog
  1429. )
  1430. {
  1431. PLIST_ENTRY entry, head, pError;
  1432. head = (PLIST_ENTRY) ErrorLog;
  1433. entry = head->Flink;
  1434. DebugPrint((3, "PERFLIB:DeleteErrorLogs Entering Critsec %x\n", ErrorLog));
  1435. RtlEnterCriticalSection(&PerfpCritSect);
  1436. while (entry != head) {
  1437. pError = entry;
  1438. entry = entry->Flink;
  1439. DebugPrint((3, "PERFLIB: Deleting error log entry %d/%x from %x\n",
  1440. ((PERROR_LOG) pError)->ErrorNumber, pError, ErrorLog));
  1441. FREEMEM(pError); // because we put ListEntry first
  1442. }
  1443. InitializeListHead(head);
  1444. RtlLeaveCriticalSection(&PerfpCritSect);
  1445. DebugPrint((3, "PERFLIB:DeleteErrorLogs Leaving Critsec\n"));
  1446. }
  1447. ULONG
  1448. PerfpThrottleError(
  1449. IN DWORD ErrorNumber,
  1450. IN HKEY hKey,
  1451. IN PERROR_LOG ErrorLog
  1452. )
  1453. {
  1454. PERROR_LOG pError;
  1455. ULONG bReportError, status;
  1456. LONG64 TimeLimit = 3600 * 24;
  1457. LONG64 SavedTime = 0;
  1458. if (lEventLogLevel > LOG_USER) { // only throttle for <= LOG_USER
  1459. return TRUE;
  1460. }
  1461. if (lEventLogLevel < LOG_USER) { // don't report error
  1462. return FALSE;
  1463. }
  1464. ErrorNumber = ErrorNumber & 0x00FFFFFF;
  1465. pError = PerfpFindError(ErrorNumber, ErrorLog);
  1466. if (pError == NULL)
  1467. return FALSE; // don't report error if no more resources
  1468. TimeLimit *= 10000000;
  1469. SavedTime = pError->LastTime;
  1470. bReportError = PerfpCheckErrorTime(pError, TimeLimit, hKey);
  1471. DebugPrint((3, "PERFLIB:PerfpThrottleError ReportError %d hKey %X\n",
  1472. bReportError, hKey));
  1473. if ((hKey == NULL) && (bReportError)) {
  1474. status = RegOpenKeyExW(
  1475. HKEY_LOCAL_MACHINE,
  1476. HKLMPerflibKey,
  1477. 0L,
  1478. KEY_READ | KEY_WRITE,
  1479. &hKey);
  1480. if (status != ERROR_SUCCESS) { // try read access anyway
  1481. status = RegOpenKeyExW(
  1482. HKEY_LOCAL_MACHINE,
  1483. HKLMPerflibKey,
  1484. 0L,
  1485. KEY_READ,
  1486. &hKey);
  1487. }
  1488. DebugPrint((3, "PERFLIB:PerfpThrottleError Perflib open status %d\n", status));
  1489. if (status == ERROR_SUCCESS) {
  1490. //
  1491. // if this is the first time we see this error, reset the time
  1492. // and see if a previous time was saved in registry
  1493. //
  1494. pError->LastTime = SavedTime;
  1495. bReportError = PerfpCheckErrorTime(pError, TimeLimit, hKey);
  1496. DebugPrint((3, "PERFLIB:PerfpThrottleError ReportError2 %d\n", bReportError));
  1497. RegCloseKey(hKey);
  1498. }
  1499. }
  1500. return bReportError;
  1501. }
  1502. #ifdef DBG
  1503. VOID
  1504. PerfpDebug(
  1505. ULONG DebugPrintLevel,
  1506. PCCHAR DebugMessage,
  1507. ...
  1508. )
  1509. /*++
  1510. Routine Description:
  1511. Debug print for all Perflib
  1512. Arguments:
  1513. Debug print level between 0 and 3, with 3 being the most verbose.
  1514. Return Value:
  1515. None
  1516. --*/
  1517. {
  1518. va_list ap;
  1519. if ((DebugPrintLevel <= (PerfLibDebug & 0x0000ffff)) ||
  1520. ((1 << (DebugPrintLevel + 15)) & PerfLibDebug)) {
  1521. DbgPrint("%d:Perflib:", GetCurrentThreadId());
  1522. }
  1523. else
  1524. return;
  1525. va_start(ap, DebugMessage);
  1526. if ((DebugPrintLevel <= (PerfLibDebug & 0x0000ffff)) ||
  1527. ((1 << (DebugPrintLevel + 15)) & PerfLibDebug)) {
  1528. _vsnprintf(
  1529. (LPSTR)PerfLibDebugBuffer, DEBUG_BUFFER_LENGTH, DebugMessage, ap);
  1530. DbgPrint((LPSTR)PerfLibDebugBuffer);
  1531. }
  1532. va_end(ap);
  1533. }
  1534. #endif // DBG