- Add missed checks of memory allocation failures
[reactos.git] / reactos / ntoskrnl / ke / profobj.c
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
23 /* FUNCTIONS *****************************************************************/
24
25 VOID
26 NTAPI
27 KeInitializeProfile(PKPROFILE Profile,
28 PKPROCESS Process,
29 PVOID ImageBase,
30 ULONG ImageSize,
31 ULONG BucketSize,
32 KPROFILE_SOURCE ProfileSource,
33 KAFFINITY Affinity)
34 {
35 /* Initialize the Header */
36 Profile->Type = ProfileObject;
37 Profile->Size = sizeof(KPROFILE);
38
39 /* Copy all the settings we were given */
40 Profile->Process = Process;
41 Profile->RangeBase = ImageBase;
42 Profile->BucketShift = BucketSize - 2; /* See ntinternals.net -- Alex */
43 Profile->RangeLimit = (PVOID)((ULONG_PTR)ImageBase + ImageSize);
44 Profile->Started = FALSE;
45 Profile->Source = ProfileSource;
46 Profile->Affinity = Affinity;
47 }
48
49 VOID
50 NTAPI
51 KeStartProfile(PKPROFILE Profile,
52 PVOID Buffer)
53 {
54 KIRQL OldIrql;
55 PKPROFILE_SOURCE_OBJECT SourceBuffer;
56 PKPROFILE_SOURCE_OBJECT CurrentSource;
57 BOOLEAN FreeBuffer = TRUE, SourceFound = FALSE;
58 PKPROCESS ProfileProcess;
59 PLIST_ENTRY NextEntry;
60
61 /* Allocate a buffer first, before we raise IRQL */
62 SourceBuffer = ExAllocatePoolWithTag(NonPagedPool,
63 sizeof(KPROFILE_SOURCE_OBJECT),
64 'forP');
65 if (!SourceBuffer) return;
66 RtlZeroMemory(SourceBuffer, sizeof(KPROFILE_SOURCE_OBJECT));
67
68 /* Raise to PROFILE_LEVEL */
69 KeRaiseIrql(PROFILE_LEVEL, &OldIrql);
70 KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
71
72 /* Make sure it's not running */
73 if (!Profile->Started)
74 {
75 /* Set it as Started */
76 Profile->Buffer = Buffer;
77 Profile->Started = TRUE;
78
79 /* Get the process, if any */
80 ProfileProcess = Profile->Process;
81
82 /* Check where we should insert it */
83 if (ProfileProcess)
84 {
85 /* Insert it into the Process List */
86 InsertTailList(&ProfileProcess->ProfileListHead, &Profile->ProfileListEntry);
87 }
88 else
89 {
90 /* Insert it into the Global List */
91 InsertTailList(&KiProfileListHead, &Profile->ProfileListEntry);
92 }
93
94 /* Start looping */
95 for (NextEntry = KiProfileSourceListHead.Flink;
96 NextEntry != &KiProfileSourceListHead;
97 NextEntry = NextEntry->Flink)
98 {
99 /* Get the entry */
100 CurrentSource = CONTAINING_RECORD(NextEntry,
101 KPROFILE_SOURCE_OBJECT,
102 ListEntry);
103
104 /* Check if it's the same as the one being requested now */
105 if (CurrentSource->Source == Profile->Source)
106 {
107 /* It is, break out */
108 SourceFound = TRUE;
109 break;
110 }
111 }
112
113 /* See if the loop found something */
114 if (!SourceFound)
115 {
116 /* Nothing found, use our allocated buffer */
117 CurrentSource = SourceBuffer;
118
119 /* Set up the Source Object */
120 CurrentSource->Source = Profile->Source;
121 InsertHeadList(&KiProfileSourceListHead, &CurrentSource->ListEntry);
122
123 /* Don't free the pool later on */
124 FreeBuffer = FALSE;
125 }
126 }
127
128 /* Lower the IRQL */
129 KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
130 KeLowerIrql(OldIrql);
131
132 /* FIXME: Tell HAL to Start the Profile Interrupt */
133 //HalStartProfileInterrupt(Profile->Source);
134
135 /* Free the pool */
136 if (FreeBuffer) ExFreePool(SourceBuffer);
137 }
138
139 BOOLEAN
140 NTAPI
141 KeStopProfile(PKPROFILE Profile)
142 {
143 KIRQL OldIrql;
144 PKPROFILE_SOURCE_OBJECT CurrentSource = NULL;
145 PLIST_ENTRY NextEntry;
146 BOOLEAN SourceFound = FALSE;
147
148 /* Raise to PROFILE_LEVEL and acquire spinlock */
149 KeRaiseIrql(PROFILE_LEVEL, &OldIrql);
150 KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
151
152 /* Make sure it's running */
153 if (Profile->Started)
154 {
155 /* Remove it from the list and disable */
156 RemoveEntryList(&Profile->ProfileListEntry);
157 Profile->Started = FALSE;
158
159 /* Start looping */
160 for (NextEntry = KiProfileSourceListHead.Flink;
161 NextEntry != &KiProfileSourceListHead;
162 NextEntry = NextEntry->Flink)
163 {
164 /* Get the entry */
165 CurrentSource = CONTAINING_RECORD(NextEntry,
166 KPROFILE_SOURCE_OBJECT,
167 ListEntry);
168
169 /* Check if this is the Source Object */
170 if (CurrentSource->Source == Profile->Source)
171 {
172 /* Remember we found one */
173 SourceFound = TRUE;
174
175 /* Remove it and break out */
176 RemoveEntryList(&CurrentSource->ListEntry);
177 break;
178 }
179 }
180
181 }
182
183 /* Lower IRQL */
184 KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
185 KeLowerIrql(OldIrql);
186
187 /* Stop Profiling. FIXME: Implement in HAL */
188 //HalStopProfileInterrupt(Profile->Source);
189
190 /* Free the Source Object */
191 if (SourceFound) ExFreePool(CurrentSource);
192
193 /* FIXME */
194 return FALSE;
195 }
196
197 ULONG
198 NTAPI
199 KeQueryIntervalProfile(KPROFILE_SOURCE ProfileSource)
200 {
201 /* Check if this is the timer profile */
202 if (ProfileSource == ProfileTime)
203 {
204 /* Return the good old 100ns sampling interval */
205 return KiProfileTimeInterval;
206 }
207 else
208 {
209 /* Request it from HAL. FIXME: What structure is used? */
210 HalQuerySystemInformation(HalProfileSourceInformation,
211 sizeof(NULL),
212 NULL,
213 NULL);
214
215 return 0;
216 }
217 }
218
219 VOID
220 NTAPI
221 KeSetIntervalProfile(KPROFILE_SOURCE ProfileSource,
222 ULONG Interval)
223 {
224 /* Check if this is the timer profile */
225 if (ProfileSource == ProfileTime)
226 {
227 /* Set the good old 100ns sampling interval */
228 KiProfileTimeInterval = Interval;
229 }
230 else
231 {
232 /* Set it with HAL. FIXME: What structure is used? */
233 HalSetSystemInformation(HalProfileSourceInformation,
234 sizeof(NULL),
235 NULL);
236 }
237 }
238
239 /*
240 * @implemented
241 */
242 VOID
243 NTAPI
244 KeProfileInterrupt(PKTRAP_FRAME TrapFrame)
245 {
246 /* Called from HAL for Timer Profiling */
247 KeProfileInterruptWithSource(TrapFrame, ProfileTime);
248 }
249
250 VOID
251 NTAPI
252 KiParseProfileList(IN PKTRAP_FRAME TrapFrame,
253 IN KPROFILE_SOURCE Source,
254 IN PLIST_ENTRY ListHead)
255 {
256 PULONG BucketValue;
257 PKPROFILE Profile;
258 PLIST_ENTRY NextEntry;
259
260 /* Loop the List */
261 for (NextEntry = ListHead->Flink;
262 NextEntry != ListHead;
263 NextEntry = NextEntry->Flink)
264 {
265 /* Get the entry */
266 Profile = CONTAINING_RECORD(NextEntry, KPROFILE, ProfileListEntry);
267
268 /* Check if the source is good, and if it's within the range */
269 #ifdef _M_IX86
270 if ((Profile->Source != Source) ||
271 (TrapFrame->Eip < (ULONG_PTR)Profile->RangeBase) ||
272 (TrapFrame->Eip > (ULONG_PTR)Profile->RangeLimit))
273 {
274 continue;
275 }
276
277 /* Get the Pointer to the Bucket Value representing this EIP */
278 BucketValue = (PULONG)((((ULONG_PTR)Profile->Buffer +
279 (TrapFrame->Eip - (ULONG_PTR)Profile->RangeBase))
280 >> Profile->BucketShift) &~ 0x3);
281 #elif defined(_M_PPC)
282 // XXX arty
283 #endif
284
285 /* Increment the value */
286 ++BucketValue;
287 }
288 }
289
290 /*
291 * @implemented
292 *
293 * Remarks:
294 * Called from HAL, this function looks up the process
295 * entries, finds the proper source object, verifies the
296 * ranges with the trapframe data, and inserts the information
297 * from the trap frame into the buffer, while using buckets and
298 * shifting like we specified. -- Alex
299 */
300 VOID
301 NTAPI
302 KeProfileInterruptWithSource(IN PKTRAP_FRAME TrapFrame,
303 IN KPROFILE_SOURCE Source)
304 {
305 PKPROCESS Process = KeGetCurrentThread()->ApcState.Process;
306
307 /* We have to parse 2 lists. Per-Process and System-Wide */
308 KiParseProfileList(TrapFrame, Source, &Process->ProfileListHead);
309 KiParseProfileList(TrapFrame, Source, &KiProfileListHead);
310 }
311
312 /*
313 * @implemented
314 */
315 VOID
316 NTAPI
317 KeSetProfileIrql(IN KIRQL ProfileIrql)
318 {
319 /* Set the IRQL at which Profiling will run */
320 KiProfileIrql = ProfileIrql;
321 }