reactos/ntoskrnl/ke/profobj.c

373 lines
11 KiB
C

/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Kernel
* FILE: ntoskrnl/ke/profobj.c
* PURPOSE: Kernel Profiling
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
KIRQL KiProfileIrql = PROFILE_LEVEL;
LIST_ENTRY KiProfileListHead;
LIST_ENTRY KiProfileSourceListHead;
KSPIN_LOCK KiProfileLock;
ULONG KiProfileTimeInterval = 78125; /* Default resolution 7.8ms (sysinternals) */
ULONG KiProfileAlignmentFixupInterval;
/* FUNCTIONS *****************************************************************/
VOID
NTAPI
KeInitializeProfile(PKPROFILE Profile,
PKPROCESS Process,
PVOID ImageBase,
SIZE_T ImageSize,
ULONG BucketSize,
KPROFILE_SOURCE ProfileSource,
KAFFINITY Affinity)
{
/* Initialize the Header */
Profile->Type = ProfileObject;
Profile->Size = sizeof(KPROFILE);
/* Copy all the settings we were given */
Profile->Process = Process;
Profile->RangeBase = ImageBase;
Profile->BucketShift = BucketSize - 2; /* See ntinternals.net -- Alex */
Profile->RangeLimit = (PVOID)((ULONG_PTR)ImageBase + ImageSize);
Profile->Started = FALSE;
Profile->Source = ProfileSource;
Profile->Affinity = Affinity;
}
BOOLEAN
NTAPI
KeStartProfile(IN PKPROFILE Profile,
IN PVOID Buffer)
{
KIRQL OldIrql;
PKPROFILE_SOURCE_OBJECT SourceBuffer;
PKPROFILE_SOURCE_OBJECT CurrentSource;
BOOLEAN FreeBuffer = TRUE, SourceFound = FALSE, StartedProfile;
PKPROCESS ProfileProcess;
PLIST_ENTRY NextEntry;
/* Allocate a buffer first, before we raise IRQL */
SourceBuffer = ExAllocatePoolWithTag(NonPagedPool,
sizeof(KPROFILE_SOURCE_OBJECT),
'forP');
if (!SourceBuffer) return FALSE;
RtlZeroMemory(SourceBuffer, sizeof(KPROFILE_SOURCE_OBJECT));
/* Raise to profile IRQL and acquire the profile lock */
KeRaiseIrql(KiProfileIrql, &OldIrql);
KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
/* Make sure it's not running */
if (!Profile->Started)
{
/* Set it as Started */
Profile->Buffer = Buffer;
Profile->Started = TRUE;
StartedProfile = TRUE;
/* Get the process, if any */
ProfileProcess = Profile->Process;
/* Check where we should insert it */
if (ProfileProcess)
{
/* Insert it into the Process List */
InsertTailList(&ProfileProcess->ProfileListHead, &Profile->ProfileListEntry);
}
else
{
/* Insert it into the Global List */
InsertTailList(&KiProfileListHead, &Profile->ProfileListEntry);
}
/* Start looping */
for (NextEntry = KiProfileSourceListHead.Flink;
NextEntry != &KiProfileSourceListHead;
NextEntry = NextEntry->Flink)
{
/* Get the entry */
CurrentSource = CONTAINING_RECORD(NextEntry,
KPROFILE_SOURCE_OBJECT,
ListEntry);
/* Check if it's the same as the one being requested now */
if (CurrentSource->Source == Profile->Source)
{
/* It is, break out */
SourceFound = TRUE;
break;
}
}
/* See if the loop found something */
if (!SourceFound)
{
/* Nothing found, use our allocated buffer */
CurrentSource = SourceBuffer;
/* Set up the Source Object */
CurrentSource->Source = Profile->Source;
InsertHeadList(&KiProfileSourceListHead, &CurrentSource->ListEntry);
/* Don't free the pool later on */
FreeBuffer = FALSE;
}
}
else
{
/* Already running so nothing to start */
StartedProfile = FALSE;
}
/* Release the profile lock */
KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
/* Tell HAL to start the profile interrupt */
HalStartProfileInterrupt(Profile->Source);
/* Lower back to original IRQL */
KeLowerIrql(OldIrql);
/* Free the pool */
if (FreeBuffer) ExFreePoolWithTag(SourceBuffer, 'forP');
/* Return whether we could start the profile */
return StartedProfile;
}
BOOLEAN
NTAPI
KeStopProfile(IN PKPROFILE Profile)
{
KIRQL OldIrql;
PKPROFILE_SOURCE_OBJECT CurrentSource = NULL;
PLIST_ENTRY NextEntry;
BOOLEAN SourceFound = FALSE, StoppedProfile;
/* Raise to profile IRQL and acquire the profile lock */
KeRaiseIrql(KiProfileIrql, &OldIrql);
KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
/* Make sure it's running */
if (Profile->Started)
{
/* Remove it from the list and disable */
RemoveEntryList(&Profile->ProfileListEntry);
Profile->Started = FALSE;
StoppedProfile = TRUE;
/* Start looping */
for (NextEntry = KiProfileSourceListHead.Flink;
NextEntry != &KiProfileSourceListHead;
NextEntry = NextEntry->Flink)
{
/* Get the entry */
CurrentSource = CONTAINING_RECORD(NextEntry,
KPROFILE_SOURCE_OBJECT,
ListEntry);
/* Check if this is the Source Object */
if (CurrentSource->Source == Profile->Source)
{
/* Remember we found one */
SourceFound = TRUE;
/* Remove it and break out */
RemoveEntryList(&CurrentSource->ListEntry);
break;
}
}
}
else
{
/* It wasn't! */
StoppedProfile = FALSE;
}
/* Release the profile lock */
KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
/* Stop the profile interrupt */
HalStopProfileInterrupt(Profile->Source);
/* Lower back to original IRQL */
KeLowerIrql(OldIrql);
/* Free the Source Object */
if (SourceFound) ExFreePool(CurrentSource);
/* Return whether we could stop the profile */
return StoppedProfile;
}
ULONG
NTAPI
KeQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource)
{
HAL_PROFILE_SOURCE_INFORMATION ProfileSourceInformation;
ULONG ReturnLength, Interval;
NTSTATUS Status;
/* Check what profile this is */
if (ProfileSource == ProfileTime)
{
/* Return the time interval */
Interval = KiProfileTimeInterval;
}
else if (ProfileSource == ProfileAlignmentFixup)
{
/* Return the alignment interval */
Interval = KiProfileAlignmentFixupInterval;
}
else
{
/* Request it from HAL */
ProfileSourceInformation.Source = ProfileSource;
Status = HalQuerySystemInformation(HalProfileSourceInformation,
sizeof(HAL_PROFILE_SOURCE_INFORMATION),
&ProfileSourceInformation,
&ReturnLength);
/* Check if HAL handled it and supports this profile */
if (NT_SUCCESS(Status) && (ProfileSourceInformation.Supported))
{
/* Get the interval */
Interval = ProfileSourceInformation.Interval;
}
else
{
/* Unsupported or invalid source, fail */
Interval = 0;
}
}
/* Return the interval we got */
return Interval;
}
VOID
NTAPI
KeSetIntervalProfile(IN ULONG Interval,
IN KPROFILE_SOURCE ProfileSource)
{
HAL_PROFILE_SOURCE_INTERVAL ProfileSourceInterval;
/* Check what profile this is */
if (ProfileSource == ProfileTime)
{
/* Set the interval through HAL */
KiProfileTimeInterval = (ULONG)HalSetProfileInterval(Interval);
}
else if (ProfileSource == ProfileAlignmentFixup)
{
/* Set the alignment interval */
KiProfileAlignmentFixupInterval = Interval;
}
else
{
/* HAL handles any other interval */
ProfileSourceInterval.Source = ProfileSource;
ProfileSourceInterval.Interval = Interval;
HalSetSystemInformation(HalProfileSourceInterval,
sizeof(HAL_PROFILE_SOURCE_INTERVAL),
&ProfileSourceInterval);
}
}
/*
* @implemented
*/
VOID
NTAPI
KeProfileInterrupt(IN PKTRAP_FRAME TrapFrame)
{
/* Called from HAL for Timer Profiling */
KeProfileInterruptWithSource(TrapFrame, ProfileTime);
}
VOID
NTAPI
KiParseProfileList(IN PKTRAP_FRAME TrapFrame,
IN KPROFILE_SOURCE Source,
IN PLIST_ENTRY ListHead)
{
PULONG BucketValue;
PKPROFILE Profile;
PLIST_ENTRY NextEntry;
ULONG_PTR ProgramCounter;
/* Get the Program Counter */
ProgramCounter = KeGetTrapFramePc(TrapFrame);
/* Loop the List */
for (NextEntry = ListHead->Flink;
NextEntry != ListHead;
NextEntry = NextEntry->Flink)
{
/* Get the entry */
Profile = CONTAINING_RECORD(NextEntry, KPROFILE, ProfileListEntry);
/* Check if the source is good, and if it's within the range */
if ((Profile->Source != Source) ||
(ProgramCounter < (ULONG_PTR)Profile->RangeBase) ||
(ProgramCounter > (ULONG_PTR)Profile->RangeLimit))
{
continue;
}
/* Get the Pointer to the Bucket Value representing this Program Counter */
BucketValue = (PULONG)((ULONG_PTR)Profile->Buffer +
(((ProgramCounter - (ULONG_PTR)Profile->RangeBase)
>> Profile->BucketShift) &~ 0x3));
/* Increment the value */
(*BucketValue)++;
}
}
/*
* @implemented
*
* Remarks:
* Called from HAL, this function looks up the process
* entries, finds the proper source object, verifies the
* ranges with the trapframe data, and inserts the information
* from the trap frame into the buffer, while using buckets and
* shifting like we specified. -- Alex
*/
VOID
NTAPI
KeProfileInterruptWithSource(IN PKTRAP_FRAME TrapFrame,
IN KPROFILE_SOURCE Source)
{
PKPROCESS Process = KeGetCurrentThread()->ApcState.Process;
/* We have to parse 2 lists. Per-Process and System-Wide */
KiParseProfileList(TrapFrame, Source, &Process->ProfileListHead);
KiParseProfileList(TrapFrame, Source, &KiProfileListHead);
}
/*
* @implemented
*/
VOID
NTAPI
KeSetProfileIrql(IN KIRQL ProfileIrql)
{
/* Set the IRQL at which Profiling will run */
KiProfileIrql = ProfileIrql;
}