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