e903050ddf862244f8dfad713620975da2285106
[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 VOID
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
166 ULONG
167 STDCALL
168 KeQueryIntervalProfile(KPROFILE_SOURCE ProfileSource)
169 {
170 /* Check if this is the timer profile */
171 if (ProfileSource == ProfileTime) {
172
173 /* Return the good old 100ns sampling interval */
174 return KiProfileTimeInterval;
175
176 } else {
177
178 /* Request it from HAL. FIXME: What structure is used? */
179 HalQuerySystemInformation(HalProfileSourceInformation,
180 sizeof(NULL),
181 NULL,
182 NULL);
183
184 return 0;
185 }
186 }
187
188 VOID
189 STDCALL
190 KeSetIntervalProfile(KPROFILE_SOURCE ProfileSource,
191 ULONG Interval)
192 {
193 /* Check if this is the timer profile */
194 if (ProfileSource == ProfileTime) {
195
196 /* Set the good old 100ns sampling interval */
197 KiProfileTimeInterval = Interval;
198
199 } else {
200
201 /* Set it with HAL. FIXME: What structure is used? */
202 HalSetSystemInformation(HalProfileSourceInformation,
203 sizeof(NULL),
204 NULL);
205
206 }
207 }
208
209 /*
210 * @implemented
211 */
212 VOID
213 STDCALL
214 KeProfileInterrupt(PKTRAP_FRAME TrapFrame)
215 {
216 /* Called from HAL for Timer Profiling */
217 KeProfileInterruptWithSource(TrapFrame, ProfileTime);
218 }
219
220 VOID
221 STDCALL
222 KiParseProfileList(IN PKTRAP_FRAME TrapFrame,
223 IN KPROFILE_SOURCE Source,
224 IN PLIST_ENTRY ListHead)
225 {
226 PULONG BucketValue;
227 PKPROFILE Profile;
228
229 /* Loop the List */
230 LIST_FOR_EACH(Profile, ListHead, KPROFILE, ProfileListEntry)
231 {
232 /* Check if the source is good, and if it's within the range */
233 if ((Profile->Source != Source) ||
234 (TrapFrame->Eip < (ULONG_PTR)Profile->RangeBase) ||
235 (TrapFrame->Eip > (ULONG_PTR)Profile->RangeLimit)) {
236
237 continue;
238 }
239
240 /* Get the Pointer to the Bucket Value representing this EIP */
241 BucketValue = (PULONG)((((ULONG_PTR)Profile->Buffer +
242 (TrapFrame->Eip - (ULONG_PTR)Profile->RangeBase))
243 >> Profile->BucketShift) &~ 0x3);
244
245 /* Increment the value */
246 ++BucketValue;
247 }
248 }
249
250 /*
251 * @implemented
252 *
253 * Remarks:
254 * Called from HAL, this function looks up the process
255 * entries, finds the proper source object, verifies the
256 * ranges with the trapframe data, and inserts the information
257 * from the trap frame into the buffer, while using buckets and
258 * shifting like we specified. -- Alex
259 */
260 VOID
261 STDCALL
262 KeProfileInterruptWithSource(IN PKTRAP_FRAME TrapFrame,
263 IN KPROFILE_SOURCE Source)
264 {
265 PKPROCESS Process = KeGetCurrentThread()->ApcState.Process;
266
267 /* We have to parse 2 lists. Per-Process and System-Wide */
268 KiParseProfileList(TrapFrame, Source, &Process->ProfileListHead);
269 KiParseProfileList(TrapFrame, Source, &KiProfileListHead);
270 }
271
272 /*
273 * @implemented
274 */
275 VOID
276 STDCALL
277 KeSetProfileIrql(IN KIRQL ProfileIrql)
278 {
279 /* Set the IRQL at which Profiling will run */
280 KiProfileIrql = ProfileIrql;
281 }