/*++

Copyright (c) 2000-2001 Microsoft Corporation

Module Name:

    filt.c

Abstract:

    Extensions for dumping filter information.

Author:

    Michael Courage (mcourage) 6-Apr-2000

Environment:

    User Mode.

Revision History:

--*/


#include "precomp.h"

//
// Private globals.
//

PSTR g_pFiltqActions[] =
    {
        "QUEUE_IRP          ",
        "DEQUEUE_IRP        ",
        "START_WRITE        ",
        "FINISH_WRITE       ",
        "BLOCK_WRITE        ",
        "WAKE_WRITE         ",
        "BLOCK_PARTIAL_WRITE",
        "WAKE_PARTIAL_WRITE "
    
    };

C_ASSERT( DIM(g_pFiltqActions) == FILTQ_ACTION_COUNT );


//
//  Public functions.
//

DECLARE_API( filter )

/*++

Routine Description:

    Dumps a filter channel.

Arguments:

    None.

Return Value:

    None.

--*/

{
    ULONG_PTR address = 0;
    ULONG result;
    UL_FILTER_CHANNEL channel;

    SNAPSHOT_EXTENSION_DATA();

    //
    // Snag the address from the command line.
    //

    address = GetExpression( args );

    if (address == 0)
    {
        address = GetExpression( "&http!g_pFilterChannel" );
    }

    if (address != 0)
    {
        if (!ReadMemory(
            address,
            &address,
            sizeof(address),
            NULL
                    ))
        {
            dprintf(
                "filter: Cannot read memory @ %p\n",
                address
                );
            return;
        }
    }

    if (address == 0)
    {
        PrintUsage( "filter" );
        return;
    }

    //
    // Read the filter.
    //

    if (!ReadMemory(
            address,
            &channel,
            sizeof(channel),
            &result
            ))
    {
        dprintf(
            "filter: cannot read UL_FILTER_CHANNEL @ %p\n",
            address
            );
        return;
    }

    DumpFilterChannel(
        "",
        "filter: ",
        address,
        &channel,
        0
        );
    

}   // filter


DECLARE_API( fproc )

/*++

Routine Description:

    Dumps a filter process.

Arguments:

    None.

Return Value:

    None.

--*/

{
    ULONG_PTR address = 0;
    ULONG result;
    UL_FILTER_PROCESS process;

    SNAPSHOT_EXTENSION_DATA();

    //
    // Snag the address from the command line.
    //

    address = GetExpression( args );

    if (address == 0)
    {
        PrintUsage( "fproc" );
        return;
    }

    //
    // Read the filter proc.
    //

    if (!ReadMemory(
            address,
            &process,
            sizeof(process),
            &result
            ))
    {
        dprintf(
            "fproc: cannot read UL_FILTER_PROCESS @ %p\n",
            address
            );
        return;
    }

    DumpFilterProc(
        "",
        "fproc: ",
        address,
        &process,
        0
        );
    

}   // fproc


DECLARE_API( fqlog )

/*++

Routine Description:

    Dumps the filter queue trace log.

Arguments:

    None.

Return Value:

    None.

--*/

{
    ULONG_PTR               address = 0;
    ULONG_PTR               context = 0;
    ULONG_PTR               flags = 0;
    ULONG_PTR               entryAddress;
    ULONG_PTR               logPointer;
    ULONG                   result;
    TRACE_LOG               logHeader;
    LONGLONG                numEntries;
    FILTQ_TRACE_LOG_ENTRY   logEntry;
    PSTR                    pAction;
    LONGLONG                index;
    ULONG                   offset1;
    ULONG                   offset2;
    CHAR                    invalidAction[sizeof("2047")];
    PSTR                    fileName;
    CHAR                    filePath[MAX_PATH];

    SNAPSHOT_EXTENSION_DATA();

    //
    // Snag the optional context from the command line.
    //

    context = GetExpression( args );

    //
    // Find the log.
    //

    address = GetExpression( "&http!g_pFilterQueueTraceLog" );

    if (address == 0)
    {
        dprintf( "fqlog: cannot find http!g_pFilterQueueTraceLog\n" );
        return;
    }

    //
    // Read the pointer.
    //

    if (!ReadMemory(
            address,
            &logPointer,
            sizeof(logPointer),
            &result
            ))
    {
        dprintf(
            "fqlog: cannot read PTRACE_LOG @ %p\n",
            address
            );
        return;
    }

    //
    // Read the log header.
    //

    if (!ReadMemory(
            logPointer,
            &logHeader,
            sizeof(logHeader),
            &result
            ))
    {
        dprintf(
            "fqlog: cannot read TRACE_LOG @ %p\n",
            logPointer
            );
        return;
    }

    dprintf(
        "fqlog: log @ %p\n"
        "    Signature = %08lx '%c%c%c%c' (%s)\n"
        "    TypeSignature = %08lx '%c%c%c%c'\n"
        "    LogSize   = %lu\n"
        "    NextEntry = %I64d\n"
        "    EntrySize = %lu\n"
        "    LogBuffer = %p\n",
        address,
        logHeader.Signature,
        DECODE_SIGNATURE(logHeader.Signature),
        logHeader.Signature == TRACE_LOG_SIGNATURE
            ? "OK"
            : logHeader.Signature == TRACE_LOG_SIGNATURE_X
                ? "FREED"
                : "INVALID",
        logHeader.TypeSignature,
        DECODE_SIGNATURE(logHeader.TypeSignature),
        logHeader.LogSize,
        logHeader.NextEntry,
        logHeader.EntrySize,
        logHeader.pLogBuffer
        );

    if (logHeader.pLogBuffer > ( (PUCHAR)address + sizeof(logHeader) ))
    {
        dprintf(
            "    ExtraData @ %p\n",
            address + sizeof(logHeader)
            );
    }

    if (logHeader.Signature != TRACE_LOG_SIGNATURE &&
        logHeader.Signature != TRACE_LOG_SIGNATURE_X)
    {
        dprintf(
            "fqlog: log @ %p has invalid signature %08lx:\n",
            address,
            logHeader.Signature
            );
        return;
    }

    if (logHeader.EntrySize != sizeof(logEntry)
        || logHeader.TypeSignature != FILTQ_TRACE_LOG_SIGNATURE)
    {
        dprintf(
            "fqlog: log @ %p is not a filter queue trace log\n",
            address
            );
        return;
    }

    if (logHeader.NextEntry == -1)
    {
        dprintf(
            "fqlog: empty log @ %p\n",
            address
            );
        return;
    }

    //
    // Calculate the log size to dump.
    //

    if (logHeader.NextEntry < logHeader.LogSize)
    {
        numEntries = logHeader.NextEntry + 1;
        index = 0;
    }
    else
    {
        numEntries = logHeader.LogSize;
        index = (logHeader.NextEntry + 1) % logHeader.LogSize;
    }

    entryAddress = (ULONG_PTR)logHeader.pLogBuffer +
        (ULONG_PTR)( index * sizeof(logEntry) );

    if (entryAddress >=
        ( (ULONG_PTR)logHeader.pLogBuffer + (ULONG_PTR)( numEntries * sizeof(logEntry) ) ) )
    {
        dprintf(
            "fqlog: log @ %p has invalid data\n",
            address
            );
        return;
    }

    //
    // Dump the log.
    //

    for (;
         numEntries > 0 ;
         index++,
         numEntries--,
         entryAddress += sizeof(logEntry))
     {
        if (CheckControlC())
        {
            break;
        }

        if (index >= logHeader.LogSize)
        {
            index = 0;
            entryAddress = (ULONG_PTR)logHeader.pLogBuffer;
        }

        if (!ReadMemory(
                entryAddress,
                &logEntry,
                sizeof(logEntry),
                NULL
                ))
        {
            dprintf(
                "fqlog: cannot read memory @ %p\n",
                entryAddress
                );
            return;
        }

        if (context == 0 ||
            context == (ULONG_PTR)logEntry.pConnection)
        {

            //
            // Grab the file name.
            //
            
            if (ReadMemory(
                    (ULONG_PTR)logEntry.pFileName,
                    filePath,
                    sizeof(filePath),
                    &result
                    ))
            {
                fileName = strrchr( filePath, '\\' );

                if (fileName != NULL)
                {
                    fileName++;
                }
                else
                {
                    fileName = filePath;
                }
            }
            else
            {
                sprintf(
                    filePath,
                    "%p",
                    logEntry.pFileName
                    );

                fileName = filePath;
            }

            //
            // And the action name.
            //
            
            if (logEntry.Action < FILTQ_ACTION_COUNT)
            {
                pAction = g_pFiltqActions[logEntry.Action];
            }
            else
            {
                sprintf( invalidAction, "%lu", (ULONG)logEntry.Action );
                pAction = invalidAction;
            }

                
            dprintf(
                "CPU=%lu Conn=%p Act=%s ReadIrps=%lu Writers=%lu, %s:%u\n",
                logEntry.Processor,
                logEntry.pConnection,
                pAction,
                logEntry.ReadIrps,
                logEntry.Writers,
                fileName,
                logEntry.LineNumber
                );
                
        }
    }

}   // fqlog