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.

781 lines
18 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. perfsup.c
  5. Abstract:
  6. This module contains support routines for performance traces.
  7. Author:
  8. Stephen Hsiao (shsiao) 01-Jan-2000
  9. Revision History:
  10. --*/
  11. #include "perfp.h"
  12. #ifdef ALLOC_PRAGMA
  13. #pragma alloc_text(PAGE, PerfInfoProfileInit)
  14. #pragma alloc_text(PAGE, PerfInfoProfileUninit)
  15. #pragma alloc_text(PAGE, PerfInfoStartLog)
  16. #pragma alloc_text(PAGE, PerfInfoStopLog)
  17. #endif //ALLOC_PRAGMA
  18. extern NTSTATUS IoPerfInit();
  19. extern NTSTATUS IoPerfReset();
  20. #ifdef NTPERF
  21. NTSTATUS
  22. PerfInfopStartLog(
  23. PERFINFO_GROUPMASK *pGroupMask,
  24. PERFINFO_START_LOG_LOCATION StartLogLocation
  25. );
  26. NTSTATUS
  27. PerfInfopStopLog(
  28. VOID
  29. );
  30. VOID
  31. PerfInfoSetProcessorSpeed(
  32. VOID
  33. );
  34. #endif // NTPERF
  35. VOID
  36. PerfInfoProfileInit(
  37. )
  38. /*++
  39. Routine description:
  40. Starts the sampled profile and initializes the cache
  41. Arguments:
  42. None
  43. Return Value:
  44. None
  45. --*/
  46. {
  47. #if !defined(NT_UP)
  48. PerfInfoSampledProfileCaching = FALSE;
  49. #else
  50. PerfInfoSampledProfileCaching = TRUE;
  51. #endif // !defined(NT_UP)
  52. PerfInfoSampledProfileFlushInProgress = 0;
  53. PerfProfileCache.Entries = 0;
  54. PerfInfoProfileSourceActive = PerfInfoProfileSourceRequested;
  55. KeSetIntervalProfile(PerfInfoProfileInterval, PerfInfoProfileSourceActive);
  56. KeInitializeProfile(&PerfInfoProfileObject,
  57. NULL,
  58. NULL,
  59. 0,
  60. 0,
  61. 0,
  62. PerfInfoProfileSourceActive,
  63. 0
  64. );
  65. KeStartProfile(&PerfInfoProfileObject, NULL);
  66. }
  67. VOID
  68. PerfInfoProfileUninit(
  69. )
  70. /*++
  71. Routine description:
  72. Stops the sampled profile
  73. Arguments:
  74. None
  75. Return Value:
  76. None
  77. --*/
  78. {
  79. PerfInfoProfileSourceActive = ProfileMaximum; // Invalid value stops us from
  80. // collecting more samples
  81. KeStopProfile(&PerfInfoProfileObject);
  82. PerfInfoFlushProfileCache();
  83. }
  84. NTSTATUS
  85. PerfInfoStartLog (
  86. PERFINFO_GROUPMASK *PGroupMask,
  87. PERFINFO_START_LOG_LOCATION StartLogLocation
  88. )
  89. /*++
  90. Routine Description:
  91. This routine is called by WMI as part of kernel logger initiaziation.
  92. Arguments:
  93. GroupMask - Masks for what to log. This pointer point to an allocated
  94. area in WMI LoggerContext.
  95. StartLogLocation - Indication of whether we're starting logging
  96. at boot time or while the system is running. If
  97. we're starting at boot time, we don't need to snapshot
  98. the open files because there aren't any and we would
  99. crash if we tried to find the list.
  100. Return Value:
  101. BUGBUG Need proper return/ error handling
  102. --*/
  103. {
  104. NTSTATUS Status = STATUS_SUCCESS;
  105. PERFINFO_CLEAR_GROUPMASK(&PerfGlobalGroupMask);
  106. PPerfGlobalGroupMask = &PerfGlobalGroupMask;
  107. if (PerfIsGroupOnInGroupMask(PERF_MEMORY, PGroupMask) ||
  108. PerfIsGroupOnInGroupMask(PERF_FILENAME, PGroupMask) ||
  109. PerfIsGroupOnInGroupMask(PERF_DRIVERS, PGroupMask)) {
  110. PERFINFO_OR_GROUP_WITH_GROUPMASK(PERF_FILENAME_ALL, PGroupMask);
  111. }
  112. if (StartLogLocation == PERFINFO_START_LOG_FROM_GLOBAL_LOGGER) {
  113. //
  114. // From the wmi global logger, need to do Rundown in kernel mode
  115. //
  116. if (PerfIsGroupOnInGroupMask(PERF_PROC_THREAD, PGroupMask)) {
  117. Status = PerfInfoProcessRunDown();
  118. }
  119. if (PerfIsGroupOnInGroupMask(PERF_PROC_THREAD, PGroupMask)) {
  120. Status = PerfInfoSysModuleRunDown();
  121. }
  122. }
  123. if ((StartLogLocation != PERFINFO_START_LOG_AT_BOOT) &&
  124. PerfIsGroupOnInGroupMask(PERF_FILENAME_ALL, PGroupMask)) {
  125. Status = PerfInfoFileNameRunDown();
  126. }
  127. if (PerfIsGroupOnInGroupMask(PERF_DRIVERS, PGroupMask)) {
  128. IoPerfInit();
  129. }
  130. if (PerfIsGroupOnInGroupMask(PERF_PROFILE, PGroupMask)
  131. && ((KeGetPreviousMode() == KernelMode) ||
  132. SeSinglePrivilegeCheck(SeSystemProfilePrivilege, UserMode))
  133. ) {
  134. //
  135. // Sampled profile
  136. //
  137. PerfInfoProfileInit();
  138. }
  139. //
  140. // Enable context swap tracing
  141. //
  142. if ( PerfIsGroupOnInGroupMask(PERF_CONTEXT_SWITCH, PGroupMask) ) {
  143. WmiStartContextSwapTrace();
  144. }
  145. #ifdef NTPERF
  146. Status = PerfInfopStartLog(PGroupMask, StartLogLocation);
  147. #else
  148. //
  149. // See if we need to empty the working set to start
  150. //
  151. if (PerfIsGroupOnInGroupMask(PERF_FOOTPRINT, PGroupMask) ||
  152. PerfIsGroupOnInGroupMask(PERF_BIGFOOT, PGroupMask) ||
  153. PerfIsGroupOnInGroupMask(PERF_FOOTPRINT_PROC, PGroupMask)) {
  154. MmEmptyAllWorkingSets ();
  155. }
  156. #endif // NTPERF
  157. if (!NT_SUCCESS(Status)) {
  158. PPerfGlobalGroupMask = NULL;
  159. PERFINFO_CLEAR_GROUPMASK(&PerfGlobalGroupMask);
  160. } else {
  161. #ifdef NTPERF
  162. if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  163. //
  164. // Make a copy of the GroupMask in PerfMem header
  165. // so user mode logging can work
  166. //
  167. PerfBufHdr()->GlobalGroupMask = *PGroupMask;
  168. }
  169. #endif // NTPERF
  170. *PPerfGlobalGroupMask = *PGroupMask;
  171. }
  172. return Status;
  173. }
  174. NTSTATUS
  175. PerfInfoStopLog (
  176. )
  177. /*++
  178. Routine Description:
  179. This routine turn off the PerfInfo trace hooks.
  180. Arguments:
  181. None.
  182. Return Value:
  183. BUGBUG Need proper return/ error handling
  184. --*/
  185. {
  186. NTSTATUS Status = STATUS_SUCCESS;
  187. BOOLEAN DisableContextSwaps=FALSE;
  188. if (PPerfGlobalGroupMask == NULL) {
  189. return Status;
  190. }
  191. if (PERFINFO_IS_GROUP_ON(PERF_MEMORY)) {
  192. MmIdentifyPhysicalMemory();
  193. }
  194. if (PERFINFO_IS_GROUP_ON(PERF_PROFILE)) {
  195. PerfInfoProfileUninit();
  196. }
  197. if (PERFINFO_IS_GROUP_ON(PERF_DRIVERS)) {
  198. IoPerfReset();
  199. }
  200. #ifdef NTPERF
  201. if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  202. //
  203. // Now clear the GroupMask in Perfmem to stop logging.
  204. //
  205. PERFINFO_CLEAR_GROUPMASK(&PerfBufHdr()->GlobalGroupMask);
  206. }
  207. Status = PerfInfopStopLog();
  208. #endif // NTPERF
  209. if ( PERFINFO_IS_GROUP_ON(PERF_CONTEXT_SWITCH) ) {
  210. DisableContextSwaps = TRUE;
  211. }
  212. //
  213. // Reset the PPerfGlobalGroupMask.
  214. //
  215. PERFINFO_CLEAR_GROUPMASK(PPerfGlobalGroupMask);
  216. PPerfGlobalGroupMask = NULL;
  217. //
  218. // Disable context swap tracing.
  219. // IMPORTANT: This must be done AFTER the global flag is set to NULL!!!
  220. //
  221. if( DisableContextSwaps ) {
  222. WmiStopContextSwapTrace();
  223. }
  224. return (Status);
  225. }
  226. #ifdef NTPERF
  227. NTSTATUS
  228. PerfInfoStartPerfMemLog (
  229. )
  230. /*++
  231. Routine Description:
  232. Indicate a logger wants to log into Perfmem. If it is the first logger,
  233. initialize the shared memory buffer. Otherwise, just increment LoggerCounts.
  234. Arguments:
  235. None
  236. Return Value:
  237. STATUS_BUFFER_TOO_SMALL - if buffer not big enough
  238. STATUS_SUCCESS - otherwize
  239. --*/
  240. {
  241. PPERF_BYTE pbCurrentStart;
  242. ULONG cbBufferSize;
  243. LARGE_INTEGER PerformanceFrequency;
  244. const PPERFINFO_TRACEBUF_HEADER Buffer = PerfBufHdr();
  245. ULONG LoggerCounts;
  246. ULONG Idx;
  247. //
  248. // Is it big enough to use?
  249. //
  250. if (PerfQueryBufferSizeBytes() <= 2 * PERFINFO_HEADER_ZONE_SIZE) {
  251. PERFINFO_SET_LOGGING_TO_PERFMEM(FALSE);
  252. return STATUS_BUFFER_TOO_SMALL;
  253. }
  254. //
  255. // It is OK to use the buffer, increment the reference count
  256. //
  257. LoggerCounts = InterlockedIncrement(&Buffer->LoggerCounts);
  258. if (LoggerCounts != 1) {
  259. //
  260. // Other logger has turned on logging, just return.
  261. //
  262. return STATUS_SUCCESS;
  263. }
  264. //
  265. // Code to acquire the buffer would go here.
  266. //
  267. Buffer->SelfPointer = Buffer;
  268. Buffer->MmSystemRangeStart = MmSystemRangeStart;
  269. //
  270. // initialize buffer version information
  271. //
  272. Buffer->usMajorVersion = PERFINFO_MAJOR_VERSION;
  273. Buffer->usMinorVersion = PERFINFO_MINOR_VERSION;
  274. //
  275. // initialize timer stuff
  276. //
  277. Buffer->BufferFlag = FLAG_CYCLE_COUNT;
  278. KeQuerySystemTime(&Buffer->PerfInitSystemTime);
  279. Buffer->PerfInitTime = PerfGetCycleCount();
  280. Buffer->LastClockRef.SystemTime = Buffer->PerfInitSystemTime;
  281. Buffer->LastClockRef.TickCount = Buffer->PerfInitTime;
  282. Buffer->CalcPerfFrequency = PerfInfoTickFrequency;
  283. Buffer->EventPerfFrequency = PerfInfoTickFrequency;
  284. Buffer->PerfBufHeaderZoneSize = PERFINFO_HEADER_ZONE_SIZE;
  285. KeQueryPerformanceCounter(&PerformanceFrequency);
  286. Buffer->KePerfFrequency = PerformanceFrequency.QuadPart;
  287. //
  288. // Determine the size of the thread hash table
  289. //
  290. Buffer->ThreadHash = (PERFINFO_THREAD_HASH_ENTRY *)
  291. (((PCHAR) Buffer) + sizeof(PERFINFO_TRACEBUF_HEADER));
  292. Buffer->ThreadHashOverflow = FALSE;
  293. RtlZeroMemory(Buffer->ThreadHash,
  294. PERFINFO_THREAD_HASH_SIZE *
  295. sizeof(PERFINFO_THREAD_HASH_ENTRY));
  296. for (Idx = 0; Idx < PERFINFO_THREAD_HASH_SIZE; Idx++)
  297. Buffer->ThreadHash[Idx].CurThread = PERFINFO_INVALID_ID;
  298. pbCurrentStart = (PPERF_BYTE) Buffer + Buffer->PerfBufHeaderZoneSize;
  299. cbBufferSize = PerfQueryBufferSizeBytes() - Buffer->PerfBufHeaderZoneSize;
  300. Buffer->Start.Ptr = Buffer->Current.Ptr = pbCurrentStart;
  301. Buffer->Max.Ptr = pbCurrentStart + cbBufferSize;
  302. //
  303. // initialize version mismatch tracking
  304. //
  305. Buffer->fVersionMismatch = FALSE;
  306. //
  307. // initialize buffer overflow counter
  308. //
  309. Buffer->BufferBytesLost = 0;
  310. //
  311. // initialize the pointer to the COWHeader
  312. //
  313. Buffer->pCOWHeader = NULL;
  314. RtlZeroMemory(Buffer->Start.Ptr, Buffer->Max.Ptr - Buffer->Start.Ptr);
  315. PERFINFO_SET_LOGGING_TO_PERFMEM(TRUE);
  316. return STATUS_SUCCESS;
  317. }
  318. NTSTATUS
  319. PerfInfoStopPerfMemLog (
  320. )
  321. /*++
  322. Routine Description:
  323. Indicate a logger finishes logging. If the loggerCounts goes to zero, the
  324. buffer will be reset the next time it is turned on.
  325. Arguments:
  326. None
  327. Return Value:
  328. STATUS_BUFFER_TOO_SMALL - if buffer not big enough
  329. STATUS_SUCCESS - otherwize
  330. --*/
  331. {
  332. ULONG LoggerCounts;
  333. const PPERFINFO_TRACEBUF_HEADER Buffer = PerfBufHdr();
  334. LoggerCounts = InterlockedDecrement(&Buffer->LoggerCounts);
  335. if (LoggerCounts == 0) {
  336. //
  337. // Other logger has turned on logging, just return.
  338. //
  339. PERFINFO_SET_LOGGING_TO_PERFMEM(FALSE);
  340. }
  341. return STATUS_SUCCESS;
  342. }
  343. NTSTATUS
  344. PerfInfopStartLog(
  345. PERFINFO_GROUPMASK *pGroupMask,
  346. PERFINFO_START_LOG_LOCATION StartLogLocation
  347. )
  348. /*++
  349. Routine Description:
  350. This routine initialize the mminfo log and turn on the monitor.
  351. Arguments:
  352. GroupMask: Masks for what to log.
  353. Return Value:
  354. BUGBUG Need proper return/ error handling
  355. --*/
  356. {
  357. NTSTATUS Status = STATUS_SUCCESS;
  358. #ifdef NTPERF_PRIVATE
  359. Status = PerfInfopStartPrivateLog(pGroupMask, StartLogLocation);
  360. if (!NT_SUCCESS(Status)) {
  361. PERFINFO_CLEAR_GROUPMASK(PPerfGlobalGroupMask);
  362. return Status;
  363. }
  364. #else
  365. UNREFERENCED_PARAMETER(pGroupMask);
  366. UNREFERENCED_PARAMETER(StartLogLocation);
  367. #endif // NTPERF_PRIVATE
  368. return Status;
  369. }
  370. NTSTATUS
  371. PerfInfopStopLog (
  372. VOID
  373. )
  374. /*++
  375. Routine Description:
  376. This routine turn off the mminfo monitor and (if needed) dump the
  377. data for user.
  378. NOTE: The shutdown and hibernate paths have similar code. Check
  379. those if you make changes.
  380. Arguments:
  381. None
  382. Return Value:
  383. STATUS_SUCCESS
  384. --*/
  385. {
  386. if (PERFINFO_IS_ANY_GROUP_ON()) {
  387. #ifdef NTPERF_PRIVATE
  388. PerfInfopStopPrivateLog();
  389. #endif // NTPERF_PRIVATE
  390. if (PERFINFO_IS_LOGGING_TO_PERFMEM()) {
  391. PerfBufHdr()->LogStopTime = PerfGetCycleCount();
  392. }
  393. }
  394. return STATUS_SUCCESS;
  395. }
  396. NTSTATUS
  397. PerfInfoSetPerformanceTraceInformation (
  398. IN PVOID SystemInformation,
  399. IN ULONG SystemInformationLength
  400. )
  401. /*++
  402. Routine Description:
  403. This routine implements the performance system information functions.
  404. Arguments:
  405. SystemInformation - A pointer to a buffer which receives the specified
  406. information. This is of type PPERFINFO_PERFORMANCE_INFORMATION.
  407. SystemInformationLength - Specifies the length in bytes of the system
  408. information buffer.
  409. Return Value:
  410. STATUS_SUCCESS if successful
  411. STATUS_INFO_LENGTH_MISMATCH if size of buffer is incorrect
  412. --*/
  413. {
  414. NTSTATUS Status = STATUS_SUCCESS;
  415. PPERFINFO_PERFORMANCE_INFORMATION PerfInfo;
  416. PVOID PerfBuffer;
  417. if (SystemInformationLength < sizeof(PERFINFO_PERFORMANCE_INFORMATION)) {
  418. return STATUS_INFO_LENGTH_MISMATCH;
  419. }
  420. PerfInfo = (PPERFINFO_PERFORMANCE_INFORMATION) SystemInformation;
  421. PerfBuffer = PerfInfo + 1;
  422. switch (PerfInfo->PerformanceType) {
  423. case PerformancePerfInfoStart:
  424. // Status = PerfInfoStartLog(&PerfInfo->StartInfo.Flags, PERFINFO_START_LOG_POST_BOOT);
  425. Status = STATUS_INVALID_INFO_CLASS;
  426. break;
  427. case PerformancePerfInfoStop:
  428. // Status = PerfInfoStopLog();
  429. Status = STATUS_INVALID_INFO_CLASS;
  430. break;
  431. #ifdef NTPERF_PRIVATE
  432. case PerformanceMmInfoMarkWithFlush:
  433. case PerformanceMmInfoMark:
  434. case PerformanceMmInfoAsyncMark:
  435. {
  436. USHORT LogType;
  437. ULONG StringLength;
  438. if (PerfInfo->PerformanceType == PerformanceMmInfoMarkWithFlush) {
  439. if (PERFINFO_IS_GROUP_ON(PERF_FOOTPRINT) ||
  440. PERFINFO_IS_GROUP_ON(PERF_FOOTPRINT_PROC)) {
  441. //
  442. // BUGBUG We should get a non-Mi* call for this...
  443. //
  444. MmEmptyAllWorkingSets();
  445. Status = MmPerfSnapShotValidPhysicalMemory();
  446. }
  447. else if (PERFINFO_IS_GROUP_ON(PERF_CLEARWS)) {
  448. MmEmptyAllWorkingSets();
  449. }
  450. } else if (PerfinfoBigFootSize) {
  451. MmEmptyAllWorkingSets();
  452. }
  453. if (PERFINFO_IS_ANY_GROUP_ON()) {
  454. PERFINFO_MARK_INFORMATION Event;
  455. StringLength = SystemInformationLength - sizeof(PERFINFO_PERFORMANCE_INFORMATION);
  456. LogType = (PerfInfo->PerformanceType == PerformanceMmInfoAsyncMark) ?
  457. PERFINFO_LOG_TYPE_ASYNCMARK :
  458. PERFINFO_LOG_TYPE_MARK;
  459. PerfInfoLogBytesAndANSIString(LogType,
  460. &Event,
  461. FIELD_OFFSET(PERFINFO_MARK_INFORMATION, Name),
  462. (PCSTR) PerfBuffer,
  463. StringLength
  464. );
  465. }
  466. if (PERFINFO_IS_GROUP_ON(PERF_FOOTPRINT_PROC)) {
  467. PerfInfoDumpWSInfo (TRUE);
  468. }
  469. break;
  470. }
  471. case PerformanceMmInfoFlush:
  472. MmEmptyAllWorkingSets();
  473. break;
  474. #endif // NTPERF_PRIVATE
  475. default:
  476. #ifdef NTPERF_PRIVATE
  477. Status = PerfInfoSetPerformanceTraceInformationPrivate(PerfInfo, SystemInformationLength);
  478. #else
  479. Status = STATUS_INVALID_INFO_CLASS;
  480. #endif // NTPERF_PRIVATE
  481. break;
  482. }
  483. return Status;
  484. }
  485. NTSTATUS
  486. PerfInfoQueryPerformanceTraceInformation (
  487. IN PVOID SystemInformation,
  488. IN ULONG SystemInformationLength,
  489. OUT PULONG ReturnLength
  490. )
  491. /*++
  492. Routine Description:
  493. Satisfy queries for performance trace state information.
  494. Arguments:
  495. SystemInformation - A pointer to a buffer which receives the specified
  496. information. This is of type PPERFINFO_PERFORMANCE_INFORMATION.
  497. SystemInformationLength - Specifies the length in bytes of the system
  498. information buffer.
  499. ReturnLength - Receives the number of bytes placed in the system information buffer.
  500. Return Value:
  501. STATUS_SUCCESS
  502. --*/
  503. {
  504. NTSTATUS Status = STATUS_SUCCESS;
  505. if (SystemInformationLength != sizeof(PERFINFO_PERFORMANCE_INFORMATION)) {
  506. return STATUS_INFO_LENGTH_MISMATCH;
  507. }
  508. #ifdef NTPERF_PRIVATE
  509. return PerfInfoQueryPerformanceTraceInformationPrivate(
  510. (PPERFINFO_PERFORMANCE_INFORMATION) SystemInformation,
  511. ReturnLength
  512. );
  513. #else
  514. UNREFERENCED_PARAMETER(ReturnLength);
  515. UNREFERENCED_PARAMETER(SystemInformation);
  516. return STATUS_INVALID_INFO_CLASS;
  517. #endif // NTPERF_PRIVATE
  518. }
  519. VOID
  520. PerfInfoSetProcessorSpeed(
  521. VOID
  522. )
  523. /*++
  524. Routine Description:
  525. Calculate and set the processor speed in MHz.
  526. Note: KPRCB->MHz, once it's set reliably, should be used instead
  527. Arguments:
  528. None
  529. Return Value:
  530. None
  531. --*/
  532. {
  533. ULONGLONG start;
  534. ULONGLONG end;
  535. ULONGLONG freq;
  536. ULONGLONG TSCStart;
  537. LARGE_INTEGER *Pstart = (LARGE_INTEGER *) &start;
  538. LARGE_INTEGER *Pend = (LARGE_INTEGER *) &end;
  539. LARGE_INTEGER Delay;
  540. ULONGLONG time[3];
  541. ULONGLONG clocks;
  542. int i;
  543. int RetryCount = 50;
  544. Delay.QuadPart = -50000; // relative delay of 5ms (100ns ticks)
  545. while (RetryCount) {
  546. for (i = 0; i < 3; i++) {
  547. *Pstart = KeQueryPerformanceCounter(NULL);
  548. TSCStart = PerfGetCycleCount();
  549. KeDelayExecutionThread (KernelMode, FALSE, &Delay);
  550. clocks = PerfGetCycleCount() - TSCStart;
  551. *Pend = KeQueryPerformanceCounter((LARGE_INTEGER*)&freq);
  552. time[i] = (((end-start) * 1000000) / freq);
  553. time[i] = (clocks + time[i]/2) / time[i];
  554. }
  555. // If all three match then use it, else try again.
  556. if (time[0] == time[1] && time[1] == time[2])
  557. break;
  558. --RetryCount;
  559. }
  560. if (!RetryCount) {
  561. // Take the largest value.
  562. if (time[1] > time[0])
  563. time[0] = time[1];
  564. if (time[2] > time[0])
  565. time[0] = time[2];
  566. }
  567. PerfInfoTickFrequency = time[0];
  568. }
  569. BOOLEAN
  570. PerfInfoIsGroupOn(
  571. ULONG Group
  572. )
  573. {
  574. return PERFINFO_IS_GROUP_ON(Group);
  575. }
  576. #ifdef NTPERF_PRIVATE
  577. #include "..\..\tools\ntperf\ntosperf\perfinfokrn.c"
  578. #endif // NTPERF_PRIVATE
  579. #endif // NTPERF