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