375 lines
9.6 KiB
C
375 lines
9.6 KiB
C
|
|
||
|
/*++
|
||
|
|
||
|
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);
|
||
|
}
|