[NTOS:EX]
[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 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Thomas Weidenmueller
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, ExpInitializeProfileImplementation)
18 #endif
19
20 #define TAG_PROFILE 'forP'
21
22 /* GLOBALS *******************************************************************/
23
24 POBJECT_TYPE ExProfileObjectType = NULL;
25 KMUTEX ExpProfileMutex;
26
27 GENERIC_MAPPING ExpProfileMapping =
28 {
29 STANDARD_RIGHTS_READ | PROFILE_CONTROL,
30 STANDARD_RIGHTS_WRITE | PROFILE_CONTROL,
31 STANDARD_RIGHTS_EXECUTE | PROFILE_CONTROL,
32 PROFILE_ALL_ACCESS
33 };
34
35 /* FUNCTIONS *****************************************************************/
36
37 VOID
38 NTAPI
39 ExpDeleteProfile(PVOID ObjectBody)
40 {
41 PEPROFILE Profile;
42 ULONG State;
43
44 /* Typecast the Object */
45 Profile = ObjectBody;
46
47 /* Check if there if the Profile was started */
48 if (Profile->LockedBufferAddress)
49 {
50 /* Stop the Profile */
51 State = KeStopProfile(Profile->ProfileObject);
52 ASSERT(State != FALSE);
53
54 /* Unmap the Locked Buffer */
55 MmUnmapLockedPages(Profile->LockedBufferAddress, Profile->Mdl);
56 MmUnlockPages(Profile->Mdl);
57 IoFreeMdl(Profile->Mdl);
58 ExFreePoolWithTag(Profile->ProfileObject, TAG_PROFILE);
59 }
60
61 /* Check if a Process is associated and reference it */
62 if (Profile->Process) ObDereferenceObject(Profile->Process);
63 }
64
65 BOOLEAN
66 INIT_FUNCTION
67 NTAPI
68 ExpInitializeProfileImplementation(VOID)
69 {
70 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
71 UNICODE_STRING Name;
72 NTSTATUS Status;
73 DPRINT("Creating Profile Object Type\n");
74
75 /* Initialize the Mutex to lock the States */
76 KeInitializeMutex(&ExpProfileMutex, 64);
77
78 /* Create the Event Pair Object Type */
79 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
80 RtlInitUnicodeString(&Name, L"Profile");
81 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
82 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KPROFILE);
83 ObjectTypeInitializer.GenericMapping = ExpProfileMapping;
84 ObjectTypeInitializer.PoolType = NonPagedPool;
85 ObjectTypeInitializer.DeleteProcedure = ExpDeleteProfile;
86 ObjectTypeInitializer.ValidAccessMask = PROFILE_ALL_ACCESS;
87 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
88 Status = ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ExProfileObjectType);
89 if (!NT_SUCCESS(Status)) return FALSE;
90 return TRUE;
91 }
92
93 NTSTATUS
94 NTAPI
95 NtCreateProfile(OUT PHANDLE ProfileHandle,
96 IN HANDLE Process OPTIONAL,
97 IN PVOID RangeBase,
98 IN ULONG RangeSize,
99 IN ULONG BucketSize,
100 IN PVOID Buffer,
101 IN ULONG BufferSize,
102 IN KPROFILE_SOURCE ProfileSource,
103 IN KAFFINITY Affinity)
104 {
105 HANDLE hProfile;
106 PEPROFILE Profile;
107 PEPROCESS pProcess;
108 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
109 OBJECT_ATTRIBUTES ObjectAttributes;
110 NTSTATUS Status;
111 ULONG Log2 = 0;
112 ULONG_PTR Segment = 0;
113 PAGED_CODE();
114
115 /* Easy way out */
116 if(!BufferSize) return STATUS_INVALID_PARAMETER_7;
117
118 /* Check if this is a low-memory profile */
119 if ((!BucketSize) && (RangeBase < (PVOID)(0x10000)))
120 {
121 /* Validate size */
122 if (BufferSize < sizeof(ULONG)) return STATUS_INVALID_PARAMETER_7;
123
124 /* This will become a segmented profile object */
125 Segment = (ULONG_PTR)RangeBase;
126 RangeBase = 0;
127
128 /* Recalculate the bucket size */
129 BucketSize = RangeSize / (BufferSize / sizeof(ULONG));
130
131 /* Convert it to log2 */
132 BucketSize--;
133 while (BucketSize >>= 1) Log2++;
134 BucketSize += Log2 + 1;
135 }
136
137 /* Validate bucket size */
138 if ((BucketSize > 31) || (BucketSize < 2))
139 {
140 DPRINT1("Bucket size invalid\n");
141 return STATUS_INVALID_PARAMETER;
142 }
143
144 /* Make sure that the buckets can map the range */
145 if ((RangeSize >> (BucketSize - 2)) > BufferSize)
146 {
147 DPRINT1("Bucket size too small\n");
148 return STATUS_BUFFER_TOO_SMALL;
149 }
150
151 /* Make sure that the range isn't too gigantic */
152 if (((ULONG_PTR)RangeBase + RangeSize) < RangeSize)
153 {
154 DPRINT1("Range too big\n");
155 return STATUS_BUFFER_OVERFLOW;
156 }
157
158 /* Check if we were called from user-mode */
159 if(PreviousMode != KernelMode)
160 {
161 /* Entry SEH */
162 _SEH2_TRY
163 {
164 /* Make sure that the handle pointer is valid */
165 ProbeForWriteHandle(ProfileHandle);
166
167 /* Check if the buffer is valid */
168 ProbeForWrite(Buffer,
169 BufferSize,
170 sizeof(ULONG));
171 }
172 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
173 {
174 /* Return the exception code */
175 _SEH2_YIELD(return _SEH2_GetExceptionCode());
176 }
177 _SEH2_END;
178 }
179
180 /* Check if a process was specified */
181 if (Process)
182 {
183 /* Reference it */
184 Status = ObReferenceObjectByHandle(Process,
185 PROCESS_QUERY_INFORMATION,
186 PsProcessType,
187 PreviousMode,
188 (PVOID*)&pProcess,
189 NULL);
190 if (!NT_SUCCESS(Status)) return(Status);
191 }
192 else
193 {
194 /* Segmented profile objects cannot be used system-wide */
195 if (Segment) return STATUS_INVALID_PARAMETER;
196
197 /* No process was specified, which means a System-Wide Profile */
198 pProcess = NULL;
199
200 /* For this, we need to check the Privilege */
201 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode))
202 {
203 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
204 return STATUS_PRIVILEGE_NOT_HELD;
205 }
206 }
207
208 /* Create the object */
209 InitializeObjectAttributes(&ObjectAttributes,
210 NULL,
211 0,
212 NULL,
213 NULL);
214 Status = ObCreateObject(KernelMode,
215 ExProfileObjectType,
216 &ObjectAttributes,
217 PreviousMode,
218 NULL,
219 sizeof(EPROFILE),
220 0,
221 sizeof(EPROFILE) + sizeof(KPROFILE),
222 (PVOID*)&Profile);
223 if (!NT_SUCCESS(Status))
224 {
225 /* Dereference the process object if it was specified */
226 if (pProcess) ObDereferenceObject(pProcess);
227
228 /* Return Status */
229 return Status;
230 }
231
232 /* Initialize it */
233 Profile->RangeBase = RangeBase;
234 Profile->RangeSize = RangeSize;
235 Profile->Buffer = Buffer;
236 Profile->BufferSize = BufferSize;
237 Profile->BucketSize = BucketSize;
238 Profile->LockedBufferAddress = NULL;
239 Profile->Segment = Segment;
240 Profile->ProfileSource = ProfileSource;
241 Profile->Affinity = Affinity;
242 Profile->Process = pProcess;
243
244 /* Insert into the Object Tree */
245 Status = ObInsertObject ((PVOID)Profile,
246 NULL,
247 PROFILE_CONTROL,
248 0,
249 NULL,
250 &hProfile);
251
252 /* Check for Success */
253 if (!NT_SUCCESS(Status))
254 {
255 /* Dereference Process on failure */
256 if (pProcess) ObDereferenceObject(pProcess);
257 return Status;
258 }
259
260 /* Enter SEH */
261 _SEH2_TRY
262 {
263 /* Copy the created handle back to the caller*/
264 *ProfileHandle = hProfile;
265 }
266 _SEH2_EXCEPT(ExSystemExceptionFilter())
267 {
268 Status = _SEH2_GetExceptionCode();
269 }
270 _SEH2_END;
271
272 /* Return Status */
273 return Status;
274 }
275
276 NTSTATUS
277 NTAPI
278 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter,
279 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
280 {
281 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
282 LARGE_INTEGER PerfFrequency;
283 NTSTATUS Status = STATUS_SUCCESS;
284
285 /* Check if we were called from user-mode */
286 if (PreviousMode != KernelMode)
287 {
288 /* Entry SEH Block */
289 _SEH2_TRY
290 {
291 /* Make sure the counter and frequency are valid */
292 ProbeForWriteLargeInteger(PerformanceCounter);
293 if (PerformanceFrequency)
294 {
295 ProbeForWriteLargeInteger(PerformanceFrequency);
296 }
297 }
298 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
299 {
300 /* Return the exception code */
301 _SEH2_YIELD(return _SEH2_GetExceptionCode());
302 }
303 _SEH2_END;
304 }
305
306 /* Enter a new SEH Block */
307 _SEH2_TRY
308 {
309 /* Query the Kernel */
310 *PerformanceCounter = KeQueryPerformanceCounter(&PerfFrequency);
311
312 /* Return Frequency if requested */
313 if (PerformanceFrequency) *PerformanceFrequency = PerfFrequency;
314 }
315 _SEH2_EXCEPT(ExSystemExceptionFilter())
316 {
317 /* Get the exception code */
318 Status = _SEH2_GetExceptionCode();
319 }
320 _SEH2_END;
321
322 /* Return status to caller */
323 return Status;
324 }
325
326 NTSTATUS
327 NTAPI
328 NtStartProfile(IN HANDLE ProfileHandle)
329 {
330 PEPROFILE Profile;
331 PKPROFILE ProfileObject;
332 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
333 PVOID TempLockedBufferAddress;
334 NTSTATUS Status;
335 PAGED_CODE();
336
337 /* Get the Object */
338 Status = ObReferenceObjectByHandle(ProfileHandle,
339 PROFILE_CONTROL,
340 ExProfileObjectType,
341 PreviousMode,
342 (PVOID*)&Profile,
343 NULL);
344 if (!NT_SUCCESS(Status)) return(Status);
345
346 /* To avoid a Race, wait on the Mutex */
347 KeWaitForSingleObject(&ExpProfileMutex,
348 Executive,
349 KernelMode,
350 FALSE,
351 NULL);
352
353 /* The Profile can still be enabled though, so handle that */
354 if (Profile->LockedBufferAddress)
355 {
356 /* Release our lock, dereference and return */
357 KeReleaseMutex(&ExpProfileMutex, FALSE);
358 ObDereferenceObject(Profile);
359 return STATUS_PROFILING_NOT_STOPPED;
360 }
361
362 /* Allocate a Kernel Profile Object. */
363 ProfileObject = ExAllocatePoolWithTag(NonPagedPool,
364 sizeof(*ProfileObject),
365 TAG_PROFILE);
366 if (!ProfileObject)
367 {
368 /* Out of memory, fail */
369 KeReleaseMutex(&ExpProfileMutex, FALSE);
370 ObDereferenceObject(Profile);
371 return STATUS_INSUFFICIENT_RESOURCES;
372 }
373
374 /* Allocate the Mdl Structure */
375 Profile->Mdl = IoAllocateMdl(Profile->Buffer, Profile->BufferSize, FALSE, FALSE, NULL);
376
377 /* Protect this in SEH as we might raise an exception */
378 _SEH2_TRY
379 {
380 /* Probe and Lock for Write Access */
381 MmProbeAndLockPages(Profile->Mdl, PreviousMode, IoWriteAccess);
382 }
383 _SEH2_EXCEPT(ExSystemExceptionFilter())
384 {
385 /* Release our lock, free the buffer, dereference and return */
386 KeReleaseMutex(&ExpProfileMutex, FALSE);
387 ObDereferenceObject(Profile);
388 ExFreePoolWithTag(ProfileObject, TAG_PROFILE);
389 _SEH2_YIELD(return _SEH2_GetExceptionCode());
390 }
391 _SEH2_END;
392
393 /* Map the pages */
394 TempLockedBufferAddress = MmMapLockedPages(Profile->Mdl, KernelMode);
395
396 /* Initialize the Kernel Profile Object */
397 Profile->ProfileObject = ProfileObject;
398 KeInitializeProfile(ProfileObject,
399 &Profile->Process->Pcb,
400 Profile->RangeBase,
401 Profile->RangeSize,
402 Profile->BucketSize,
403 Profile->ProfileSource,
404 Profile->Affinity);
405
406 /* Start the Profiling */
407 KeStartProfile(ProfileObject, TempLockedBufferAddress);
408
409 /* Now it's safe to save this */
410 Profile->LockedBufferAddress = TempLockedBufferAddress;
411
412 /* Release mutex, dereference and return */
413 KeReleaseMutex(&ExpProfileMutex, FALSE);
414 ObDereferenceObject(Profile);
415 return STATUS_SUCCESS;
416 }
417
418 NTSTATUS
419 NTAPI
420 NtStopProfile(IN HANDLE ProfileHandle)
421 {
422 PEPROFILE Profile;
423 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
424 NTSTATUS Status;
425 PAGED_CODE();
426
427 /* Get the Object */
428 Status = ObReferenceObjectByHandle(ProfileHandle,
429 PROFILE_CONTROL,
430 ExProfileObjectType,
431 PreviousMode,
432 (PVOID*)&Profile,
433 NULL);
434 if (!NT_SUCCESS(Status)) return(Status);
435
436 /* Get the Mutex */
437 KeWaitForSingleObject(&ExpProfileMutex,
438 Executive,
439 KernelMode,
440 FALSE,
441 NULL);
442
443 /* Make sure the Profile Object is really Started */
444 if (!Profile->LockedBufferAddress)
445 {
446 Status = STATUS_PROFILING_NOT_STARTED;
447 goto Exit;
448 }
449
450 /* Stop the Profile */
451 KeStopProfile(Profile->ProfileObject);
452
453 /* Unlock the Buffer */
454 MmUnmapLockedPages(Profile->LockedBufferAddress, Profile->Mdl);
455 MmUnlockPages(Profile->Mdl);
456 ExFreePoolWithTag(Profile->ProfileObject, TAG_PROFILE);
457
458 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
459 Profile->LockedBufferAddress = NULL;
460
461 Exit:
462 /* Release Mutex, Dereference and Return */
463 KeReleaseMutex(&ExpProfileMutex, FALSE);
464 ObDereferenceObject(Profile);
465 return Status;
466 }
467
468 NTSTATUS
469 NTAPI
470 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource,
471 OUT PULONG Interval)
472 {
473 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
474 ULONG ReturnInterval;
475 NTSTATUS Status = STATUS_SUCCESS;
476 PAGED_CODE();
477
478 /* Check if we were called from user-mode */
479 if (PreviousMode != KernelMode)
480 {
481 /* Enter SEH Block */
482 _SEH2_TRY
483 {
484 /* Validate interval */
485 ProbeForWriteUlong(Interval);
486 }
487 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
488 {
489 /* Return the exception code */
490 _SEH2_YIELD(return _SEH2_GetExceptionCode());
491 }
492 _SEH2_END;
493 }
494
495 /* Query the Interval */
496 ReturnInterval = (ULONG)KeQueryIntervalProfile(ProfileSource);
497
498 /* Enter SEH block for return */
499 _SEH2_TRY
500 {
501 /* Return the data */
502 *Interval = ReturnInterval;
503 }
504 _SEH2_EXCEPT(ExSystemExceptionFilter())
505 {
506 /* Get the exception code */
507 Status = _SEH2_GetExceptionCode();
508 }
509 _SEH2_END;
510
511 /* Return Success */
512 return Status;
513 }
514
515 NTSTATUS
516 NTAPI
517 NtSetIntervalProfile(IN ULONG Interval,
518 IN KPROFILE_SOURCE Source)
519 {
520 /* Let the Kernel do the job */
521 KeSetIntervalProfile(Interval, Source);
522
523 /* Nothing can go wrong */
524 return STATUS_SUCCESS;
525 }