- Fix KiDispatchException to unmask KI_EXCEPTION_INTERNAL when setting the exception...
[reactos.git] / reactos / ntoskrnl / ke / profile.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/profile.c
5 * PURPOSE: Kernel Profiling
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 */
9
10 /* INCLUDES *****************************************************************/
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <internal/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 STDCALL
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 STDCALL
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;
58 PKPROCESS ProfileProcess;
59
60 /* Allocate a buffer first, before we raise IRQL */
61 SourceBuffer = ExAllocatePoolWithTag(NonPagedPool,
62 sizeof(KPROFILE_SOURCE_OBJECT),
63 TAG('P', 'r', 'o', 'f'));
64 RtlZeroMemory(SourceBuffer, sizeof(KPROFILE_SOURCE_OBJECT));
65
66 /* Raise to PROFILE_LEVEL */
67 KeRaiseIrql(PROFILE_LEVEL, &OldIrql);
68 KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
69
70 /* Make sure it's not running */
71 if (!Profile->Started) {
72
73 /* Set it as Started */
74 Profile->Buffer = Buffer;
75 Profile->Started = TRUE;
76
77 /* Get the process, if any */
78 ProfileProcess = Profile->Process;
79
80 /* Insert it into the Process List or Global List */
81 if (ProfileProcess) {
82
83 InsertTailList(&ProfileProcess->ProfileListHead, &Profile->ProfileListEntry);
84
85 } else {
86
87 InsertTailList(&KiProfileListHead, &Profile->ProfileListEntry);
88 }
89
90 /* Check if this type of profile (source) is already running */
91 LIST_FOR_EACH(CurrentSource, &KiProfileSourceListHead, KPROFILE_SOURCE_OBJECT, ListEntry)
92 {
93 /* Check if it's the same as the one being requested now */
94 if (CurrentSource->Source == Profile->Source) {
95 break;
96 }
97 }
98
99 /* See if the loop found something */
100 if (!CurrentSource) {
101
102 /* Nothing found, use our allocated buffer */
103 CurrentSource = SourceBuffer;
104
105 /* Set up the Source Object */
106 CurrentSource->Source = Profile->Source;
107 InsertHeadList(&KiProfileSourceListHead, &CurrentSource->ListEntry);
108
109 /* Don't free the pool later on */
110 FreeBuffer = FALSE;
111 }
112 }
113
114 /* Lower the IRQL */
115 KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
116 KeLowerIrql(OldIrql);
117
118 /* FIXME: Tell HAL to Start the Profile Interrupt */
119 //HalStartProfileInterrupt(Profile->Source);
120
121 /* Free the pool */
122 if (!FreeBuffer) ExFreePool(SourceBuffer);
123 }
124
125 BOOLEAN
126 STDCALL
127 KeStopProfile(PKPROFILE Profile)
128 {
129 KIRQL OldIrql;
130 PKPROFILE_SOURCE_OBJECT CurrentSource = NULL;
131
132 /* Raise to PROFILE_LEVEL and acquire spinlock */
133 KeRaiseIrql(PROFILE_LEVEL, &OldIrql);
134 KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
135
136 /* Make sure it's running */
137 if (Profile->Started) {
138
139 /* Remove it from the list and disable */
140 RemoveEntryList(&Profile->ProfileListEntry);
141 Profile->Started = FALSE;
142
143 /* Find the Source Object */
144 LIST_FOR_EACH(CurrentSource, &KiProfileSourceListHead, KPROFILE_SOURCE_OBJECT, ListEntry)
145 {
146 if (CurrentSource->Source == Profile->Source) {
147 /* Remove it */
148 RemoveEntryList(&CurrentSource->ListEntry);
149 break;
150 }
151 }
152
153 }
154
155 /* Lower IRQL */
156 KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
157 KeLowerIrql(OldIrql);
158
159 /* Stop Profiling. FIXME: Implement in HAL */
160 //HalStopProfileInterrupt(Profile->Source);
161
162 /* Free the Source Object */
163 if (CurrentSource) ExFreePool(CurrentSource);
164
165 /* FIXME */
166 return FALSE;
167 }
168
169 ULONG
170 STDCALL
171 KeQueryIntervalProfile(KPROFILE_SOURCE ProfileSource)
172 {
173 /* Check if this is the timer profile */
174 if (ProfileSource == ProfileTime) {
175
176 /* Return the good old 100ns sampling interval */
177 return KiProfileTimeInterval;
178
179 } else {
180
181 /* Request it from HAL. FIXME: What structure is used? */
182 HalQuerySystemInformation(HalProfileSourceInformation,
183 sizeof(NULL),
184 NULL,
185 NULL);
186
187 return 0;
188 }
189 }
190
191 VOID
192 STDCALL
193 KeSetIntervalProfile(KPROFILE_SOURCE ProfileSource,
194 ULONG Interval)
195 {
196 /* Check if this is the timer profile */
197 if (ProfileSource == ProfileTime) {
198
199 /* Set the good old 100ns sampling interval */
200 KiProfileTimeInterval = Interval;
201
202 } else {
203
204 /* Set it with HAL. FIXME: What structure is used? */
205 HalSetSystemInformation(HalProfileSourceInformation,
206 sizeof(NULL),
207 NULL);
208
209 }
210 }
211
212 /*
213 * @implemented
214 */
215 VOID
216 STDCALL
217 KeProfileInterrupt(PKTRAP_FRAME TrapFrame)
218 {
219 /* Called from HAL for Timer Profiling */
220 KeProfileInterruptWithSource(TrapFrame, ProfileTime);
221 }
222
223 VOID
224 STDCALL
225 KiParseProfileList(IN PKTRAP_FRAME TrapFrame,
226 IN KPROFILE_SOURCE Source,
227 IN PLIST_ENTRY ListHead)
228 {
229 PULONG BucketValue;
230 PKPROFILE Profile;
231
232 /* Loop the List */
233 LIST_FOR_EACH(Profile, ListHead, KPROFILE, ProfileListEntry)
234 {
235 /* Check if the source is good, and if it's within the range */
236 if ((Profile->Source != Source) ||
237 (TrapFrame->Eip < (ULONG_PTR)Profile->RangeBase) ||
238 (TrapFrame->Eip > (ULONG_PTR)Profile->RangeLimit)) {
239
240 continue;
241 }
242
243 /* Get the Pointer to the Bucket Value representing this EIP */
244 BucketValue = (PULONG)((((ULONG_PTR)Profile->Buffer +
245 (TrapFrame->Eip - (ULONG_PTR)Profile->RangeBase))
246 >> Profile->BucketShift) &~ 0x3);
247
248 /* Increment the value */
249 ++BucketValue;
250 }
251 }
252
253 /*
254 * @implemented
255 *
256 * Remarks:
257 * Called from HAL, this function looks up the process
258 * entries, finds the proper source object, verifies the
259 * ranges with the trapframe data, and inserts the information
260 * from the trap frame into the buffer, while using buckets and
261 * shifting like we specified. -- Alex
262 */
263 VOID
264 STDCALL
265 KeProfileInterruptWithSource(IN PKTRAP_FRAME TrapFrame,
266 IN KPROFILE_SOURCE Source)
267 {
268 PKPROCESS Process = KeGetCurrentThread()->ApcState.Process;
269
270 /* We have to parse 2 lists. Per-Process and System-Wide */
271 KiParseProfileList(TrapFrame, Source, &Process->ProfileListHead);
272 KiParseProfileList(TrapFrame, Source, &KiProfileListHead);
273 }
274
275 /*
276 * @implemented
277 */
278 VOID
279 STDCALL
280 KeSetProfileIrql(IN KIRQL ProfileIrql)
281 {
282 /* Set the IRQL at which Profiling will run */
283 KiProfileIrql = ProfileIrql;
284 }