-/* $Id: profile.c,v 1.1 2004/06/23 22:31:51 ion Exp $
- *
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS kernel
* FILE: ntoskrnl/ke/profile.c
* PURPOSE: Kernel Profiling
- * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
- * UPDATE HISTORY:
- * Created 23/06/04
+ *
+ * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
*/
-#include <ddk/ntddk.h>
-#include <internal/io.h>
-#include <internal/ps.h>
-#include <internal/pool.h>
-
+/* INCLUDES *****************************************************************/
+#include <ntoskrnl.h>
#define NDEBUG
#include <internal/debug.h>
-/*
- * @unimplemented
- */
+/* 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;
+
+ /* Insert it into the Process List or Global List */
+ if (ProfileProcess) {
+
+ InsertTailList(&ProfileProcess->ProfileListHead, &Profile->ProfileListEntry);
+
+ } else {
+
+ 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
-KeProfileInterrupt(
- PKTRAP_FRAME TrapFrame
-)
+STDCALL
+KeSetIntervalProfile(KPROFILE_SOURCE ProfileSource,
+ ULONG Interval)
{
- UNIMPLEMENTED;
+ /* 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);
+
+ }
}
/*
- * @unimplemented
+ * @implemented
*/
+VOID
STDCALL
+KeProfileInterrupt(PKTRAP_FRAME TrapFrame)
+{
+ /* Called from HAL for Timer Profiling */
+ KeProfileInterruptWithSource(TrapFrame, ProfileTime);
+}
+
VOID
-KeProfileInterruptWithSource(
- IN PKTRAP_FRAME TrapFrame,
- IN KPROFILE_SOURCE Source
-)
+STDCALL
+KiParseProfileList(IN PKTRAP_FRAME TrapFrame,
+ IN KPROFILE_SOURCE Source,
+ IN PLIST_ENTRY ListHead)
{
- UNIMPLEMENTED;
+ 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 */
+ 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);
+
+ /* Increment the value */
+ ++BucketValue;
+ }
}
/*
- * @unimplemented
+ * @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
-KeSetProfileIrql(
- IN KIRQL ProfileIrql
-)
+STDCALL
+KeSetProfileIrql(IN KIRQL ProfileIrql)
{
- UNIMPLEMENTED;
+ /* Set the IRQL at which Profiling will run */
+ KiProfileIrql = ProfileIrql;
}