/*++

Copyright (c) 2001  Microsoft Corporation

Module Name:

    verifier.c

Abstract:

    Application verifier debugger extension for both ntsd and kd.

Author:

    Silviu Calinoiu (SilviuC) 4-Mar-2001

Environment:

    User Mode.

Revision History:

--*/

#include "precomp.h"
#pragma hdrstop

ULONG
VrfGetArguments (
    PCHAR ArgsString,
    PCHAR Args[],
    ULONG NoOfArgs
    );

VOID
VrfHelp (
    );

BOOLEAN
VrfTraceInitialize (
    );

ULONG64
VrfTraceAddress ( 
    ULONG TraceIndex
    );

VOID
VrfTraceDump (
    ULONG TraceIndex
    );

VOID
VrfDumpSettings (
    );

VOID
VrfDumpVspaceLog (
    ULONG NoOfEntries,
    ULONG64 Address
    );

VOID
VrfDumpHeapLog (
    ULONG NoOfEntries,
    ULONG64 Address
    );

DECLARE_API( avrf )

/*++

Routine Description:

    Application verifier debugger extension.

Arguments:

    args - 

Return Value:

    None

--*/
{
    PCHAR Args[16];
    ULONG NoOfArgs, I;

    INIT_API();

    //
    // Parse arguments.
    //

    NoOfArgs = VrfGetArguments ((PCHAR)args,
                                Args,
                                16);
#if 0
    for (I = 0; I < NoOfArgs; I += 1) {
        dprintf ("%02u: %s\n", I, Args[I]);
    }
#endif

    //
    // Check if help needed
    //

    if (NoOfArgs > 0 && strstr (Args[0], "?") != NULL) {
        VrfHelp ();
        goto Exit;
    }

    if (VrfTraceInitialize() == FALSE) {
        goto Exit;
    }

    if (NoOfArgs > 1 && _stricmp (Args[0], "-trace") == 0) {
        VrfTraceDump (atoi(Args[1]));
        goto Exit;
    }
    
    if (NoOfArgs > 1 && _stricmp (Args[0], "-vs") == 0) {

        if (NoOfArgs > 2 && _stricmp (Args[1], "-a") == 0) {

            ULONG64 Address;
            BOOL Result;
            PCSTR Remainder;

            Result = GetExpressionEx (Args[2], &Address, &Remainder);

            if (Result == FALSE) {
                dprintf ("\nFailed to convert `%s' to an address.\n", Args[2]);
                goto Exit;
            }

            // sscanf (Args[2], "%I64X", &Address);
            dprintf ("Searching in vspace log for address %I64X ...\n\n", Address);
            VrfDumpVspaceLog (0, Address);
            goto Exit;
        }
        else {

            VrfDumpVspaceLog (atoi(Args[1]), 0);
            goto Exit;
        }
    }
    
    if (NoOfArgs > 1 && _stricmp (Args[0], "-hp") == 0) {

        if (NoOfArgs > 2 && _stricmp (Args[1], "-a") == 0) {

            ULONG64 Address;
            BOOL Result;
            PCSTR Remainder;

            Result = GetExpressionEx (Args[2], &Address, &Remainder);

            if (Result == FALSE) {
                dprintf ("\nFailed to convert `%s' to an address.\n", Args[2]);
                goto Exit;
            }

            // sscanf (Args[2], "%I64X", &Address);
            dprintf ("Searching in vspace log for address %I64X ...\n\n", Address);
            VrfDumpHeapLog (0, Address);
            goto Exit;
        }
        else {

            VrfDumpHeapLog (atoi(Args[1]), 0);
            goto Exit;
        }
    }
    
    //
    // If no option specified then we just print current settings.
    //

    VrfDumpSettings ();

Exit:

    EXIT_API();
    return S_OK;
}


VOID
VrfHelp (
    )
{
    dprintf ("Application verifier debugger extension                    \n"
             "                                                           \n"
             "!avrf                displays current settings and stop    \n"
             "                     data if a verifier stop happened.     \n"
             "!avrf -vs N          dumps last N entries from vspace log. \n"
             "!avrf -vs -a ADDR    searches ADDR in the vspace log.      \n"
             "!avrf -hp N          dumps last N entries from heap log.   \n"
             "!avrf -hp -a ADDR    searches ADDR in the heap log.        \n"
             "                                                           \n");
}


/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////// Argument parsing routines
/////////////////////////////////////////////////////////////////////

PCHAR 
VrfGetArgument (
    PCHAR Args,
    PCHAR * Next
    )
{
    PCHAR Start;

    if (Args == NULL) {
        return NULL;
    }

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

    if (*Args == '\0') {
        return NULL;
    }

    Start = Args;

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

    if (*Args == '\0') {
        *Next = NULL;
    }
    else {
        *Args = '\0';
        *Next = Args + 1;
    }

    return Start;
}

ULONG
VrfGetArguments (
    PCHAR ArgsString,
    PCHAR Args[],
    ULONG NoOfArgs
    )
{
    PCHAR Arg = ArgsString;
    PCHAR Next;
    ULONG Index;
    
    for (Index = 0; Index < NoOfArgs; Index += 1) {

        Arg = VrfGetArgument (Arg, &Next);

        if (Arg) {
            Args[Index] = Arg;
        }
        else {
            break;
        }

        Arg = Next;
    }

    return Index;
}

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////// Dump stack traces
/////////////////////////////////////////////////////////////////////

ULONG64 TraceDbArrayEnd;
ULONG PvoidSize;

BOOLEAN
VrfTraceInitialize (
    )
{
    ULONG64 TraceDatabaseAddress;
    ULONG64 TraceDatabase;

    //
    // Stack trace database address
    //

    TraceDatabaseAddress = GetExpression("ntdll!RtlpStackTraceDataBase");
    
    if ( TraceDatabaseAddress == 0 ) {
        dprintf( "Unable to resolve ntdll!RtlpStackTraceDataBase symbolic name.\n");
        return FALSE;
    }

    if (ReadPtr (TraceDatabaseAddress, &TraceDatabase ) != S_OK) {
        dprintf( "Cannot read pointer at ntdll!RtlpStackTraceDataBase\n" );
        return FALSE;
    }

    if (TraceDatabase == 0) {
        dprintf( "Stack traces not enabled (ntdll!RtlpStackTraceDataBase is null).\n" );
        return FALSE;
    }

    //
    // Find the array of stack traces
    //

    if (InitTypeRead(TraceDatabase, ntdll!_STACK_TRACE_DATABASE)) {
        dprintf("Unable to read type ntdll!_STACK_TRACE_DATABASE @ %p\n", TraceDatabase);
        return FALSE;
    }

    TraceDbArrayEnd = ReadField (EntryIndexArray);

    PvoidSize = GetTypeSize ("ntdll!PVOID");

    return TRUE;
}


ULONG64
VrfTraceAddress ( 
    ULONG TraceIndex
    )
{
    ULONG64 TracePointerAddress;
    ULONG64 TracePointer;

    TracePointerAddress = TraceDbArrayEnd - TraceIndex * PvoidSize;

    if (ReadPtr (TracePointerAddress, &TracePointer) != S_OK) {
        dprintf ("Cannot read stack trace address @ %p\n", TracePointerAddress);
        return 0;
    }

    return TracePointer;
}

VOID
VrfTraceDump (
    ULONG TraceIndex
    )
{
    ULONG64 TraceAddress;
    ULONG64 TraceArray;
    ULONG TraceDepth;
    ULONG Offset;
    ULONG Index;
    ULONG64 ReturnAddress;
    CHAR Symbol[ 1024 ];
    ULONG64 Displacement;

    //
    // Get real address of the trace.
    //

    TraceAddress = VrfTraceAddress (TraceIndex);

    if (TraceAddress == 0) {
        return;
    }

    //
    // Read the stack trace depth
    //

    if (InitTypeRead(TraceAddress, ntdll!_RTL_STACK_TRACE_ENTRY)) {
        dprintf("Unable to read type ntdll!_RTL_STACK_TRACE_ENTRY @ %p\n", TraceAddress);
        return;
    }

    TraceDepth = (ULONG)ReadField (Depth);

    //
    // Limit the depth to 20 to protect ourselves from corrupted data
    //

    TraceDepth = __min (TraceDepth, 16);

    //
    // Get a pointer to the BackTrace array
    //

    GetFieldOffset ("ntdll!_RTL_STACK_TRACE_ENTRY", "BackTrace", &Offset);
    TraceArray = TraceAddress + Offset;

    //
    // Dump this stack trace. Skip first two entries.
    //

    TraceArray += 2 * PvoidSize;

    for (Index = 2; Index < TraceDepth; Index += 1) {

        if (ReadPtr (TraceArray, &ReturnAddress) != S_OK) {
            dprintf ("Cannot read address @ %p\n", TraceArray);
            return;
        }

        GetSymbol (ReturnAddress, Symbol, &Displacement);

        dprintf ("\t%p: %s+0x%I64X\n",
                 ReturnAddress,
                 Symbol,
                 Displacement );

        TraceArray += PvoidSize;
    }
}

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////// Dump settings
/////////////////////////////////////////////////////////////////////

VOID
VrfDumpSettings (
    )
{
    ULONG64 FlagsAddress;
    ULONG Flags;
    ULONG BytesRead;
    ULONG64 StopAddress;
    ULONG64 StopData[5];
    ULONG I;
    ULONG UlongPtrSize;

    UlongPtrSize = GetTypeSize ("ntdll!ULONG_PTR");
    FlagsAddress = GetExpression("ntdll!AVrfpVerifierFlags");
    
    if (FlagsAddress == 0) {
        dprintf( "Unable to resolve ntdll!AVrfpVerifierFlags symbolic name.\n");
        return;
    }

    if (ReadMemory (FlagsAddress, &Flags, sizeof Flags, &BytesRead) == FALSE) {
        dprintf ("Cannot read value @ %p (ntdll!AVrfpVerifierFlags) \n", FlagsAddress);
        return;
    }

    dprintf ("Application verifier settings (%08X): \n\n", Flags);

    if (Flags & RTL_VRF_FLG_FULL_PAGE_HEAP) {
        dprintf ("   - full page heap\n");
    }
    else {
        dprintf ("   - light page heap\n");
    }
           
    if (Flags & RTL_VRF_FLG_LOCK_CHECKS) {
        dprintf ("   - lock checks (critical section verifier)\n");
    }
    
    if (Flags & RTL_VRF_FLG_HANDLE_CHECKS) {
        dprintf ("   - handle checks\n");
    }
    
    if (Flags & RTL_VRF_FLG_STACK_CHECKS) {
        dprintf ("   - stack checks (disable automatic stack extensions)\n");
    }

    dprintf ("\n");

    //
    // Check if a verifier stop has been encountered.
    //

    StopAddress = GetExpression("ntdll!AVrfpStopData");
    
    if (StopAddress == 0) {
        dprintf( "Unable to resolve ntdll!AVrfpStopData symbolic name.\n");
        return;
    }

    for (I = 0; I < 5; I += 1) {

        if (ReadPtr (StopAddress + I * UlongPtrSize, &(StopData[I])) != S_OK) {
            dprintf ("Cannot read value @ %p \n", StopAddress + I * UlongPtrSize);
        }
    }

    if (StopData[0] != 0) {

        dprintf ("Stop %p: %p %p %p %p \n", 
                 StopData[0],
                 StopData[1],
                 StopData[2],
                 StopData[3],
                 StopData[4]);

        // silviuc: dump more text info about the verifier_stop
    }
}

/////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////// Dump vspace log
/////////////////////////////////////////////////////////////////////

VOID
VrfDumpVspaceLog (
    ULONG NoOfEntries,
    ULONG64 Address
    )
{
    ULONG64 IndexAddress;
    ULONG Index;
    ULONG64 LogAddress;
    ULONG EntrySize;
    ULONG MaxIndex = 8192; // silviuc: should not be hard coded
    ULONG BytesRead;
    ULONG64 EntryAddress;
    ULONG TraceIndex;
    ULONG I;
    PCHAR OpName;
    BOOLEAN Found = FALSE;

    if (Address) {
        NoOfEntries = MaxIndex;
    }
    else {
        if (NoOfEntries == 0) {
            NoOfEntries = 8;
        }
    }

    EntrySize = GetTypeSize ("verifier!_VS_CALL");
    IndexAddress = GetExpression("verifier!VsCallsIndex");
    LogAddress = GetExpression("verifier!VsCalls");
    
    if (IndexAddress == 0 || LogAddress == 0 || EntrySize == 0) {
        dprintf( "Incorrect symbols for verifier.dll.\n");
        return;
    }

    if (ReadMemory (IndexAddress, &Index, sizeof Index, &BytesRead) == FALSE) {
        dprintf ("Cannot read value @ %p (verifier!VsCallsIndex) \n", IndexAddress);
        return;
    }

    for (I = 0; I < NoOfEntries; I += 1) {

        ULONG64 VsAddress;
        ULONG64 VsSize;
        BOOLEAN PrintTrace;

        EntryAddress = LogAddress + EntrySize * ((Index - I) % MaxIndex);

        if (InitTypeRead (EntryAddress, verifier!_VS_CALL)) {
            dprintf("Unable to read type verifier!_VS_CALL @ %p\n", EntryAddress);
            return;
        }

        switch ((ULONG)ReadField(Type)) {
            case 0: OpName = "VirtualAlloc"; break;
            case 1: OpName = "VirtualFree"; break;
            case 2: OpName = "MapView"; break;
            case 3: OpName = "UnmapView"; break;
            default:OpName = "Unknown?"; break;
        }

        VsAddress = ReadField(Address);
        VsSize = ReadField (Size);

        if (Address) {
            if (VsAddress <= Address && Address < VsAddress + VsSize) {
                PrintTrace = TRUE;
            }
            else {
                PrintTrace = FALSE;
            }
        }
        else {
            PrintTrace = TRUE;
        }

        if (PrintTrace) {

            Found = TRUE; 

            dprintf ("%s (tid: 0x%X): \n"
                     "address: %p \n"
                     "size: %p\n"
                     "operation: %X\n"
                     "protection: %X\n",
                     OpName,
                     (ULONG)ReadField(Thread),
                     VsAddress,
                     VsSize,
                     (ULONG)ReadField(Operation),
                     (ULONG)ReadField(Protection));

            TraceIndex = (ULONG) ReadField (Trace);
            VrfTraceDump (TraceIndex);
            dprintf ("\n");
        }
    }
    
    if (! Found) {
        dprintf ("No entries found. \n");
    }
}


/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////// Dump heap log
/////////////////////////////////////////////////////////////////////

VOID
VrfDumpHeapLog (
    ULONG NoOfEntries,
    ULONG64 Address
    )
{
    ULONG64 IndexAddress;
    ULONG Index;
    ULONG64 LogAddress;
    ULONG EntrySize;
    ULONG MaxIndex = 8192; // silviuc: should not be hard coded
    ULONG BytesRead;
    ULONG64 EntryAddress;
    ULONG TraceIndex;
    ULONG I;
    PCHAR OpName;
    BOOLEAN Found = FALSE;

    if (Address) {
        NoOfEntries = MaxIndex;
    }
    else {
        if (NoOfEntries == 0) {
            NoOfEntries = 8;
        }
    }

    EntrySize = GetTypeSize ("verifier!_HEAP_CALL");
    IndexAddress = GetExpression("verifier!HeapCallsIndex");
    LogAddress = GetExpression("verifier!HeapCalls");
    
    if (IndexAddress == 0 || LogAddress == 0 || EntrySize == 0) {
        dprintf( "Incorrect symbols for verifier.dll.\n");
        return;
    }

    if (ReadMemory (IndexAddress, &Index, sizeof Index, &BytesRead) == FALSE) {
        dprintf ("Cannot read value @ %p (verifier!HeapCallsIndex) \n", IndexAddress);
        return;
    }

    for (I = 0; I < NoOfEntries; I += 1) {

        ULONG64 VsAddress;
        ULONG64 VsSize;
        BOOLEAN PrintTrace;

        EntryAddress = LogAddress + EntrySize * ((Index - I) % MaxIndex);

        if (InitTypeRead (EntryAddress, verifier!_HEAP_CALL)) {
            dprintf("Unable to read type verifier!_HEAP_CALL @ %p\n", EntryAddress);
            return;
        }

        VsAddress = ReadField(Address);
        VsSize = ReadField (Size);

        if (VsSize == 0) {
            OpName = "free";
        }
        else {
            OpName = "alloc";
        }

        if (Address) {
            if (VsAddress <= Address && Address <= VsAddress + VsSize) {
                PrintTrace = TRUE;
            }
            else {
                PrintTrace = FALSE;
            }
        }
        else {
            PrintTrace = TRUE;
        }

        if (PrintTrace) {

            Found = TRUE;

            dprintf ("%s (tid: 0x%X): \n"
                     "address: %p \n"
                     "size: %p\n",
                     OpName,
                     (ULONG)ReadField(Thread),
                     VsAddress,
                     VsSize);

            TraceIndex = (ULONG) ReadField (Trace);
            VrfTraceDump (TraceIndex);
            dprintf ("\n");
        }
    }

    if (! Found) {
        dprintf ("No entries found. \n");
    }
}