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.

843 lines
20 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. profobj.c
  5. Abstract:
  6. This module implements the kernel Profile Object. Functions are
  7. provided to initialize, start, and stop profile objects and to set
  8. and query the profile interval.
  9. Author:
  10. Bryan M. Willman (bryanwi) 19-Sep-1990
  11. Environment:
  12. Kernel mode only.
  13. Revision History:
  14. --*/
  15. #include "ki.h"
  16. #pragma alloc_text (PAGE, KeQueryIntervalProfile)
  17. //
  18. // The following assert macro is used to check that an input profile object is
  19. // really a kprofile and not something else, like deallocated pool.
  20. //
  21. #define ASSERT_PROFILE(E) { \
  22. ASSERT((E)->Type == ProfileObject); \
  23. }
  24. //
  25. // Structure representing an active profile source
  26. //
  27. typedef struct _KACTIVE_PROFILE_SOURCE {
  28. LIST_ENTRY ListEntry;
  29. KPROFILE_SOURCE Source;
  30. KAFFINITY Affinity;
  31. ULONG ProcessorCount[1]; // variable-sized, one per processor
  32. } KACTIVE_PROFILE_SOURCE, *PKACTIVE_PROFILE_SOURCE;
  33. //
  34. // Prototypes for IPI target functions
  35. //
  36. VOID
  37. KiStartProfileInterrupt (
  38. IN PKIPI_CONTEXT SignalDone,
  39. IN PVOID Parameter1,
  40. IN PVOID Parameter2,
  41. IN PVOID Parameter3
  42. );
  43. VOID
  44. KiStopProfileInterrupt (
  45. IN PKIPI_CONTEXT SignalDone,
  46. IN PVOID Parameter1,
  47. IN PVOID Parameter2,
  48. IN PVOID Parameter3
  49. );
  50. VOID
  51. KeInitializeProfile (
  52. IN PKPROFILE Profile,
  53. IN PKPROCESS Process OPTIONAL,
  54. IN PVOID RangeBase,
  55. IN SIZE_T RangeSize,
  56. IN ULONG BucketSize,
  57. IN ULONG Segment,
  58. IN KPROFILE_SOURCE ProfileSource,
  59. IN KAFFINITY ProfileAffinity
  60. )
  61. /*++
  62. Routine Description:
  63. This function initializes a kernel profile object. The process,
  64. address range, bucket size, and buffer are set. The profile is
  65. set to the stopped state.
  66. Arguments:
  67. Profile - Supplies a pointer to control object of type profile.
  68. Process - Supplies an optional pointer to a process object that
  69. describes the address space to profile. If not specified,
  70. then all address spaces are included in the profile.
  71. RangeBase - Supplies the address of the first byte of the address
  72. range for which profiling information is to be collected.
  73. RangeSize - Supplies the size of the address range for which profiling
  74. information is to be collected. The RangeBase and RangeSize
  75. parameters are interpreted such that RangeBase <= address <
  76. RangeBase + RangeSize generates a profile hit.
  77. BucketSize - Supplies the log base 2 of the size of a profiling bucket.
  78. Thus, BucketSize = 2 yields 4-byte buckets, BucketSize = 7 yields
  79. 128-byte buckets.
  80. Segment - Supplies the non-Flat code segment to profile. If this
  81. is zero, then the flat profiling is done. This will only
  82. be non-zero on an x86 machine.
  83. ProfileSource - Supplies the profile interrupt source.
  84. ProfileAffinity - Supplies the set of processor to count hits for.
  85. Return Value:
  86. None.
  87. --*/
  88. {
  89. #if !defined(i386)
  90. ASSERT(Segment == 0);
  91. #endif
  92. //
  93. // Initialize the standard control object header.
  94. //
  95. Profile->Type = ProfileObject;
  96. Profile->Size = sizeof(KPROFILE);
  97. //
  98. // Initialize the process address space, range base, range limit,
  99. // bucket shift count, and set started FALSE.
  100. //
  101. if (ARGUMENT_PRESENT(Process)) {
  102. Profile->Process = Process;
  103. } else {
  104. Profile->Process = NULL;
  105. }
  106. Profile->RangeBase = RangeBase;
  107. Profile->RangeLimit = (PUCHAR)RangeBase + RangeSize;
  108. Profile->BucketShift = BucketSize - 2;
  109. Profile->Started = FALSE;
  110. Profile->Segment = Segment;
  111. Profile->Source = (CSHORT)ProfileSource;
  112. Profile->Affinity = ProfileAffinity & KeActiveProcessors;
  113. if (Profile->Affinity == 0) {
  114. Profile->Affinity = KeActiveProcessors;
  115. }
  116. return;
  117. }
  118. ULONG
  119. KeQueryIntervalProfile (
  120. IN KPROFILE_SOURCE ProfileSource
  121. )
  122. /*++
  123. Routine Description:
  124. This function returns the profile sample interval the system is
  125. currently using.
  126. Arguments:
  127. ProfileSource - Supplies the profile source to be queried.
  128. Return Value:
  129. Sample interval in units of 100ns.
  130. --*/
  131. {
  132. HAL_PROFILE_SOURCE_INFORMATION ProfileSourceInfo;
  133. ULONG ReturnedLength;
  134. NTSTATUS Status;
  135. #if !defined(_IA64_)
  136. if (ProfileSource == ProfileTime) {
  137. //
  138. // Return the current sampling interval in 100ns units.
  139. //
  140. return KiProfileInterval;
  141. }
  142. else
  143. #endif // !_IA64_
  144. if (ProfileSource == ProfileAlignmentFixup) {
  145. return KiProfileAlignmentFixupInterval;
  146. } else {
  147. //
  148. // The HAL is responsible for tracking this profile interval.
  149. //
  150. ProfileSourceInfo.Source = ProfileSource;
  151. Status = HalQuerySystemInformation(HalProfileSourceInformation,
  152. sizeof(HAL_PROFILE_SOURCE_INFORMATION),
  153. &ProfileSourceInfo,
  154. &ReturnedLength);
  155. if (NT_SUCCESS(Status) && ProfileSourceInfo.Supported) {
  156. return ProfileSourceInfo.Interval;
  157. } else {
  158. return 0;
  159. }
  160. }
  161. }
  162. VOID
  163. KeSetIntervalProfile (
  164. IN ULONG Interval,
  165. IN KPROFILE_SOURCE Source
  166. )
  167. /*++
  168. Routine Description:
  169. This function sets the profile sampling interval. The interval is in
  170. 100ns units. The interval will actually be set to some value in a set
  171. of preset values (at least on pc based hardware), using the one closest
  172. to what the user asked for.
  173. Arguments:
  174. Interval - Supplies the length of the sampling interval in 100ns units.
  175. Return Value:
  176. None.
  177. --*/
  178. {
  179. HAL_PROFILE_SOURCE_INTERVAL ProfileSourceInterval;
  180. #if !defined(_IA64_)
  181. if (Source == ProfileTime) {
  182. //
  183. // If the specified sampling interval is less than the minimum
  184. // sampling interval, then set the sampling interval to the minimum
  185. // sampling interval.
  186. //
  187. if (Interval < MINIMUM_PROFILE_INTERVAL) {
  188. Interval = MINIMUM_PROFILE_INTERVAL;
  189. }
  190. //
  191. // Set the sampling interval.
  192. //
  193. KiProfileInterval = (ULONG)KiIpiGenericCall(HalSetProfileInterval, Interval);
  194. }
  195. else
  196. #endif // !_IA64_
  197. if (Source == ProfileAlignmentFixup) {
  198. KiProfileAlignmentFixupInterval = Interval;
  199. } else {
  200. //
  201. // The HAL is responsible for setting this profile interval.
  202. //
  203. ProfileSourceInterval.Source = Source;
  204. ProfileSourceInterval.Interval = Interval;
  205. HalSetSystemInformation(HalProfileSourceInterval,
  206. sizeof(HAL_PROFILE_SOURCE_INTERVAL),
  207. &ProfileSourceInterval);
  208. }
  209. return;
  210. }
  211. BOOLEAN
  212. KeStartProfile (
  213. IN PKPROFILE Profile,
  214. IN PULONG Buffer
  215. )
  216. /*++
  217. Routine Description:
  218. This function starts profile data gathering on the specified profile
  219. object. The profile object is marked started, and is registered with
  220. the profile interrupt procedure.
  221. If the number of active profile objects was previously zero, then the
  222. profile interrupt is enabled.
  223. N.B. For the current implementation, an arbitrary number of profile
  224. objects may be active at once. This can present a large system
  225. overhead. It is assumed that the caller appropriately limits the
  226. the number of active profiles.
  227. Arguments:
  228. Profile - Supplies a pointer to a control object of type profile.
  229. Buffer - Supplies a pointer to an array of counters, which record
  230. the number of hits in the corresponding bucket.
  231. Return Value:
  232. A value of TRUE is returned if profiling was previously stopped for
  233. the specified profile object. Otherwise, a value of FALSE is returned.
  234. --*/
  235. {
  236. KIRQL OldIrql, OldIrql2;
  237. PKPROCESS Process;
  238. BOOLEAN Started;
  239. KAFFINITY TargetProcessors;
  240. PKPRCB Prcb;
  241. PKACTIVE_PROFILE_SOURCE ActiveSource = NULL;
  242. PKACTIVE_PROFILE_SOURCE CurrentActiveSource;
  243. PKACTIVE_PROFILE_SOURCE AllocatedPool;
  244. PLIST_ENTRY ListEntry;
  245. ULONG SourceSize;
  246. KAFFINITY AffinitySet;
  247. PULONG Reference;
  248. ASSERT_PROFILE(Profile);
  249. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  250. //
  251. // Allocate pool that may be required before raising to PROFILE_LEVEL.
  252. //
  253. SourceSize = sizeof(KACTIVE_PROFILE_SOURCE) + sizeof(ULONG) *
  254. (KeNumberProcessors - 1);
  255. AllocatedPool = ExAllocatePoolWithTag(NonPagedPool, SourceSize, 'forP');
  256. if (AllocatedPool == NULL) {
  257. return(TRUE);
  258. }
  259. //
  260. // Raise to dispatch level
  261. //
  262. KeRaiseIrql (DISPATCH_LEVEL, &OldIrql);
  263. Prcb = KeGetCurrentPrcb();
  264. //
  265. // Raise IRQL to PROFILE_LEVEL and acquire the profile lock.
  266. //
  267. KeRaiseIrql(KiProfileIrql, &OldIrql2);
  268. KiAcquireSpinLock(&KiProfileLock);
  269. //
  270. // Assume object already started
  271. //
  272. Started = FALSE;
  273. AffinitySet = 0L;
  274. TargetProcessors = 0L;
  275. //
  276. // If the specified profile object is not started, set started to TRUE,
  277. // set the address of the profile buffer, set the profile object to started,
  278. // insert the profile object in the appropriate profile list, and start
  279. // profile interrupts if the number of active profile objects was previously zero.
  280. //
  281. if (Profile->Started == FALSE) {
  282. Started = TRUE;
  283. Profile->Buffer = Buffer;
  284. Profile->Started = TRUE;
  285. Process = Profile->Process;
  286. if (Profile->Buffer) {
  287. if (Process != NULL) {
  288. InsertTailList(&Process->ProfileListHead, &Profile->ProfileListEntry);
  289. } else {
  290. InsertTailList(&KiProfileListHead, &Profile->ProfileListEntry);
  291. }
  292. } else {
  293. //
  294. // If we don't have a buffer passed, we'll use only the
  295. // event profiling
  296. //
  297. InitializeListHead(&Profile->ProfileListEntry);
  298. }
  299. //
  300. // Check the profile source list to see if this profile source is
  301. // already started. If so, update the reference counts. If not,
  302. // allocate a profile source object, initialize the reference
  303. // counts, and add it to the list.
  304. //
  305. ListEntry = KiProfileSourceListHead.Flink;
  306. while (ListEntry != &KiProfileSourceListHead) {
  307. CurrentActiveSource = CONTAINING_RECORD(ListEntry,
  308. KACTIVE_PROFILE_SOURCE,
  309. ListEntry);
  310. if (CurrentActiveSource->Source == Profile->Source) {
  311. ActiveSource = CurrentActiveSource;
  312. break;
  313. }
  314. ListEntry = ListEntry->Flink;
  315. }
  316. if (ActiveSource == NULL) {
  317. //
  318. // This source was not found, allocate and initialize a new entry and add
  319. // it to the head of the list.
  320. //
  321. ActiveSource = AllocatedPool;
  322. AllocatedPool = NULL;
  323. RtlZeroMemory(ActiveSource, SourceSize);
  324. ActiveSource->Source = Profile->Source;
  325. InsertHeadList(&KiProfileSourceListHead, &ActiveSource->ListEntry);
  326. if (Profile->Source == ProfileAlignmentFixup) {
  327. KiProfileAlignmentFixup = TRUE;
  328. }
  329. }
  330. //
  331. // Increment the reference counts for each processor in the
  332. // affinity set.
  333. //
  334. AffinitySet = Profile->Affinity;
  335. Reference = &ActiveSource->ProcessorCount[0];
  336. while (AffinitySet != 0) {
  337. if (AffinitySet & 1) {
  338. *Reference = *Reference + 1;
  339. }
  340. AffinitySet = AffinitySet >> 1;
  341. Reference = Reference + 1;
  342. }
  343. //
  344. // Compute the processors which the profile interrupt is
  345. // required and not already started
  346. //
  347. AffinitySet = Profile->Affinity & ~ActiveSource->Affinity;
  348. TargetProcessors = AffinitySet & ~Prcb->SetMember;
  349. //
  350. // Update set of processors on which this source is active.
  351. //
  352. ActiveSource->Affinity |= Profile->Affinity;
  353. }
  354. //
  355. // Release the profile lock, lower IRQL to its previous value, and
  356. // return whether profiling was started.
  357. //
  358. KiReleaseSpinLock(&KiProfileLock);
  359. KeLowerIrql(OldIrql2);
  360. //
  361. // Start profile interrupt on pending processors
  362. //
  363. #if !defined(NT_UP)
  364. if (TargetProcessors != 0) {
  365. KiIpiSendPacket(TargetProcessors,
  366. KiStartProfileInterrupt,
  367. (PVOID)Profile->Source,
  368. NULL,
  369. NULL);
  370. }
  371. #endif
  372. if (AffinitySet & Prcb->SetMember) {
  373. if (Profile->Source == ProfileAlignmentFixup) {
  374. KiEnableAlignmentExceptions();
  375. }
  376. HalStartProfileInterrupt(Profile->Source);
  377. }
  378. #if !defined(NT_UP)
  379. if (TargetProcessors != 0) {
  380. KiIpiStallOnPacketTargets(TargetProcessors);
  381. }
  382. #endif
  383. //
  384. // Lower to original IRQL
  385. //
  386. KeLowerIrql(OldIrql);
  387. //
  388. // If the allocated pool was not used, free it now.
  389. //
  390. if (AllocatedPool != NULL) {
  391. ExFreePool(AllocatedPool);
  392. }
  393. return Started;
  394. }
  395. BOOLEAN
  396. KeStopProfile (
  397. IN PKPROFILE Profile
  398. )
  399. /*++
  400. Routine Description:
  401. This function stops profile data gathering on the specified profile
  402. object. The object is marked stopped, and is removed from the active
  403. profile list.
  404. If the number of active profile objects goes to zero, then the profile
  405. interrupt is disabled.
  406. Arguments:
  407. Profile - Supplies a pointer to a control object of type profile.
  408. Return Value:
  409. A value of TRUE is returned if profiling was previously started for
  410. the specified profile object. Otherwise, a value of FALSE is returned.
  411. --*/
  412. {
  413. KIRQL OldIrql, OldIrql2;
  414. BOOLEAN Stopped;
  415. KAFFINITY TargetProcessors;
  416. PKPRCB Prcb;
  417. BOOLEAN StopInterrupt = TRUE;
  418. PLIST_ENTRY ListEntry;
  419. PKACTIVE_PROFILE_SOURCE ActiveSource;
  420. PKACTIVE_PROFILE_SOURCE PoolToFree=NULL;
  421. KAFFINITY AffinitySet = 0;
  422. KAFFINITY CurrentProcessor;
  423. PULONG Reference;
  424. ASSERT_PROFILE(Profile);
  425. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  426. //
  427. // Raise to disaptch level
  428. //
  429. KeRaiseIrql (DISPATCH_LEVEL, &OldIrql);
  430. Prcb = KeGetCurrentPrcb();
  431. //
  432. // Raise IRQL to PROFILE_LEVEL and acquire the profile lock.
  433. //
  434. KeRaiseIrql(KiProfileIrql, &OldIrql2);
  435. KiAcquireSpinLock(&KiProfileLock);
  436. //
  437. // Assume object already stopped
  438. //
  439. Stopped = FALSE;
  440. AffinitySet = 0L;
  441. TargetProcessors = 0L;
  442. //
  443. // If the specified profile object is not stopped, set stopped to TRUE, set
  444. // the profile object to stopped, remove the profile object object from the
  445. // appropriate profilelist, and stop profile interrupts if the number of
  446. // active profile objects is zero.
  447. //
  448. if (Profile->Started != FALSE) {
  449. Stopped = TRUE;
  450. Profile->Started = FALSE;
  451. if (!IsListEmpty(&Profile->ProfileListEntry)) {
  452. RemoveEntryList(&Profile->ProfileListEntry);
  453. }
  454. //
  455. // Search the profile source list to find the entry for this
  456. // profile source.
  457. //
  458. ListEntry = KiProfileSourceListHead.Flink;
  459. do {
  460. ASSERT(ListEntry != &KiProfileSourceListHead);
  461. ActiveSource = CONTAINING_RECORD(ListEntry,
  462. KACTIVE_PROFILE_SOURCE,
  463. ListEntry);
  464. ListEntry = ListEntry->Flink;
  465. } while ( ActiveSource->Source != Profile->Source );
  466. //
  467. // Decrement the reference counts for each processor in the
  468. // affinity set and build up a mask of the processors that
  469. // now have a reference count of zero.
  470. //
  471. CurrentProcessor = 1;
  472. TargetProcessors = 0;
  473. AffinitySet = Profile->Affinity;
  474. Reference = &ActiveSource->ProcessorCount[0];
  475. while (AffinitySet != 0) {
  476. if (AffinitySet & 1) {
  477. *Reference = *Reference - 1;
  478. if (*Reference == 0) {
  479. TargetProcessors = TargetProcessors | CurrentProcessor;
  480. }
  481. }
  482. AffinitySet = AffinitySet >> 1;
  483. Reference = Reference + 1;
  484. CurrentProcessor = CurrentProcessor << 1;
  485. }
  486. //
  487. // Compute the processors whose profile interrupt reference
  488. // count has dropped to zero.
  489. //
  490. AffinitySet = TargetProcessors;
  491. TargetProcessors = AffinitySet & ~Prcb->SetMember;
  492. //
  493. // Update set of processors on which this source is active.
  494. //
  495. ActiveSource->Affinity &= ~AffinitySet;
  496. //
  497. // Determine whether this profile source is stopped on all
  498. // processors. If so, remove it from the list and free it.
  499. //
  500. if (ActiveSource->Affinity == 0) {
  501. RemoveEntryList(&ActiveSource->ListEntry);
  502. PoolToFree = ActiveSource;
  503. if (Profile->Source == ProfileAlignmentFixup) {
  504. KiProfileAlignmentFixup = FALSE;
  505. }
  506. }
  507. }
  508. //
  509. // Release the profile lock, lower IRQL to its previous value, and
  510. // return whether profiling was stopped.
  511. //
  512. KiReleaseSpinLock(&KiProfileLock);
  513. KeLowerIrql(OldIrql2);
  514. //
  515. // Stop profile interrupt on pending processors
  516. //
  517. #if !defined(NT_UP)
  518. if (TargetProcessors != 0) {
  519. KiIpiSendPacket(TargetProcessors,
  520. KiStopProfileInterrupt,
  521. (PVOID)Profile->Source,
  522. NULL,
  523. NULL);
  524. }
  525. #endif
  526. if (AffinitySet & Prcb->SetMember) {
  527. if (Profile->Source == ProfileAlignmentFixup) {
  528. KiDisableAlignmentExceptions();
  529. }
  530. HalStopProfileInterrupt(Profile->Source);
  531. }
  532. #if !defined(NT_UP)
  533. if (TargetProcessors != 0) {
  534. KiIpiStallOnPacketTargets(TargetProcessors);
  535. }
  536. #endif
  537. //
  538. // Lower to original IRQL
  539. //
  540. KeLowerIrql (OldIrql);
  541. //
  542. // Now that IRQL has been lowered, free the profile source if
  543. // necessary.
  544. //
  545. if (PoolToFree != NULL) {
  546. ExFreePool(PoolToFree);
  547. }
  548. return Stopped;
  549. }
  550. #if !defined(NT_UP)
  551. VOID
  552. KiStopProfileInterrupt (
  553. IN PKIPI_CONTEXT SignalDone,
  554. IN PVOID Parameter1,
  555. IN PVOID Parameter2,
  556. IN PVOID Parameter3
  557. )
  558. /*++
  559. Routine Description:
  560. This is the target function for stopping the profile interrupt on target
  561. processors.
  562. Arguments:
  563. SignalDone - Supplies a pointer to a variable that is cleared when the
  564. requested operation has been performed
  565. Parameter1 - Supplies the profile source
  566. Parameter2 - Parameter3 - not used
  567. Return Value:
  568. None.
  569. --*/
  570. {
  571. KPROFILE_SOURCE ProfileSource;
  572. //
  573. // Stop the profile interrupt on the current processor and clear the
  574. // data cache packet address to signal the source to continue.
  575. //
  576. ProfileSource = (KPROFILE_SOURCE) PtrToUlong(Parameter1);
  577. if (ProfileSource == ProfileAlignmentFixup) {
  578. KiDisableAlignmentExceptions();
  579. }
  580. HalStopProfileInterrupt(ProfileSource);
  581. KiIpiSignalPacketDone(SignalDone);
  582. return;
  583. }
  584. VOID
  585. KiStartProfileInterrupt (
  586. IN PKIPI_CONTEXT SignalDone,
  587. IN PVOID Parameter1,
  588. IN PVOID Parameter2,
  589. IN PVOID Parameter3
  590. )
  591. /*++
  592. Routine Description:
  593. This is the target function for stopping the profile interrupt on target
  594. processors.
  595. Arguments:
  596. SignalDone - Supplies a pointer to a variable that is cleared when the
  597. requested operation has been performed
  598. Parameter1 - Supplies the profile source
  599. Parameter2 - Parameter3 - not used
  600. Return Value:
  601. None.
  602. --*/
  603. {
  604. KPROFILE_SOURCE ProfileSource;
  605. //
  606. // Start the profile interrupt on the current processor and clear the
  607. // data cache packet address to signal the source to continue.
  608. //
  609. ProfileSource = (KPROFILE_SOURCE)PtrToUlong(Parameter1);
  610. if (ProfileSource == ProfileAlignmentFixup) {
  611. KiEnableAlignmentExceptions();
  612. }
  613. HalStartProfileInterrupt(ProfileSource);
  614. KiIpiSignalPacketDone(SignalDone);
  615. return;
  616. }
  617. #endif