/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

   ioPerf.c

Abstract:

    This module contains the routines to collect performance info for driver calls...

Author:

    Mike Fortin (mrfortin) May 8, 2000

Revision History:

--*/

#include "iomgr.h"

#if (( defined(_X86_) ) && ( FPO ))
#pragma optimize( "y", off )    // disable FPO for consistent stack traces
#endif

NTSTATUS
IoPerfInit(
    );

NTSTATUS
IoPerfReset(
    );

NTSTATUS
FASTCALL
IoPerfCallDriver(
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  OUT PIRP    Irp
    );

VOID
FASTCALL 
IoPerfCompleteRequest (
    IN PIRP Irp,
    IN CCHAR PriorityBoost
    );

#ifndef NTPERF
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGEWMI, IoPerfCallDriver)
#pragma alloc_text(PAGEWMI, IoPerfInit)
#pragma alloc_text(PAGEWMI, IoPerfReset)
#endif
#endif // NTPERF


NTSTATUS
IoPerfInit(
    )
{
    if ( IopVerifierOn ){ 
        // We will not log driver hooks if the verifier has
        // also been turned on
        // Probably want to log some event or make a testenv note about
        // the perf implications of having the verifier turned on
        //
        return STATUS_UNSUCCESSFUL;
    }

    //
    // Enable and hook in the Perf Routines
    //
    InterlockedExchangePointer((PVOID *)&pIofCallDriver, (PVOID) IoPerfCallDriver);
    InterlockedExchangePointer((PVOID *)&pIofCompleteRequest, (PVOID) IoPerfCompleteRequest);

    ASSERT(KeGetCurrentIrql() <= APC_LEVEL);

    return STATUS_SUCCESS;
}

NTSTATUS
IoPerfReset(
    )
{
    if ( IopVerifierOn ){ 
        // We did not replace the function ptrs if the verifier
        // also was turned on, so just return
        //
        return STATUS_UNSUCCESSFUL;
    }

    //
    // Reset to init values, see IopSetIoRoutines
    //
    InterlockedExchangePointer((PVOID *)&pIofCallDriver, (PVOID) IopfCallDriver);
    InterlockedExchangePointer((PVOID *)&pIofCompleteRequest, (PVOID) IopfCompleteRequest);

    ASSERT(KeGetCurrentIrql() <= APC_LEVEL);

    return STATUS_SUCCESS;
}


ULONG IopPerfDriverUniqueMatchId=0;

NTSTATUS
FASTCALL
IoPerfCallDriver(
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  OUT PIRP    Irp
    )

/*++

Routine Description:

    This routine is invoked to pass an I/O Request Packet (IRP) to another
    driver at its dispatch routine, logging perf data along the way.

Arguments:

    DeviceObject - Pointer to device object to which the IRP should be passed.

    Irp - Pointer to IRP for request.

Return Value:

    Return status from driver's dispatch routine.

--*/

{
    PIO_STACK_LOCATION irpSp;
    PDRIVER_OBJECT driverObject;
    NTSTATUS status;
    PVOID PerfInfoRoutineAddr;
    ULONG MatchId;
#ifdef NTPERF
    ULONGLONG  PerfInfoTimeOfCall = PerfGetCycleCount();
#endif // NTPERF

    //
    // Ensure that this is really an I/O Request Packet.
    //

    ASSERT( Irp->Type == IO_TYPE_IRP );

    irpSp = IoGetNextIrpStackLocation( Irp );

    //
    // Invoke the driver at its dispatch routine entry point.
    //

    driverObject = DeviceObject->DriverObject;

    //
    // Prevent the driver from unloading.
    //

    ObReferenceObject(DeviceObject);

    MatchId = InterlockedIncrement( &IopPerfDriverUniqueMatchId );

    PerfInfoRoutineAddr = driverObject->MajorFunction[irpSp->MajorFunction];

    //
    // Log the Call Event
    //
    if (PERFINFO_IS_GROUP_ON(PERF_DRIVERS)) {                                                           
        PERFINFO_DRIVER_MAJORFUNCTION MFInfo;                                                           
        MFInfo.MajorFunction = irpSp->MajorFunction;                                                  
        MFInfo.MinorFunction = irpSp->MinorFunction;                                                  
        MFInfo.RoutineAddr = driverObject->MajorFunction[irpSp->MajorFunction];                              
        MFInfo.Irp = Irp;
        MFInfo.UniqMatchId = MatchId;                                                          
        if (Irp->Flags & IRP_ASSOCIATED_IRP) {
            ASSERT (Irp->AssociatedIrp.MasterIrp != NULL);
            if (Irp->AssociatedIrp.MasterIrp != NULL) {
                //
                // The check for MasterIrp is defensive code.
                // We have hit a bugcechk when a filter driver set the
                // IRP_ASSOCIATED_IRP bit while MasterIrp pointing to NULL.
                //
                // The ASSERT above was to catch similar problems before we release.
                //
                MFInfo.FileNamePointer = Irp->AssociatedIrp.MasterIrp->Tail.Overlay.OriginalFileObject;
            } else {
                MFInfo.FileNamePointer = NULL;
            }
        } else {                                                                                        
            MFInfo.FileNamePointer = Irp->Tail.Overlay.OriginalFileObject;
        }                                                                                               
        PerfInfoLogBytes(                                                                               
            PERFINFO_LOG_TYPE_DRIVER_MAJORFUNCTION_CALL,                                                
            &MFInfo,                                                                                    
            sizeof(MFInfo)                                                                              
            );                                                                                          
    }

    //
    // Do the normal IopfCallDriver work
    //
    status = IopfCallDriver(DeviceObject, Irp );

    //
    // Log the Return
    //
    if (PERFINFO_IS_GROUP_ON(PERF_DRIVERS)) {                                                           
        PERFINFO_DRIVER_MAJORFUNCTION_RET MFInfo;                                                       
        MFInfo.Irp = Irp;
        MFInfo.UniqMatchId = MatchId;

        PERFINFO_DRIVER_INTENTIONAL_DELAY();

        PerfInfoLogBytes(
            PERFINFO_LOG_TYPE_DRIVER_MAJORFUNCTION_RETURN,
            &MFInfo,
            sizeof(MFInfo)
            );

        PERFINFO_DRIVER_STACKTRACE();
    }

    ObDereferenceObject(DeviceObject);

    return status;
}

VOID
FASTCALL 
IoPerfCompleteRequest (
    IN PIRP Irp,
    IN CCHAR PriorityBoost
    )

/*++

Routine Description:

    This routine is invoked when a driver completes an IRP, logging perf data 
    along the way.

Arguments:

    Irp - Pointer to IRP for completed request.

    PriorityBoost - Priority boost specified by the driver completing the IRP.

Return Value:

    None.
    
--*/

{
    PERFINFO_DRIVER_COMPLETE_REQUEST CompleteRequest;
    PERFINFO_DRIVER_COMPLETE_REQUEST_RET CompleteRequestRet;
    PIO_STACK_LOCATION irpSp;
    PVOID DriverRoutineAddr;
    ULONG MatchId;

    //
    // Initialize locals.
    //

    DriverRoutineAddr = NULL;
    
    //
    // If the packet looks weird/improper pass it on to the real IO completion routine
    // directly.
    //

    if (Irp->Type != IO_TYPE_IRP || Irp->CurrentLocation > (CCHAR) (Irp->StackCount + 1)) {
        IopfCompleteRequest(Irp, PriorityBoost);
        return;
    }

    //
    // Get current stack location and save the driver routine address to 
    // identify the driver that was processing the IRP when it got completed. If
    // device object is NULL, try to get the completion routine addr.
    //

    irpSp = IoGetCurrentIrpStackLocation( Irp );

    if (irpSp->DeviceObject) {

        //
        // We don't want to cause a bugcheck in this code even when something else is
        // corrupt.
        //

        ASSERT(irpSp->DeviceObject->DriverObject);

        if (irpSp->DeviceObject->DriverObject) {

            ASSERT(irpSp->MajorFunction <= IRP_MJ_MAXIMUM_FUNCTION);

            if (irpSp->MajorFunction <= IRP_MJ_MAXIMUM_FUNCTION) {

                DriverRoutineAddr = irpSp->DeviceObject->DriverObject->MajorFunction[irpSp->MajorFunction];
            }
        }
        
    } else {

        DriverRoutineAddr = irpSp->CompletionRoutine;
    }
    
    //
    // Bump the ID that gets used to match COMPLETE_REQUEST and COMPLETE_REQUEST_RET 
    // entries logged for an IRP completion.
    //

    MatchId = InterlockedIncrement( &IopPerfDriverUniqueMatchId );

    //
    // Log the start of the completion.
    //
    
    if (PERFINFO_IS_GROUP_ON(PERF_DRIVERS)) {                                                           

        CompleteRequest.Irp = Irp;
        CompleteRequest.UniqMatchId = MatchId;
        CompleteRequest.RoutineAddr = DriverRoutineAddr;

        PerfInfoLogBytes(                                                                               
            PERFINFO_LOG_TYPE_DRIVER_COMPLETE_REQUEST,
            &CompleteRequest,
            sizeof(CompleteRequest)
            );                                                                                          
    }

    //
    // Do the normal IopfCompleteIrp work.
    //

    IopfCompleteRequest(Irp, PriorityBoost);

    //
    // After this point no fields of Irp should be accessed. E.g. the Irp may
    // have been freed / reallocated etc. by a completion routine.
    //

    //
    // Log the return.
    //
    
    if (PERFINFO_IS_GROUP_ON(PERF_DRIVERS)) {                                                           

        CompleteRequestRet.Irp = Irp;
        CompleteRequestRet.UniqMatchId = MatchId;

        PerfInfoLogBytes(
            PERFINFO_LOG_TYPE_DRIVER_COMPLETE_REQUEST_RETURN,
            &CompleteRequestRet,
            sizeof(CompleteRequestRet)
            );
    }

    return;
}


VOID
IopPerfLogFileCreate(
    IN PFILE_OBJECT FileObject,
    IN PUNICODE_STRING CompleteName
    )
{
    PERFINFO_LOG_FILE_CREATE(FileObject, CompleteName);
}