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