reactos/reactos/ntoskrnl/ke/profobj.c

292 lines
8.1 KiB
C
Raw Normal View History

/*
* 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 <internal/debug.h>
/* GLOBALS *******************************************************************/
KIRQL KiProfileIrql = PROFILE_LEVEL;
LIST_ENTRY KiProfileListHead;
LIST_ENTRY KiProfileSourceListHead;
KSPIN_LOCK KiProfileLock;
ULONG KiProfileTimeInterval = 78125; /* Default resolution 7.8ms (sysinternals) */
/* FUNCTIONS *****************************************************************/
VOID
STDCALL
KeInitializeProfile(PKPROFILE Profile,
PKPROCESS Process,
PVOID ImageBase,
ULONG 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;
}
VOID
STDCALL
KeStartProfile(PKPROFILE Profile,
PVOID Buffer)
{
KIRQL OldIrql;
PKPROFILE_SOURCE_OBJECT SourceBuffer;
PKPROFILE_SOURCE_OBJECT CurrentSource;
BOOLEAN FreeBuffer = TRUE;
PKPROCESS ProfileProcess;
/* Allocate a buffer first, before we raise IRQL */
SourceBuffer = ExAllocatePoolWithTag(NonPagedPool,
sizeof(KPROFILE_SOURCE_OBJECT),
TAG('P', 'r', 'o', 'f'));
RtlZeroMemory(SourceBuffer, sizeof(KPROFILE_SOURCE_OBJECT));
/* Raise to PROFILE_LEVEL */
KeRaiseIrql(PROFILE_LEVEL, &OldIrql);
KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
/* Make sure it's not running */
if (!Profile->Started)
{
/* Set it as Started */
Profile->Buffer = Buffer;
Profile->Started = 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);
}
/* Check if this type of profile (source) is already running */
LIST_FOR_EACH(CurrentSource, &KiProfileSourceListHead, KPROFILE_SOURCE_OBJECT, ListEntry)
{
/* Check if it's the same as the one being requested now */
if (CurrentSource->Source == Profile->Source)
{
break;
}
}
/* See if the loop found something */
if (!CurrentSource)
{
/* 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;
}
}
/* Lower the IRQL */
KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
KeLowerIrql(OldIrql);
/* FIXME: Tell HAL to Start the Profile Interrupt */
//HalStartProfileInterrupt(Profile->Source);
/* Free the pool */
if (!FreeBuffer) ExFreePool(SourceBuffer);
}
BOOLEAN
STDCALL
KeStopProfile(PKPROFILE Profile)
{
KIRQL OldIrql;
PKPROFILE_SOURCE_OBJECT CurrentSource = NULL;
/* Raise to PROFILE_LEVEL and acquire spinlock */
KeRaiseIrql(PROFILE_LEVEL, &OldIrql);
KeAcquireSpinLockAtDpcLevel(&KiProfileLock);
/* Make sure it's running */
if (Profile->Started)
{
/* Remove it from the list and disable */
RemoveEntryList(&Profile->ProfileListEntry);
Profile->Started = FALSE;
/* Find the Source Object */
LIST_FOR_EACH(CurrentSource, &KiProfileSourceListHead, KPROFILE_SOURCE_OBJECT, ListEntry)
{
if (CurrentSource->Source == Profile->Source)
{
/* Remove it */
RemoveEntryList(&CurrentSource->ListEntry);
break;
}
}
}
/* Lower IRQL */
KeReleaseSpinLockFromDpcLevel(&KiProfileLock);
KeLowerIrql(OldIrql);
/* Stop Profiling. FIXME: Implement in HAL */
//HalStopProfileInterrupt(Profile->Source);
/* Free the Source Object */
if (CurrentSource) ExFreePool(CurrentSource);
/* FIXME */
return FALSE;
}
ULONG
STDCALL
KeQueryIntervalProfile(KPROFILE_SOURCE ProfileSource)
{
/* Check if this is the timer profile */
if (ProfileSource == ProfileTime)
{
/* Return the good old 100ns sampling interval */
return KiProfileTimeInterval;
}
else
{
/* Request it from HAL. FIXME: What structure is used? */
HalQuerySystemInformation(HalProfileSourceInformation,
sizeof(NULL),
NULL,
NULL);
return 0;
}
}
VOID
STDCALL
KeSetIntervalProfile(KPROFILE_SOURCE ProfileSource,
ULONG Interval)
{
/* Check if this is the timer profile */
if (ProfileSource == ProfileTime)
{
/* Set the good old 100ns sampling interval */
KiProfileTimeInterval = Interval;
}
else
{
/* Set it with HAL. FIXME: What structure is used? */
HalSetSystemInformation(HalProfileSourceInformation,
sizeof(NULL),
NULL);
}
}
/*
* @implemented
*/
VOID
STDCALL
KeProfileInterrupt(PKTRAP_FRAME TrapFrame)
{
/* Called from HAL for Timer Profiling */
KeProfileInterruptWithSource(TrapFrame, ProfileTime);
}
VOID
STDCALL
KiParseProfileList(IN PKTRAP_FRAME TrapFrame,
IN KPROFILE_SOURCE Source,
IN PLIST_ENTRY ListHead)
{
PULONG BucketValue;
PKPROFILE Profile;
/* Loop the List */
LIST_FOR_EACH(Profile, ListHead, KPROFILE, ProfileListEntry)
{
/* Check if the source is good, and if it's within the range */
#ifdef _M_IX86
if ((Profile->Source != Source) ||
(TrapFrame->Eip < (ULONG_PTR)Profile->RangeBase) ||
(TrapFrame->Eip > (ULONG_PTR)Profile->RangeLimit))
{
continue;
}
/* Get the Pointer to the Bucket Value representing this EIP */
BucketValue = (PULONG)((((ULONG_PTR)Profile->Buffer +
(TrapFrame->Eip - (ULONG_PTR)Profile->RangeBase))
>> Profile->BucketShift) &~ 0x3);
#elif defined(_M_PPC)
// XXX arty
#endif
/* 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
STDCALL
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
STDCALL
KeSetProfileIrql(IN KIRQL ProfileIrql)
{
/* Set the IRQL at which Profiling will run */
KiProfileIrql = ProfileIrql;
}