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.

945 lines
25 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. profile.c
  5. Abstract:
  6. This module implements the executive profile object. Functions are provided
  7. to create, start, stop, and query profile objects.
  8. Author:
  9. Lou Perazzoli (loup) 21-Sep-1990
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. --*/
  14. #include "exp.h"
  15. //
  16. // Executive profile object.
  17. //
  18. typedef struct _EPROFILE {
  19. PKPROCESS Process;
  20. PVOID RangeBase;
  21. SIZE_T RangeSize;
  22. PVOID Buffer;
  23. ULONG BufferSize;
  24. ULONG BucketSize;
  25. PKPROFILE ProfileObject;
  26. PVOID LockedBufferAddress;
  27. PMDL Mdl;
  28. ULONG Segment;
  29. KPROFILE_SOURCE ProfileSource;
  30. KAFFINITY Affinity;
  31. } EPROFILE, *PEPROFILE;
  32. //
  33. // Address of event object type descriptor.
  34. //
  35. POBJECT_TYPE ExProfileObjectType;
  36. KMUTEX ExpProfileStateMutex;
  37. #ifdef ALLOC_DATA_PRAGMA
  38. #pragma const_seg("PAGECONST")
  39. #endif
  40. const ULONG ExpCurrentProfileUsage = 0;
  41. #ifdef ALLOC_DATA_PRAGMA
  42. #pragma const_seg("INITCONST")
  43. #endif
  44. const GENERIC_MAPPING ExpProfileMapping = {
  45. STANDARD_RIGHTS_READ | PROFILE_CONTROL,
  46. STANDARD_RIGHTS_WRITE | PROFILE_CONTROL,
  47. STANDARD_RIGHTS_EXECUTE | PROFILE_CONTROL,
  48. PROFILE_ALL_ACCESS
  49. };
  50. #ifdef ALLOC_DATA_PRAGMA
  51. #pragma const_seg()
  52. #endif
  53. #define ACTIVE_PROFILE_LIMIT 8
  54. #ifdef ALLOC_PRAGMA
  55. #pragma alloc_text(INIT, ExpProfileInitialization)
  56. #pragma alloc_text(PAGE, ExpProfileDelete)
  57. #pragma alloc_text(PAGE, NtCreateProfile)
  58. #pragma alloc_text(PAGE, NtStartProfile)
  59. #pragma alloc_text(PAGE, NtStopProfile)
  60. #pragma alloc_text(PAGE, NtSetIntervalProfile)
  61. #pragma alloc_text(PAGE, NtQueryIntervalProfile)
  62. #pragma alloc_text(PAGE, NtQueryPerformanceCounter)
  63. #endif
  64. BOOLEAN
  65. ExpProfileInitialization (
  66. )
  67. /*++
  68. Routine Description:
  69. This function creates the profile object type descriptor at system
  70. initialization and stores the address of the object type descriptor
  71. in global storage.
  72. Arguments:
  73. None.
  74. Return Value:
  75. A value of TRUE is returned if the profile object type descriptor is
  76. successfully initialized. Otherwise a value of FALSE is returned.
  77. --*/
  78. {
  79. OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
  80. NTSTATUS Status;
  81. UNICODE_STRING TypeName;
  82. //
  83. // Initialize mutex for synchronizing start and stop operations.
  84. //
  85. KeInitializeMutex (&ExpProfileStateMutex, MUTEX_LEVEL_EX_PROFILE);
  86. //
  87. // Initialize string descriptor.
  88. //
  89. RtlInitUnicodeString(&TypeName, L"Profile");
  90. //
  91. // Create event object type descriptor.
  92. //
  93. RtlZeroMemory(&ObjectTypeInitializer,sizeof(ObjectTypeInitializer));
  94. ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
  95. ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
  96. ObjectTypeInitializer.PoolType = NonPagedPool;
  97. ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(EPROFILE);
  98. ObjectTypeInitializer.ValidAccessMask = PROFILE_ALL_ACCESS;
  99. ObjectTypeInitializer.DeleteProcedure = ExpProfileDelete;
  100. ObjectTypeInitializer.GenericMapping = ExpProfileMapping;
  101. Status = ObCreateObjectType(&TypeName,
  102. &ObjectTypeInitializer,
  103. (PSECURITY_DESCRIPTOR)NULL,
  104. &ExProfileObjectType);
  105. //
  106. // If the event object type descriptor was successfully created, then
  107. // return a value of TRUE. Otherwise return a value of FALSE.
  108. //
  109. return (BOOLEAN)(NT_SUCCESS(Status));
  110. }
  111. VOID
  112. ExpProfileDelete (
  113. IN PVOID Object
  114. )
  115. /*++
  116. Routine Description:
  117. This routine is called by the object management procedures whenever
  118. the last reference to a profile object has been removed. This routine
  119. stops profiling, returns locked buffers and pages, dereferences the
  120. specified process and returns.
  121. Arguments:
  122. Object - a pointer to the body of the profile object.
  123. Return Value:
  124. None.
  125. --*/
  126. {
  127. PEPROFILE Profile;
  128. BOOLEAN State;
  129. PEPROCESS ProcessAddress;
  130. Profile = (PEPROFILE)Object;
  131. if (Profile->LockedBufferAddress != NULL) {
  132. //
  133. // Stop profiling and unlock the buffers and deallocate pool.
  134. //
  135. State = KeStopProfile (Profile->ProfileObject);
  136. ASSERT (State != FALSE);
  137. MmUnmapLockedPages (Profile->LockedBufferAddress, Profile->Mdl);
  138. MmUnlockPages (Profile->Mdl);
  139. ExFreePool (Profile->ProfileObject);
  140. }
  141. if (Profile->Process != NULL) {
  142. ProcessAddress = CONTAINING_RECORD(Profile->Process, EPROCESS, Pcb);
  143. ObDereferenceObject ((PVOID)ProcessAddress);
  144. }
  145. return;
  146. }
  147. NTSTATUS
  148. NtCreateProfile (
  149. OUT PHANDLE ProfileHandle,
  150. IN HANDLE Process OPTIONAL,
  151. IN PVOID RangeBase,
  152. IN SIZE_T RangeSize,
  153. IN ULONG BucketSize,
  154. IN PULONG Buffer,
  155. IN ULONG BufferSize,
  156. IN KPROFILE_SOURCE ProfileSource,
  157. IN KAFFINITY Affinity
  158. )
  159. /*++
  160. Routine Description:
  161. This function creates a profile object.
  162. Arguments:
  163. ProfileHandle - Supplies a pointer to a variable that will receive
  164. the profile object handle.
  165. Process - Optionally, supplies the handle to the process whose
  166. address space to profile. If the value is NULL (0), then
  167. all address spaces are included in the profile.
  168. RangeBase - Supplies the address of the first byte of the address
  169. space for which profiling information is to be collected.
  170. RangeSize - Supplies the size of the range to profile in the
  171. address space. RangeBase and RangeSize are interpreted
  172. such that RangeBase <= address < RangeBase+RangeSize
  173. will generate a profile hit.
  174. BucketSize - Supplies the LOG base 2 of the size of the profiling
  175. bucket. Thus, BucketSize = 2 yields four-byte
  176. buckets, BucketSize = 7 yields 128-byte buckets.
  177. All profile hits in a given bucket will increment
  178. the corresponding counter in Buffer. Buckets
  179. cannot be smaller than a ULONG. The acceptable range
  180. of this value is 2 to 30 inclusive.
  181. Buffer - Supplies an array of ULONGs. Each ULONG is a hit counter,
  182. which records the number of hits of the corresponding
  183. bucket.
  184. BufferSize - Size in bytes of Buffer.
  185. ProfileSource - Supplies the source for the profile interrupt
  186. Affinity - Supplies the processor set for the profile interrupt
  187. Return Value:
  188. TBS
  189. --*/
  190. {
  191. PEPROFILE Profile;
  192. HANDLE Handle;
  193. KPROCESSOR_MODE PreviousMode;
  194. NTSTATUS Status;
  195. PEPROCESS ProcessAddress;
  196. OBJECT_ATTRIBUTES ObjectAttributes;
  197. BOOLEAN HasPrivilege = FALSE;
  198. ULONG Segment = FALSE;
  199. #ifdef i386
  200. USHORT PowerOf2;
  201. #endif
  202. //
  203. // Verify that the base and size arguments are reasonable.
  204. //
  205. if (BufferSize == 0) {
  206. return STATUS_INVALID_PARAMETER_7;
  207. }
  208. #ifdef i386
  209. //
  210. // sleazy use of bucket size. If bucket size is zero, and
  211. // RangeBase < 64K, then create a profile object to attach
  212. // to a non-flat code segment. In this case, RangeBase is
  213. // the non-flat CS for this profile object.
  214. //
  215. if ((BucketSize == 0) && (RangeBase < (PVOID)(64 * 1024))) {
  216. if (BufferSize < sizeof(ULONG)) {
  217. return STATUS_INVALID_PARAMETER_7;
  218. }
  219. Segment = (ULONG)RangeBase;
  220. RangeBase = 0;
  221. BucketSize = RangeSize / (BufferSize / sizeof(ULONG));
  222. //
  223. // Convert Bucket size of log2(BucketSize)
  224. //
  225. PowerOf2 = 0;
  226. BucketSize = BucketSize - 1;
  227. while (BucketSize >>= 1) {
  228. PowerOf2++;
  229. }
  230. BucketSize = PowerOf2 + 1;
  231. if (BucketSize < 2) {
  232. BucketSize = 2;
  233. }
  234. }
  235. #endif
  236. if ((BucketSize > 31) || (BucketSize < 2)) {
  237. return STATUS_INVALID_PARAMETER;
  238. }
  239. if ((RangeSize >> (BucketSize - 2)) > BufferSize) {
  240. return STATUS_BUFFER_TOO_SMALL;
  241. }
  242. if (((ULONG_PTR)RangeBase + RangeSize) < RangeSize) {
  243. return STATUS_BUFFER_OVERFLOW;
  244. }
  245. //
  246. // Establish an exception handler, probe the output handle address, and
  247. // attempt to create a profile object. If the probe fails, then return the
  248. // exception code as the service status. Otherwise return the status value
  249. // returned by the object insertion routine.
  250. //
  251. try {
  252. //
  253. // Get previous processor mode and probe output handle address if
  254. // necessary.
  255. //
  256. PreviousMode = KeGetPreviousMode ();
  257. if (PreviousMode != KernelMode) {
  258. ProbeForWriteHandle(ProfileHandle);
  259. ProbeForWrite(Buffer,
  260. BufferSize,
  261. sizeof(ULONG));
  262. }
  263. //
  264. // If an exception occurs during the probe of the output handle address,
  265. // then always handle the exception and return the exception code as the
  266. // status value.
  267. //
  268. } except (EXCEPTION_EXECUTE_HANDLER) {
  269. return GetExceptionCode();
  270. }
  271. //
  272. // TODO post NT5:
  273. //
  274. // Currently, if a process isn't specified, there is no privilege check if
  275. // RangeBase > MM_HIGHEST_USER_ADDRESS.
  276. // The check for user-space addresses is SeSystemProfilePrivilege.
  277. // Querying a specific process requires only PROCESS_QUERY_INFORMATION.
  278. //
  279. // The spec says:
  280. //
  281. // Process - If specified, a handle to a process which describes the address space to profile.
  282. // If not present, then all address spaces are included in the profile.
  283. // Profiling a process requires PROCESS_QUERY_INFORMATION access to that process and
  284. // SeProfileSingleProcessPrivilege privilege.
  285. // Profiling all processes requires SeSystemProfilePrivilege privilege.
  286. //
  287. // So two changes appear needed.
  288. // A check on SeProfileSingleProcessPrivilege needs to be added to the single process case,
  289. // and SeSystemProfilePrivilege privilege should be required for both user and system address profiling.
  290. //
  291. if (!ARGUMENT_PRESENT(Process)) {
  292. //
  293. // Don't attach segmented profile objects to all processes
  294. //
  295. if (Segment) {
  296. return STATUS_INVALID_PARAMETER;
  297. }
  298. //
  299. // Profile all processes. Make sure that the specified
  300. // address range is in system space, unless SeSystemProfilePrivilege.
  301. //
  302. if (RangeBase <= MM_HIGHEST_USER_ADDRESS) {
  303. //
  304. // Check for privilege before allowing a user to profile
  305. // all processes and USER addresses.
  306. //
  307. if (PreviousMode != KernelMode) {
  308. HasPrivilege = SeSinglePrivilegeCheck(
  309. SeSystemProfilePrivilege,
  310. PreviousMode
  311. );
  312. if (!HasPrivilege) {
  313. #if DBG
  314. DbgPrint("SeSystemProfilePrivilege needed to profile all USER addresses.\n");
  315. #endif //DBG
  316. return( STATUS_PRIVILEGE_NOT_HELD );
  317. }
  318. }
  319. }
  320. ProcessAddress = NULL;
  321. } else {
  322. //
  323. // Reference the specified process.
  324. //
  325. Status = ObReferenceObjectByHandle ( Process,
  326. PROCESS_QUERY_INFORMATION,
  327. PsProcessType,
  328. PreviousMode,
  329. (PVOID *)&ProcessAddress,
  330. NULL );
  331. if (!NT_SUCCESS(Status)) {
  332. return Status;
  333. }
  334. }
  335. InitializeObjectAttributes( &ObjectAttributes,
  336. NULL,
  337. OBJ_EXCLUSIVE,
  338. NULL,
  339. NULL );
  340. Status = ObCreateObject( KernelMode,
  341. ExProfileObjectType,
  342. &ObjectAttributes,
  343. PreviousMode,
  344. NULL,
  345. sizeof(EPROFILE),
  346. 0,
  347. sizeof(EPROFILE) + sizeof(KPROFILE),
  348. (PVOID *)&Profile);
  349. //
  350. // If the profile object was successfully allocated, initialize
  351. // the profile object.
  352. //
  353. if (NT_SUCCESS(Status)) {
  354. if (ProcessAddress != NULL) {
  355. Profile->Process = &ProcessAddress->Pcb;
  356. } else {
  357. Profile->Process = NULL;
  358. }
  359. Profile->RangeBase = RangeBase;
  360. Profile->RangeSize = RangeSize;
  361. Profile->Buffer = Buffer;
  362. Profile->BufferSize = BufferSize;
  363. Profile->BucketSize = BucketSize;
  364. Profile->LockedBufferAddress = NULL;
  365. Profile->Segment = Segment;
  366. Profile->ProfileSource = ProfileSource;
  367. Profile->Affinity = Affinity;
  368. Status = ObInsertObject(Profile,
  369. NULL,
  370. PROFILE_CONTROL,
  371. 0,
  372. (PVOID *)NULL,
  373. &Handle);
  374. //
  375. // If the profile object was successfully inserted in the current
  376. // process' handle table, then attempt to write the profile object
  377. // handle value. If the write attempt fails, then do not report
  378. // an error. When the caller attempts to access the handle value,
  379. // an access violation will occur.
  380. //
  381. if (NT_SUCCESS(Status)) {
  382. try {
  383. *ProfileHandle = Handle;
  384. } except(EXCEPTION_EXECUTE_HANDLER) {
  385. }
  386. }
  387. } else {
  388. //
  389. // We failed, remove our reference to the process object.
  390. //
  391. if (ProcessAddress != NULL) {
  392. ObDereferenceObject (ProcessAddress);
  393. }
  394. }
  395. //
  396. // Return service status.
  397. //
  398. return Status;
  399. }
  400. NTSTATUS
  401. NtStartProfile (
  402. IN HANDLE ProfileHandle
  403. )
  404. /*++
  405. Routine Description:
  406. The NtStartProfile routine starts the collecting data for the
  407. specified profile object. This involved allocating nonpaged
  408. pool to lock the specified buffer in memory, creating a kernel
  409. profile object and starting collecting on that profile object.
  410. Arguments:
  411. ProfileHandle - Supplies the profile handle to start profiling on.
  412. Return Value:
  413. TBS
  414. --*/
  415. {
  416. KPROCESSOR_MODE PreviousMode;
  417. NTSTATUS Status;
  418. PEPROFILE Profile;
  419. PKPROFILE ProfileObject;
  420. PVOID LockedVa;
  421. BOOLEAN State;
  422. PMDL Mdl;
  423. PreviousMode = KeGetPreviousMode();
  424. Status = ObReferenceObjectByHandle (ProfileHandle,
  425. PROFILE_CONTROL,
  426. ExProfileObjectType,
  427. PreviousMode,
  428. &Profile,
  429. NULL);
  430. if (!NT_SUCCESS(Status)) {
  431. return Status;
  432. }
  433. //
  434. // Acquire the profile state mutex so two threads can't
  435. // operate on the same profile object simultaneously.
  436. //
  437. KeWaitForSingleObject (&ExpProfileStateMutex,
  438. Executive,
  439. KernelMode,
  440. FALSE,
  441. (PLARGE_INTEGER)NULL);
  442. //
  443. // Make sure profiling is not already enabled.
  444. //
  445. if (Profile->LockedBufferAddress != NULL) {
  446. KeReleaseMutex (&ExpProfileStateMutex, FALSE);
  447. ObDereferenceObject (Profile);
  448. return STATUS_PROFILING_NOT_STOPPED;
  449. }
  450. if (ExpCurrentProfileUsage == ACTIVE_PROFILE_LIMIT) {
  451. KeReleaseMutex (&ExpProfileStateMutex, FALSE);
  452. ObDereferenceObject (Profile);
  453. return STATUS_PROFILING_AT_LIMIT;
  454. }
  455. ProfileObject = ExAllocatePoolWithTag (NonPagedPool,
  456. MmSizeOfMdl(Profile->Buffer,
  457. Profile->BufferSize) +
  458. sizeof(KPROFILE),
  459. 'forP');
  460. if (ProfileObject == NULL) {
  461. KeReleaseMutex (&ExpProfileStateMutex, FALSE);
  462. ObDereferenceObject (Profile);
  463. return STATUS_INSUFFICIENT_RESOURCES;
  464. }
  465. Mdl = (PMDL)(ProfileObject + 1);
  466. Profile->Mdl = Mdl;
  467. Profile->ProfileObject = ProfileObject;
  468. //
  469. // Probe and lock the specified buffer.
  470. //
  471. MmInitializeMdl(Mdl, Profile->Buffer, Profile->BufferSize);
  472. LockedVa = NULL;
  473. try {
  474. MmProbeAndLockPages (Mdl,
  475. PreviousMode,
  476. IoWriteAccess );
  477. } except (EXCEPTION_EXECUTE_HANDLER) {
  478. KeReleaseMutex (&ExpProfileStateMutex, FALSE);
  479. ExFreePool (ProfileObject);
  480. ObDereferenceObject (Profile);
  481. return GetExceptionCode();
  482. }
  483. //
  484. // Since kernel space is specified below, this call cannot raise
  485. // an exception.
  486. //
  487. LockedVa = MmMapLockedPagesSpecifyCache (Profile->Mdl,
  488. KernelMode,
  489. MmCached,
  490. NULL,
  491. FALSE,
  492. NormalPagePriority);
  493. if (LockedVa == NULL) {
  494. KeReleaseMutex (&ExpProfileStateMutex, FALSE);
  495. MmUnlockPages (Mdl);
  496. ExFreePool (ProfileObject);
  497. ObDereferenceObject (Profile);
  498. return STATUS_INSUFFICIENT_RESOURCES;
  499. }
  500. //
  501. // Initialize the profile object.
  502. //
  503. KeInitializeProfile (ProfileObject,
  504. Profile->Process,
  505. Profile->RangeBase,
  506. Profile->RangeSize,
  507. Profile->BucketSize,
  508. Profile->Segment,
  509. Profile->ProfileSource,
  510. Profile->Affinity);
  511. State = KeStartProfile (ProfileObject, LockedVa);
  512. ASSERT (State != FALSE);
  513. Profile->LockedBufferAddress = LockedVa;
  514. KeReleaseMutex (&ExpProfileStateMutex, FALSE);
  515. ObDereferenceObject (Profile);
  516. return STATUS_SUCCESS;
  517. }
  518. NTSTATUS
  519. NtStopProfile (
  520. IN HANDLE ProfileHandle
  521. )
  522. /*++
  523. Routine Description:
  524. The NtStopProfile routine stops collecting data for the
  525. specified profile object. This involves stopping the data
  526. collection on the profile object, unlocking the locked buffers,
  527. and deallocating the pool for the MDL and profile object.
  528. Arguments:
  529. ProfileHandle - Supplies a the profile handle to stop profiling.
  530. Return Value:
  531. TBS
  532. --*/
  533. {
  534. PEPROFILE Profile;
  535. KPROCESSOR_MODE PreviousMode;
  536. NTSTATUS Status;
  537. BOOLEAN State;
  538. PKPROFILE ProfileObject;
  539. PMDL Mdl;
  540. PVOID LockedBufferAddress;
  541. PreviousMode = KeGetPreviousMode();
  542. Status = ObReferenceObjectByHandle( ProfileHandle,
  543. PROFILE_CONTROL,
  544. ExProfileObjectType,
  545. PreviousMode,
  546. (PVOID *)&Profile,
  547. NULL);
  548. if (!NT_SUCCESS(Status)) {
  549. return Status;
  550. }
  551. KeWaitForSingleObject( &ExpProfileStateMutex,
  552. Executive,
  553. KernelMode,
  554. FALSE,
  555. (PLARGE_INTEGER)NULL);
  556. //
  557. // Check to see if profiling is not active.
  558. //
  559. if (Profile->LockedBufferAddress == NULL) {
  560. KeReleaseMutex (&ExpProfileStateMutex, FALSE);
  561. ObDereferenceObject (Profile);
  562. return STATUS_PROFILING_NOT_STARTED;
  563. }
  564. //
  565. // Stop profiling and unlock the buffer.
  566. //
  567. State = KeStopProfile (Profile->ProfileObject);
  568. ASSERT (State != FALSE);
  569. LockedBufferAddress = Profile->LockedBufferAddress;
  570. Profile->LockedBufferAddress = NULL;
  571. Mdl = Profile->Mdl;
  572. ProfileObject = Profile->ProfileObject;
  573. KeReleaseMutex (&ExpProfileStateMutex, FALSE);
  574. MmUnmapLockedPages (LockedBufferAddress, Mdl);
  575. MmUnlockPages (Mdl);
  576. ExFreePool (ProfileObject);
  577. ObDereferenceObject (Profile);
  578. return STATUS_SUCCESS;
  579. }
  580. NTSTATUS
  581. NtSetIntervalProfile (
  582. IN ULONG Interval,
  583. IN KPROFILE_SOURCE Source
  584. )
  585. /*++
  586. Routine Description:
  587. This routine allows the system-wide interval (and thus the profiling
  588. rate) for profiling to be set.
  589. Arguments:
  590. Interval - Supplies the sampling interval in 100ns units.
  591. Source - Specifies the profile source to be set.
  592. Return Value:
  593. TBS
  594. --*/
  595. {
  596. KeSetIntervalProfile (Interval, Source);
  597. return STATUS_SUCCESS;
  598. }
  599. NTSTATUS
  600. NtQueryIntervalProfile (
  601. IN KPROFILE_SOURCE ProfileSource,
  602. OUT PULONG Interval
  603. )
  604. /*++
  605. Routine Description:
  606. This routine queries the system-wide interval (and thus the profiling
  607. rate) for profiling.
  608. Arguments:
  609. Source - Specifies the profile source to be queried.
  610. Interval - Returns the sampling interval in 100ns units.
  611. Return Value:
  612. TBS
  613. --*/
  614. {
  615. ULONG CapturedInterval;
  616. KPROCESSOR_MODE PreviousMode;
  617. PreviousMode = KeGetPreviousMode ();
  618. if (PreviousMode != KernelMode) {
  619. //
  620. // Probe accessibility of user's buffer.
  621. //
  622. try {
  623. ProbeForWriteUlong (Interval);
  624. } except (EXCEPTION_EXECUTE_HANDLER) {
  625. //
  626. // If an exception occurs during the probe or capture
  627. // of the initial values, then handle the exception and
  628. // return the exception code as the status value.
  629. //
  630. return GetExceptionCode();
  631. }
  632. }
  633. CapturedInterval = KeQueryIntervalProfile (ProfileSource);
  634. if (PreviousMode != KernelMode) {
  635. try {
  636. *Interval = CapturedInterval;
  637. } except (EXCEPTION_EXECUTE_HANDLER) {
  638. NOTHING;
  639. }
  640. }
  641. else {
  642. *Interval = CapturedInterval;
  643. }
  644. return STATUS_SUCCESS;
  645. }
  646. NTSTATUS
  647. NtQueryPerformanceCounter (
  648. OUT PLARGE_INTEGER PerformanceCounter,
  649. OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
  650. )
  651. /*++
  652. Routine Description:
  653. This function returns current value of performance counter and,
  654. optionally, the frequency of the performance counter.
  655. Performance frequency is the frequency of the performance counter
  656. in Hertz, i.e., counts/second. Note that this value is implementation
  657. dependent. If the implementation does not have hardware to support
  658. performance timing, the value returned is 0.
  659. Arguments:
  660. PerformanceCounter - supplies the address of a variable to receive
  661. the current Performance Counter value.
  662. PerformanceFrequency - Optionally, supplies the address of a
  663. variable to receive the performance counter frequency.
  664. Return Value:
  665. STATUS_ACCESS_VIOLATION or STATUS_SUCCESS.
  666. --*/
  667. {
  668. KPROCESSOR_MODE PreviousMode;
  669. LARGE_INTEGER KernelPerformanceFrequency;
  670. PreviousMode = KeGetPreviousMode();
  671. if (PreviousMode != KernelMode) {
  672. //
  673. // Probe accessibility of user's buffer.
  674. //
  675. try {
  676. ProbeForWriteSmallStructure (PerformanceCounter,
  677. sizeof (LARGE_INTEGER),
  678. sizeof (ULONG));
  679. if (ARGUMENT_PRESENT(PerformanceFrequency)) {
  680. ProbeForWriteSmallStructure (PerformanceFrequency,
  681. sizeof (LARGE_INTEGER),
  682. sizeof (ULONG));
  683. }
  684. *PerformanceCounter = KeQueryPerformanceCounter (&KernelPerformanceFrequency);
  685. if (ARGUMENT_PRESENT(PerformanceFrequency)) {
  686. *PerformanceFrequency = KernelPerformanceFrequency;
  687. }
  688. } except (EXCEPTION_EXECUTE_HANDLER) {
  689. //
  690. // If an exception occurs during the probe or capture
  691. // of the initial values, then handle the exception and
  692. // return the exception code as the status value.
  693. //
  694. return GetExceptionCode();
  695. }
  696. }
  697. else {
  698. *PerformanceCounter = KeQueryPerformanceCounter (&KernelPerformanceFrequency);
  699. if (ARGUMENT_PRESENT(PerformanceFrequency)) {
  700. *PerformanceFrequency = KernelPerformanceFrequency;
  701. }
  702. }
  703. return STATUS_SUCCESS;
  704. }