/*++

Copyright (c) 1995-1999  Microsoft Corporation

Module Name:

    dbginet.cxx

Abstract:

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

Author:

    Murali R. Krishnan (MuraliK)  16-Sept-1996

Revision History:

--*/

#include "inetdbgp.h"



/************************************************************
 * Scheduler Related functions
 ************************************************************/

// Keep this array in synch with the SCHED_ITEM_STATE enumeration

char * g_rgchSchedState[] = {
    "SiError",
    "SiIdle",
    "SiActive",
    "SiActivePeriodic",
    "SiCallbackPeriodic",
    "SiToBeDeleted",
    "SiMaxItems"
};

#define LookupSchedState( ItemState ) \
            ((((ItemState) >= SI_ERROR) && ((ItemState) <= SI_MAX_ITEMS)) ? \
             g_rgchSchedState[ (ItemState)] : "<Invalid>")


// Initialize class static members
CSchedData*       CSchedData::sm_psd = NULL;
CLockedDoubleList CSchedData::sm_lstSchedulers;
LONG              CSchedData::sm_nID = 0;
LONG              CThreadData::sm_nID = 1000;
LONG      SCHED_ITEM::sm_lSerialNumber = SCHED_ITEM::SERIAL_NUM_INITIAL_VALUE;


VOID
PrintSchedItem( SCHED_ITEM * pschDebuggee,
                SCHED_ITEM * pschDebugger,
                CHAR Verbosity);

VOID
PrintThreadDataThunk( PVOID ptdDebuggee,
                      PVOID ptdDebugger,
                      CHAR  chVerbosity,
                      DWORD iThunk);

VOID
PrintThreadData( CThreadData* ptdDebuggee,
                 CThreadData* ptdDebugger,
                 CHAR chVerbosity);

VOID
PrintSchedData( CSchedData* psdDebuggee,
                CSchedData* psdDebugger,
                CHAR chVerbosity);

VOID
PrintSchedDataThunk( PVOID psdDebuggee,
                      PVOID psdDebugger,
                      CHAR  chVerbosity,
                      DWORD iThunk);

VOID
DumpSchedItemList(
    CHAR Verbosity
    );

VOID
DumpSchedulersList(
    CHAR Verbosity
    );



DECLARE_API( sched )

/*++

Routine Description:

    This function is called as an NTSD extension to format and dump
    an object attributes structure.

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.

--*/
{
    BOOL          fRet;
    SCHED_ITEM    sch( NULL, NULL, NULL);
    SCHED_ITEM *  psch;

    INIT_API();

    while (*lpArgumentString == ' ')
        lpArgumentString++;

    if ( !*lpArgumentString )
    {
        PrintUsage( "sched" );
        return;
    }

    if ( *lpArgumentString == '-' )
    {
        lpArgumentString++;

        if ( *lpArgumentString == 'h' )
        {
            PrintUsage( "sched" );
            return;
        }
        else if ( *lpArgumentString == 'l' )
        {
            DumpSchedItemList( lpArgumentString[1] );
            return;
        }
        else if ( *lpArgumentString == 's' )
        {
            DumpSchedulersList( lpArgumentString[1] );
            return;
        }
        else if ( *lpArgumentString == 'S' )
        {
            CSchedData* psd = (CSchedData*) GetExpression(++lpArgumentString);

            if ( !psd )
            {
                dprintf( "inetdbg.sched: Unable to evaluate \"%s\"\n",
                         lpArgumentString );

                return;
            }

            DEFINE_CPP_VAR(CSchedData, sd);
            move(sd, psd);

            PrintSchedData(psd, GET_CPP_VAR_PTR(CSchedData, sd), '2');
            return;
        }
        else if ( *lpArgumentString == 'T' )
        {
            CThreadData* ptd =(CThreadData*) GetExpression(++lpArgumentString);

            if ( !ptd )
            {
                dprintf( "inetdbg.sched: Unable to evaluate \"%s\"\n",
                         lpArgumentString );

                return;
            }

            DEFINE_CPP_VAR(CThreadData, td);
            move(td, ptd);

            PrintThreadData(ptd, GET_CPP_VAR_PTR(CThreadData, td), '2');
            return;
        }
    } // while

    //
    //  Treat the argument as the address of a SCHED_ITEM
    //

    psch = (SCHED_ITEM * ) GetExpression( lpArgumentString );

    if ( !psch )
    {
        dprintf( "inetdbg.sched: Unable to evaluate \"%s\"\n",
                 lpArgumentString );

        return;
    }

    move( sch, psch );
    PrintSchedItem( psch, &sch, '2' );

    return;
} // DECLARE_API( sched )


VOID
PrintSchedItem( SCHED_ITEM  * pschDebuggee,
                SCHED_ITEM * pschDebugger,
                CHAR chVerbosity)
{
    if ( chVerbosity >= '0' )
    {
        //  Print all with one line summary info
        dprintf( "%p: Serial=%-6d  Flink=%p, Blink=%p, State=%s\n",
                 pschDebuggee,
                 pschDebugger->_dwSerialNumber,
                 pschDebugger->_ListEntry.Flink,
                 pschDebugger->_ListEntry.Blink,
                 LookupSchedState( pschDebugger->_siState) );
    }

    if ( chVerbosity >= '1')
    {
        UCHAR szSymFnCallback[MAX_SYMBOL_LEN];
        ULONG_PTR offset;

        GetSymbol((ULONG_PTR) pschDebugger->_pfnCallback,
                  szSymFnCallback, &offset);

        if (!*szSymFnCallback)
            sprintf((char*) szSymFnCallback, "%p()",
                    pschDebugger->_pfnCallback);

        dprintf( "\tSignature    = '%c%c%c%c'    Context     = %08p\n"
                 "\tmsecInterval = %8d  msecExpires = %I64d\n"
                 "\tpfnCallBack  = %s\n",
                 DECODE_SIGNATURE(pschDebugger->_Signature),
                 pschDebugger->_pContext,
                 pschDebugger->_msecInterval,
                 pschDebugger->_msecExpires,
                 szSymFnCallback
                 );
    }

    return;
} // PrintSchedItem()



VOID
PrintSchedItemThunk( PVOID pschDebuggee,
                     PVOID pschDebugger,
                     CHAR  chVerbosity,
                     DWORD iThunk)
{
    if ( ((SCHED_ITEM * )pschDebugger)->_Signature != SIGNATURE_SCHED_ITEM) {

        dprintf( "SCHED_ITEM(%p) signature %08lx doesn't"
                 " match expected %08lx\n",
                 pschDebuggee,
                 ((SCHED_ITEM * )pschDebugger)->_Signature,
                 SIGNATURE_SCHED_ITEM
                 );
        return;
    }

    PrintSchedItem( (SCHED_ITEM*) pschDebuggee,
                    (SCHED_ITEM*) pschDebugger,
                    chVerbosity);

} // PrintSchedItemThunk()


VOID
PrintThreadData( CThreadData* ptdDebuggee,
                 CThreadData* ptdDebugger,
                 CHAR chVerbosity)
{
    if ( chVerbosity >= '0' )
    {
        //  Print all with one line summary info
        dprintf( "CThreadData %p: ID=%-6d, Flink=%p, Blink=%p\n",
                 ptdDebuggee,
                 ptdDebugger->m_nID,
                 ptdDebugger->m_leThreads.Flink,
                 ptdDebugger->m_leThreads.Blink
                 );
    }

    if ( chVerbosity >= '1')
    {
        dprintf( "\tm_dwSignature   = '%c%c%c%c'   m_psdOwner      = %08lp\n"
                 "\tm_hevtShutdown  = %08lp  m_hThreadSelf   = %08lx.\n"
                 ,
                 DECODE_SIGNATURE(ptdDebugger->m_dwSignature),
                 ptdDebugger->m_psdOwner,
                 ptdDebugger->m_hevtShutdown,
                 ptdDebugger->m_hThreadSelf
                 );
    }

    return;
} // PrintThreadData()



VOID
PrintThreadDataThunk( PVOID ptdDebuggee,
                      PVOID ptdDebugger,
                      CHAR  chVerbosity,
                      DWORD iThunk)
{
    if (((CThreadData*) ptdDebugger)->m_dwSignature != SIGNATURE_THREADDATA)
    {
        dprintf( "CThreadData(%08p) signature %08lx doesn't"
                 " match expected: %08lx\n",
                 ptdDebuggee,
                 ((CThreadData * )ptdDebugger)->m_dwSignature,
                 SIGNATURE_THREADDATA
                 );
        return;
    }

    PrintThreadData( (CThreadData *) ptdDebuggee,
                     (CThreadData *) ptdDebugger,
                     chVerbosity);

} // PrintThreadDataThunk()


VOID
PrintSchedData( CSchedData* psdDebuggee,
                CSchedData* psdDebugger,
                CHAR chVerbosity)
{
    if ( chVerbosity >= '0' )
    {
        //  Print all with one line summary info
        dprintf( "CSchedData %p: ID=%d, Flink=%p, Blink=%p\n",
                 psdDebuggee,
                 psdDebugger->m_nID,
                 psdDebugger->m_leGlobalList.Flink,
                 psdDebugger->m_leGlobalList.Blink
                 );
    }

    if ( chVerbosity >= '1')
    {
        dprintf( "\tm_dwSignature   = '%c%c%c%c'    m_lstItems      = %08lp\n"
                 "\tm_lstThreads    = %08lp  m_cRefs         = %d\n"
                 "\tm_hevtNotify    = %08lp  m_fShutdown     = %s\n"
                 "\tm_pachSchedItems= %08lp\n"
                 ,
                 DECODE_SIGNATURE(psdDebugger->m_dwSignature),
                 &psdDebuggee->m_lstItems,
                 &psdDebuggee->m_lstThreads,
                 psdDebugger->m_cRefs,
                 psdDebugger->m_hevtNotify,
                 BoolValue(psdDebugger->m_fShutdown),
                 psdDebugger->m_pachSchedItems
                 );
    }

    if ( chVerbosity >= '2')
    {
        dprintf("Threads\n");

        EnumLinkedList((LIST_ENTRY*)&psdDebuggee->m_lstThreads.m_list.m_leHead,
                       PrintThreadDataThunk,
                       chVerbosity,
                       sizeof(CThreadData),
                       FIELD_OFFSET( CThreadData, m_leThreads)
                       );
    }

    return;
} // PrintSchedData()



VOID
PrintSchedDataThunk( PVOID psdDebuggee,
                     PVOID psdDebugger,
                     CHAR  chVerbosity,
                     DWORD iThunk)
{
    if (((CSchedData*) psdDebugger)->m_dwSignature != SIGNATURE_SCHEDDATA)
    {
        dprintf( "CSchedData(%08p) signature %08lx doesn't"
                 " match expected: %08lx\n",
                 psdDebuggee,
                 ((CSchedData*) psdDebugger)->m_dwSignature,
                 SIGNATURE_SCHEDDATA
                 );
        return;
    }

    PrintSchedData( (CSchedData*) psdDebuggee,
                    (CSchedData*) psdDebugger,
                    chVerbosity);

} // PrintSchedDataThunk()




VOID
DumpSchedItemList(
    CHAR Verbosity
    )
{
    CSchedData** ppsd = (CSchedData**) GetExpression(
                        IisRtlVar("&%s!CSchedData__sm_psd"));
    if (NULL == ppsd)
    {
        dprintf("Unable to get %s\n", IisRtlVar("&%s!CSchedData__sm_psd"));
        return;
    }

    CSchedData* psd;
    move(psd, ppsd);

    DEFINE_CPP_VAR( CSchedData, sd);
    move(sd, psd);

    PrintSchedData(psd, GET_CPP_VAR_PTR(CSchedData, sd), Verbosity);
    dprintf("\n");

    EnumLinkedList( (LIST_ENTRY*) &psd->m_lstItems.m_list.m_leHead,
                    PrintSchedItemThunk,
                    Verbosity,
                    sizeof( SCHED_ITEM),
                    FIELD_OFFSET( SCHED_ITEM, _ListEntry)
                    );
    return;
} // DumpSchedItemList()



VOID
DumpSchedulersList(
    CHAR Verbosity
    )
{
    CLockedDoubleList* plstSchedulers = (CLockedDoubleList*) GetExpression(
                        IisRtlVar("&%s!CSchedData__sm_lstSchedulers"));

    if (NULL == plstSchedulers)
    {
        dprintf("Unable to get %s\n",
                IisRtlVar("&%s!CSchedData__sm_lstSchedulers"));
        return;
    }

    EnumLinkedList( (LIST_ENTRY*) &plstSchedulers->m_list.m_leHead,
                    PrintSchedDataThunk,
                    Verbosity,
                    sizeof(CSchedData),
                    FIELD_OFFSET( CSchedData, m_leGlobalList)
                    );
    return;
} // DumpSchedulersList()




/************************************************************
 * Allocation cache Related functions
 ************************************************************/

ALLOC_CACHE_HANDLER::ALLOC_CACHE_HANDLER(
    IN LPCSTR pszName,
    IN const ALLOC_CACHE_CONFIGURATION * pacConfig)
{}

ALLOC_CACHE_HANDLER::~ALLOC_CACHE_HANDLER(VOID)
{}


VOID
PrintAcacheHandler( IN ALLOC_CACHE_HANDLER * pachDebuggee,
                    IN ALLOC_CACHE_HANDLER * pachDebugger,
                    IN CHAR chVerbostity);

VOID
DumpAcacheGlobals( VOID );


VOID
DumpAcacheList(
    CHAR Verbosity
    );


DECLARE_API( acache )

/*++

Routine Description:

    This function is called as an NTSD extension to format and dump
    an object attributes structure.

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.

--*/
{
    BOOL          fRet;
    ALLOC_CACHE_HANDLER * pach;

    //
    // Since ALLOC_CACHE_HANDLER is a C++ object with a non-void
    //  constructor, we will have to copy the contents to a temporary
    //  buffer and cast the value appropriately.
    //
    CHAR                 achItem[sizeof(ALLOC_CACHE_HANDLER)];
    ALLOC_CACHE_HANDLER * pachCopy = (ALLOC_CACHE_HANDLER *) achItem;

    INIT_API();

    while (*lpArgumentString == ' ')
        lpArgumentString++;

    if ( !*lpArgumentString )
    {
        PrintUsage( "acache" );
        return;
    }

    if ( *lpArgumentString == '-' )
    {
        lpArgumentString++;

        if ( *lpArgumentString == 'h' )
        {
            PrintUsage( "acache" );
            return;
        }

        if ( *lpArgumentString == 'g' )
        {
            DumpAcacheGlobals();
            return;
        }

        if ( *lpArgumentString == 'l' ) {

            DumpAcacheList( lpArgumentString[1] );
            return;
        }

    } // while

    //
    //  Treat the argument as the address of an AtqContext
    //

    dprintf( "inetdbg.acache: Trying to access %s\n",
             lpArgumentString );

    pach = (ALLOC_CACHE_HANDLER * ) GetExpression( lpArgumentString );

    if ( !pach )
    {
        dprintf( "inetdbg.acache: Unable to evaluate \"%s\"\n",
                 lpArgumentString );

        return;
    }

    moveBlock( achItem, pach, sizeof(ALLOC_CACHE_HANDLER));
    PrintAcacheHandler( pach, pachCopy, '2' );

    return;
} // DECLARE_API( acache )


VOID
PrintAcacheHandler( ALLOC_CACHE_HANDLER * pachDebuggee,
                    ALLOC_CACHE_HANDLER * pachDebugger,
                    CHAR  chVerbosity)
{
    if ( chVerbosity >= '0') {
        dprintf(
                "ACACHE[%8p] "
                , pachDebuggee
                );
        dstring( "Name", (PVOID) pachDebugger->m_pszName, 40);
    }

    if ( chVerbosity >= '1') {
        dprintf("\t(Size=%d bytes, Concurrency=%d, Threshold=%u)"
                " FillPattern=%08lX\n"
                "\tTotal=%d."
                " Calls:(Alloc=%d, Free=%d)"
                " FreeEntries=%d. Heap=%p.\n"
                ,
                pachDebugger->m_acConfig.cbSize,
                pachDebugger->m_acConfig.nConcurrency,
                pachDebugger->m_acConfig.nThreshold,
                pachDebugger->m_nFillPattern,
                pachDebugger->m_nTotal,
                pachDebugger->m_nAllocCalls, pachDebugger->m_nFreeCalls,
                pachDebugger->m_nFreeEntries, pachDebugger->m_hHeap
                );
    }

    return;
} // PrintAcacheHandler()

VOID
PrintAcacheHandlerThunk( PVOID pachDebuggee,
                         PVOID pachDebugger,
                         CHAR  chVerbosity,
                         DWORD iCount)
{
    dprintf( "[%d] ", iCount);
    PrintAcacheHandler( (ALLOC_CACHE_HANDLER *) pachDebuggee,
                        (ALLOC_CACHE_HANDLER *) pachDebugger,
                        chVerbosity);
    return;
} // PrintAcacheHandlerThunk()

VOID
DumpAcacheGlobals( VOID )
{
    LIST_ENTRY * pachList;
    LIST_ENTRY   achList;

    dprintf("Allocation Cache Globals:\n");

    pachList = (LIST_ENTRY *)
        GetExpression( IisRtlVar("&%s!ALLOC_CACHE_HANDLER__sm_lItemsHead"));

    if ( NULL == pachList) {

        dprintf( " Unable to get Allocation cache list object, %s\n",
                 IisRtlVar("&%s!ALLOC_CACHE_HANDLER__sm_lItemsHead"));
        return;
    }

    move( achList, pachList);

    dprintf( " AllocCacheList  Flink = %08p  Blink = %08p\n",
             achList.Flink, achList.Blink
             );

    dprintf("\tsizeof(ALLOC_CACHE_HANDLER) = %d\n",
            sizeof(ALLOC_CACHE_HANDLER));
    return;
} // DumpAcacheGlobals()




VOID
DumpAcacheList(
    CHAR Verbosity
    )
{
    LIST_ENTRY *         pachListHead;

    pachListHead = (LIST_ENTRY *)
        GetExpression( IisRtlVar("&%s!ALLOC_CACHE_HANDLER__sm_lItemsHead"));

    if ( NULL == pachListHead) {

        dprintf( " Unable to get Alloc Cache List object, %s\n",
                 IisRtlVar("&%s!ALLOC_CACHE_HANDLER__sm_lItemsHead"));
        return;
    }

    EnumLinkedList( pachListHead, PrintAcacheHandlerThunk, Verbosity,
                    sizeof( ALLOC_CACHE_HANDLER),
                    FIELD_OFFSET( ALLOC_CACHE_HANDLER, m_lItemsEntry)
                    );
    return;
} // DumpAcacheList()




/************************************************************
 * Dump Symbols from stack
 ************************************************************/


DECLARE_API( ds )

/*++

Routine Description:

    This function is called as an NTSD extension to format and dump
    symbols on the stack.

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 startingAddress;
    ULONG_PTR stack;
    DWORD i;
    UCHAR symbol[MAX_SYMBOL_LEN];
    ULONG_PTR offset;
    PCHAR format;
    BOOL validSymbolsOnly = FALSE;
    MODULE_INFO moduleInfo;


    INIT_API();

    //
    // Skip leading blanks.
    //

    while( *lpArgumentString == ' ' ) {
        lpArgumentString++;
    }

    if( *lpArgumentString == '-' ) {
        lpArgumentString++;
        switch( *lpArgumentString ) {
        case 'v' :
        case 'V' :
            validSymbolsOnly = TRUE;
            lpArgumentString++;
            break;

        default :
            PrintUsage( "ds" );
            return;
        }
    }

    while( *lpArgumentString == ' ' ) {
        lpArgumentString++;
    }

    //
    // By default, start at the current stack location. Otherwise,
    // start at the given address.
    //

    if( !*lpArgumentString ) {
#if defined(_X86_)
        lpArgumentString = "esp";
#elif defined(_AMD64_)
        lpArgumentString = "rsp";
#elif defined(_IA64_)
        lpArgumentString = "sp";
#else
#error "unsupported CPU"
#endif
    }

    startingAddress = GetExpression( lpArgumentString );

    if( startingAddress == 0 ) {

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

        return;

    }

    //
    // Ensure startingAddress is DWORD aligned.
    //

    startingAddress &= ~(sizeof(ULONG_PTR)-1);

    //
    // Read the stack.
    //

    for( i = 0 ; i < NUM_STACK_SYMBOLS_TO_DUMP ; startingAddress += sizeof(ULONG_PTR) ) {

        if( CheckControlC() ) {
            break;
        }

        if( ReadMemory(
                startingAddress,
                &stack,
                sizeof(stack),
                NULL
                ) ) {

            GetSymbol(
                stack,
                symbol,
                &offset
                );

            if( symbol[0] == '\0' ) {
                if( FindModuleByAddress(
                        stack,
                        &moduleInfo
                        ) ) {
                    strcpy( (CHAR *)symbol, moduleInfo.FullName );
                    offset = DIFF(stack - moduleInfo.DllBase);
                }
            }

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

            dprintf(
                format,
                startingAddress,
                stack,
                symbol,
                (DWORD)offset
                );

            i++;

        } else {

            dprintf(
                "!inetdbg.ds: cannot read memory @ %p\n",
                startingAddress
                );

            return;

        }

    }

    dprintf(
        "!inetdbg.ds %s%lx to dump next block\n",
        validSymbolsOnly ? "-v " : "",
        startingAddress
        );

} // DECLARE_API( ds )