2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/profile.c
5 * PURPOSE: Kernel Profiling
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
10 /* INCLUDES *****************************************************************/
13 #include <internal/debug.h>
15 /* GLOBALS *******************************************************************/
17 KIRQL KiProfileIrql
= PROFILE_LEVEL
;
18 LIST_ENTRY KiProfileListHead
;
19 LIST_ENTRY KiProfileSourceListHead
;
20 KSPIN_LOCK KiProfileLock
;
21 ULONG KiProfileTimeInterval
= 78125; /* Default resolution 7.8ms (sysinternals) */
23 /* FUNCTIONS *****************************************************************/
27 KeInitializeProfile(PKPROFILE Profile
,
32 KPROFILE_SOURCE ProfileSource
,
35 /* Initialize the Header */
36 Profile
->Type
= ProfileObject
;
37 Profile
->Size
= sizeof(KPROFILE
);
39 /* Copy all the settings we were given */
40 Profile
->Process
= Process
;
41 Profile
->RegionStart
= ImageBase
;
42 Profile
->BucketShift
= BucketSize
- 2; /* See ntinternals.net -- Alex */
43 Profile
->RegionEnd
= (PVOID
)(ULONG_PTR
)ImageBase
+ ImageSize
;
44 Profile
->Active
= FALSE
;
45 Profile
->Source
= ProfileSource
;
46 Profile
->Affinity
= Affinity
;
51 KeStartProfile(PKPROFILE Profile
,
55 PKPROFILE_SOURCE_OBJECT SourceBuffer
;
56 PKPROFILE_SOURCE_OBJECT Source
= NULL
;
57 PKPROFILE_SOURCE_OBJECT CurrentSource
;
58 BOOLEAN FreeBuffer
= TRUE
;
59 PKPROCESS ProfileProcess
;
60 PLIST_ENTRY ListEntry
;
62 /* Allocate a buffer first, before we raise IRQL */
63 SourceBuffer
= ExAllocatePoolWithTag(NonPagedPool
,
64 sizeof(KPROFILE_SOURCE_OBJECT
),
65 TAG('P', 'r', 'o', 'f'));
66 RtlZeroMemory(Source
, sizeof(KPROFILE_SOURCE_OBJECT
));
68 /* Raise to PROFILE_LEVEL */
69 KeRaiseIrql(PROFILE_LEVEL
, &OldIrql
);
70 KeAcquireSpinLockAtDpcLevel(&KiProfileLock
);
72 /* Make sure it's not running */
73 if (!Profile
->Active
) {
75 /* Set it as active */
76 Profile
->Buffer
= Buffer
;
77 Profile
->Active
= TRUE
;
79 /* Get the process, if any */
80 ProfileProcess
= Profile
->Process
;
82 /* Insert it into the Process List or Global List */
85 InsertTailList(&ProfileProcess
->ProfileListHead
, &Profile
->ListEntry
);
89 InsertTailList(&KiProfileListHead
, &Profile
->ListEntry
);
92 /* Check if this type of profile (source) is already running */
93 for (ListEntry
= KiProfileSourceListHead
.Flink
;
94 ListEntry
!= &KiProfileSourceListHead
;
95 ListEntry
= ListEntry
->Flink
) {
97 /* Get the Source Object */
98 CurrentSource
= CONTAINING_RECORD(ListEntry
,
99 KPROFILE_SOURCE_OBJECT
,
102 /* Check if it's the same as the one being requested now */
103 if (CurrentSource
->Source
== Profile
->Source
) {
105 Source
= CurrentSource
;
110 /* See if the loop found something */
113 /* Nothing found, use our allocated buffer */
114 Source
= SourceBuffer
;
116 /* Set up the Source Object */
117 Source
->Source
= Profile
->Source
;
118 InsertHeadList(&KiProfileSourceListHead
, &Source
->ListEntry
);
120 /* Don't free the pool later on */
126 KeReleaseSpinLockFromDpcLevel(&KiProfileLock
);
127 KeLowerIrql(OldIrql
);
129 /* FIXME: Tell HAL to Start the Profile Interrupt */
130 //HalStartProfileInterrupt(Profile->Source);
133 if (!FreeBuffer
) ExFreePool(SourceBuffer
);
138 KeStopProfile(PKPROFILE Profile
)
141 PLIST_ENTRY ListEntry
;
142 PKPROFILE_SOURCE_OBJECT CurrentSource
= NULL
;
144 /* Raise to PROFILE_LEVEL and acquire spinlock */
145 KeRaiseIrql(PROFILE_LEVEL
, &OldIrql
);
146 KeAcquireSpinLockAtDpcLevel(&KiProfileLock
);
148 /* Make sure it's running */
149 if (Profile
->Active
) {
151 /* Remove it from the list and disable */
152 RemoveEntryList(&Profile
->ListEntry
);
153 Profile
->Active
= FALSE
;
155 /* Find the Source Object */
156 for (ListEntry
= KiProfileSourceListHead
.Flink
;
157 CurrentSource
->Source
!= Profile
->Source
;
158 ListEntry
= ListEntry
->Flink
) {
160 /* Get the Source Object */
161 CurrentSource
= CONTAINING_RECORD(ListEntry
,
162 KPROFILE_SOURCE_OBJECT
,
167 RemoveEntryList(&CurrentSource
->ListEntry
);
171 KeReleaseSpinLockFromDpcLevel(&KiProfileLock
);
172 KeLowerIrql(OldIrql
);
174 /* Stop Profiling. FIXME: Implement in HAL */
175 //HalStopProfileInterrupt(Profile->Source);
177 /* Free the Source Object */
178 if (CurrentSource
) ExFreePool(CurrentSource
);
183 KeQueryIntervalProfile(KPROFILE_SOURCE ProfileSource
)
185 /* Check if this is the timer profile */
186 if (ProfileSource
== ProfileTime
) {
188 /* Return the good old 100ns sampling interval */
189 return KiProfileTimeInterval
;
193 /* Request it from HAL. FIXME: What structure is used? */
194 HalQuerySystemInformation(HalProfileSourceInformation
,
205 KeSetIntervalProfile(KPROFILE_SOURCE ProfileSource
,
208 /* Check if this is the timer profile */
209 if (ProfileSource
== ProfileTime
) {
211 /* Set the good old 100ns sampling interval */
212 KiProfileTimeInterval
= Interval
;
216 /* Set it with HAL. FIXME: What structure is used? */
217 HalSetSystemInformation(HalProfileSourceInformation
,
229 KeProfileInterrupt(PKTRAP_FRAME TrapFrame
)
231 /* Called from HAL for Timer Profiling */
232 KeProfileInterruptWithSource(TrapFrame
, ProfileTime
);
237 KiParseProfileList(IN PKTRAP_FRAME TrapFrame
,
238 IN KPROFILE_SOURCE Source
,
239 IN PLIST_ENTRY ListHead
)
243 PLIST_ENTRY NextEntry
;
246 for (NextEntry
= ListHead
->Flink
; NextEntry
!= ListHead
; NextEntry
= NextEntry
->Flink
) {
248 /* Get the Current Profile in the List */
249 Profile
= CONTAINING_RECORD(NextEntry
, KPROFILE
, ListEntry
);
251 /* Check if the source is good, and if it's within the range */
252 if ((Profile
->Source
!= Source
) ||
253 (TrapFrame
->Eip
< (ULONG_PTR
)Profile
->RegionStart
) ||
254 (TrapFrame
->Eip
> (ULONG_PTR
)Profile
->RegionEnd
)) {
259 /* Get the Pointer to the Bucket Value representing this EIP */
260 BucketValue
= (PULONG
)(((ULONG_PTR
)(Profile
->Buffer
+
261 (TrapFrame
->Eip
- (ULONG_PTR
)Profile
->RegionStart
))
262 >> Profile
->BucketShift
) &~ 0x3);
264 /* Increment the value */
273 * Called from HAL, this function looks up the process
274 * entries, finds the proper source object, verifies the
275 * ranges with the trapframe data, and inserts the information
276 * from the trap frame into the buffer, while using buckets and
277 * shifting like we specified. -- Alex
281 KeProfileInterruptWithSource(IN PKTRAP_FRAME TrapFrame
,
282 IN KPROFILE_SOURCE Source
)
284 PKPROCESS Process
= KeGetCurrentThread()->ApcState
.Process
;
286 /* We have to parse 2 lists. Per-Process and System-Wide */
287 KiParseProfileList(TrapFrame
, Source
, &Process
->ProfileListHead
);
288 KiParseProfileList(TrapFrame
, Source
, &KiProfileListHead
);
296 KeSetProfileIrql(IN KIRQL ProfileIrql
)
298 /* Set the IRQL at which Profiling will run */
299 KiProfileIrql
= ProfileIrql
;