Fix kernel-mode executive atom implementation (mostly add SEH and tidy up the code...
[reactos.git] / reactos / ntoskrnl / ex / profile.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ex/profile.c
5 * PURPOSE: Support for Executive Profile Objects
6 *
7 * PROGRAMMERS: Alex Ionescu
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #define NDEBUG
13 #include <ntoskrnl.h>
14 #include <internal/debug.h>
15
16 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, ExpInitializeProfileImplementation)
18 #endif
19
20 /* FIXME: NDK This structure is a *GUESS* -- Alex */
21 typedef struct _EPROFILE {
22 PEPROCESS Process;
23 PVOID ImageBase;
24 ULONG ImageSize;
25 ULONG BucketSize;
26 PVOID Buffer;
27 ULONG BufferSize;
28 PKPROFILE KeProfile;
29 KPROFILE_SOURCE ProfileSource;
30 KAFFINITY Affinity;
31 PMDL Mdl;
32 PVOID LockedBuffer;
33 } EPROFILE, *PEPROFILE;
34
35 /* GLOBALS *******************************************************************/
36
37 POBJECT_TYPE ExProfileObjectType = NULL;
38
39 static KMUTEX ExpProfileMutex;
40
41 #define PROFILE_CONTROL 1
42
43 static GENERIC_MAPPING ExpProfileMapping = {
44 STANDARD_RIGHTS_READ | PROFILE_CONTROL,
45 STANDARD_RIGHTS_WRITE | PROFILE_CONTROL,
46 STANDARD_RIGHTS_EXECUTE | PROFILE_CONTROL,
47 STANDARD_RIGHTS_ALL};
48
49 VOID
50 STDCALL
51 ExpDeleteProfile(PVOID ObjectBody)
52 {
53 PEPROFILE Profile;
54
55 /* Typecast the Object */
56 Profile = (PEPROFILE)ObjectBody;
57
58 /* Check if there if the Profile was started */
59 if (Profile->LockedBuffer) {
60
61 /* Stop the Profile */
62 KeStopProfile(Profile->KeProfile);
63
64 /* Unmap the Locked Buffer */
65 MmUnmapLockedPages(Profile->LockedBuffer, Profile->Mdl);
66 MmUnlockPages(Profile->Mdl);
67 ExFreePool(Profile->Mdl);
68 }
69
70 /* Check if a Process is associated */
71 if (Profile->Process != NULL) {
72
73 /* Dereference it */
74 ObDereferenceObject(Profile->Process);
75 Profile->Process = NULL;
76 }
77 }
78
79 VOID
80 INIT_FUNCTION
81 STDCALL
82 ExpInitializeProfileImplementation(VOID)
83 {
84 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
85 UNICODE_STRING Name;
86
87 /* Initialize the Mutex to lock the States */
88 KeInitializeMutex(&ExpProfileMutex, 0x40);
89
90 DPRINT("Creating Profile Object Type\n");
91
92 /* Create the Event Pair Object Type */
93 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
94 RtlInitUnicodeString(&Name, L"Profile");
95 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
96 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KPROFILE);
97 ObjectTypeInitializer.GenericMapping = ExpProfileMapping;
98 ObjectTypeInitializer.PoolType = NonPagedPool;
99 ObjectTypeInitializer.DeleteProcedure = ExpDeleteProfile;
100 ObjectTypeInitializer.ValidAccessMask = STANDARD_RIGHTS_ALL;
101 ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &ExProfileObjectType);
102 }
103
104 NTSTATUS
105 STDCALL
106 NtCreateProfile(OUT PHANDLE ProfileHandle,
107 IN HANDLE Process OPTIONAL,
108 IN PVOID ImageBase,
109 IN ULONG ImageSize,
110 IN ULONG BucketSize,
111 IN PVOID Buffer,
112 IN ULONG BufferSize,
113 IN KPROFILE_SOURCE ProfileSource,
114 IN KAFFINITY Affinity)
115 {
116 HANDLE hProfile;
117 PEPROFILE Profile;
118 PEPROCESS pProcess;
119 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
120 OBJECT_ATTRIBUTES ObjectAttributes;
121 NTSTATUS Status = STATUS_SUCCESS;
122
123 PAGED_CODE();
124
125 /* Easy way out */
126 if(BufferSize == 0) return STATUS_INVALID_PARAMETER_7;
127
128 /* Check the Parameters for validity */
129 if(PreviousMode != KernelMode) {
130
131 _SEH_TRY {
132
133 ProbeForWriteHandle(ProfileHandle);
134
135 ProbeForWrite(Buffer,
136 BufferSize,
137 sizeof(ULONG));
138 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
139
140 Status = _SEH_GetExceptionCode();
141 } _SEH_END;
142
143 if(!NT_SUCCESS(Status)) return Status;
144 }
145
146 /* Check if a process was specified */
147 if (Process) {
148
149 /* Reference it */
150 Status = ObReferenceObjectByHandle(Process,
151 PROCESS_QUERY_INFORMATION,
152 PsProcessType,
153 PreviousMode,
154 (PVOID*)&pProcess,
155 NULL);
156 if (!NT_SUCCESS(Status)) return(Status);
157
158 } else {
159
160 /* No process was specified, which means a System-Wide Profile */
161 pProcess = NULL;
162
163 /* For this, we need to check the Privilege */
164 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode)) {
165
166 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
167 return STATUS_PRIVILEGE_NOT_HELD;
168 }
169 }
170
171 /* Create the object */
172 InitializeObjectAttributes(&ObjectAttributes,
173 NULL,
174 0,
175 NULL,
176 NULL);
177 Status = ObCreateObject(KernelMode,
178 ExProfileObjectType,
179 &ObjectAttributes,
180 PreviousMode,
181 NULL,
182 sizeof(EPROFILE),
183 0,
184 0,
185 (PVOID*)&Profile);
186 if (!NT_SUCCESS(Status)) return(Status);
187
188 /* Initialize it */
189 Profile->ImageBase = ImageBase;
190 Profile->ImageSize = ImageSize;
191 Profile->Buffer = Buffer;
192 Profile->BufferSize = BufferSize;
193 Profile->BucketSize = BucketSize;
194 Profile->LockedBuffer = NULL;
195 Profile->Affinity = Affinity;
196 Profile->Process = pProcess;
197
198 /* Insert into the Object Tree */
199 Status = ObInsertObject ((PVOID)Profile,
200 NULL,
201 PROFILE_CONTROL,
202 0,
203 NULL,
204 &hProfile);
205 ObDereferenceObject(Profile);
206
207 /* Check for Success */
208 if (!NT_SUCCESS(Status)) {
209
210 /* Dereference Process on failure */
211 if (pProcess) ObDereferenceObject(pProcess);
212 return Status;
213 }
214
215 /* Copy the created handle back to the caller*/
216 _SEH_TRY {
217
218 *ProfileHandle = hProfile;
219
220 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
221
222 Status = _SEH_GetExceptionCode();
223 } _SEH_END;
224
225 /* Return Status */
226 return Status;
227 }
228
229 NTSTATUS
230 STDCALL
231 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter,
232 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
233 {
234 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
235 LARGE_INTEGER PerfFrequency;
236 NTSTATUS Status = STATUS_SUCCESS;
237
238 /* Check the Parameters for validity */
239 if(PreviousMode != KernelMode) {
240
241 _SEH_TRY {
242
243 ProbeForWriteLargeInteger(PerformanceCounter);
244
245 ProbeForWriteLargeInteger(PerformanceFrequency);
246 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
247
248 Status = _SEH_GetExceptionCode();
249 } _SEH_END;
250
251 if(!NT_SUCCESS(Status)) return Status;
252 }
253
254 _SEH_TRY {
255
256 /* Query the Kernel */
257 *PerformanceCounter = KeQueryPerformanceCounter(&PerfFrequency);
258
259 /* Return Frequency if requested */
260 if(PerformanceFrequency) {
261
262 *PerformanceFrequency = PerfFrequency;
263 }
264 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
265
266 Status = _SEH_GetExceptionCode();
267
268 } _SEH_END;
269
270 return Status;
271 }
272
273 NTSTATUS
274 STDCALL
275 NtStartProfile(IN HANDLE ProfileHandle)
276 {
277 PEPROFILE Profile;
278 PKPROFILE KeProfile;
279 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
280 PVOID TempLockedBuffer;
281 NTSTATUS Status;
282
283 PAGED_CODE();
284
285 /* Get the Object */
286 Status = ObReferenceObjectByHandle(ProfileHandle,
287 PROFILE_CONTROL,
288 ExProfileObjectType,
289 PreviousMode,
290 (PVOID*)&Profile,
291 NULL);
292 if (!NT_SUCCESS(Status)) return(Status);
293
294 /* To avoid a Race, wait on the Mutex */
295 KeWaitForSingleObject(&ExpProfileMutex,
296 Executive,
297 KernelMode,
298 FALSE,
299 NULL);
300
301 /* The Profile can still be enabled though, so handle that */
302 if (Profile->LockedBuffer) {
303
304 /* Release our lock, dereference and return */
305 KeReleaseMutex(&ExpProfileMutex, FALSE);
306 ObDereferenceObject(Profile);
307 return STATUS_PROFILING_NOT_STOPPED;
308 }
309
310 /* Allocate a Kernel Profile Object. */
311 KeProfile = ExAllocatePoolWithTag(NonPagedPool,
312 sizeof(EPROFILE),
313 TAG('P', 'r', 'o', 'f'));
314
315 /* Allocate the Mdl Structure */
316 Profile->Mdl = MmCreateMdl(NULL, Profile->Buffer, Profile->BufferSize);
317
318 /* Probe and Lock for Write Access */
319 MmProbeAndLockPages(Profile->Mdl, PreviousMode, IoWriteAccess);
320
321 /* Map the pages */
322 TempLockedBuffer = MmMapLockedPages(Profile->Mdl, KernelMode);
323
324 /* Initialize the Kernel Profile Object */
325 Profile->KeProfile = KeProfile;
326 KeInitializeProfile(KeProfile,
327 (PKPROCESS)Profile->Process,
328 Profile->ImageBase,
329 Profile->ImageSize,
330 Profile->BucketSize,
331 Profile->ProfileSource,
332 Profile->Affinity);
333
334 /* Start the Profiling */
335 KeStartProfile(KeProfile, TempLockedBuffer);
336
337 /* Now it's safe to save this */
338 Profile->LockedBuffer = TempLockedBuffer;
339
340 /* Release mutex, dereference and return */
341 KeReleaseMutex(&ExpProfileMutex, FALSE);
342 ObDereferenceObject(Profile);
343 return STATUS_SUCCESS;
344 }
345
346 NTSTATUS
347 STDCALL
348 NtStopProfile(IN HANDLE ProfileHandle)
349 {
350 PEPROFILE Profile;
351 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
352 NTSTATUS Status;
353
354 PAGED_CODE();
355
356 /* Get the Object */
357 Status = ObReferenceObjectByHandle(ProfileHandle,
358 PROFILE_CONTROL,
359 ExProfileObjectType,
360 PreviousMode,
361 (PVOID*)&Profile,
362 NULL);
363 if (!NT_SUCCESS(Status)) return(Status);
364
365 /* Get the Mutex */
366 KeWaitForSingleObject(&ExpProfileMutex,
367 Executive,
368 KernelMode,
369 FALSE,
370 NULL);
371
372 /* Make sure the Profile Object is really Started */
373 if (!Profile->LockedBuffer) {
374
375 Status = STATUS_PROFILING_NOT_STARTED;
376 goto Exit;
377 }
378
379 /* Stop the Profile */
380 KeStopProfile(Profile->KeProfile);
381
382 /* Unlock the Buffer */
383 MmUnmapLockedPages(Profile->LockedBuffer, Profile->Mdl);
384 MmUnlockPages(Profile->Mdl);
385 ExFreePool(Profile->KeProfile);
386
387 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
388 Profile->LockedBuffer = NULL;
389
390 Exit:
391 /* Release Mutex, Dereference and Return */
392 KeReleaseMutex(&ExpProfileMutex, FALSE);
393 ObDereferenceObject(Profile);
394 return Status;
395 }
396
397 NTSTATUS
398 STDCALL
399 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource,
400 OUT PULONG Interval)
401 {
402 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
403 ULONG ReturnInterval;
404 NTSTATUS Status = STATUS_SUCCESS;
405
406 PAGED_CODE();
407
408 /* Check the Parameters for validity */
409 if(PreviousMode != KernelMode) {
410
411 _SEH_TRY {
412
413 ProbeForWriteUlong(Interval);
414
415 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
416
417 Status = _SEH_GetExceptionCode();
418 } _SEH_END;
419
420 if(!NT_SUCCESS(Status)) return Status;
421 }
422
423 /* Query the Interval */
424 ReturnInterval = KeQueryIntervalProfile(ProfileSource);
425
426 /* Return the data */
427 _SEH_TRY {
428
429 *Interval = ReturnInterval;
430
431 } _SEH_EXCEPT(_SEH_ExSystemExceptionFilter) {
432
433 Status = _SEH_GetExceptionCode();
434
435 } _SEH_END;
436
437 /* Return Success */
438 return STATUS_SUCCESS;
439 }
440
441 NTSTATUS
442 STDCALL
443 NtSetIntervalProfile(IN ULONG Interval,
444 IN KPROFILE_SOURCE Source)
445 {
446 /* Let the Kernel do the job */
447 KeSetIntervalProfile(Interval, Source);
448
449 /* Nothing can go wrong */
450 return STATUS_SUCCESS;
451 }