xref: /reactos/ntoskrnl/ke/profobj.c (revision c2c66aff)
1*c2c66affSColin Finck /*
2*c2c66affSColin Finck  * COPYRIGHT:       GPL - See COPYING in the top level directory
3*c2c66affSColin Finck  * PROJECT:         ReactOS Kernel
4*c2c66affSColin Finck  * FILE:            ntoskrnl/ke/profobj.c
5*c2c66affSColin Finck  * PURPOSE:         Kernel Profiling
6*c2c66affSColin Finck  * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
7*c2c66affSColin Finck  */
8*c2c66affSColin Finck 
9*c2c66affSColin Finck /* INCLUDES *****************************************************************/
10*c2c66affSColin Finck 
11*c2c66affSColin Finck #include <ntoskrnl.h>
12*c2c66affSColin Finck #define NDEBUG
13*c2c66affSColin Finck #include <debug.h>
14*c2c66affSColin Finck 
15*c2c66affSColin Finck /* GLOBALS *******************************************************************/
16*c2c66affSColin Finck 
17*c2c66affSColin Finck KIRQL KiProfileIrql = PROFILE_LEVEL;
18*c2c66affSColin Finck LIST_ENTRY KiProfileListHead;
19*c2c66affSColin Finck LIST_ENTRY KiProfileSourceListHead;
20*c2c66affSColin Finck KSPIN_LOCK KiProfileLock;
21*c2c66affSColin Finck ULONG KiProfileTimeInterval = 78125; /* Default resolution 7.8ms (sysinternals) */
22*c2c66affSColin Finck ULONG KiProfileAlignmentFixupInterval;
23*c2c66affSColin Finck 
24*c2c66affSColin Finck /* FUNCTIONS *****************************************************************/
25*c2c66affSColin Finck 
26*c2c66affSColin Finck VOID
27*c2c66affSColin Finck NTAPI
KeInitializeProfile(PKPROFILE Profile,PKPROCESS Process,PVOID ImageBase,SIZE_T ImageSize,ULONG BucketSize,KPROFILE_SOURCE ProfileSource,KAFFINITY Affinity)28*c2c66affSColin Finck KeInitializeProfile(PKPROFILE Profile,
29*c2c66affSColin Finck                     PKPROCESS Process,
30*c2c66affSColin Finck                     PVOID ImageBase,
31*c2c66affSColin Finck                     SIZE_T ImageSize,
32*c2c66affSColin Finck                     ULONG BucketSize,
33*c2c66affSColin Finck                     KPROFILE_SOURCE ProfileSource,
34*c2c66affSColin Finck                     KAFFINITY Affinity)
35*c2c66affSColin Finck {
36*c2c66affSColin Finck     /* Initialize the Header */
37*c2c66affSColin Finck     Profile->Type = ProfileObject;
38*c2c66affSColin Finck     Profile->Size = sizeof(KPROFILE);
39*c2c66affSColin Finck 
40*c2c66affSColin Finck     /* Copy all the settings we were given */
41*c2c66affSColin Finck     Profile->Process = Process;
42*c2c66affSColin Finck     Profile->RangeBase = ImageBase;
43*c2c66affSColin Finck     Profile->BucketShift = BucketSize - 2; /* See ntinternals.net -- Alex */
44*c2c66affSColin Finck     Profile->RangeLimit = (PVOID)((ULONG_PTR)ImageBase + ImageSize);
45*c2c66affSColin Finck     Profile->Started = FALSE;
46*c2c66affSColin Finck     Profile->Source = ProfileSource;
47*c2c66affSColin Finck     Profile->Affinity = Affinity;
48*c2c66affSColin Finck }
49*c2c66affSColin Finck 
50*c2c66affSColin Finck BOOLEAN
51*c2c66affSColin Finck NTAPI
KeStartProfile(IN PKPROFILE Profile,IN PVOID Buffer)52*c2c66affSColin Finck KeStartProfile(IN PKPROFILE Profile,
53*c2c66affSColin Finck                IN PVOID Buffer)
54*c2c66affSColin Finck {
55*c2c66affSColin Finck     KIRQL OldIrql;
56*c2c66affSColin Finck     PKPROFILE_SOURCE_OBJECT SourceBuffer;
57*c2c66affSColin Finck     PKPROFILE_SOURCE_OBJECT CurrentSource;
58*c2c66affSColin Finck     BOOLEAN FreeBuffer = TRUE, SourceFound = FALSE, StartedProfile;
59*c2c66affSColin Finck     PKPROCESS ProfileProcess;
60*c2c66affSColin Finck     PLIST_ENTRY NextEntry;
61*c2c66affSColin Finck 
62*c2c66affSColin Finck     /* Allocate a buffer first, before we raise IRQL */
63*c2c66affSColin Finck     SourceBuffer = ExAllocatePoolWithTag(NonPagedPool,
64*c2c66affSColin Finck                                          sizeof(KPROFILE_SOURCE_OBJECT),
65*c2c66affSColin Finck                                          'forP');
66*c2c66affSColin Finck     if (!SourceBuffer) return FALSE;
67*c2c66affSColin Finck     RtlZeroMemory(SourceBuffer, sizeof(KPROFILE_SOURCE_OBJECT));
68*c2c66affSColin Finck 
69*c2c66affSColin Finck     /* Raise to profile IRQL and acquire the profile lock */
70*c2c66affSColin Finck     KeRaiseIrql(KiProfileIrql, &OldIrql);
71*c2c66affSColin Finck     KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
72*c2c66affSColin Finck 
73*c2c66affSColin Finck     /* Make sure it's not running */
74*c2c66affSColin Finck     if (!Profile->Started)
75*c2c66affSColin Finck     {
76*c2c66affSColin Finck         /* Set it as Started */
77*c2c66affSColin Finck         Profile->Buffer = Buffer;
78*c2c66affSColin Finck         Profile->Started = TRUE;
79*c2c66affSColin Finck         StartedProfile = TRUE;
80*c2c66affSColin Finck 
81*c2c66affSColin Finck         /* Get the process, if any */
82*c2c66affSColin Finck         ProfileProcess = Profile->Process;
83*c2c66affSColin Finck 
84*c2c66affSColin Finck         /* Check where we should insert it */
85*c2c66affSColin Finck         if (ProfileProcess)
86*c2c66affSColin Finck         {
87*c2c66affSColin Finck             /* Insert it into the Process List */
88*c2c66affSColin Finck             InsertTailList(&ProfileProcess->ProfileListHead, &Profile->ProfileListEntry);
89*c2c66affSColin Finck         }
90*c2c66affSColin Finck         else
91*c2c66affSColin Finck         {
92*c2c66affSColin Finck             /* Insert it into the Global List */
93*c2c66affSColin Finck             InsertTailList(&KiProfileListHead, &Profile->ProfileListEntry);
94*c2c66affSColin Finck         }
95*c2c66affSColin Finck 
96*c2c66affSColin Finck         /* Start looping */
97*c2c66affSColin Finck         for (NextEntry = KiProfileSourceListHead.Flink;
98*c2c66affSColin Finck              NextEntry != &KiProfileSourceListHead;
99*c2c66affSColin Finck              NextEntry = NextEntry->Flink)
100*c2c66affSColin Finck         {
101*c2c66affSColin Finck             /* Get the entry */
102*c2c66affSColin Finck             CurrentSource = CONTAINING_RECORD(NextEntry,
103*c2c66affSColin Finck                                               KPROFILE_SOURCE_OBJECT,
104*c2c66affSColin Finck                                               ListEntry);
105*c2c66affSColin Finck 
106*c2c66affSColin Finck             /* Check if it's the same as the one being requested now */
107*c2c66affSColin Finck             if (CurrentSource->Source == Profile->Source)
108*c2c66affSColin Finck             {
109*c2c66affSColin Finck                 /* It is, break out */
110*c2c66affSColin Finck                 SourceFound = TRUE;
111*c2c66affSColin Finck                 break;
112*c2c66affSColin Finck             }
113*c2c66affSColin Finck         }
114*c2c66affSColin Finck 
115*c2c66affSColin Finck         /* See if the loop found something */
116*c2c66affSColin Finck         if (!SourceFound)
117*c2c66affSColin Finck         {
118*c2c66affSColin Finck             /* Nothing found, use our allocated buffer */
119*c2c66affSColin Finck             CurrentSource = SourceBuffer;
120*c2c66affSColin Finck 
121*c2c66affSColin Finck             /* Set up the Source Object */
122*c2c66affSColin Finck             CurrentSource->Source = Profile->Source;
123*c2c66affSColin Finck             InsertHeadList(&KiProfileSourceListHead, &CurrentSource->ListEntry);
124*c2c66affSColin Finck 
125*c2c66affSColin Finck             /* Don't free the pool later on */
126*c2c66affSColin Finck             FreeBuffer = FALSE;
127*c2c66affSColin Finck         }
128*c2c66affSColin Finck     }
129*c2c66affSColin Finck     else
130*c2c66affSColin Finck     {
131*c2c66affSColin Finck         /* Already running so nothing to start */
132*c2c66affSColin Finck         StartedProfile = FALSE;
133*c2c66affSColin Finck     }
134*c2c66affSColin Finck 
135*c2c66affSColin Finck     /* Release the profile lock */
136*c2c66affSColin Finck     KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
137*c2c66affSColin Finck 
138*c2c66affSColin Finck     /* Tell HAL to start the profile interrupt */
139*c2c66affSColin Finck     HalStartProfileInterrupt(Profile->Source);
140*c2c66affSColin Finck 
141*c2c66affSColin Finck     /* Lower back to original IRQL */
142*c2c66affSColin Finck     KeLowerIrql(OldIrql);
143*c2c66affSColin Finck 
144*c2c66affSColin Finck     /* Free the pool */
145*c2c66affSColin Finck     if (FreeBuffer) ExFreePoolWithTag(SourceBuffer, 'forP');
146*c2c66affSColin Finck 
147*c2c66affSColin Finck     /* Return whether we could start the profile */
148*c2c66affSColin Finck     return StartedProfile;
149*c2c66affSColin Finck }
150*c2c66affSColin Finck 
151*c2c66affSColin Finck BOOLEAN
152*c2c66affSColin Finck NTAPI
KeStopProfile(IN PKPROFILE Profile)153*c2c66affSColin Finck KeStopProfile(IN PKPROFILE Profile)
154*c2c66affSColin Finck {
155*c2c66affSColin Finck     KIRQL OldIrql;
156*c2c66affSColin Finck     PKPROFILE_SOURCE_OBJECT CurrentSource = NULL;
157*c2c66affSColin Finck     PLIST_ENTRY NextEntry;
158*c2c66affSColin Finck     BOOLEAN SourceFound = FALSE, StoppedProfile;
159*c2c66affSColin Finck 
160*c2c66affSColin Finck     /* Raise to profile IRQL and acquire the profile lock */
161*c2c66affSColin Finck     KeRaiseIrql(KiProfileIrql, &OldIrql);
162*c2c66affSColin Finck     KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
163*c2c66affSColin Finck 
164*c2c66affSColin Finck     /* Make sure it's running */
165*c2c66affSColin Finck     if (Profile->Started)
166*c2c66affSColin Finck     {
167*c2c66affSColin Finck         /* Remove it from the list and disable */
168*c2c66affSColin Finck         RemoveEntryList(&Profile->ProfileListEntry);
169*c2c66affSColin Finck         Profile->Started = FALSE;
170*c2c66affSColin Finck         StoppedProfile = TRUE;
171*c2c66affSColin Finck 
172*c2c66affSColin Finck         /* Start looping */
173*c2c66affSColin Finck         for (NextEntry = KiProfileSourceListHead.Flink;
174*c2c66affSColin Finck              NextEntry != &KiProfileSourceListHead;
175*c2c66affSColin Finck              NextEntry = NextEntry->Flink)
176*c2c66affSColin Finck         {
177*c2c66affSColin Finck             /* Get the entry */
178*c2c66affSColin Finck             CurrentSource = CONTAINING_RECORD(NextEntry,
179*c2c66affSColin Finck                                               KPROFILE_SOURCE_OBJECT,
180*c2c66affSColin Finck                                               ListEntry);
181*c2c66affSColin Finck 
182*c2c66affSColin Finck             /* Check if this is the Source Object */
183*c2c66affSColin Finck             if (CurrentSource->Source == Profile->Source)
184*c2c66affSColin Finck             {
185*c2c66affSColin Finck                 /* Remember we found one */
186*c2c66affSColin Finck                 SourceFound = TRUE;
187*c2c66affSColin Finck 
188*c2c66affSColin Finck                 /* Remove it and break out */
189*c2c66affSColin Finck                 RemoveEntryList(&CurrentSource->ListEntry);
190*c2c66affSColin Finck                 break;
191*c2c66affSColin Finck             }
192*c2c66affSColin Finck         }
193*c2c66affSColin Finck 
194*c2c66affSColin Finck     }
195*c2c66affSColin Finck     else
196*c2c66affSColin Finck     {
197*c2c66affSColin Finck         /* It wasn't! */
198*c2c66affSColin Finck         StoppedProfile = FALSE;
199*c2c66affSColin Finck     }
200*c2c66affSColin Finck 
201*c2c66affSColin Finck     /* Release the profile lock */
202*c2c66affSColin Finck     KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
203*c2c66affSColin Finck 
204*c2c66affSColin Finck     /* Stop the profile interrupt */
205*c2c66affSColin Finck     HalStopProfileInterrupt(Profile->Source);
206*c2c66affSColin Finck 
207*c2c66affSColin Finck     /* Lower back to original IRQL */
208*c2c66affSColin Finck     KeLowerIrql(OldIrql);
209*c2c66affSColin Finck 
210*c2c66affSColin Finck     /* Free the Source Object */
211*c2c66affSColin Finck     if (SourceFound) ExFreePool(CurrentSource);
212*c2c66affSColin Finck 
213*c2c66affSColin Finck     /* Return whether we could stop the profile */
214*c2c66affSColin Finck     return StoppedProfile;
215*c2c66affSColin Finck }
216*c2c66affSColin Finck 
217*c2c66affSColin Finck ULONG
218*c2c66affSColin Finck NTAPI
KeQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource)219*c2c66affSColin Finck KeQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource)
220*c2c66affSColin Finck {
221*c2c66affSColin Finck     HAL_PROFILE_SOURCE_INFORMATION ProfileSourceInformation;
222*c2c66affSColin Finck     ULONG ReturnLength, Interval;
223*c2c66affSColin Finck     NTSTATUS Status;
224*c2c66affSColin Finck 
225*c2c66affSColin Finck     /* Check what profile this is */
226*c2c66affSColin Finck     if (ProfileSource == ProfileTime)
227*c2c66affSColin Finck     {
228*c2c66affSColin Finck         /* Return the time interval */
229*c2c66affSColin Finck         Interval = KiProfileTimeInterval;
230*c2c66affSColin Finck     }
231*c2c66affSColin Finck     else if (ProfileSource == ProfileAlignmentFixup)
232*c2c66affSColin Finck     {
233*c2c66affSColin Finck         /* Return the alignment interval */
234*c2c66affSColin Finck         Interval = KiProfileAlignmentFixupInterval;
235*c2c66affSColin Finck     }
236*c2c66affSColin Finck     else
237*c2c66affSColin Finck     {
238*c2c66affSColin Finck         /* Request it from HAL */
239*c2c66affSColin Finck         ProfileSourceInformation.Source = ProfileSource;
240*c2c66affSColin Finck         Status = HalQuerySystemInformation(HalProfileSourceInformation,
241*c2c66affSColin Finck                                            sizeof(HAL_PROFILE_SOURCE_INFORMATION),
242*c2c66affSColin Finck                                            &ProfileSourceInformation,
243*c2c66affSColin Finck                                            &ReturnLength);
244*c2c66affSColin Finck 
245*c2c66affSColin Finck         /* Check if HAL handled it and supports this profile */
246*c2c66affSColin Finck         if (NT_SUCCESS(Status) && (ProfileSourceInformation.Supported))
247*c2c66affSColin Finck         {
248*c2c66affSColin Finck             /* Get the interval */
249*c2c66affSColin Finck             Interval = ProfileSourceInformation.Interval;
250*c2c66affSColin Finck         }
251*c2c66affSColin Finck         else
252*c2c66affSColin Finck         {
253*c2c66affSColin Finck             /* Unsupported or invalid source, fail */
254*c2c66affSColin Finck             Interval = 0;
255*c2c66affSColin Finck         }
256*c2c66affSColin Finck     }
257*c2c66affSColin Finck 
258*c2c66affSColin Finck     /* Return the interval we got */
259*c2c66affSColin Finck     return Interval;
260*c2c66affSColin Finck }
261*c2c66affSColin Finck 
262*c2c66affSColin Finck VOID
263*c2c66affSColin Finck NTAPI
KeSetIntervalProfile(IN ULONG Interval,IN KPROFILE_SOURCE ProfileSource)264*c2c66affSColin Finck KeSetIntervalProfile(IN ULONG Interval,
265*c2c66affSColin Finck                      IN KPROFILE_SOURCE ProfileSource)
266*c2c66affSColin Finck {
267*c2c66affSColin Finck     HAL_PROFILE_SOURCE_INTERVAL ProfileSourceInterval;
268*c2c66affSColin Finck 
269*c2c66affSColin Finck     /* Check what profile this is */
270*c2c66affSColin Finck     if (ProfileSource == ProfileTime)
271*c2c66affSColin Finck     {
272*c2c66affSColin Finck         /* Set the interval through HAL */
273*c2c66affSColin Finck         KiProfileTimeInterval = (ULONG)HalSetProfileInterval(Interval);
274*c2c66affSColin Finck     }
275*c2c66affSColin Finck     else if (ProfileSource == ProfileAlignmentFixup)
276*c2c66affSColin Finck     {
277*c2c66affSColin Finck         /* Set the alignment interval */
278*c2c66affSColin Finck         KiProfileAlignmentFixupInterval = Interval;
279*c2c66affSColin Finck     }
280*c2c66affSColin Finck     else
281*c2c66affSColin Finck     {
282*c2c66affSColin Finck         /* HAL handles any other interval */
283*c2c66affSColin Finck         ProfileSourceInterval.Source = ProfileSource;
284*c2c66affSColin Finck         ProfileSourceInterval.Interval = Interval;
285*c2c66affSColin Finck         HalSetSystemInformation(HalProfileSourceInterval,
286*c2c66affSColin Finck                                 sizeof(HAL_PROFILE_SOURCE_INTERVAL),
287*c2c66affSColin Finck                                 &ProfileSourceInterval);
288*c2c66affSColin Finck     }
289*c2c66affSColin Finck }
290*c2c66affSColin Finck 
291*c2c66affSColin Finck /*
292*c2c66affSColin Finck  * @implemented
293*c2c66affSColin Finck  */
294*c2c66affSColin Finck VOID
295*c2c66affSColin Finck NTAPI
KeProfileInterrupt(IN PKTRAP_FRAME TrapFrame)296*c2c66affSColin Finck KeProfileInterrupt(IN PKTRAP_FRAME TrapFrame)
297*c2c66affSColin Finck {
298*c2c66affSColin Finck     /* Called from HAL for Timer Profiling */
299*c2c66affSColin Finck     KeProfileInterruptWithSource(TrapFrame, ProfileTime);
300*c2c66affSColin Finck }
301*c2c66affSColin Finck 
302*c2c66affSColin Finck VOID
303*c2c66affSColin Finck NTAPI
KiParseProfileList(IN PKTRAP_FRAME TrapFrame,IN KPROFILE_SOURCE Source,IN PLIST_ENTRY ListHead)304*c2c66affSColin Finck KiParseProfileList(IN PKTRAP_FRAME TrapFrame,
305*c2c66affSColin Finck                    IN KPROFILE_SOURCE Source,
306*c2c66affSColin Finck                    IN PLIST_ENTRY ListHead)
307*c2c66affSColin Finck {
308*c2c66affSColin Finck     PULONG BucketValue;
309*c2c66affSColin Finck     PKPROFILE Profile;
310*c2c66affSColin Finck     PLIST_ENTRY NextEntry;
311*c2c66affSColin Finck     ULONG_PTR ProgramCounter;
312*c2c66affSColin Finck 
313*c2c66affSColin Finck     /* Get the Program Counter */
314*c2c66affSColin Finck     ProgramCounter = KeGetTrapFramePc(TrapFrame);
315*c2c66affSColin Finck 
316*c2c66affSColin Finck     /* Loop the List */
317*c2c66affSColin Finck     for (NextEntry = ListHead->Flink;
318*c2c66affSColin Finck          NextEntry != ListHead;
319*c2c66affSColin Finck          NextEntry = NextEntry->Flink)
320*c2c66affSColin Finck     {
321*c2c66affSColin Finck         /* Get the entry */
322*c2c66affSColin Finck         Profile = CONTAINING_RECORD(NextEntry, KPROFILE, ProfileListEntry);
323*c2c66affSColin Finck 
324*c2c66affSColin Finck         /* Check if the source is good, and if it's within the range */
325*c2c66affSColin Finck         if ((Profile->Source != Source) ||
326*c2c66affSColin Finck             (ProgramCounter < (ULONG_PTR)Profile->RangeBase) ||
327*c2c66affSColin Finck             (ProgramCounter > (ULONG_PTR)Profile->RangeLimit))
328*c2c66affSColin Finck         {
329*c2c66affSColin Finck             continue;
330*c2c66affSColin Finck         }
331*c2c66affSColin Finck 
332*c2c66affSColin Finck         /* Get the Pointer to the Bucket Value representing this Program Counter */
333*c2c66affSColin Finck         BucketValue = (PULONG)((ULONG_PTR)Profile->Buffer +
334*c2c66affSColin Finck                                (((ProgramCounter - (ULONG_PTR)Profile->RangeBase)
335*c2c66affSColin Finck                                 >> Profile->BucketShift) &~ 0x3));
336*c2c66affSColin Finck 
337*c2c66affSColin Finck         /* Increment the value */
338*c2c66affSColin Finck         (*BucketValue)++;
339*c2c66affSColin Finck     }
340*c2c66affSColin Finck }
341*c2c66affSColin Finck 
342*c2c66affSColin Finck /*
343*c2c66affSColin Finck  * @implemented
344*c2c66affSColin Finck  *
345*c2c66affSColin Finck  * Remarks:
346*c2c66affSColin Finck  *         Called from HAL, this function looks up the process
347*c2c66affSColin Finck  *         entries, finds the proper source object, verifies the
348*c2c66affSColin Finck  *         ranges with the trapframe data, and inserts the information
349*c2c66affSColin Finck  *         from the trap frame into the buffer, while using buckets and
350*c2c66affSColin Finck  *         shifting like we specified. -- Alex
351*c2c66affSColin Finck  */
352*c2c66affSColin Finck VOID
353*c2c66affSColin Finck NTAPI
KeProfileInterruptWithSource(IN PKTRAP_FRAME TrapFrame,IN KPROFILE_SOURCE Source)354*c2c66affSColin Finck KeProfileInterruptWithSource(IN PKTRAP_FRAME TrapFrame,
355*c2c66affSColin Finck                              IN KPROFILE_SOURCE Source)
356*c2c66affSColin Finck {
357*c2c66affSColin Finck     PKPROCESS Process = KeGetCurrentThread()->ApcState.Process;
358*c2c66affSColin Finck 
359*c2c66affSColin Finck     /* We have to parse 2 lists. Per-Process and System-Wide */
360*c2c66affSColin Finck     KiParseProfileList(TrapFrame, Source, &Process->ProfileListHead);
361*c2c66affSColin Finck     KiParseProfileList(TrapFrame, Source, &KiProfileListHead);
362*c2c66affSColin Finck }
363*c2c66affSColin Finck 
364*c2c66affSColin Finck /*
365*c2c66affSColin Finck  * @implemented
366*c2c66affSColin Finck  */
367*c2c66affSColin Finck VOID
368*c2c66affSColin Finck NTAPI
KeSetProfileIrql(IN KIRQL ProfileIrql)369*c2c66affSColin Finck KeSetProfileIrql(IN KIRQL ProfileIrql)
370*c2c66affSColin Finck {
371*c2c66affSColin Finck     /* Set the IRQL at which Profiling will run */
372*c2c66affSColin Finck     KiProfileIrql = ProfileIrql;
373*c2c66affSColin Finck }
374