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