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 */
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 IoFreeMdl(Profile
->Mdl
);
58 ExFreePoolWithTag(Profile
->ProfileObject
, TAG_PROFILE
);
61 /* Check if a Process is associated and reference it */
62 if (Profile
->Process
) ObDereferenceObject(Profile
->Process
);
68 ExpInitializeProfileImplementation(VOID
)
70 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer
;
73 DPRINT("Creating Profile Object Type\n");
75 /* Initialize the Mutex to lock the States */
76 KeInitializeMutex(&ExpProfileMutex
, 64);
78 /* Create the Event Pair Object Type */
79 RtlZeroMemory(&ObjectTypeInitializer
, sizeof(ObjectTypeInitializer
));
80 RtlInitUnicodeString(&Name
, L
"Profile");
81 ObjectTypeInitializer
.Length
= sizeof(ObjectTypeInitializer
);
82 ObjectTypeInitializer
.DefaultNonPagedPoolCharge
= sizeof(KPROFILE
);
83 ObjectTypeInitializer
.GenericMapping
= ExpProfileMapping
;
84 ObjectTypeInitializer
.PoolType
= NonPagedPool
;
85 ObjectTypeInitializer
.DeleteProcedure
= ExpDeleteProfile
;
86 ObjectTypeInitializer
.ValidAccessMask
= PROFILE_ALL_ACCESS
;
87 ObjectTypeInitializer
.InvalidAttributes
= OBJ_OPENLINK
;
88 Status
= ObCreateObjectType(&Name
, &ObjectTypeInitializer
, NULL
, &ExProfileObjectType
);
89 if (!NT_SUCCESS(Status
)) return FALSE
;
95 NtCreateProfile(OUT PHANDLE ProfileHandle
,
96 IN HANDLE Process OPTIONAL
,
102 IN KPROFILE_SOURCE ProfileSource
,
103 IN KAFFINITY Affinity
)
108 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
109 OBJECT_ATTRIBUTES ObjectAttributes
;
112 ULONG_PTR Segment
= 0;
116 if(!BufferSize
) return STATUS_INVALID_PARAMETER_7
;
118 /* Check if this is a low-memory profile */
119 if ((!BucketSize
) && (RangeBase
< (PVOID
)(0x10000)))
122 if (BufferSize
< sizeof(ULONG
)) return STATUS_INVALID_PARAMETER_7
;
124 /* This will become a segmented profile object */
125 Segment
= (ULONG_PTR
)RangeBase
;
128 /* Recalculate the bucket size */
129 BucketSize
= RangeSize
/ (BufferSize
/ sizeof(ULONG
));
131 /* Convert it to log2 */
133 while (BucketSize
>>= 1) Log2
++;
134 BucketSize
+= Log2
+ 1;
137 /* Validate bucket size */
138 if ((BucketSize
> 31) || (BucketSize
< 2))
140 DPRINT1("Bucket size invalid\n");
141 return STATUS_INVALID_PARAMETER
;
144 /* Make sure that the buckets can map the range */
145 if ((RangeSize
>> (BucketSize
- 2)) > BufferSize
)
147 DPRINT1("Bucket size too small\n");
148 return STATUS_BUFFER_TOO_SMALL
;
151 /* Make sure that the range isn't too gigantic */
152 if (((ULONG_PTR
)RangeBase
+ RangeSize
) < RangeSize
)
154 DPRINT1("Range too big\n");
155 return STATUS_BUFFER_OVERFLOW
;
158 /* Check if we were called from user-mode */
159 if(PreviousMode
!= KernelMode
)
164 /* Make sure that the handle pointer is valid */
165 ProbeForWriteHandle(ProfileHandle
);
167 /* Check if the buffer is valid */
168 ProbeForWrite(Buffer
,
172 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
174 /* Return the exception code */
175 _SEH2_YIELD(return _SEH2_GetExceptionCode());
180 /* Check if a process was specified */
184 Status
= ObReferenceObjectByHandle(Process
,
185 PROCESS_QUERY_INFORMATION
,
190 if (!NT_SUCCESS(Status
)) return(Status
);
194 /* Segmented profile objects cannot be used system-wide */
195 if (Segment
) return STATUS_INVALID_PARAMETER
;
197 /* No process was specified, which means a System-Wide Profile */
200 /* For this, we need to check the Privilege */
201 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege
, PreviousMode
))
203 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
204 return STATUS_PRIVILEGE_NOT_HELD
;
208 /* Create the object */
209 InitializeObjectAttributes(&ObjectAttributes
,
214 Status
= ObCreateObject(KernelMode
,
221 sizeof(EPROFILE
) + sizeof(KPROFILE
),
223 if (!NT_SUCCESS(Status
))
225 /* Dereference the process object if it was specified */
226 if (pProcess
) ObDereferenceObject(pProcess
);
233 Profile
->RangeBase
= RangeBase
;
234 Profile
->RangeSize
= RangeSize
;
235 Profile
->Buffer
= Buffer
;
236 Profile
->BufferSize
= BufferSize
;
237 Profile
->BucketSize
= BucketSize
;
238 Profile
->LockedBufferAddress
= NULL
;
239 Profile
->Segment
= Segment
;
240 Profile
->ProfileSource
= ProfileSource
;
241 Profile
->Affinity
= Affinity
;
242 Profile
->Process
= pProcess
;
244 /* Insert into the Object Tree */
245 Status
= ObInsertObject ((PVOID
)Profile
,
252 /* Check for Success */
253 if (!NT_SUCCESS(Status
))
255 /* Dereference Process on failure */
256 if (pProcess
) ObDereferenceObject(pProcess
);
263 /* Copy the created handle back to the caller*/
264 *ProfileHandle
= hProfile
;
266 _SEH2_EXCEPT(ExSystemExceptionFilter())
268 Status
= _SEH2_GetExceptionCode();
278 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter
,
279 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
)
281 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
282 LARGE_INTEGER PerfFrequency
;
283 NTSTATUS Status
= STATUS_SUCCESS
;
285 /* Check if we were called from user-mode */
286 if (PreviousMode
!= KernelMode
)
288 /* Entry SEH Block */
291 /* Make sure the counter and frequency are valid */
292 ProbeForWriteLargeInteger(PerformanceCounter
);
293 if (PerformanceFrequency
)
295 ProbeForWriteLargeInteger(PerformanceFrequency
);
298 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
300 /* Return the exception code */
301 _SEH2_YIELD(return _SEH2_GetExceptionCode());
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 /* Get the exception code */
318 Status
= _SEH2_GetExceptionCode();
322 /* Return status to caller */
328 NtStartProfile(IN HANDLE ProfileHandle
)
331 PKPROFILE ProfileObject
;
332 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
333 PVOID TempLockedBufferAddress
;
338 Status
= ObReferenceObjectByHandle(ProfileHandle
,
344 if (!NT_SUCCESS(Status
)) return(Status
);
346 /* To avoid a Race, wait on the Mutex */
347 KeWaitForSingleObject(&ExpProfileMutex
,
353 /* The Profile can still be enabled though, so handle that */
354 if (Profile
->LockedBufferAddress
)
356 /* Release our lock, dereference and return */
357 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
358 ObDereferenceObject(Profile
);
359 return STATUS_PROFILING_NOT_STOPPED
;
362 /* Allocate a Kernel Profile Object. */
363 ProfileObject
= ExAllocatePoolWithTag(NonPagedPool
,
364 sizeof(*ProfileObject
),
368 /* Out of memory, fail */
369 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
370 ObDereferenceObject(Profile
);
371 return STATUS_INSUFFICIENT_RESOURCES
;
374 /* Allocate the Mdl Structure */
375 Profile
->Mdl
= IoAllocateMdl(Profile
->Buffer
, Profile
->BufferSize
, FALSE
, FALSE
, NULL
);
377 /* Protect this in SEH as we might raise an exception */
380 /* Probe and Lock for Write Access */
381 MmProbeAndLockPages(Profile
->Mdl
, PreviousMode
, IoWriteAccess
);
383 _SEH2_EXCEPT(ExSystemExceptionFilter())
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());
394 TempLockedBufferAddress
= MmMapLockedPages(Profile
->Mdl
, KernelMode
);
396 /* Initialize the Kernel Profile Object */
397 Profile
->ProfileObject
= ProfileObject
;
398 KeInitializeProfile(ProfileObject
,
399 &Profile
->Process
->Pcb
,
403 Profile
->ProfileSource
,
406 /* Start the Profiling */
407 KeStartProfile(ProfileObject
, TempLockedBufferAddress
);
409 /* Now it's safe to save this */
410 Profile
->LockedBufferAddress
= TempLockedBufferAddress
;
412 /* Release mutex, dereference and return */
413 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
414 ObDereferenceObject(Profile
);
415 return STATUS_SUCCESS
;
420 NtStopProfile(IN HANDLE ProfileHandle
)
423 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
428 Status
= ObReferenceObjectByHandle(ProfileHandle
,
434 if (!NT_SUCCESS(Status
)) return(Status
);
437 KeWaitForSingleObject(&ExpProfileMutex
,
443 /* Make sure the Profile Object is really Started */
444 if (!Profile
->LockedBufferAddress
)
446 Status
= STATUS_PROFILING_NOT_STARTED
;
450 /* Stop the Profile */
451 KeStopProfile(Profile
->ProfileObject
);
453 /* Unlock the Buffer */
454 MmUnmapLockedPages(Profile
->LockedBufferAddress
, Profile
->Mdl
);
455 MmUnlockPages(Profile
->Mdl
);
456 IoFreeMdl(Profile
->Mdl
);
457 ExFreePoolWithTag(Profile
->ProfileObject
, TAG_PROFILE
);
459 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
460 Profile
->LockedBufferAddress
= NULL
;
463 /* Release Mutex, Dereference and Return */
464 KeReleaseMutex(&ExpProfileMutex
, FALSE
);
465 ObDereferenceObject(Profile
);
471 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource
,
474 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
475 ULONG ReturnInterval
;
476 NTSTATUS Status
= STATUS_SUCCESS
;
479 /* Check if we were called from user-mode */
480 if (PreviousMode
!= KernelMode
)
482 /* Enter SEH Block */
485 /* Validate interval */
486 ProbeForWriteUlong(Interval
);
488 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
490 /* Return the exception code */
491 _SEH2_YIELD(return _SEH2_GetExceptionCode());
496 /* Query the Interval */
497 ReturnInterval
= (ULONG
)KeQueryIntervalProfile(ProfileSource
);
499 /* Enter SEH block for return */
502 /* Return the data */
503 *Interval
= ReturnInterval
;
505 _SEH2_EXCEPT(ExSystemExceptionFilter())
507 /* Get the exception code */
508 Status
= _SEH2_GetExceptionCode();
518 NtSetIntervalProfile(IN ULONG Interval
,
519 IN KPROFILE_SOURCE Source
)
521 /* Let the Kernel do the job */
522 KeSetIntervalProfile(Interval
, Source
);
524 /* Nothing can go wrong */
525 return STATUS_SUCCESS
;