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.

863 lines
21 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. } else
  142. #endif // !defined(_IA64_)
  143. if (ProfileSource == ProfileAlignmentFixup) {
  144. return KiProfileAlignmentFixupInterval;
  145. } else {
  146. //
  147. // The HAL is responsible for tracking this profile interval.
  148. //
  149. ProfileSourceInfo.Source = ProfileSource;
  150. Status = HalQuerySystemInformation(HalProfileSourceInformation,
  151. sizeof(HAL_PROFILE_SOURCE_INFORMATION),
  152. &ProfileSourceInfo,
  153. &ReturnedLength);
  154. if (NT_SUCCESS(Status) && ProfileSourceInfo.Supported) {
  155. return ProfileSourceInfo.Interval;
  156. } else {
  157. return 0;
  158. }
  159. }
  160. }
  161. VOID
  162. KeSetIntervalProfile (
  163. IN ULONG Interval,
  164. IN KPROFILE_SOURCE Source
  165. )
  166. /*++
  167. Routine Description:
  168. This function sets the profile sampling interval. The interval is in
  169. 100ns units. The interval will actually be set to some value in a set
  170. of preset values (at least on pc based hardware), using the one closest
  171. to what the user asked for.
  172. Arguments:
  173. Interval - Supplies the length of the sampling interval in 100ns units.
  174. Return Value:
  175. None.
  176. --*/
  177. {
  178. HAL_PROFILE_SOURCE_INTERVAL ProfileSourceInterval;
  179. #if !defined(_IA64_)
  180. if (Source == ProfileTime) {
  181. //
  182. // If the specified sampling interval is less than the minimum
  183. // sampling interval, then set the sampling interval to the minimum
  184. // sampling interval.
  185. //
  186. if (Interval < MINIMUM_PROFILE_INTERVAL) {
  187. Interval = MINIMUM_PROFILE_INTERVAL;
  188. }
  189. //
  190. // Set the sampling interval.
  191. //
  192. KiProfileInterval = (ULONG)KeIpiGenericCall(HalSetProfileInterval, Interval);
  193. } else
  194. #endif // !defined(_IA64_)
  195. if (Source == ProfileAlignmentFixup) {
  196. KiProfileAlignmentFixupInterval = Interval;
  197. } else {
  198. //
  199. // The HAL is responsible for setting this profile interval.
  200. //
  201. ProfileSourceInterval.Source = Source;
  202. ProfileSourceInterval.Interval = Interval;
  203. HalSetSystemInformation(HalProfileSourceInterval,
  204. sizeof(HAL_PROFILE_SOURCE_INTERVAL),
  205. &ProfileSourceInterval);
  206. }
  207. return;
  208. }
  209. BOOLEAN
  210. KeStartProfile (
  211. IN PKPROFILE Profile,
  212. IN PULONG Buffer
  213. )
  214. /*++
  215. Routine Description:
  216. This function starts profile data gathering on the specified profile
  217. object. The profile object is marked started, and is registered with
  218. the profile interrupt procedure.
  219. If the number of active profile objects was previously zero, then the
  220. profile interrupt is enabled.
  221. N.B. For the current implementation, an arbitrary number of profile
  222. objects may be active at once. This can present a large system
  223. overhead. It is assumed that the caller appropriately limits the
  224. the number of active profiles.
  225. Arguments:
  226. Profile - Supplies a pointer to a control object of type profile.
  227. Buffer - Supplies a pointer to an array of counters, which record
  228. the number of hits in the corresponding bucket.
  229. Return Value:
  230. A value of TRUE is returned if profiling was previously stopped for
  231. the specified profile object. Otherwise, a value of FALSE is returned.
  232. --*/
  233. {
  234. KIRQL OldIrql;
  235. PKPROCESS Process;
  236. BOOLEAN Started;
  237. KAFFINITY TargetProcessors;
  238. PKPRCB Prcb;
  239. PKACTIVE_PROFILE_SOURCE ActiveSource = NULL;
  240. PKACTIVE_PROFILE_SOURCE CurrentActiveSource;
  241. PKACTIVE_PROFILE_SOURCE AllocatedPool;
  242. PLIST_ENTRY ListEntry;
  243. ULONG SourceSize;
  244. KAFFINITY AffinitySet;
  245. PULONG Reference;
  246. ASSERT_PROFILE(Profile);
  247. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  248. //
  249. // Allocate pool that may be required before raising to PROFILE_LEVEL.
  250. //
  251. SourceSize =
  252. sizeof(KACTIVE_PROFILE_SOURCE) + sizeof(ULONG) * (KeNumberProcessors - 1);
  253. AllocatedPool = ExAllocatePoolWithTag(NonPagedPool, SourceSize, 'forP');
  254. if (AllocatedPool == NULL) {
  255. return(TRUE);
  256. }
  257. //
  258. // Raise IRQL to profile level and acquire the profile lock.
  259. //
  260. OldIrql = KfRaiseIrql(KiProfileIrql);
  261. KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
  262. //
  263. // Assume object already started.
  264. //
  265. Started = FALSE;
  266. AffinitySet = 0L;
  267. TargetProcessors = 0L;
  268. //
  269. // If the specified profile object is not started, set started to TRUE,
  270. // set the address of the profile buffer, set the profile object to started,
  271. // insert the profile object in the appropriate profile list, and start
  272. // profile interrupts if the number of active profile objects was previously zero.
  273. //
  274. Prcb = KeGetCurrentPrcb();
  275. if (Profile->Started == FALSE) {
  276. Started = TRUE;
  277. Profile->Buffer = Buffer;
  278. Profile->Started = TRUE;
  279. Process = Profile->Process;
  280. if (Profile->Buffer) {
  281. if (Process != NULL) {
  282. InsertTailList(&Process->ProfileListHead, &Profile->ProfileListEntry);
  283. } else {
  284. InsertTailList(&KiProfileListHead, &Profile->ProfileListEntry);
  285. }
  286. } else {
  287. //
  288. // If we don't have a buffer passed, we'll use only the
  289. // event profiling
  290. //
  291. InitializeListHead(&Profile->ProfileListEntry);
  292. }
  293. //
  294. // Check the profile source list to see if this profile source is
  295. // already started. If so, update the reference counts. If not,
  296. // allocate a profile source object, initialize the reference
  297. // counts, and add it to the list.
  298. //
  299. ListEntry = KiProfileSourceListHead.Flink;
  300. while (ListEntry != &KiProfileSourceListHead) {
  301. CurrentActiveSource = CONTAINING_RECORD(ListEntry,
  302. KACTIVE_PROFILE_SOURCE,
  303. ListEntry);
  304. if (CurrentActiveSource->Source == Profile->Source) {
  305. ActiveSource = CurrentActiveSource;
  306. break;
  307. }
  308. ListEntry = ListEntry->Flink;
  309. }
  310. if (ActiveSource == NULL) {
  311. //
  312. // This source was not found, allocate and initialize a new entry and add
  313. // it to the head of the list.
  314. //
  315. ActiveSource = AllocatedPool;
  316. AllocatedPool = NULL;
  317. RtlZeroMemory(ActiveSource, SourceSize);
  318. ActiveSource->Source = Profile->Source;
  319. InsertHeadList(&KiProfileSourceListHead, &ActiveSource->ListEntry);
  320. if (Profile->Source == ProfileAlignmentFixup) {
  321. KiProfileAlignmentFixup = TRUE;
  322. }
  323. }
  324. //
  325. // Increment the reference counts for each processor in the
  326. // affinity set.
  327. //
  328. AffinitySet = Profile->Affinity;
  329. Reference = &ActiveSource->ProcessorCount[0];
  330. while (AffinitySet != 0) {
  331. if (AffinitySet & 1) {
  332. *Reference = *Reference + 1;
  333. }
  334. AffinitySet = AffinitySet >> 1;
  335. Reference = Reference + 1;
  336. }
  337. //
  338. // Compute the processors which the profile interrupt is
  339. // required and not already started
  340. //
  341. AffinitySet = Profile->Affinity & ~ActiveSource->Affinity;
  342. TargetProcessors = AffinitySet & ~Prcb->SetMember;
  343. //
  344. // Update set of processors on which this source is active.
  345. //
  346. ActiveSource->Affinity |= Profile->Affinity;
  347. }
  348. //
  349. // Release the profile lock, lower IRQL to its previous value, and
  350. // return whether profiling was started.
  351. //
  352. KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
  353. KeLowerIrql(DISPATCH_LEVEL);
  354. //
  355. // Start profile interrupt on pending processors.
  356. //
  357. #if !defined(NT_UP)
  358. if (TargetProcessors != 0) {
  359. KiIpiSendPacket(TargetProcessors,
  360. KiStartProfileInterrupt,
  361. (PVOID)(ULONG_PTR)Profile->Source,
  362. NULL,
  363. NULL);
  364. }
  365. #endif
  366. if (AffinitySet & Prcb->SetMember) {
  367. if (Profile->Source == ProfileAlignmentFixup) {
  368. KiEnableAlignmentExceptions();
  369. }
  370. KfRaiseIrql(KiProfileIrql);
  371. HalStartProfileInterrupt(Profile->Source);
  372. KeLowerIrql(DISPATCH_LEVEL);
  373. }
  374. #if !defined(NT_UP)
  375. if (TargetProcessors != 0) {
  376. KiIpiStallOnPacketTargets(TargetProcessors);
  377. }
  378. #endif
  379. //
  380. // Lower to original IRQL
  381. //
  382. KeLowerIrql(OldIrql);
  383. //
  384. // If the allocated pool was not used, free it now.
  385. //
  386. if (AllocatedPool != NULL) {
  387. ExFreePool(AllocatedPool);
  388. }
  389. return Started;
  390. }
  391. BOOLEAN
  392. KeStopProfile (
  393. IN PKPROFILE Profile
  394. )
  395. /*++
  396. Routine Description:
  397. This function stops profile data gathering on the specified profile
  398. object. The object is marked stopped, and is removed from the active
  399. profile list.
  400. If the number of active profile objects goes to zero, then the profile
  401. interrupt is disabled.
  402. Arguments:
  403. Profile - Supplies a pointer to a control object of type profile.
  404. Return Value:
  405. A value of TRUE is returned if profiling was previously started for
  406. the specified profile object. Otherwise, a value of FALSE is returned.
  407. --*/
  408. {
  409. KIRQL OldIrql;
  410. BOOLEAN Stopped;
  411. KAFFINITY TargetProcessors;
  412. PKPRCB Prcb;
  413. PLIST_ENTRY ListEntry;
  414. PKACTIVE_PROFILE_SOURCE ActiveSource;
  415. PKACTIVE_PROFILE_SOURCE PoolToFree=NULL;
  416. KAFFINITY AffinitySet = 0;
  417. KAFFINITY CurrentProcessor;
  418. PULONG Reference;
  419. ASSERT_PROFILE(Profile);
  420. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  421. //
  422. // Assume object already stopped
  423. //
  424. Stopped = FALSE;
  425. AffinitySet = 0L;
  426. TargetProcessors = 0L;
  427. //
  428. // Raise IRQL to profile level and acquire the profile lock.
  429. //
  430. OldIrql = KfRaiseIrql(KiProfileIrql);
  431. KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
  432. //
  433. // If the specified profile object is not stopped, set stopped to TRUE, set
  434. // the profile object to stopped, remove the profile object object from the
  435. // appropriate profilelist, and stop profile interrupts if the number of
  436. // active profile objects is zero.
  437. //
  438. Prcb = KeGetCurrentPrcb();
  439. if (Profile->Started != FALSE) {
  440. Stopped = TRUE;
  441. Profile->Started = FALSE;
  442. if (!IsListEmpty(&Profile->ProfileListEntry)) {
  443. RemoveEntryList(&Profile->ProfileListEntry);
  444. }
  445. //
  446. // Search the profile source list to find the entry for this
  447. // profile source.
  448. //
  449. ListEntry = KiProfileSourceListHead.Flink;
  450. do {
  451. ASSERT(ListEntry != &KiProfileSourceListHead);
  452. ActiveSource = CONTAINING_RECORD(ListEntry,
  453. KACTIVE_PROFILE_SOURCE,
  454. ListEntry);
  455. ListEntry = ListEntry->Flink;
  456. } while (ActiveSource->Source != Profile->Source);
  457. //
  458. // Decrement the reference counts for each processor in the
  459. // affinity set and build up a mask of the processors that
  460. // now have a reference count of zero.
  461. //
  462. CurrentProcessor = 1;
  463. TargetProcessors = 0;
  464. AffinitySet = Profile->Affinity;
  465. Reference = &ActiveSource->ProcessorCount[0];
  466. while (AffinitySet != 0) {
  467. if (AffinitySet & 1) {
  468. *Reference = *Reference - 1;
  469. if (*Reference == 0) {
  470. TargetProcessors = TargetProcessors | CurrentProcessor;
  471. }
  472. }
  473. AffinitySet = AffinitySet >> 1;
  474. Reference = Reference + 1;
  475. CurrentProcessor = CurrentProcessor << 1;
  476. }
  477. //
  478. // Compute the processors whose profile interrupt reference
  479. // count has dropped to zero.
  480. //
  481. AffinitySet = TargetProcessors;
  482. TargetProcessors = AffinitySet & ~Prcb->SetMember;
  483. //
  484. // Update set of processors on which this source is active.
  485. //
  486. ActiveSource->Affinity &= ~AffinitySet;
  487. //
  488. // Determine whether this profile source is stopped on all
  489. // processors. If so, remove it from the list and free it.
  490. //
  491. if (ActiveSource->Affinity == 0) {
  492. RemoveEntryList(&ActiveSource->ListEntry);
  493. PoolToFree = ActiveSource;
  494. if (Profile->Source == ProfileAlignmentFixup) {
  495. KiProfileAlignmentFixup = FALSE;
  496. }
  497. }
  498. }
  499. //
  500. // Release the profile lock, lower IRQL to its previous value, and
  501. // return whether profiling was stopped.
  502. //
  503. KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
  504. KeLowerIrql(DISPATCH_LEVEL);
  505. //
  506. // Stop profile interrupt on pending processors
  507. //
  508. #if !defined(NT_UP)
  509. if (TargetProcessors != 0) {
  510. KiIpiSendPacket(TargetProcessors,
  511. KiStopProfileInterrupt,
  512. (PVOID)(ULONG_PTR)Profile->Source,
  513. NULL,
  514. NULL);
  515. }
  516. #endif
  517. if (AffinitySet & Prcb->SetMember) {
  518. if (Profile->Source == ProfileAlignmentFixup) {
  519. KiDisableAlignmentExceptions();
  520. }
  521. KfRaiseIrql(KiProfileIrql);
  522. HalStopProfileInterrupt(Profile->Source);
  523. KeLowerIrql(DISPATCH_LEVEL);
  524. }
  525. #if !defined(NT_UP)
  526. if (TargetProcessors != 0) {
  527. KiIpiStallOnPacketTargets(TargetProcessors);
  528. }
  529. #endif
  530. //
  531. // Lower to original IRQL
  532. //
  533. KeLowerIrql(OldIrql);
  534. //
  535. // Now that IRQL has been lowered, free the profile source if
  536. // necessary.
  537. //
  538. if (PoolToFree != NULL) {
  539. ExFreePool(PoolToFree);
  540. }
  541. return Stopped;
  542. }
  543. #if !defined(NT_UP)
  544. VOID
  545. KiStopProfileInterrupt (
  546. IN PKIPI_CONTEXT SignalDone,
  547. IN PVOID Parameter1,
  548. IN PVOID Parameter2,
  549. IN PVOID Parameter3
  550. )
  551. /*++
  552. Routine Description:
  553. This is the target function for stopping the profile interrupt on target
  554. processors.
  555. Arguments:
  556. SignalDone - Supplies a pointer to a variable that is cleared when the
  557. requested operation has been performed
  558. Parameter1 - Supplies the profile source
  559. Parameter2 - Parameter3 - not used
  560. Return Value:
  561. None.
  562. --*/
  563. {
  564. KIRQL OldIrql;
  565. KPROFILE_SOURCE ProfileSource;
  566. UNREFERENCED_PARAMETER(Parameter2);
  567. UNREFERENCED_PARAMETER(Parameter3);
  568. //
  569. // Stop the profile interrupt on the current processor and clear the
  570. // data cache packet address to signal the source to continue.
  571. //
  572. ProfileSource = (KPROFILE_SOURCE)PtrToUlong(Parameter1);
  573. if (ProfileSource == ProfileAlignmentFixup) {
  574. KiDisableAlignmentExceptions();
  575. }
  576. OldIrql = KeGetCurrentIrql();
  577. if (OldIrql < KiProfileIrql) {
  578. KfRaiseIrql(KiProfileIrql);
  579. }
  580. HalStopProfileInterrupt(ProfileSource);
  581. KeLowerIrql(OldIrql);
  582. KiIpiSignalPacketDone(SignalDone);
  583. return;
  584. }
  585. VOID
  586. KiStartProfileInterrupt (
  587. IN PKIPI_CONTEXT SignalDone,
  588. IN PVOID Parameter1,
  589. IN PVOID Parameter2,
  590. IN PVOID Parameter3
  591. )
  592. /*++
  593. Routine Description:
  594. This is the target function for stopping the profile interrupt on target
  595. processors.
  596. Arguments:
  597. SignalDone - Supplies a pointer to a variable that is cleared when the
  598. requested operation has been performed
  599. Parameter1 - Supplies the profile source
  600. Parameter2 - Parameter3 - not used
  601. Return Value:
  602. None.
  603. --*/
  604. {
  605. KIRQL OldIrql;
  606. KPROFILE_SOURCE ProfileSource;
  607. UNREFERENCED_PARAMETER(Parameter2);
  608. UNREFERENCED_PARAMETER(Parameter3);
  609. //
  610. // Start the profile interrupt on the current processor and clear the
  611. // data cache packet address to signal the source to continue.
  612. //
  613. ProfileSource = (KPROFILE_SOURCE)PtrToUlong(Parameter1);
  614. if (ProfileSource == ProfileAlignmentFixup) {
  615. KiEnableAlignmentExceptions();
  616. }
  617. OldIrql = KeGetCurrentIrql();
  618. if (OldIrql < KiProfileIrql) {
  619. KfRaiseIrql(KiProfileIrql);
  620. }
  621. HalStartProfileInterrupt(ProfileSource);
  622. KeLowerIrql(OldIrql);
  623. KiIpiSignalPacketDone(SignalDone);
  624. return;
  625. }
  626. #endif