938 lines
24 KiB
C
938 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) {
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we failed, remove our reference to the process object.
|
|||
|
//
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
if (ProcessAddress != NULL) {
|
|||
|
ObDereferenceObject ((PVOID)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;
|
|||
|
|
|||
|
PreviousMode = KeGetPreviousMode();
|
|||
|
|
|||
|
Status = ObReferenceObjectByHandle( ProfileHandle,
|
|||
|
PROFILE_CONTROL,
|
|||
|
ExProfileObjectType,
|
|||
|
PreviousMode,
|
|||
|
(PVOID *)&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 ((PVOID)Profile);
|
|||
|
return STATUS_PROFILING_NOT_STOPPED;
|
|||
|
}
|
|||
|
|
|||
|
if (ExpCurrentProfileUsage == ACTIVE_PROFILE_LIMIT) {
|
|||
|
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
|||
|
ObDereferenceObject ((PVOID)Profile);
|
|||
|
return STATUS_PROFILING_AT_LIMIT;
|
|||
|
}
|
|||
|
|
|||
|
ProfileObject = ExAllocatePoolWithTag (NonPagedPool,
|
|||
|
MmSizeOfMdl(Profile->Buffer,
|
|||
|
Profile->BufferSize) +
|
|||
|
sizeof(KPROFILE),
|
|||
|
'forP');
|
|||
|
|
|||
|
if (ProfileObject == NULL) {
|
|||
|
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
|||
|
ObDereferenceObject ((PVOID)Profile);
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Profile->Mdl = (PMDL)(ProfileObject + 1);
|
|||
|
Profile->ProfileObject = ProfileObject;
|
|||
|
|
|||
|
//
|
|||
|
// Probe and lock the specified buffer.
|
|||
|
//
|
|||
|
|
|||
|
MmInitializeMdl(Profile->Mdl, Profile->Buffer, Profile->BufferSize);
|
|||
|
|
|||
|
LockedVa = NULL;
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
MmProbeAndLockPages (Profile->Mdl,
|
|||
|
PreviousMode,
|
|||
|
IoWriteAccess );
|
|||
|
|
|||
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
|
|||
|
ExFreePool (ProfileObject);
|
|||
|
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
|||
|
ObDereferenceObject ((PVOID)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) {
|
|||
|
MmUnlockPages (Profile->Mdl);
|
|||
|
ExFreePool (ProfileObject);
|
|||
|
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
|||
|
ObDereferenceObject ((PVOID)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 ((PVOID)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;
|
|||
|
|
|||
|
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 ((PVOID)Profile);
|
|||
|
return STATUS_PROFILING_NOT_STARTED;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Stop profiling and unlock the buffer.
|
|||
|
//
|
|||
|
|
|||
|
State = KeStopProfile (Profile->ProfileObject);
|
|||
|
ASSERT (State != FALSE);
|
|||
|
|
|||
|
MmUnmapLockedPages (Profile->LockedBufferAddress, Profile->Mdl);
|
|||
|
MmUnlockPages (Profile->Mdl);
|
|||
|
ExFreePool (Profile->ProfileObject);
|
|||
|
Profile->LockedBufferAddress = NULL;
|
|||
|
KeReleaseMutex (&ExpProfileStateMutex, FALSE);
|
|||
|
|
|||
|
ObDereferenceObject ((PVOID)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;
|
|||
|
}
|