xref: /reactos/ntoskrnl/ex/profile.c (revision 845faec4)
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 SIZE_T 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     IoFreeMdl(Profile->Mdl);
457     ExFreePoolWithTag(Profile->ProfileObject, TAG_PROFILE);
458 
459     /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
460     Profile->LockedBufferAddress = NULL;
461 
462 Exit:
463     /* Release Mutex, Dereference and Return */
464     KeReleaseMutex(&ExpProfileMutex, FALSE);
465     ObDereferenceObject(Profile);
466     return Status;
467 }
468 
469 NTSTATUS
470 NTAPI
471 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource,
472                        OUT PULONG Interval)
473 {
474     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
475     ULONG ReturnInterval;
476     NTSTATUS Status = STATUS_SUCCESS;
477     PAGED_CODE();
478 
479     /* Check if we were called from user-mode */
480     if (PreviousMode != KernelMode)
481     {
482         /* Enter SEH Block */
483         _SEH2_TRY
484         {
485             /* Validate interval */
486             ProbeForWriteUlong(Interval);
487         }
488         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
489         {
490             /* Return the exception code */
491             _SEH2_YIELD(return _SEH2_GetExceptionCode());
492         }
493         _SEH2_END;
494     }
495 
496     /* Query the Interval */
497     ReturnInterval = (ULONG)KeQueryIntervalProfile(ProfileSource);
498 
499     /* Enter SEH block for return */
500     _SEH2_TRY
501     {
502         /* Return the data */
503         *Interval = ReturnInterval;
504     }
505     _SEH2_EXCEPT(ExSystemExceptionFilter())
506     {
507         /* Get the exception code */
508         Status = _SEH2_GetExceptionCode();
509     }
510     _SEH2_END;
511 
512     /* Return Success */
513     return Status;
514 }
515 
516 NTSTATUS
517 NTAPI
518 NtSetIntervalProfile(IN ULONG Interval,
519                      IN KPROFILE_SOURCE Source)
520 {
521     /* Let the Kernel do the job */
522     KeSetIntervalProfile(Interval, Source);
523 
524     /* Nothing can go wrong */
525     return STATUS_SUCCESS;
526 }
527