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