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.

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