946 lines
24 KiB
C
946 lines
24 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
profile.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the executive profile object. Functions are provided
|
||
to create, start, stop, and query profile objects.
|
||
|
||
Author:
|
||
|
||
Lou Perazzoli (loup) 21-Sep-1990
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "exp.h"
|
||
|
||
//
|
||
// Executive profile object.
|
||
//
|
||
|
||
typedef struct _EPROFILE {
|
||
PKPROCESS Process;
|
||
PVOID RangeBase;
|
||
SIZE_T RangeSize;
|
||
PVOID Buffer;
|
||
ULONG BufferSize;
|
||
ULONG BucketSize;
|
||
PKPROFILE ProfileObject;
|
||
PVOID LockedBufferAddress;
|
||
PMDL Mdl;
|
||
ULONG Segment;
|
||
KPROFILE_SOURCE ProfileSource;
|
||
KAFFINITY Affinity;
|
||
} EPROFILE, *PEPROFILE;
|
||
|
||
//
|
||
// Address of event object type descriptor.
|
||
//
|
||
|
||
POBJECT_TYPE ExProfileObjectType;
|
||
|
||
KMUTEX ExpProfileStateMutex;
|
||
|
||
#ifdef ALLOC_DATA_PRAGMA
|
||
#pragma const_seg("PAGECONST")
|
||
#endif
|
||
|
||
const ULONG ExpCurrentProfileUsage = 0;
|
||
|
||
#ifdef ALLOC_DATA_PRAGMA
|
||
#pragma const_seg("INITCONST")
|
||
#endif
|
||
const GENERIC_MAPPING ExpProfileMapping = {
|
||
STANDARD_RIGHTS_READ | PROFILE_CONTROL,
|
||
STANDARD_RIGHTS_WRITE | PROFILE_CONTROL,
|
||
STANDARD_RIGHTS_EXECUTE | PROFILE_CONTROL,
|
||
PROFILE_ALL_ACCESS
|
||
};
|
||
#ifdef ALLOC_DATA_PRAGMA
|
||
#pragma const_seg()
|
||
#endif
|
||
|
||
#define ACTIVE_PROFILE_LIMIT 8
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT, ExpProfileInitialization)
|
||
#pragma alloc_text(PAGE, ExpProfileDelete)
|
||
#pragma alloc_text(PAGE, NtCreateProfile)
|
||
#pragma alloc_text(PAGE, NtStartProfile)
|
||
#pragma alloc_text(PAGE, NtStopProfile)
|
||
#pragma alloc_text(PAGE, NtSetIntervalProfile)
|
||
#pragma alloc_text(PAGE, NtQueryIntervalProfile)
|
||
#pragma alloc_text(PAGE, NtQueryPerformanceCounter)
|
||
#endif
|
||
|
||
|
||
BOOLEAN
|
||
ExpProfileInitialization (
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function creates the profile object type descriptor at system
|
||
initialization and stores the address of the object type descriptor
|
||
in global storage.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
A value of TRUE is returned if the profile object type descriptor is
|
||
successfully initialized. Otherwise a value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
|
||
NTSTATUS Status;
|
||
UNICODE_STRING TypeName;
|
||
|
||
//
|
||
// Initialize mutex for synchronizing start and stop operations.
|
||
//
|
||
|
||
KeInitializeMutex (&ExpProfileStateMutex, MUTEX_LEVEL_EX_PROFILE);
|
||
|
||
//
|
||
// Initialize string descriptor.
|
||
//
|
||
|
||
RtlInitUnicodeString(&TypeName, L"Profile");
|
||
|
||
//
|
||
// Create event object type descriptor.
|
||
//
|
||
|
||
RtlZeroMemory(&ObjectTypeInitializer,sizeof(ObjectTypeInitializer));
|
||
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
|
||
ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
|
||
ObjectTypeInitializer.PoolType = NonPagedPool;
|
||
ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(EPROFILE);
|
||
ObjectTypeInitializer.ValidAccessMask = PROFILE_ALL_ACCESS;
|
||
ObjectTypeInitializer.DeleteProcedure = ExpProfileDelete;
|
||
ObjectTypeInitializer.GenericMapping = ExpProfileMapping;
|
||
|
||
Status = ObCreateObjectType(&TypeName,
|
||
&ObjectTypeInitializer,
|
||
(PSECURITY_DESCRIPTOR)NULL,
|
||
&ExProfileObjectType);
|
||
|
||
//
|
||
// If the event object type descriptor was successfully created, then
|
||
// return a value of TRUE. Otherwise return a value of FALSE.
|
||
//
|
||
|
||
return (BOOLEAN)(NT_SUCCESS(Status));
|
||
}
|
||
|
||
VOID
|
||
ExpProfileDelete (
|
||
IN PVOID Object
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
This routine is called by the object management procedures whenever
|
||
the last reference to a profile object has been removed. This routine
|
||
stops profiling, returns locked buffers and pages, dereferences the
|
||
specified process and returns.
|
||
|
||
Arguments:
|
||
|
||
Object - a pointer to the body of the profile object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PEPROFILE Profile;
|
||
BOOLEAN State;
|
||
PEPROCESS ProcessAddress;
|
||
|
||
Profile = (PEPROFILE)Object;
|
||
|
||
if (Profile->LockedBufferAddress != NULL) {
|
||
|
||
//
|
||
// Stop profiling and unlock the buffers and deallocate pool.
|
||
//
|
||
|
||
State = KeStopProfile (Profile->ProfileObject);
|
||
ASSERT (State != FALSE);
|
||
|
||
MmUnmapLockedPages (Profile->LockedBufferAddress, Profile->Mdl);
|
||
MmUnlockPages (Profile->Mdl);
|
||
ExFreePool (Profile->ProfileObject);
|
||
}
|
||
|
||
if (Profile->Process != NULL) {
|
||
ProcessAddress = CONTAINING_RECORD(Profile->Process, EPROCESS, Pcb);
|
||
ObDereferenceObject ((PVOID)ProcessAddress);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
NTSTATUS
|
||
NtCreateProfile (
|
||
OUT PHANDLE ProfileHandle,
|
||
IN HANDLE Process OPTIONAL,
|
||
IN PVOID RangeBase,
|
||
IN SIZE_T RangeSize,
|
||
IN ULONG BucketSize,
|
||
IN PULONG Buffer,
|
||
IN ULONG BufferSize,
|
||
IN KPROFILE_SOURCE ProfileSource,
|
||
IN KAFFINITY Affinity
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function creates a profile object.
|
||
|
||
Arguments:
|
||
|
||
ProfileHandle - Supplies a pointer to a variable that will receive
|
||
the profile object handle.
|
||
|
||
Process - Optionally, supplies the handle to the process whose
|
||
address space to profile. If the value is NULL (0), then
|
||
all address spaces are included in the profile.
|
||
|
||
RangeBase - Supplies the address of the first byte of the address
|
||
space for which profiling information is to be collected.
|
||
|
||
|
||
RangeSize - Supplies the size of the range to profile in the
|
||
address space. RangeBase and RangeSize are interpreted
|
||
such that RangeBase <= address < RangeBase+RangeSize
|
||
will generate a profile hit.
|
||
|
||
BucketSize - Supplies the LOG base 2 of the size of the profiling
|
||
bucket. Thus, BucketSize = 2 yields four-byte
|
||
buckets, BucketSize = 7 yields 128-byte buckets.
|
||
All profile hits in a given bucket will increment
|
||
the corresponding counter in Buffer. Buckets
|
||
cannot be smaller than a ULONG. The acceptable range
|
||
of this value is 2 to 30 inclusive.
|
||
|
||
Buffer - Supplies an array of ULONGs. Each ULONG is a hit counter,
|
||
which records the number of hits of the corresponding
|
||
bucket.
|
||
|
||
BufferSize - Size in bytes of Buffer.
|
||
|
||
ProfileSource - Supplies the source for the profile interrupt
|
||
|
||
Affinity - Supplies the processor set for the profile interrupt
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PEPROFILE Profile;
|
||
HANDLE Handle;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
PEPROCESS ProcessAddress;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
BOOLEAN HasPrivilege = FALSE;
|
||
ULONG Segment = FALSE;
|
||
#ifdef i386
|
||
USHORT PowerOf2;
|
||
#endif
|
||
|
||
//
|
||
// Verify that the base and size arguments are reasonable.
|
||
//
|
||
|
||
if (BufferSize == 0) {
|
||
return STATUS_INVALID_PARAMETER_7;
|
||
}
|
||
|
||
#ifdef i386
|
||
//
|
||
// sleazy use of bucket size. If bucket size is zero, and
|
||
// RangeBase < 64K, then create a profile object to attach
|
||
// to a non-flat code segment. In this case, RangeBase is
|
||
// the non-flat CS for this profile object.
|
||
//
|
||
|
||
if ((BucketSize == 0) && (RangeBase < (PVOID)(64 * 1024))) {
|
||
|
||
if (BufferSize < sizeof(ULONG)) {
|
||
return STATUS_INVALID_PARAMETER_7;
|
||
}
|
||
|
||
Segment = (ULONG)RangeBase;
|
||
RangeBase = 0;
|
||
BucketSize = RangeSize / (BufferSize / sizeof(ULONG));
|
||
|
||
//
|
||
// Convert Bucket size of log2(BucketSize)
|
||
//
|
||
PowerOf2 = 0;
|
||
BucketSize = BucketSize - 1;
|
||
while (BucketSize >>= 1) {
|
||
PowerOf2++;
|
||
}
|
||
|
||
BucketSize = PowerOf2 + 1;
|
||
|
||
if (BucketSize < 2) {
|
||
BucketSize = 2;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if ((BucketSize > 31) || (BucketSize < 2)) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if ((RangeSize >> (BucketSize - 2)) > BufferSize) {
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
if (((ULONG_PTR)RangeBase + RangeSize) < RangeSize) {
|
||
return STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
//
|
||
// Establish an exception handler, probe the output handle address, and
|
||
// attempt to create a profile object. If the probe fails, then return the
|
||
// exception code as the service status. Otherwise return the status value
|
||
// returned by the object insertion routine.
|
||
//
|
||
|
||
try {
|
||
//
|
||
// Get previous processor mode and probe output handle address if
|
||
// necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode ();
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
ProbeForWriteHandle(ProfileHandle);
|
||
|
||
ProbeForWrite(Buffer,
|
||
BufferSize,
|
||
sizeof(ULONG));
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the probe of the output handle address,
|
||
// then always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// TODO post NT5:
|
||
//
|
||
// Currently, if a process isn't specified, there is no privilege check if
|
||
// RangeBase > MM_HIGHEST_USER_ADDRESS.
|
||
// The check for user-space addresses is SeSystemProfilePrivilege.
|
||
// Querying a specific process requires only PROCESS_QUERY_INFORMATION.
|
||
//
|
||
// The spec says:
|
||
//
|
||
// Process - If specified, a handle to a process which describes the address space to profile.
|
||
// If not present, then all address spaces are included in the profile.
|
||
// Profiling a process requires PROCESS_QUERY_INFORMATION access to that process and
|
||
// SeProfileSingleProcessPrivilege privilege.
|
||
// Profiling all processes requires SeSystemProfilePrivilege privilege.
|
||
//
|
||
// So two changes appear needed.
|
||
// A check on SeProfileSingleProcessPrivilege needs to be added to the single process case,
|
||
// and SeSystemProfilePrivilege privilege should be required for both user and system address profiling.
|
||
//
|
||
|
||
|
||
if (!ARGUMENT_PRESENT(Process)) {
|
||
|
||
//
|
||
// Don't attach segmented profile objects to all processes
|
||
//
|
||
|
||
if (Segment) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Profile all processes. Make sure that the specified
|
||
// address range is in system space, unless SeSystemProfilePrivilege.
|
||
//
|
||
|
||
if (RangeBase <= MM_HIGHEST_USER_ADDRESS) {
|
||
|
||
//
|
||
// Check for privilege before allowing a user to profile
|
||
// all processes and USER addresses.
|
||
//
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
HasPrivilege = SeSinglePrivilegeCheck(
|
||
SeSystemProfilePrivilege,
|
||
PreviousMode
|
||
);
|
||
|
||
if (!HasPrivilege) {
|
||
#if DBG
|
||
DbgPrint("SeSystemProfilePrivilege needed to profile all USER addresses.\n");
|
||
#endif //DBG
|
||
return( STATUS_PRIVILEGE_NOT_HELD );
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
ProcessAddress = NULL;
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// Reference the specified process.
|
||
//
|
||
|
||
Status = ObReferenceObjectByHandle ( Process,
|
||
PROCESS_QUERY_INFORMATION,
|
||
PsProcessType,
|
||
PreviousMode,
|
||
(PVOID *)&ProcessAddress,
|
||
NULL );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
InitializeObjectAttributes( &ObjectAttributes,
|
||
NULL,
|
||
OBJ_EXCLUSIVE,
|
||
NULL,
|
||
NULL );
|
||
|
||
Status = ObCreateObject( KernelMode,
|
||
ExProfileObjectType,
|
||
&ObjectAttributes,
|
||
PreviousMode,
|
||
NULL,
|
||
sizeof(EPROFILE),
|
||
0,
|
||
sizeof(EPROFILE) + sizeof(KPROFILE),
|
||
(PVOID *)&Profile);
|
||
|
||
//
|
||
// If the profile object was successfully allocated, initialize
|
||
// the profile object.
|
||
//
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
|
||
if (ProcessAddress != NULL) {
|
||
Profile->Process = &ProcessAddress->Pcb;
|
||
} else {
|
||
Profile->Process = NULL;
|
||
}
|
||
|
||
Profile->RangeBase = RangeBase;
|
||
Profile->RangeSize = RangeSize;
|
||
Profile->Buffer = Buffer;
|
||
Profile->BufferSize = BufferSize;
|
||
Profile->BucketSize = BucketSize;
|
||
Profile->LockedBufferAddress = NULL;
|
||
Profile->Segment = Segment;
|
||
Profile->ProfileSource = ProfileSource;
|
||
Profile->Affinity = Affinity;
|
||
|
||
Status = ObInsertObject(Profile,
|
||
NULL,
|
||
PROFILE_CONTROL,
|
||
0,
|
||
(PVOID *)NULL,
|
||
&Handle);
|
||
//
|
||
// If the profile object was successfully inserted in the current
|
||
// process' handle table, then attempt to write the profile object
|
||
// handle value. If the write attempt fails, then do not report
|
||
// an error. When the caller attempts to access the handle value,
|
||
// an access violation will occur.
|
||
//
|
||
if (NT_SUCCESS(Status)) {
|
||
try {
|
||
*ProfileHandle = Handle;
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
}
|
||
}
|
||
} else {
|
||
//
|
||
// We failed, remove our reference to the process object.
|
||
//
|
||
|
||
if (ProcessAddress != NULL) {
|
||
ObDereferenceObject (ProcessAddress);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Return service status.
|
||
//
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
NtStartProfile (
|
||
IN HANDLE ProfileHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The NtStartProfile routine starts the collecting data for the
|
||
specified profile object. This involved allocating nonpaged
|
||
pool to lock the specified buffer in memory, creating a kernel
|
||
profile object and starting collecting on that profile object.
|
||
|
||
Arguments:
|
||
|
||
ProfileHandle - Supplies the profile handle to start profiling on.
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
PEPROFILE Profile;
|
||
PKPROFILE ProfileObject;
|
||
PVOID LockedVa;
|
||
BOOLEAN State;
|
||
PMDL Mdl;
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
Status = ObReferenceObjectByHandle (ProfileHandle,
|
||
PROFILE_CONTROL,
|
||
ExProfileObjectType,
|
||
PreviousMode,
|
||
&Profile,
|
||
NULL);
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Acquire the profile state mutex so two threads can't
|
||
// operate on the same profile object simultaneously.
|
||
//
|
||
|
||
KeWaitForSingleObject (&ExpProfileStateMutex,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)NULL);
|
||
|
||
//
|
||
// Make sure profiling is not already enabled.
|
||
//
|
||
|
||
if (Profile->LockedBufferAddress != NULL) {
|
||
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
||
ObDereferenceObject (Profile);
|
||
return STATUS_PROFILING_NOT_STOPPED;
|
||
}
|
||
|
||
if (ExpCurrentProfileUsage == ACTIVE_PROFILE_LIMIT) {
|
||
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
||
ObDereferenceObject (Profile);
|
||
return STATUS_PROFILING_AT_LIMIT;
|
||
}
|
||
|
||
ProfileObject = ExAllocatePoolWithTag (NonPagedPool,
|
||
MmSizeOfMdl(Profile->Buffer,
|
||
Profile->BufferSize) +
|
||
sizeof(KPROFILE),
|
||
'forP');
|
||
|
||
if (ProfileObject == NULL) {
|
||
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
||
ObDereferenceObject (Profile);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
Mdl = (PMDL)(ProfileObject + 1);
|
||
Profile->Mdl = Mdl;
|
||
Profile->ProfileObject = ProfileObject;
|
||
|
||
//
|
||
// Probe and lock the specified buffer.
|
||
//
|
||
|
||
MmInitializeMdl(Mdl, Profile->Buffer, Profile->BufferSize);
|
||
|
||
LockedVa = NULL;
|
||
|
||
try {
|
||
|
||
MmProbeAndLockPages (Mdl,
|
||
PreviousMode,
|
||
IoWriteAccess );
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
||
ExFreePool (ProfileObject);
|
||
ObDereferenceObject (Profile);
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Since kernel space is specified below, this call cannot raise
|
||
// an exception.
|
||
//
|
||
|
||
LockedVa = MmMapLockedPagesSpecifyCache (Profile->Mdl,
|
||
KernelMode,
|
||
MmCached,
|
||
NULL,
|
||
FALSE,
|
||
NormalPagePriority);
|
||
|
||
if (LockedVa == NULL) {
|
||
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
||
|
||
MmUnlockPages (Mdl);
|
||
ExFreePool (ProfileObject);
|
||
ObDereferenceObject (Profile);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Initialize the profile object.
|
||
//
|
||
|
||
KeInitializeProfile (ProfileObject,
|
||
Profile->Process,
|
||
Profile->RangeBase,
|
||
Profile->RangeSize,
|
||
Profile->BucketSize,
|
||
Profile->Segment,
|
||
Profile->ProfileSource,
|
||
Profile->Affinity);
|
||
|
||
State = KeStartProfile (ProfileObject, LockedVa);
|
||
ASSERT (State != FALSE);
|
||
|
||
Profile->LockedBufferAddress = LockedVa;
|
||
|
||
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
||
ObDereferenceObject (Profile);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
NtStopProfile (
|
||
IN HANDLE ProfileHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The NtStopProfile routine stops collecting data for the
|
||
specified profile object. This involves stopping the data
|
||
collection on the profile object, unlocking the locked buffers,
|
||
and deallocating the pool for the MDL and profile object.
|
||
|
||
Arguments:
|
||
|
||
ProfileHandle - Supplies a the profile handle to stop profiling.
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PEPROFILE Profile;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
BOOLEAN State;
|
||
PKPROFILE ProfileObject;
|
||
PMDL Mdl;
|
||
PVOID LockedBufferAddress;
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
Status = ObReferenceObjectByHandle( ProfileHandle,
|
||
PROFILE_CONTROL,
|
||
ExProfileObjectType,
|
||
PreviousMode,
|
||
(PVOID *)&Profile,
|
||
NULL);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
KeWaitForSingleObject( &ExpProfileStateMutex,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)NULL);
|
||
|
||
//
|
||
// Check to see if profiling is not active.
|
||
//
|
||
|
||
if (Profile->LockedBufferAddress == NULL) {
|
||
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
||
ObDereferenceObject (Profile);
|
||
return STATUS_PROFILING_NOT_STARTED;
|
||
}
|
||
|
||
//
|
||
// Stop profiling and unlock the buffer.
|
||
//
|
||
|
||
State = KeStopProfile (Profile->ProfileObject);
|
||
ASSERT (State != FALSE);
|
||
|
||
LockedBufferAddress = Profile->LockedBufferAddress;
|
||
Profile->LockedBufferAddress = NULL;
|
||
Mdl = Profile->Mdl;
|
||
ProfileObject = Profile->ProfileObject;
|
||
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
||
|
||
MmUnmapLockedPages (LockedBufferAddress, Mdl);
|
||
MmUnlockPages (Mdl);
|
||
ExFreePool (ProfileObject);
|
||
ObDereferenceObject (Profile);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
NtSetIntervalProfile (
|
||
IN ULONG Interval,
|
||
IN KPROFILE_SOURCE Source
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allows the system-wide interval (and thus the profiling
|
||
rate) for profiling to be set.
|
||
|
||
Arguments:
|
||
|
||
Interval - Supplies the sampling interval in 100ns units.
|
||
|
||
Source - Specifies the profile source to be set.
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
KeSetIntervalProfile (Interval, Source);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
NtQueryIntervalProfile (
|
||
IN KPROFILE_SOURCE ProfileSource,
|
||
OUT PULONG Interval
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queries the system-wide interval (and thus the profiling
|
||
rate) for profiling.
|
||
|
||
Arguments:
|
||
|
||
Source - Specifies the profile source to be queried.
|
||
|
||
Interval - Returns the sampling interval in 100ns units.
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG CapturedInterval;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
|
||
PreviousMode = KeGetPreviousMode ();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe accessibility of user's buffer.
|
||
//
|
||
|
||
try {
|
||
ProbeForWriteUlong (Interval);
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// If an exception occurs during the probe or capture
|
||
// of the initial values, then handle the exception and
|
||
// return the exception code as the status value.
|
||
//
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
}
|
||
|
||
CapturedInterval = KeQueryIntervalProfile (ProfileSource);
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
try {
|
||
*Interval = CapturedInterval;
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
NOTHING;
|
||
}
|
||
}
|
||
else {
|
||
*Interval = CapturedInterval;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
NtQueryPerformanceCounter (
|
||
OUT PLARGE_INTEGER PerformanceCounter,
|
||
OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns current value of performance counter and,
|
||
optionally, the frequency of the performance counter.
|
||
|
||
Performance frequency is the frequency of the performance counter
|
||
in Hertz, i.e., counts/second. Note that this value is implementation
|
||
dependent. If the implementation does not have hardware to support
|
||
performance timing, the value returned is 0.
|
||
|
||
Arguments:
|
||
|
||
PerformanceCounter - supplies the address of a variable to receive
|
||
the current Performance Counter value.
|
||
|
||
PerformanceFrequency - Optionally, supplies the address of a
|
||
variable to receive the performance counter frequency.
|
||
|
||
Return Value:
|
||
|
||
STATUS_ACCESS_VIOLATION or STATUS_SUCCESS.
|
||
|
||
--*/
|
||
|
||
{
|
||
KPROCESSOR_MODE PreviousMode;
|
||
LARGE_INTEGER KernelPerformanceFrequency;
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe accessibility of user's buffer.
|
||
//
|
||
|
||
try {
|
||
ProbeForWriteSmallStructure (PerformanceCounter,
|
||
sizeof (LARGE_INTEGER),
|
||
sizeof (ULONG));
|
||
|
||
if (ARGUMENT_PRESENT(PerformanceFrequency)) {
|
||
ProbeForWriteSmallStructure (PerformanceFrequency,
|
||
sizeof (LARGE_INTEGER),
|
||
sizeof (ULONG));
|
||
}
|
||
|
||
*PerformanceCounter = KeQueryPerformanceCounter (&KernelPerformanceFrequency);
|
||
|
||
if (ARGUMENT_PRESENT(PerformanceFrequency)) {
|
||
*PerformanceFrequency = KernelPerformanceFrequency;
|
||
}
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
//
|
||
// If an exception occurs during the probe or capture
|
||
// of the initial values, then handle the exception and
|
||
// return the exception code as the status value.
|
||
//
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
}
|
||
else {
|
||
*PerformanceCounter = KeQueryPerformanceCounter (&KernelPerformanceFrequency);
|
||
if (ARGUMENT_PRESENT(PerformanceFrequency)) {
|
||
*PerformanceFrequency = KernelPerformanceFrequency;
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|