2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ke/profobj.c
5 * PURPOSE: Kernel Profiling
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
9 /* INCLUDES *****************************************************************/
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) */
22 ULONG KiProfileAlignmentFixupInterval
;
24 /* FUNCTIONS *****************************************************************/
28 KeInitializeProfile(PKPROFILE Profile
,
33 KPROFILE_SOURCE ProfileSource
,
36 /* Initialize the Header */
37 Profile
->Type
= ProfileObject
;
38 Profile
->Size
= sizeof(KPROFILE
);
40 /* Copy all the settings we were given */
41 Profile
->Process
= Process
;
42 Profile
->RangeBase
= ImageBase
;
43 Profile
->BucketShift
= BucketSize
- 2; /* See ntinternals.net -- Alex */
44 Profile
->RangeLimit
= (PVOID
)((ULONG_PTR
)ImageBase
+ ImageSize
);
45 Profile
->Started
= FALSE
;
46 Profile
->Source
= ProfileSource
;
47 Profile
->Affinity
= Affinity
;
52 KeStartProfile(IN PKPROFILE Profile
,
56 PKPROFILE_SOURCE_OBJECT SourceBuffer
;
57 PKPROFILE_SOURCE_OBJECT CurrentSource
;
58 BOOLEAN FreeBuffer
= TRUE
, SourceFound
= FALSE
, StartedProfile
;
59 PKPROCESS ProfileProcess
;
60 PLIST_ENTRY NextEntry
;
62 /* Allocate a buffer first, before we raise IRQL */
63 SourceBuffer
= ExAllocatePoolWithTag(NonPagedPool
,
64 sizeof(KPROFILE_SOURCE_OBJECT
),
66 if (!SourceBuffer
) return FALSE
;
67 RtlZeroMemory(SourceBuffer
, sizeof(KPROFILE_SOURCE_OBJECT
));
69 /* Raise to profile IRQL and acquire the profile lock */
70 KeRaiseIrql(KiProfileIrql
, &OldIrql
);
71 KeAcquireSpinLockAtDpcLevel(&KiProfileLock
);
73 /* Make sure it's not running */
74 if (!Profile
->Started
)
76 /* Set it as Started */
77 Profile
->Buffer
= Buffer
;
78 Profile
->Started
= TRUE
;
79 StartedProfile
= TRUE
;
81 /* Get the process, if any */
82 ProfileProcess
= Profile
->Process
;
84 /* Check where we should insert it */
87 /* Insert it into the Process List */
88 InsertTailList(&ProfileProcess
->ProfileListHead
, &Profile
->ProfileListEntry
);
92 /* Insert it into the Global List */
93 InsertTailList(&KiProfileListHead
, &Profile
->ProfileListEntry
);
97 for (NextEntry
= KiProfileSourceListHead
.Flink
;
98 NextEntry
!= &KiProfileSourceListHead
;
99 NextEntry
= NextEntry
->Flink
)
102 CurrentSource
= CONTAINING_RECORD(NextEntry
,
103 KPROFILE_SOURCE_OBJECT
,
106 /* Check if it's the same as the one being requested now */
107 if (CurrentSource
->Source
== Profile
->Source
)
109 /* It is, break out */
115 /* See if the loop found something */
118 /* Nothing found, use our allocated buffer */
119 CurrentSource
= SourceBuffer
;
121 /* Set up the Source Object */
122 CurrentSource
->Source
= Profile
->Source
;
123 InsertHeadList(&KiProfileSourceListHead
, &CurrentSource
->ListEntry
);
125 /* Don't free the pool later on */
131 /* Already running so nothing to start */
132 StartedProfile
= FALSE
;
135 /* Release the profile lock */
136 KeReleaseSpinLockFromDpcLevel(&KiProfileLock
);
138 /* Tell HAL to start the profile interrupt */
139 HalStartProfileInterrupt(Profile
->Source
);
141 /* Lower back to original IRQL */
142 KeLowerIrql(OldIrql
);
145 if (FreeBuffer
) ExFreePoolWithTag(SourceBuffer
, 'forP');
147 /* Return whether we could start the profile */
148 return StartedProfile
;
153 KeStopProfile(IN PKPROFILE Profile
)
156 PKPROFILE_SOURCE_OBJECT CurrentSource
= NULL
;
157 PLIST_ENTRY NextEntry
;
158 BOOLEAN SourceFound
= FALSE
, StoppedProfile
;
160 /* Raise to profile IRQL and acquire the profile lock */
161 KeRaiseIrql(KiProfileIrql
, &OldIrql
);
162 KeAcquireSpinLockAtDpcLevel(&KiProfileLock
);
164 /* Make sure it's running */
165 if (Profile
->Started
)
167 /* Remove it from the list and disable */
168 RemoveEntryList(&Profile
->ProfileListEntry
);
169 Profile
->Started
= FALSE
;
170 StoppedProfile
= TRUE
;
173 for (NextEntry
= KiProfileSourceListHead
.Flink
;
174 NextEntry
!= &KiProfileSourceListHead
;
175 NextEntry
= NextEntry
->Flink
)
178 CurrentSource
= CONTAINING_RECORD(NextEntry
,
179 KPROFILE_SOURCE_OBJECT
,
182 /* Check if this is the Source Object */
183 if (CurrentSource
->Source
== Profile
->Source
)
185 /* Remember we found one */
188 /* Remove it and break out */
189 RemoveEntryList(&CurrentSource
->ListEntry
);
198 StoppedProfile
= FALSE
;
201 /* Release the profile lock */
202 KeReleaseSpinLockFromDpcLevel(&KiProfileLock
);
204 /* Stop the profile interrupt */
205 HalStopProfileInterrupt(Profile
->Source
);
207 /* Lower back to original IRQL */
208 KeLowerIrql(OldIrql
);
210 /* Free the Source Object */
211 if (SourceFound
) ExFreePool(CurrentSource
);
213 /* Return whether we could stop the profile */
214 return StoppedProfile
;
219 KeQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource
)
221 HAL_PROFILE_SOURCE_INFORMATION ProfileSourceInformation
;
222 ULONG ReturnLength
, Interval
;
225 /* Check what profile this is */
226 if (ProfileSource
== ProfileTime
)
228 /* Return the time interval */
229 Interval
= KiProfileTimeInterval
;
231 else if (ProfileSource
== ProfileAlignmentFixup
)
233 /* Return the alignment interval */
234 Interval
= KiProfileAlignmentFixupInterval
;
238 /* Request it from HAL */
239 ProfileSourceInformation
.Source
= ProfileSource
;
240 Status
= HalQuerySystemInformation(HalProfileSourceInformation
,
241 sizeof(HAL_PROFILE_SOURCE_INFORMATION
),
242 &ProfileSourceInformation
,
245 /* Check if HAL handled it and supports this profile */
246 if (NT_SUCCESS(Status
) && (ProfileSourceInformation
.Supported
))
248 /* Get the interval */
249 Interval
= ProfileSourceInformation
.Interval
;
253 /* Unsupported or invalid source, fail */
258 /* Return the interval we got */
264 KeSetIntervalProfile(IN ULONG Interval
,
265 IN KPROFILE_SOURCE ProfileSource
)
267 HAL_PROFILE_SOURCE_INTERVAL ProfileSourceInterval
;
269 /* Check what profile this is */
270 if (ProfileSource
== ProfileTime
)
272 /* Set the interval through HAL */
273 KiProfileTimeInterval
= (ULONG
)HalSetProfileInterval(Interval
);
275 else if (ProfileSource
== ProfileAlignmentFixup
)
277 /* Set the alignment interval */
278 KiProfileAlignmentFixupInterval
= Interval
;
282 /* HAL handles any other interval */
283 ProfileSourceInterval
.Source
= ProfileSource
;
284 ProfileSourceInterval
.Interval
= Interval
;
285 HalSetSystemInformation(HalProfileSourceInterval
,
286 sizeof(HAL_PROFILE_SOURCE_INTERVAL
),
287 &ProfileSourceInterval
);
296 KeProfileInterrupt(IN PKTRAP_FRAME TrapFrame
)
298 /* Called from HAL for Timer Profiling */
299 KeProfileInterruptWithSource(TrapFrame
, ProfileTime
);
304 KiParseProfileList(IN PKTRAP_FRAME TrapFrame
,
305 IN KPROFILE_SOURCE Source
,
306 IN PLIST_ENTRY ListHead
)
310 PLIST_ENTRY NextEntry
;
311 ULONG_PTR ProgramCounter
;
313 /* Get the Program Counter */
314 ProgramCounter
= KeGetTrapFramePc(TrapFrame
);
317 for (NextEntry
= ListHead
->Flink
;
318 NextEntry
!= ListHead
;
319 NextEntry
= NextEntry
->Flink
)
322 Profile
= CONTAINING_RECORD(NextEntry
, KPROFILE
, ProfileListEntry
);
324 /* Check if the source is good, and if it's within the range */
325 if ((Profile
->Source
!= Source
) ||
326 (ProgramCounter
< (ULONG_PTR
)Profile
->RangeBase
) ||
327 (ProgramCounter
> (ULONG_PTR
)Profile
->RangeLimit
))
332 /* Get the Pointer to the Bucket Value representing this Program Counter */
333 BucketValue
= (PULONG
)((ULONG_PTR
)Profile
->Buffer
+
334 (((ProgramCounter
- (ULONG_PTR
)Profile
->RangeBase
)
335 >> Profile
->BucketShift
) &~ 0x3));
337 /* Increment the value */
346 * Called from HAL, this function looks up the process
347 * entries, finds the proper source object, verifies the
348 * ranges with the trapframe data, and inserts the information
349 * from the trap frame into the buffer, while using buckets and
350 * shifting like we specified. -- Alex
354 KeProfileInterruptWithSource(IN PKTRAP_FRAME TrapFrame
,
355 IN KPROFILE_SOURCE Source
)
357 PKPROCESS Process
= KeGetCurrentThread()->ApcState
.Process
;
359 /* We have to parse 2 lists. Per-Process and System-Wide */
360 KiParseProfileList(TrapFrame
, Source
, &Process
->ProfileListHead
);
361 KiParseProfileList(TrapFrame
, Source
, &KiProfileListHead
);
369 KeSetProfileIrql(IN KIRQL ProfileIrql
)
371 /* Set the IRQL at which Profiling will run */
372 KiProfileIrql
= ProfileIrql
;