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 *****************************************************************/
16 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, ExpInitializeProfileImplementation)
20 #define TAG_PROFILE 'forP'
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
->LockedBufferAddress
)
50 /* Stop the Profile */
51 State
= KeStopProfile(Profile
->ProfileObject
);
52 ASSERT(State
!= FALSE
);
54 /* Unmap the Locked Buffer */
55 MmUnmapLockedPages(Profile
->LockedBufferAddress
, 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 ObCreateObjectType(&Name
, &ObjectTypeInitializer
, NULL
, &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
;
107 PVOID Segment
= NULL
;
111 if(!BufferSize
) return STATUS_INVALID_PARAMETER_7
;
113 /* Check if this is a low-memory profile */
114 if ((!BucketSize
) && (RangeBase
< (PVOID
)(0x10000)))
117 if (BufferSize
< sizeof(ULONG
)) return STATUS_INVALID_PARAMETER_7
;
119 /* This will become a segmented profile object */
123 /* Recalculate the bucket size */
124 BucketSize
= RangeSize
/ (BufferSize
/ sizeof(ULONG
));
126 /* Convert it to log2 */
128 while (BucketSize
>>= 1) Log2
++;
129 BucketSize
+= Log2
+ 1;
132 /* Validate bucket size */
133 if ((BucketSize
> 31) || (BucketSize
< 2))
135 DPRINT1("Bucket size invalid\n");
136 return STATUS_INVALID_PARAMETER
;
139 /* Make sure that the buckets can map the range */
140 if ((RangeSize
>> (BucketSize
- 2)) > BufferSize
)
142 DPRINT1("Bucket size too small\n");
143 return STATUS_BUFFER_TOO_SMALL
;
146 /* Make sure that the range isn't too gigantic */
147 if (((ULONG_PTR
)RangeBase
+ RangeSize
) < RangeSize
)
149 DPRINT1("Range too big\n");
150 return STATUS_BUFFER_OVERFLOW
;
153 /* Check if we were called from user-mode */
154 if(PreviousMode
!= KernelMode
)
159 /* Make sure that the handle pointer is valid */
160 ProbeForWriteHandle(ProfileHandle
);
162 /* Check if the buffer is valid */
163 ProbeForWrite(Buffer
,
167 _SEH2_EXCEPT(ExSystemExceptionFilter())
169 Status
= _SEH2_GetExceptionCode();
173 /* Bail out if we failed */
174 if(!NT_SUCCESS(Status
)) return Status
;
177 /* Check if a process was specified */
181 Status
= ObReferenceObjectByHandle(Process
,
182 PROCESS_QUERY_INFORMATION
,
187 if (!NT_SUCCESS(Status
)) return(Status
);
191 /* Segmented profile objects cannot be used system-wide */
192 if (Segment
) return STATUS_INVALID_PARAMETER
;
194 /* No process was specified, which means a System-Wide Profile */
197 /* For this, we need to check the Privilege */
198 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege
, PreviousMode
))
200 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
201 return STATUS_PRIVILEGE_NOT_HELD
;
205 /* Create the object */
206 InitializeObjectAttributes(&ObjectAttributes
,
211 Status
= ObCreateObject(KernelMode
,
218 sizeof(EPROFILE
) + sizeof(KPROFILE
),
220 if (!NT_SUCCESS(Status
))
222 /* Dereference the process object if it was specified */
223 if (pProcess
) ObDereferenceObject(pProcess
);
230 Profile
->RangeBase
= RangeBase
;
231 Profile
->RangeSize
= RangeSize
;
232 Profile
->Buffer
= Buffer
;
233 Profile
->BufferSize
= BufferSize
;
234 Profile
->BucketSize
= BucketSize
;
235 Profile
->LockedBufferAddress
= NULL
;
236 Profile
->Segment
= Segment
;
237 Profile
->ProfileSource
= ProfileSource
;
238 Profile
->Affinity
= Affinity
;
239 Profile
->Process
= pProcess
;
241 /* Insert into the Object Tree */
242 Status
= ObInsertObject ((PVOID
)Profile
,
248 ObDereferenceObject(Profile
);
250 /* Check for Success */
251 if (!NT_SUCCESS(Status
))
253 /* Dereference Process on failure */
254 if (pProcess
) ObDereferenceObject(pProcess
);
261 /* Copy the created handle back to the caller*/
262 *ProfileHandle
= hProfile
;
264 _SEH2_EXCEPT(ExSystemExceptionFilter())
266 Status
= _SEH2_GetExceptionCode();
276 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter
,
277 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
)
279 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
280 LARGE_INTEGER PerfFrequency
;
281 NTSTATUS Status
= STATUS_SUCCESS
;
283 /* Check if we were called from user-mode */
284 if(PreviousMode
!= KernelMode
)
286 /* Entry SEH Block */
289 /* Make sure the counter and frequency are valid */
290 ProbeForWriteLargeInteger(PerformanceCounter
);
291 if (PerformanceFrequency
)
293 ProbeForWriteLargeInteger(PerformanceFrequency
);
296 _SEH2_EXCEPT(ExSystemExceptionFilter())
298 Status
= _SEH2_GetExceptionCode();
302 /* If the pointers are invalid, bail out */
303 if(!NT_SUCCESS(Status
)) return Status
;
306 /* Enter a new SEH Block */
309 /* Query the Kernel */
310 *PerformanceCounter
= KeQueryPerformanceCounter(&PerfFrequency
);
312 /* Return Frequency if requested */
313 if(PerformanceFrequency
) *PerformanceFrequency
= PerfFrequency
;
315 _SEH2_EXCEPT(ExSystemExceptionFilter())
317 Status
= _SEH2_GetExceptionCode();
321 /* Return status to caller */
327 NtStartProfile(IN HANDLE ProfileHandle
)
330 PKPROFILE ProfileObject
;
331 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
332 PVOID TempLockedBufferAddress
;
337 Status
= ObReferenceObjectByHandle(ProfileHandle
,
343 if (!NT_SUCCESS(Status
)) return(Status
);
345 /* To avoid a Race, wait on the Mutex */
346 KeWaitForSingleObject(&ExpProfileMutex
,
352 /* The Profile can still be enabled though, so handle that */
353 if (Profile
->LockedBufferAddress
)
355 /* Release our lock, dereference and return */
356 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
357 ObDereferenceObject(Profile
);
358 return STATUS_PROFILING_NOT_STOPPED
;
361 /* Allocate a Kernel Profile Object. */
362 ProfileObject
= ExAllocatePoolWithTag(NonPagedPool
,
367 /* Out of memory, fail */
368 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
369 ObDereferenceObject(Profile
);
370 return STATUS_INSUFFICIENT_RESOURCES
;
373 /* Allocate the Mdl Structure */
374 Profile
->Mdl
= MmCreateMdl(NULL
, Profile
->Buffer
, Profile
->BufferSize
);
376 /* Protect this in SEH as we might raise an exception */
379 /* Probe and Lock for Write Access */
380 MmProbeAndLockPages(Profile
->Mdl
, PreviousMode
, IoWriteAccess
);
382 _SEH2_EXCEPT(ExSystemExceptionFilter())
384 /* Get the exception code */
385 Status
= _SEH2_GetExceptionCode();
389 /* Fail if we raised an exception */
390 if (!NT_SUCCESS(Status
))
392 /* Release our lock, free the buffer, dereference and return */
393 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
394 ObDereferenceObject(Profile
);
395 ExFreePool(ProfileObject
);
400 TempLockedBufferAddress
= MmMapLockedPages(Profile
->Mdl
, KernelMode
);
402 /* Initialize the Kernel Profile Object */
403 Profile
->ProfileObject
= ProfileObject
;
404 KeInitializeProfile(ProfileObject
,
405 (PKPROCESS
)Profile
->Process
,
409 Profile
->ProfileSource
,
412 /* Start the Profiling */
413 KeStartProfile(ProfileObject
, TempLockedBufferAddress
);
415 /* Now it's safe to save this */
416 Profile
->LockedBufferAddress
= TempLockedBufferAddress
;
418 /* Release mutex, dereference and return */
419 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
420 ObDereferenceObject(Profile
);
421 return STATUS_SUCCESS
;
426 NtStopProfile(IN HANDLE ProfileHandle
)
429 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
434 Status
= ObReferenceObjectByHandle(ProfileHandle
,
440 if (!NT_SUCCESS(Status
)) return(Status
);
443 KeWaitForSingleObject(&ExpProfileMutex
,
449 /* Make sure the Profile Object is really Started */
450 if (!Profile
->LockedBufferAddress
)
452 Status
= STATUS_PROFILING_NOT_STARTED
;
456 /* Stop the Profile */
457 KeStopProfile(Profile
->ProfileObject
);
459 /* Unlock the Buffer */
460 MmUnmapLockedPages(Profile
->LockedBufferAddress
, Profile
->Mdl
);
461 MmUnlockPages(Profile
->Mdl
);
462 ExFreePool(Profile
->ProfileObject
);
464 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
465 Profile
->LockedBufferAddress
= NULL
;
468 /* Release Mutex, Dereference and Return */
469 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
470 ObDereferenceObject(Profile
);
476 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource
,
479 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
480 ULONG ReturnInterval
;
481 NTSTATUS Status
= STATUS_SUCCESS
;
484 /* Check if we were called from user-mode */
485 if(PreviousMode
!= KernelMode
)
487 /* Enter SEH Block */
490 /* Validate interval */
491 ProbeForWriteUlong(Interval
);
493 _SEH2_EXCEPT(ExSystemExceptionFilter())
495 Status
= _SEH2_GetExceptionCode();
499 /* If pointer was invalid, bail out */
500 if(!NT_SUCCESS(Status
)) return Status
;
503 /* Query the Interval */
504 ReturnInterval
= KeQueryIntervalProfile(ProfileSource
);
506 /* Enter SEH block for return */
509 /* Return the data */
510 *Interval
= ReturnInterval
;
512 _SEH2_EXCEPT(ExSystemExceptionFilter())
514 Status
= _SEH2_GetExceptionCode();
519 return STATUS_SUCCESS
;
524 NtSetIntervalProfile(IN ULONG Interval
,
525 IN KPROFILE_SOURCE Source
)
527 /* Let the Kernel do the job */
528 KeSetIntervalProfile(Interval
, Source
);
530 /* Nothing can go wrong */
531 return STATUS_SUCCESS
;