/*++

Copyright (c) 1995-1997  Microsoft Corporation

Module Name:

    ref.cxx

Abstract:

    This module contains the default ntsd debugger extensions for
    Internet Information Server.

Author:

    Keith Moore (keithmo) 27-Aug-1997

Revision History:

--*/

#include "inetdbgp.h"

//
// The maximum number of contexts that may be passed to the "ref"
// extension command on the command line.
//

#define MAX_REF_CONTEXT 64


/************************************************************
 * Dump Reference Traces
 ************************************************************/


BOOL
IsContextInList(
    IN PVOID Context,
    IN PVOID * ContextList,
    IN LONG NumContextsInList
    )

/*++

Routine Description:

    Scans the given context list looking for the specified context value.

Arguments:

    Context - The context value to look for.

    ContextList - The context list to scan.

    NumContextsInList - The number of contexts in the context list.

Return Value:

    BOOL - TRUE if the context value is in the list, FALSE if not.

--*/

{
    while( NumContextsInList > 0 ) {
        if( *ContextList == Context ) {
            return TRUE;
        }

        ContextList++;
        NumContextsInList--;
    }

    return FALSE;
}


VOID
DumpReferenceLog(
    IN PSTR lpArgumentString,
    IN BOOLEAN fReverse
    )

/*++

Routine Description:

    Dumps the specified reference log either forwards (fReverse == FALSE)
    or backwards (fReverse == TRUE).

Arguments:

    lpArgumentString - An expression specifying the reference log to
        dump.

    fReverse - The dump direction.

Return Value:

    None.

--*/

{
    ULONG_PTR refLogAddress = 0;
    ULONG_PTR entryAddress;
    LONG numEntries;
    TRACE_LOG logHeader;
    REF_TRACE_LOG_ENTRY logEntry;
    LONG i;
    DWORD_PTR offset;
    PCHAR format;
    PVOID specificContexts[MAX_REF_CONTEXT];
    LONG numSpecificContexts = 0;
    LONG index;
    LONG direction;
    PSTR cmdName;
    UCHAR symbol[MAX_SYMBOL_LEN];

    direction = fReverse ? -1 : 1;
    cmdName = fReverse ? "rref" : "ref";

    //
    // Skip leading blanks.
    //

    while( *lpArgumentString == ' ' ||
           *lpArgumentString == '\t' ) {
        lpArgumentString++;
    }

    if( *lpArgumentString == '\0' ) {
        PrintUsage( cmdName );
        return;
    }

    refLogAddress = (ULONG_PTR)GetExpression( lpArgumentString );

    if( refLogAddress == 0 ) {

        dprintf(
            "inetdbg.%s: cannot evaluate \"%s\"\n",
            cmdName,
            lpArgumentString
            );

        return;

    }

    //
    // Skip to end of expression, then skip any blanks.
    //

    while( *lpArgumentString != ' ' &&
           *lpArgumentString != '\t' &&
           *lpArgumentString != '\0' ) {
        lpArgumentString++;
    }

    while( *lpArgumentString == ' ' ||
           *lpArgumentString == '\t' ) {
        lpArgumentString++;
    }

    //
    // If we have context values, use them.
    //

    while( *lpArgumentString != '\0' && numSpecificContexts < MAX_REF_CONTEXT ) {

        specificContexts[numSpecificContexts++] =
            (PVOID)GetExpression( lpArgumentString );

        while( *lpArgumentString != ' ' &&
               *lpArgumentString != '\t' &&
               *lpArgumentString != '\0' ) {
            lpArgumentString++;
        }

        while( *lpArgumentString == ' ' ||
               *lpArgumentString == '\t' ) {
            lpArgumentString++;
        }

    }

    //
    // Read the log header, perform some sanity checks.
    //

    if( !ReadMemory(
            refLogAddress,
            &logHeader,
            sizeof(logHeader),
            NULL
            ) ) {

        dprintf(
            "inetdbg.%s: cannot read memory @ %p\n",
            cmdName,
            (PVOID)refLogAddress
            );

        return;

    }

    dprintf(
        "inetdbg.%s: log @ %p:\n"
        "    Signature = %08lx (%s)\n"
        "    LogSize   = %lu\n"
        "    NextEntry = %lu\n"
        "    EntrySize = %lu\n"
        "    LogBuffer = %p\n",
        cmdName,
        (PVOID)refLogAddress,
        logHeader.Signature,
        logHeader.Signature == TRACE_LOG_SIGNATURE
            ? "OK"
            : logHeader.Signature == TRACE_LOG_SIGNATURE_X
              ? "FREED"
              : "INVALID",
        logHeader.LogSize,
        logHeader.NextEntry,
        logHeader.EntrySize,
        logHeader.LogBuffer
        );

    if( logHeader.LogBuffer > ( (PUCHAR)refLogAddress + sizeof(logHeader) ) ) {
        dprintf(
            "    Extra Data @ %p\n",
            (PVOID)( refLogAddress + sizeof(logHeader) )
            );
    }

    if( logHeader.Signature != TRACE_LOG_SIGNATURE &&
        logHeader.Signature != TRACE_LOG_SIGNATURE_X ) {

        dprintf(
            "inetdbg.%s: log @ %p has invalid signature %08lx:\n",
            cmdName,
            (PVOID)refLogAddress,
            logHeader.Signature
            );

        return;

    }

    if( logHeader.EntrySize != sizeof(logEntry) ) {

        dprintf(
            "inetdbg.%s: log @ %p is not a ref count log\n",
            cmdName,
            (PVOID)refLogAddress
            );

        return;

    }

    if( logHeader.NextEntry == -1 ) {

        dprintf(
            "inetdbg.%s: empty log @ %p\n",
            cmdName,
            (PVOID)refLogAddress
            );

        return;

    }

    //
    // Calculate the starting address and number of entries.
    //

    if( fReverse ) {
        if( logHeader.NextEntry < logHeader.LogSize ) {
            numEntries = logHeader.NextEntry + 1;
            index = logHeader.NextEntry;
        } else {
            numEntries = logHeader.LogSize;
            index = logHeader.NextEntry % logHeader.LogSize;
        }
    } else {
        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.LogBuffer + (ULONG_PTR)( index * sizeof(logEntry) );

    if( entryAddress >=
        ( (ULONG_PTR)logHeader.LogBuffer + (ULONG_PTR)( numEntries * sizeof(logEntry) ) ) ) {

        dprintf(
            "inetdbg.%s: log @ %p has invalid data\n",
            cmdName,
            (PVOID)refLogAddress
            );

        return;

    }

    //
    // Dump the log.
    //

    for( ;
         numEntries > 0 ;
         index += direction,
         numEntries--,
         entryAddress += ( direction * sizeof(logEntry) ) ) {

        if( CheckControlC() ) {
            break;
        }

        if( index >= logHeader.LogSize ) {
            index = 0;
            entryAddress = (ULONG_PTR)logHeader.LogBuffer;
        } else if( index < 0 ) {
            index = logHeader.LogSize - 1;
            entryAddress = (ULONG_PTR)logHeader.LogBuffer + (ULONG_PTR)( index * sizeof(logEntry) );
        }

        if( !ReadMemory(
                entryAddress,
                &logEntry,
                sizeof(logEntry),
                NULL
                ) ) {

            dprintf(
                "inetdbg.%s: cannot read memory @ %p\n",
                cmdName,
                (ULONG_PTR)entryAddress
                );

            return;

        }

        if( ( numSpecificContexts == 0 ) ||
            IsContextInList(
                logEntry.Context,
                specificContexts,
                numSpecificContexts
                ) ) {

            dprintf(
                "\nThread = %08p, Context = %08p, NewRefCount = %-10ld : %ld\n",
                logEntry.Thread,
                logEntry.Context,
                logEntry.NewRefCount,
                index
                );

            if (    logEntry.Context1 != REF_TRACE_EMPTY_CONTEXT
                 || logEntry.Context2 != REF_TRACE_EMPTY_CONTEXT
                 || logEntry.Context3 != REF_TRACE_EMPTY_CONTEXT
                 ) {

                //
                //  if the caller passed extended context values,
                //  write them to the log
                //
                //  NOTE we use REF_TRACE_EMPTY_CONTEXT in all extended
                //  contexts as the signal that a caller does not use
                //  extended context - avoids spew for callers who don't care.
                //

                dprintf(
                    "Context1 = %08p, Context2 = %08p, Context3 = %08p\n",
                    logEntry.Context1,
                    logEntry.Context2,
                    logEntry.Context3
                    );
            }

            for( i = 0 ; i < REF_TRACE_LOG_STACK_DEPTH ; i++ ) {

                if( logEntry.Stack[i] == NULL ) {
                    break;
                }

                GetSymbol(
                    (ULONG_PTR) logEntry.Stack[i],
                    symbol,
                    &offset
                    );

                if( symbol[0] == '\0' ) {
                    format = "    %08p\n";
                } else
                if( offset == 0 ) {
                    format = "    %08p : %s\n";
                } else {
                    format = "    %08p : %s+0x%lx\n";
                }

                dprintf(
                    format,
                    logEntry.Stack[i],
                    symbol,
                    offset
                    );

            }

        }

    }

} // DumpReferenceLog


DECLARE_API( ref )

/*++

Routine Description:

    This function is called as an NTSD extension to format and dump
    a reference trace log.

Arguments:

    hCurrentProcess - Supplies a handle to the current process (at the
        time the extension was called).

    hCurrentThread - Supplies a handle to the current thread (at the
        time the extension was called).

    CurrentPc - Supplies the current pc at the time the extension is
        called.

    lpExtensionApis - Supplies the address of the functions callable
        by this extension.

    lpArgumentString - Supplies the asciiz string that describes the
        ansi string to be dumped.

Return Value:

    None.

--*/

{

    INIT_API();
    DumpReferenceLog( lpArgumentString, FALSE );

} // DECLARE_API( ref )


DECLARE_API( rref )

/*++

Routine Description:

    This function is called as an NTSD extension to format and dump
    a reference trace log backwards.

Arguments:

    hCurrentProcess - Supplies a handle to the current process (at the
        time the extension was called).

    hCurrentThread - Supplies a handle to the current thread (at the
        time the extension was called).

    CurrentPc - Supplies the current pc at the time the extension is
        called.

    lpExtensionApis - Supplies the address of the functions callable
        by this extension.

    lpArgumentString - Supplies the asciiz string that describes the
        ansi string to be dumped.

Return Value:

    None.

--*/

{

    INIT_API();
    DumpReferenceLog( lpArgumentString, TRUE );

} // DECLARE_API( rref )


DECLARE_API( resetref )

/*++

Routine Description:

    This function is called as an NTSD extension to reset a reference
    trace log back to its initial state.

Arguments:

    hCurrentProcess - Supplies a handle to the current process (at the
        time the extension was called).

    hCurrentThread - Supplies a handle to the current thread (at the
        time the extension was called).

    CurrentPc - Supplies the current pc at the time the extension is
        called.

    lpExtensionApis - Supplies the address of the functions callable
        by this extension.

    lpArgumentString - Supplies the asciiz string that describes the
        ansi string to be dumped.

Return Value:

    None.

--*/

{
    ULONG_PTR refLogAddress = 0;
    TRACE_LOG logHeader;

    INIT_API();

    //
    // Skip leading blanks.
    //

    while( *lpArgumentString == ' ' ||
           *lpArgumentString == '\t' ) {
        lpArgumentString++;
    }

    if( *lpArgumentString == '\0' ) {
        PrintUsage( "resetref" );
        return;
    }

    refLogAddress = GetExpression( lpArgumentString );

    if( refLogAddress == 0 ) {

        dprintf(
            "inetdbg.resetref: cannot evaluate \"%s\"\n",
            lpArgumentString
            );

        return;

    }

    //
    // Read the log header, perform some sanity checks.
    //

    if( !ReadMemory(
            refLogAddress,
            &logHeader,
            sizeof(logHeader),
            NULL
            ) ) {

        dprintf(
            "inetdbg.resetref: cannot read memory @ %p\n",
            refLogAddress
            );

        return;

    }

    dprintf(
        "inetdbg.resetref: log @ %p:\n"
        "    Signature = %08lx (%s)\n"
        "    LogSize   = %lu\n"
        "    NextEntry = %lu\n"
        "    EntrySize = %lu\n"
        "    LogBuffer = %08lp\n",
        (PVOID) refLogAddress,
        logHeader.Signature,
        logHeader.Signature == TRACE_LOG_SIGNATURE
            ? "OK"
            : logHeader.Signature == TRACE_LOG_SIGNATURE_X
              ? "FREED"
              : "INVALID",
        logHeader.LogSize,
        logHeader.NextEntry,
        logHeader.EntrySize,
        logHeader.LogBuffer
        );

    if( logHeader.LogBuffer > ( (PUCHAR)refLogAddress + sizeof(logHeader) ) ) {
        dprintf(
            "    Extra Data @ %08p\n",
            (PVOID) (refLogAddress + sizeof(logHeader))
            );
    }

    if( logHeader.Signature != TRACE_LOG_SIGNATURE &&
        logHeader.Signature != TRACE_LOG_SIGNATURE_X ) {

        dprintf(
            "inetdbg.resetref: log @ %p has invalid signature %08lx:\n",
            (PVOID) refLogAddress,
            logHeader.Signature
            );

        return;

    }

    if( logHeader.EntrySize != sizeof(REF_TRACE_LOG_ENTRY) ) {

        dprintf(
            "inetdbg.resetref: log @ %p is not a ref count log\n",
            (PVOID) refLogAddress
            );

        return;

    }

    //
    // Reset it.
    //

    logHeader.NextEntry = -1;

    if( !WriteMemory(
            refLogAddress,
            &logHeader,
            sizeof(logHeader),
            NULL
            ) ) {

        dprintf(
            "inetdbg.resetref: cannot write memory @ %p\n",
            (PVOID) refLogAddress
            );

        return;

    }

    dprintf(
        "inetdbg.resetref: log @ %p reset\n",
        (PVOID) refLogAddress
        );

} // DECLARE_API( resetref )