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)
10 /* INCLUDES *****************************************************************/
14 #include <internal/debug.h>
16 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, ExpInitializeProfileImplementation)
20 #define TAG_PROFILE TAG('P', 'r', 'o', 'f')
22 /* GLOBALS *******************************************************************/
24 POBJECT_TYPE ExProfileObjectType
= NULL
;
25 KMUTEX ExpProfileMutex
;
27 GENERIC_MAPPING ExpProfileMapping
=
29 STANDARD_RIGHTS_READ
| PROFILE_CONTROL
,
30 STANDARD_RIGHTS_WRITE
| PROFILE_CONTROL
,
31 STANDARD_RIGHTS_EXECUTE
| PROFILE_CONTROL
,
35 /* FUNCTIONS *****************************************************************/
39 ExpDeleteProfile(PVOID ObjectBody
)
44 /* Typecast the Object */
45 Profile
= (PEPROFILE
)ObjectBody
;
47 /* Check if there if the Profile was started */
48 if (Profile
->LockedBuffer
)
50 /* Stop the Profile */
51 State
= KeStopProfile(Profile
->KeProfile
);
52 ASSERT(State
!= FALSE
);
54 /* Unmap the Locked Buffer */
55 MmUnmapLockedPages(Profile
->LockedBuffer
, Profile
->Mdl
);
56 MmUnlockPages(Profile
->Mdl
);
57 ExFreePool(Profile
->Mdl
);
60 /* Check if a Process is associated and reference it */
61 if (Profile
->Process
) ObDereferenceObject(Profile
->Process
);
67 ExpInitializeProfileImplementation(VOID
)
69 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer
;
71 DPRINT("Creating Profile Object Type\n");
73 /* Initialize the Mutex to lock the States */
74 KeInitializeMutex(&ExpProfileMutex
, 64);
76 /* Create the Event Pair Object Type */
77 RtlZeroMemory(&ObjectTypeInitializer
, sizeof(ObjectTypeInitializer
));
78 RtlInitUnicodeString(&Name
, L
"Profile");
79 ObjectTypeInitializer
.Length
= sizeof(ObjectTypeInitializer
);
80 ObjectTypeInitializer
.DefaultNonPagedPoolCharge
= sizeof(KPROFILE
);
81 ObjectTypeInitializer
.GenericMapping
= ExpProfileMapping
;
82 ObjectTypeInitializer
.PoolType
= NonPagedPool
;
83 ObjectTypeInitializer
.DeleteProcedure
= ExpDeleteProfile
;
84 ObjectTypeInitializer
.ValidAccessMask
= PROFILE_ALL_ACCESS
;
85 ObpCreateTypeObject(&ObjectTypeInitializer
, &Name
, &ExProfileObjectType
);
90 NtCreateProfile(OUT PHANDLE ProfileHandle
,
91 IN HANDLE Process OPTIONAL
,
97 IN KPROFILE_SOURCE ProfileSource
,
98 IN KAFFINITY Affinity
)
103 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
104 OBJECT_ATTRIBUTES ObjectAttributes
;
105 NTSTATUS Status
= STATUS_SUCCESS
;
106 ULONG Segment
= 0, Log2
= 0;
110 if(!BufferSize
) return STATUS_INVALID_PARAMETER_7
;
112 /* Check if this is a low-memory profile */
113 if ((!BucketSize
) && (ImageBase
< (PVOID
)(0x10000)))
116 if (BufferSize
< sizeof(ULONG
)) return STATUS_INVALID_PARAMETER_7
;
118 /* This will become a segmented profile object */
119 Segment
= (ULONG
)ImageBase
;
122 /* Recalculate the bucket size */
123 BucketSize
= ImageSize
/ (BufferSize
/ sizeof(ULONG
));
125 /* Convert it to log2 */
127 while (BucketSize
>>= 1) Log2
++;
128 BucketSize
+= Log2
+ 1;
131 /* Validate bucket size */
132 if ((BucketSize
> 31) || (BucketSize
< 2))
134 DPRINT1("Bucket size invalid\n");
135 return STATUS_INVALID_PARAMETER
;
138 /* Make sure that the buckets can map the range */
139 if ((ImageSize
>> (BucketSize
- 2)) > BufferSize
)
141 DPRINT1("Bucket size too small\n");
142 return STATUS_BUFFER_TOO_SMALL
;
145 /* Make sure that the range isn't too gigantic */
146 if (((ULONG_PTR
)ImageBase
+ ImageSize
) < ImageSize
)
148 DPRINT1("Range too big\n");
149 return STATUS_BUFFER_OVERFLOW
;
152 /* Check if we were called from user-mode */
153 if(PreviousMode
!= KernelMode
)
158 /* Make sure that the handle pointer is valid */
159 ProbeForWriteHandle(ProfileHandle
);
161 /* Check if the buffer is valid */
162 ProbeForWrite(Buffer
,
166 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
168 Status
= _SEH_GetExceptionCode();
172 /* Bail out if we failed */
173 if(!NT_SUCCESS(Status
)) return Status
;
176 /* Check if a process was specified */
180 Status
= ObReferenceObjectByHandle(Process
,
181 PROCESS_QUERY_INFORMATION
,
186 if (!NT_SUCCESS(Status
)) return(Status
);
190 /* Segmented profile objects cannot be used system-wide */
191 if (Segment
) return STATUS_INVALID_PARAMETER
;
193 /* No process was specified, which means a System-Wide Profile */
196 /* For this, we need to check the Privilege */
197 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege
, PreviousMode
))
199 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
200 return STATUS_PRIVILEGE_NOT_HELD
;
204 /* Create the object */
205 InitializeObjectAttributes(&ObjectAttributes
,
210 Status
= ObCreateObject(KernelMode
,
217 sizeof(EPROFILE
) + sizeof(KPROFILE
),
219 if (!NT_SUCCESS(Status
)) return(Status
);
222 Profile
->ImageBase
= ImageBase
;
223 Profile
->ImageSize
= ImageSize
;
224 Profile
->Buffer
= Buffer
;
225 Profile
->BufferSize
= BufferSize
;
226 Profile
->BucketSize
= BucketSize
;
227 Profile
->LockedBuffer
= NULL
;
228 Profile
->Segment
= Segment
;
229 Profile
->ProfileSource
= ProfileSource
;
230 Profile
->Affinity
= Affinity
;
231 Profile
->Process
= pProcess
;
233 /* Insert into the Object Tree */
234 Status
= ObInsertObject ((PVOID
)Profile
,
240 ObDereferenceObject(Profile
);
242 /* Check for Success */
243 if (!NT_SUCCESS(Status
))
245 /* Dereference Process on failure */
246 if (pProcess
) ObDereferenceObject(pProcess
);
253 /* Copy the created handle back to the caller*/
254 *ProfileHandle
= hProfile
;
256 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
258 Status
= _SEH_GetExceptionCode();
268 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter
,
269 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
)
271 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
272 LARGE_INTEGER PerfFrequency
;
273 NTSTATUS Status
= STATUS_SUCCESS
;
275 /* Check if we were called from user-mode */
276 if(PreviousMode
!= KernelMode
)
278 /* Entry SEH Block */
281 /* Make sure the counter and frequency are valid */
282 ProbeForWriteLargeInteger(PerformanceCounter
);
283 if (PerformanceFrequency
)
285 ProbeForWriteLargeInteger(PerformanceFrequency
);
288 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
290 Status
= _SEH_GetExceptionCode();
294 /* If the pointers are invalid, bail out */
295 if(!NT_SUCCESS(Status
)) return Status
;
298 /* Enter a new SEH Block */
301 /* Query the Kernel */
302 *PerformanceCounter
= KeQueryPerformanceCounter(&PerfFrequency
);
304 /* Return Frequency if requested */
305 if(PerformanceFrequency
) *PerformanceFrequency
= PerfFrequency
;
307 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
309 Status
= _SEH_GetExceptionCode();
313 /* Return status to caller */
319 NtStartProfile(IN HANDLE ProfileHandle
)
323 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
324 PVOID TempLockedBuffer
;
329 Status
= ObReferenceObjectByHandle(ProfileHandle
,
335 if (!NT_SUCCESS(Status
)) return(Status
);
337 /* To avoid a Race, wait on the Mutex */
338 KeWaitForSingleObject(&ExpProfileMutex
,
344 /* The Profile can still be enabled though, so handle that */
345 if (Profile
->LockedBuffer
)
347 /* Release our lock, dereference and return */
348 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
349 ObDereferenceObject(Profile
);
350 return STATUS_PROFILING_NOT_STOPPED
;
353 /* Allocate a Kernel Profile Object. */
354 KeProfile
= ExAllocatePoolWithTag(NonPagedPool
,
358 /* Allocate the Mdl Structure */
359 Profile
->Mdl
= MmCreateMdl(NULL
, Profile
->Buffer
, Profile
->BufferSize
);
361 /* Probe and Lock for Write Access */
362 MmProbeAndLockPages(Profile
->Mdl
, PreviousMode
, IoWriteAccess
);
365 TempLockedBuffer
= MmMapLockedPages(Profile
->Mdl
, KernelMode
);
367 /* Initialize the Kernel Profile Object */
368 Profile
->KeProfile
= KeProfile
;
369 KeInitializeProfile(KeProfile
,
370 (PKPROCESS
)Profile
->Process
,
374 Profile
->ProfileSource
,
377 /* Start the Profiling */
378 KeStartProfile(KeProfile
, TempLockedBuffer
);
380 /* Now it's safe to save this */
381 Profile
->LockedBuffer
= TempLockedBuffer
;
383 /* Release mutex, dereference and return */
384 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
385 ObDereferenceObject(Profile
);
386 return STATUS_SUCCESS
;
391 NtStopProfile(IN HANDLE ProfileHandle
)
394 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
399 Status
= ObReferenceObjectByHandle(ProfileHandle
,
405 if (!NT_SUCCESS(Status
)) return(Status
);
408 KeWaitForSingleObject(&ExpProfileMutex
,
414 /* Make sure the Profile Object is really Started */
415 if (!Profile
->LockedBuffer
)
417 Status
= STATUS_PROFILING_NOT_STARTED
;
421 /* Stop the Profile */
422 KeStopProfile(Profile
->KeProfile
);
424 /* Unlock the Buffer */
425 MmUnmapLockedPages(Profile
->LockedBuffer
, Profile
->Mdl
);
426 MmUnlockPages(Profile
->Mdl
);
427 ExFreePool(Profile
->KeProfile
);
429 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
430 Profile
->LockedBuffer
= NULL
;
433 /* Release Mutex, Dereference and Return */
434 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
435 ObDereferenceObject(Profile
);
441 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource
,
444 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
445 ULONG ReturnInterval
;
446 NTSTATUS Status
= STATUS_SUCCESS
;
449 /* Check if we were called from user-mode */
450 if(PreviousMode
!= KernelMode
)
452 /* Enter SEH Block */
455 /* Validate interval */
456 ProbeForWriteUlong(Interval
);
458 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
460 Status
= _SEH_GetExceptionCode();
464 /* If pointer was invalid, bail out */
465 if(!NT_SUCCESS(Status
)) return Status
;
468 /* Query the Interval */
469 ReturnInterval
= KeQueryIntervalProfile(ProfileSource
);
471 /* Enter SEH block for return */
474 /* Return the data */
475 *Interval
= ReturnInterval
;
477 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter
)
479 Status
= _SEH_GetExceptionCode();
484 return STATUS_SUCCESS
;
489 NtSetIntervalProfile(IN ULONG Interval
,
490 IN KPROFILE_SOURCE Source
)
492 /* Let the Kernel do the job */
493 KeSetIntervalProfile(Interval
, Source
);
495 /* Nothing can go wrong */
496 return STATUS_SUCCESS
;