xref: /reactos/ntoskrnl/ex/profile.c (revision 527f2f90)
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 = (PEPROFILE)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     }
59 
60     /* Check if a Process is associated and reference it */
61     if (Profile->Process) ObDereferenceObject(Profile->Process);
62 }
63 
64 BOOLEAN
65 INIT_FUNCTION
66 NTAPI
67 ExpInitializeProfileImplementation(VOID)
68 {
69     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
70     UNICODE_STRING Name;
71     NTSTATUS Status;
72     DPRINT("Creating Profile Object Type\n");
73 
74     /* Initialize the Mutex to lock the States */
75     KeInitializeMutex(&ExpProfileMutex, 64);
76 
77     /* Create the Event Pair Object Type */
78     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
79     RtlInitUnicodeString(&Name, L"Profile");
80     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
81     ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KPROFILE);
82     ObjectTypeInitializer.GenericMapping = ExpProfileMapping;
83     ObjectTypeInitializer.PoolType = NonPagedPool;
84     ObjectTypeInitializer.DeleteProcedure = ExpDeleteProfile;
85     ObjectTypeInitializer.ValidAccessMask = PROFILE_ALL_ACCESS;
86     ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
87     Status = ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ExProfileObjectType);
88     if (!NT_SUCCESS(Status)) return FALSE;
89     return TRUE;
90 }
91 
92 NTSTATUS
93 NTAPI
94 NtCreateProfile(OUT PHANDLE ProfileHandle,
95                 IN HANDLE Process OPTIONAL,
96                 IN PVOID RangeBase,
97                 IN ULONG RangeSize,
98                 IN ULONG BucketSize,
99                 IN PVOID Buffer,
100                 IN ULONG BufferSize,
101                 IN KPROFILE_SOURCE ProfileSource,
102                 IN KAFFINITY Affinity)
103 {
104     HANDLE hProfile;
105     PEPROFILE Profile;
106     PEPROCESS pProcess;
107     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
108     OBJECT_ATTRIBUTES ObjectAttributes;
109     NTSTATUS Status;
110     ULONG Log2 = 0;
111     ULONG_PTR Segment = 0;
112     PAGED_CODE();
113 
114     /* Easy way out */
115     if(!BufferSize) return STATUS_INVALID_PARAMETER_7;
116 
117     /* Check if this is a low-memory profile */
118     if ((!BucketSize) && (RangeBase < (PVOID)(0x10000)))
119     {
120         /* Validate size */
121         if (BufferSize < sizeof(ULONG)) return STATUS_INVALID_PARAMETER_7;
122 
123         /* This will become a segmented profile object */
124         Segment = (ULONG_PTR)RangeBase;
125         RangeBase = 0;
126 
127         /* Recalculate the bucket size */
128         BucketSize = RangeSize / (BufferSize / sizeof(ULONG));
129 
130         /* Convert it to log2 */
131         BucketSize--;
132         while (BucketSize >>= 1) Log2++;
133         BucketSize += Log2 + 1;
134     }
135 
136     /* Validate bucket size */
137     if ((BucketSize > 31) || (BucketSize < 2))
138     {
139         DPRINT1("Bucket size invalid\n");
140         return STATUS_INVALID_PARAMETER;
141     }
142 
143     /* Make sure that the buckets can map the range */
144     if ((RangeSize >> (BucketSize - 2)) > BufferSize)
145     {
146         DPRINT1("Bucket size too small\n");
147         return STATUS_BUFFER_TOO_SMALL;
148     }
149 
150     /* Make sure that the range isn't too gigantic */
151     if (((ULONG_PTR)RangeBase + RangeSize) < RangeSize)
152     {
153         DPRINT1("Range too big\n");
154         return STATUS_BUFFER_OVERFLOW;
155     }
156 
157     /* Check if we were called from user-mode */
158     if(PreviousMode != KernelMode)
159     {
160         /* Entry SEH */
161         _SEH2_TRY
162         {
163             /* Make sure that the handle pointer is valid */
164             ProbeForWriteHandle(ProfileHandle);
165 
166             /* Check if the buffer is valid */
167             ProbeForWrite(Buffer,
168                           BufferSize,
169                           sizeof(ULONG));
170         }
171         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
172         {
173             /* Return the exception code */
174             _SEH2_YIELD(return _SEH2_GetExceptionCode());
175         }
176         _SEH2_END;
177     }
178 
179     /* Check if a process was specified */
180     if (Process)
181     {
182         /* Reference it */
183         Status = ObReferenceObjectByHandle(Process,
184                                            PROCESS_QUERY_INFORMATION,
185                                            PsProcessType,
186                                            PreviousMode,
187                                            (PVOID*)&pProcess,
188                                            NULL);
189         if (!NT_SUCCESS(Status)) return(Status);
190     }
191     else
192     {
193         /* Segmented profile objects cannot be used system-wide */
194         if (Segment) return STATUS_INVALID_PARAMETER;
195 
196         /* No process was specified, which means a System-Wide Profile */
197         pProcess = NULL;
198 
199         /* For this, we need to check the Privilege */
200         if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode))
201         {
202             DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
203             return STATUS_PRIVILEGE_NOT_HELD;
204         }
205     }
206 
207     /* Create the object */
208     InitializeObjectAttributes(&ObjectAttributes,
209                                NULL,
210                                0,
211                                NULL,
212                                NULL);
213     Status = ObCreateObject(KernelMode,
214                             ExProfileObjectType,
215                             &ObjectAttributes,
216                             PreviousMode,
217                             NULL,
218                             sizeof(EPROFILE),
219                             0,
220                             sizeof(EPROFILE) + sizeof(KPROFILE),
221                             (PVOID*)&Profile);
222     if (!NT_SUCCESS(Status))
223     {
224         /* Dereference the process object if it was specified */
225         if (pProcess) ObDereferenceObject(pProcess);
226 
227         /* Return Status */
228         return Status;
229     }
230 
231     /* Initialize it */
232     Profile->RangeBase = RangeBase;
233     Profile->RangeSize = RangeSize;
234     Profile->Buffer = Buffer;
235     Profile->BufferSize = BufferSize;
236     Profile->BucketSize = BucketSize;
237     Profile->LockedBufferAddress = NULL;
238     Profile->Segment = Segment;
239     Profile->ProfileSource = ProfileSource;
240     Profile->Affinity = Affinity;
241     Profile->Process = pProcess;
242 
243     /* Insert into the Object Tree */
244     Status = ObInsertObject ((PVOID)Profile,
245                              NULL,
246                              PROFILE_CONTROL,
247                              0,
248                              NULL,
249                              &hProfile);
250     ObDereferenceObject(Profile);
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(EPROFILE),
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                         (PKPROCESS)Profile->Process,
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 }
526