- NDK 0.98, now with versionned headers. Too many changes to list, see the TinyKRNL...
[reactos.git] / reactos / ntoskrnl / ex / profile.c
index cb9c86b..8bc0690 100644 (file)
 /*
  * COPYRIGHT:       See COPYING in the top level directory
- * PROJECT:         ReactOS kernel
+ * PROJECT:         ReactOS Kernel
  * FILE:            ntoskrnl/ex/profile.c
  * PURPOSE:         Support for Executive Profile Objects
- * 
- * PROGRAMMERS:     Alex Ionescu
+ * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
+ *                  Thomas Weidenmueller
  */
 
 /* INCLUDES *****************************************************************/
 
 #include <ntoskrnl.h>
+#define NDEBUG
 #include <internal/debug.h>
 
-/* This structure is a *GUESS* -- Alex */
-typedef struct _EPROFILE {
-    PEPROCESS Process;
-    PVOID ImageBase;
-    ULONG ImageSize;
-    ULONG BucketSize;
-    PVOID Buffer;
-    ULONG BufferSize;
-    PKPROFILE KeProfile;
-    KPROFILE_SOURCE ProfileSource;
-    KAFFINITY Affinity;
-    PMDL Mdl;
-    PVOID LockedBuffer;
-} EPROFILE, *PEPROFILE;
+#if defined (ALLOC_PRAGMA)
+#pragma alloc_text(INIT, ExpInitializeProfileImplementation)
+#endif
 
-/* GLOBALS *******************************************************************/
-
-POBJECT_TYPE EXPORTED ExProfileObjectType = NULL;
+#define TAG_PROFILE TAG('P', 'r', 'o', 'f')
 
-static KMUTEX ExpProfileMutex;
+/* GLOBALS *******************************************************************/
 
-#define PROFILE_CONTROL 1
+POBJECT_TYPE ExProfileObjectType = NULL;
+KMUTEX ExpProfileMutex;
 
-static GENERIC_MAPPING ExpProfileMapping = {
+GENERIC_MAPPING ExpProfileMapping =
+{
     STANDARD_RIGHTS_READ    | PROFILE_CONTROL,
     STANDARD_RIGHTS_WRITE   | PROFILE_CONTROL,
     STANDARD_RIGHTS_EXECUTE | PROFILE_CONTROL,
-    STANDARD_RIGHTS_ALL};
+    PROFILE_ALL_ACCESS
+};
+
+/* FUNCTIONS *****************************************************************/
 
-VOID 
-STDCALL
+VOID
+NTAPI
 ExpDeleteProfile(PVOID ObjectBody)
 {
     PEPROFILE Profile;
+    ULONG State;
 
     /* Typecast the Object */
     Profile = (PEPROFILE)ObjectBody;
-    
+
     /* Check if there if the Profile was started */
-    if (Profile->LockedBuffer) {
-        
+    if (Profile->LockedBufferAddress)
+    {
         /* Stop the Profile */
-        KeStopProfile(Profile->KeProfile);
-    
+        State = KeStopProfile(Profile->ProfileObject);
+        ASSERT(State != FALSE);
+
         /* Unmap the Locked Buffer */
-        MmUnmapLockedPages(Profile->LockedBuffer, Profile->Mdl);
+        MmUnmapLockedPages(Profile->LockedBufferAddress, Profile->Mdl);
         MmUnlockPages(Profile->Mdl);
         ExFreePool(Profile->Mdl);
     }
-  
-    /* Check if a Process is associated */
-    if (Profile->Process != NULL) {
-        
-        /* Dereference it */
-        ObDereferenceObject(Profile->Process);
-        Profile->Process = NULL;
-    }
+
+    /* Check if a Process is associated and reference it */
+    if (Profile->Process) ObDereferenceObject(Profile->Process);
 }
 
-VOID 
+VOID
 INIT_FUNCTION
+NTAPI
 ExpInitializeProfileImplementation(VOID)
 {
+    OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
+    UNICODE_STRING Name;
+    DPRINT("Creating Profile Object Type\n");
+
     /* Initialize the Mutex to lock the States */
-    KeInitializeMutex(&ExpProfileMutex, 0x40);
-    
-    /* Create the Object Type */
-    ExProfileObjectType = ExAllocatePool(NonPagedPool,sizeof(OBJECT_TYPE));
-    RtlInitUnicodeString(&ExProfileObjectType->TypeName, L"Profile");
-    ExProfileObjectType->Tag = TAG('P', 'R', 'O', 'F');
-    ExProfileObjectType->PeakObjects = 0;
-    ExProfileObjectType->PeakHandles = 0;
-    ExProfileObjectType->TotalObjects = 0;
-    ExProfileObjectType->TotalHandles = 0;
-    ExProfileObjectType->PagedPoolCharge = 0;
-    ExProfileObjectType->NonpagedPoolCharge = sizeof(EPROFILE);
-    ExProfileObjectType->Mapping = &ExpProfileMapping;
-    ExProfileObjectType->Dump = NULL;
-    ExProfileObjectType->Open = NULL;
-    ExProfileObjectType->Close = NULL;
-    ExProfileObjectType->Delete = ExpDeleteProfile;
-    ExProfileObjectType->Parse = NULL;
-    ExProfileObjectType->Security = NULL;
-    ExProfileObjectType->QueryName = NULL;
-    ExProfileObjectType->OkayToClose = NULL;
-    ExProfileObjectType->Create = NULL;
-    ObpCreateTypeObject(ExProfileObjectType);
+    KeInitializeMutex(&ExpProfileMutex, 64);
+
+    /* Create the Event Pair Object Type */
+    RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
+    RtlInitUnicodeString(&Name, L"Profile");
+    ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
+    ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KPROFILE);
+    ObjectTypeInitializer.GenericMapping = ExpProfileMapping;
+    ObjectTypeInitializer.PoolType = NonPagedPool;
+    ObjectTypeInitializer.DeleteProcedure = ExpDeleteProfile;
+    ObjectTypeInitializer.ValidAccessMask = PROFILE_ALL_ACCESS;
+    ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &ExProfileObjectType);
 }
 
-NTSTATUS 
-STDCALL 
+NTSTATUS
+NTAPI
 NtCreateProfile(OUT PHANDLE ProfileHandle,
                 IN HANDLE Process OPTIONAL,
-                IN PVOID ImageBase, 
-                IN ULONG ImageSize, 
+                IN PVOID RangeBase,
+                IN ULONG RangeSize,
                 IN ULONG BucketSize,
                 IN PVOID Buffer,
                 IN ULONG BufferSize,
@@ -119,35 +103,79 @@ NtCreateProfile(OUT PHANDLE ProfileHandle,
     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
     OBJECT_ATTRIBUTES ObjectAttributes;
     NTSTATUS Status = STATUS_SUCCESS;
-  
+    ULONG Segment = 0, Log2 = 0;
     PAGED_CODE();
 
     /* Easy way out */
-    if(BufferSize == 0) return STATUS_INVALID_PARAMETER_7;
-
-    /* Check the Parameters for validity */
-    if(PreviousMode != KernelMode) {
-        
-        _SEH_TRY {
-            
-            ProbeForWrite(ProfileHandle,
-                          sizeof(HANDLE),
-                          sizeof(ULONG));
-            
+    if(!BufferSize) return STATUS_INVALID_PARAMETER_7;
+
+    /* Check if this is a low-memory profile */
+    if ((!BucketSize) && (RangeBase < (PVOID)(0x10000)))
+    {
+        /* Validate size */
+        if (BufferSize < sizeof(ULONG)) return STATUS_INVALID_PARAMETER_7;
+
+        /* This will become a segmented profile object */
+        Segment = (ULONG)RangeBase;
+        RangeBase = 0;
+
+        /* Recalculate the bucket size */
+        BucketSize = RangeSize / (BufferSize / sizeof(ULONG));
+
+        /* Convert it to log2 */
+        BucketSize--;
+        while (BucketSize >>= 1) Log2++;
+        BucketSize += Log2 + 1;
+    }
+
+    /* Validate bucket size */
+    if ((BucketSize > 31) || (BucketSize < 2))
+    {
+        DPRINT1("Bucket size invalid\n");
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* Make sure that the buckets can map the range */
+    if ((RangeSize >> (BucketSize - 2)) > BufferSize)
+    {
+        DPRINT1("Bucket size too small\n");
+        return STATUS_BUFFER_TOO_SMALL;
+    }
+
+    /* Make sure that the range isn't too gigantic */
+    if (((ULONG_PTR)RangeBase + RangeSize) < RangeSize)
+    {
+        DPRINT1("Range too big\n");
+        return STATUS_BUFFER_OVERFLOW;
+    }
+
+    /* Check if we were called from user-mode */
+    if(PreviousMode != KernelMode)
+    {
+        /* Entry SEH */
+        _SEH_TRY
+        {
+            /* Make sure that the handle pointer is valid */
+            ProbeForWriteHandle(ProfileHandle);
+
+            /* Check if the buffer is valid */
             ProbeForWrite(Buffer,
                           BufferSize,
                           sizeof(ULONG));
-        } _SEH_HANDLE {
-            
+        }
+        _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
+        {
             Status = _SEH_GetExceptionCode();
-        } _SEH_END;
-    
+        }
+        _SEH_END;
+
+        /* Bail out if we failed */
         if(!NT_SUCCESS(Status)) return Status;
     }
 
     /* Check if a process was specified */
-    if (Process) {
-        
+    if (Process)
+    {
         /* Reference it */
         Status = ObReferenceObjectByHandle(Process,
                                            PROCESS_QUERY_INFORMATION,
@@ -156,15 +184,18 @@ NtCreateProfile(OUT PHANDLE ProfileHandle,
                                            (PVOID*)&pProcess,
                                            NULL);
         if (!NT_SUCCESS(Status)) return(Status);
-        
-    } else {
-        
+    }
+    else
+    {
+        /* Segmented profile objects cannot be used system-wide */
+        if (Segment) return STATUS_INVALID_PARAMETER;
+
         /* No process was specified, which means a System-Wide Profile */
         pProcess = NULL;
-        
+
         /* For this, we need to check the Privilege */
-        if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode)) {
-            
+        if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode))
+        {
             DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
             return STATUS_PRIVILEGE_NOT_HELD;
         }
@@ -183,20 +214,22 @@ NtCreateProfile(OUT PHANDLE ProfileHandle,
                             NULL,
                             sizeof(EPROFILE),
                             0,
-                            0,
+                            sizeof(EPROFILE) + sizeof(KPROFILE),
                             (PVOID*)&Profile);
     if (!NT_SUCCESS(Status)) return(Status);
 
     /* Initialize it */
-    Profile->ImageBase = ImageBase;
-    Profile->ImageSize = ImageSize;
+    Profile->RangeBase = RangeBase;
+    Profile->RangeSize = RangeSize;
     Profile->Buffer = Buffer;
     Profile->BufferSize = BufferSize;
     Profile->BucketSize = BucketSize;
-    Profile->LockedBuffer = NULL;
+    Profile->LockedBufferAddress = NULL;
+    Profile->Segment = Segment;
+    Profile->ProfileSource = ProfileSource;
     Profile->Affinity = Affinity;
     Profile->Process = pProcess;
-    
+
     /* Insert into the Object Tree */
     Status = ObInsertObject ((PVOID)Profile,
                              NULL,
@@ -205,87 +238,91 @@ NtCreateProfile(OUT PHANDLE ProfileHandle,
                              NULL,
                              &hProfile);
     ObDereferenceObject(Profile);
-    
+
     /* Check for Success */
-    if (!NT_SUCCESS(Status)) {
-        
+    if (!NT_SUCCESS(Status))
+    {
         /* Dereference Process on failure */
         if (pProcess) ObDereferenceObject(pProcess);
         return Status;
     }
-    
-    /* Copy the created handle back to the caller*/
-    _SEH_TRY {
-        
+
+    /* Enter SEH */
+    _SEH_TRY
+    {
+        /* Copy the created handle back to the caller*/
         *ProfileHandle = hProfile;
-        
-    } _SEH_HANDLE {
-        
+    }
+    _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
+    {
         Status = _SEH_GetExceptionCode();
-    } _SEH_END;
+    }
+    _SEH_END;
 
     /* Return Status */
     return Status;
 }
 
-NTSTATUS 
-STDCALL
+NTSTATUS
+NTAPI
 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter,
                           OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
 {
     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
     LARGE_INTEGER PerfFrequency;
     NTSTATUS Status = STATUS_SUCCESS;
-    
-    /* Check the Parameters for validity */
-    if(PreviousMode != KernelMode) {
-        
-        _SEH_TRY {
-            
-            ProbeForWrite(PerformanceCounter,
-                          sizeof(LARGE_INTEGER),
-                          sizeof(ULONG));
-            
-            ProbeForWrite(PerformanceFrequency,
-                          sizeof(LARGE_INTEGER),
-                          sizeof(ULONG));
-        } _SEH_HANDLE {
-            
+
+    /* Check if we were called from user-mode */
+    if(PreviousMode != KernelMode)
+    {
+        /* Entry SEH Block */
+        _SEH_TRY
+        {
+            /* Make sure the counter and frequency are valid */
+            ProbeForWriteLargeInteger(PerformanceCounter);
+            if (PerformanceFrequency)
+            {
+                ProbeForWriteLargeInteger(PerformanceFrequency);
+            }
+        }
+        _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
+        {
             Status = _SEH_GetExceptionCode();
-        } _SEH_END;
-    
+        }
+        _SEH_END;
+
+        /* If the pointers are invalid, bail out */
         if(!NT_SUCCESS(Status)) return Status;
     }
 
-    _SEH_TRY {
-        
+    /* Enter a new SEH Block */
+    _SEH_TRY
+    {
         /* Query the Kernel */
         *PerformanceCounter = KeQueryPerformanceCounter(&PerfFrequency);
-        
+
         /* Return Frequency if requested */
-        if(PerformanceFrequency) {
-            
-            *PerformanceFrequency = PerfFrequency;
-        }
-    } _SEH_HANDLE {
-        
+        if(PerformanceFrequency) *PerformanceFrequency = PerfFrequency;
+    }
+    _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
+    {
         Status = _SEH_GetExceptionCode();
-    
-    _SEH_END;
+    }
+    _SEH_END;
 
+    /* Return status to caller */
     return Status;
 }
 
-NTSTATUS 
-STDCALL 
+NTSTATUS
+NTAPI
 NtStartProfile(IN HANDLE ProfileHandle)
 {
     PEPROFILE Profile;
-    PKPROFILE KeProfile;
+    PKPROFILE ProfileObject;
     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
-    PVOID TempLockedBuffer;
+    PVOID TempLockedBufferAddress;
     NTSTATUS Status;
-  
     PAGED_CODE();
 
     /* Get the Object */
@@ -296,53 +333,53 @@ NtStartProfile(IN HANDLE ProfileHandle)
                                        (PVOID*)&Profile,
                                        NULL);
     if (!NT_SUCCESS(Status)) return(Status);
-    
+
     /* To avoid a Race, wait on the Mutex */
-    KeWaitForSingleObject(&ExpProfileMutex, 
+    KeWaitForSingleObject(&ExpProfileMutex,
                           Executive,
                           KernelMode,
                           FALSE,
                           NULL);
-    
+
     /* The Profile can still be enabled though, so handle that */
-    if (Profile->LockedBuffer) {
-    
+    if (Profile->LockedBufferAddress)
+    {
         /* Release our lock, dereference and return */
         KeReleaseMutex(&ExpProfileMutex, FALSE);
         ObDereferenceObject(Profile);
         return STATUS_PROFILING_NOT_STOPPED;
     }
-    
+
     /* Allocate a Kernel Profile Object. */
-    KeProfile = ExAllocatePoolWithTag(NonPagedPool, 
+    ProfileObject = ExAllocatePoolWithTag(NonPagedPool,
                                       sizeof(EPROFILE),
-                                      TAG('P', 'r', 'o', 'f'));
-   
+                                      TAG_PROFILE);
+
     /* Allocate the Mdl Structure */
     Profile->Mdl = MmCreateMdl(NULL, Profile->Buffer, Profile->BufferSize);
-    
+
     /* Probe and Lock for Write Access */
     MmProbeAndLockPages(Profile->Mdl, PreviousMode, IoWriteAccess);
-    
+
     /* Map the pages */
-    TempLockedBuffer = MmMapLockedPages(Profile->Mdl, KernelMode);
-    
+    TempLockedBufferAddress = MmMapLockedPages(Profile->Mdl, KernelMode);
+
     /* Initialize the Kernel Profile Object */
-    Profile->KeProfile = KeProfile;
-    KeInitializeProfile(KeProfile,
+    Profile->ProfileObject = ProfileObject;
+    KeInitializeProfile(ProfileObject,
                         (PKPROCESS)Profile->Process,
-                        Profile->ImageBase,
-                        Profile->ImageSize,
+                        Profile->RangeBase,
+                        Profile->RangeSize,
                         Profile->BucketSize,
                         Profile->ProfileSource,
                         Profile->Affinity);
-    
+
     /* Start the Profiling */
-    KeStartProfile(KeProfile, TempLockedBuffer);
-    
+    KeStartProfile(ProfileObject, TempLockedBufferAddress);
+
     /* Now it's safe to save this */
-    Profile->LockedBuffer = TempLockedBuffer;
-    
+    Profile->LockedBufferAddress = TempLockedBufferAddress;
+
     /* Release mutex, dereference and return */
     KeReleaseMutex(&ExpProfileMutex, FALSE);
     ObDereferenceObject(Profile);
@@ -350,13 +387,12 @@ NtStartProfile(IN HANDLE ProfileHandle)
 }
 
 NTSTATUS
-STDCALL 
+NTAPI
 NtStopProfile(IN HANDLE ProfileHandle)
 {
     PEPROFILE Profile;
     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
     NTSTATUS Status;
-  
     PAGED_CODE();
 
     /* Get the Object */
@@ -367,32 +403,32 @@ NtStopProfile(IN HANDLE ProfileHandle)
                                        (PVOID*)&Profile,
                                        NULL);
     if (!NT_SUCCESS(Status)) return(Status);
-    
+
     /* Get the Mutex */
-    KeWaitForSingleObject(&ExpProfileMutex, 
+    KeWaitForSingleObject(&ExpProfileMutex,
                           Executive,
                           KernelMode,
                           FALSE,
                           NULL);
-    
+
     /* Make sure the Profile Object is really Started */
-    if (!Profile->LockedBuffer) {
-    
+    if (!Profile->LockedBufferAddress)
+    {
         Status = STATUS_PROFILING_NOT_STARTED;
         goto Exit;
     }
-    
+
     /* Stop the Profile */
-    KeStopProfile(Profile->KeProfile);
-    
+    KeStopProfile(Profile->ProfileObject);
+
     /* Unlock the Buffer */
-    MmUnmapLockedPages(Profile->LockedBuffer, Profile->Mdl);
+    MmUnmapLockedPages(Profile->LockedBufferAddress, Profile->Mdl);
     MmUnlockPages(Profile->Mdl);
-    ExFreePool(Profile->KeProfile);
-    
+    ExFreePool(Profile->ProfileObject);
+
     /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
-    Profile->LockedBuffer = NULL;
-    
+    Profile->LockedBufferAddress = NULL;
+
 Exit:
     /* Release Mutex, Dereference and Return */
     KeReleaseMutex(&ExpProfileMutex, FALSE);
@@ -400,60 +436,62 @@ Exit:
     return Status;
 }
 
-NTSTATUS 
-STDCALL 
-NtQueryIntervalProfile(IN  KPROFILE_SOURCE ProfileSource,
+NTSTATUS
+NTAPI
+NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource,
                        OUT PULONG Interval)
 {
     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
     ULONG ReturnInterval;
     NTSTATUS Status = STATUS_SUCCESS;
-  
     PAGED_CODE();
-    
-    /* Check the Parameters for validity */
-    if(PreviousMode != KernelMode) {
-        
-        _SEH_TRY {
-            
-            ProbeForWrite(Interval,
-                          sizeof(ULONG),
-                          sizeof(ULONG));
-            
-        } _SEH_HANDLE {
-            
+
+    /* Check if we were called from user-mode */
+    if(PreviousMode != KernelMode)
+    {
+        /* Enter SEH Block */
+        _SEH_TRY
+        {
+            /* Validate interval */
+            ProbeForWriteUlong(Interval);
+        }
+        _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
+        {
             Status = _SEH_GetExceptionCode();
-        } _SEH_END;
-    
+        }
+        _SEH_END;
+
+        /* If pointer was invalid, bail out */
         if(!NT_SUCCESS(Status)) return Status;
     }
-  
+
     /* Query the Interval */
     ReturnInterval = KeQueryIntervalProfile(ProfileSource);
 
-    /* Return the data */
-    _SEH_TRY  {
-        
+    /* Enter SEH block for return */
+    _SEH_TRY
+    {
+        /* Return the data */
         *Interval = ReturnInterval;
-        
-    } _SEH_HANDLE {
-        
+    }
+    _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
+    {
         Status = _SEH_GetExceptionCode();
-        
-    _SEH_END;
-    
-    /* Return Success */  
+    }
+    _SEH_END;
+
+    /* Return Success */
     return STATUS_SUCCESS;
 }
 
-NTSTATUS 
-STDCALL 
+NTSTATUS
+NTAPI
 NtSetIntervalProfile(IN ULONG Interval,
                      IN KPROFILE_SOURCE Source)
 {
     /* Let the Kernel do the job */
     KeSetIntervalProfile(Interval, Source);
-    
+
     /* Nothing can go wrong */
     return STATUS_SUCCESS;
 }