/*++

Copyright (c) 1998-2001 Microsoft Corporation

Module Name:

    ref.c

Abstract:

    Implements the ref command.

Author:

    Keith Moore (keithmo) 17-Jun-1998

Environment:

    User Mode.

Revision History:

--*/


#include "precomp.h"

#undef BEGIN_REF_ACTION
#undef END_REF_ACTION
#undef REF_ACTION

#define BEGIN_REF_ACTION()      NAMED_REFTRACE_ACTION g_RefTraceActions[] = {
#define END_REF_ACTION()        };
#define REF_ACTION(x)           { REF_ACTION_##x, #x },

#include "..\drv\refaction.h"

#define NUM_REFTRACE_ACTIONS \
    (sizeof(g_RefTraceActions) / sizeof(g_RefTraceActions[0]))


const CHAR*
Action2Name(
    ULONG Action)
{
    if (Action < NUM_REFTRACE_ACTIONS)
    {
        ASSERT(g_RefTraceActions[Action].Action == Action);
        return g_RefTraceActions[Action].Name;
    }
    else
        return "????";
}

typedef
BOOLEAN
(*FN_MATCH_CONTEXT)(
    IN ULONG_PTR            context,
    IN PREF_TRACE_LOG_ENTRY plogEntry
    );

BOOLEAN
RefMatchContext(
    IN ULONG_PTR            context,
    IN PREF_TRACE_LOG_ENTRY plogEntry)
{
    return (context == 0 || context == (ULONG_PTR) plogEntry->pContext);
}

BOOLEAN
ThreadMatchContext(
    IN ULONG_PTR            context,
    IN PREF_TRACE_LOG_ENTRY plogEntry)
{
    return (context == 0 || context == (ULONG_PTR) plogEntry->pThread);
}


// Do all the real
VOID
DumpRefTrace(
    PCSTR args,
    FN_MATCH_CONTEXT pfnMatchContext,
    PCSTR cmd)
{
    ULONG_PTR           address = 0;
    ULONG_PTR           context = 0;
    ULONG_PTR           flags = 0;
    ULONG_PTR           entryAddress;
    ULONG               result;
    TRACE_LOG           logHeader;
    REF_TRACE_LOG_ENTRY logEntry;
    PSTR                fileName;
    LONGLONG            index;
    ULONGLONG           index2;
    ULONGLONG           index1000;
    ULONG_PTR           offset1;
    ULONG_PTR           offset2;
    CHAR                filePath[MAX_PATH];
    PVOID               pPrevFilePath;
    CHAR                symbol1[MAX_SYMBOL_LENGTH];
    CHAR                symbol2[MAX_SYMBOL_LENGTH];
    ULONG               Dumped = 0;
    ULONG               NonMatch = 0;
    ULONG64             address64 = 0;
    ULONG64             context64 = 0;
    ULONG64             flags64 = 0;
    ULONG               NumToDump = 0;

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

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

        switch (*args)
        {
        case 'l' :
            for (index = 0;  index < NUM_REFTRACE_ACTIONS;  ++index)
            {
                dprintf(
                    "%4u: REF_ACTION_%s\n",
                    g_RefTraceActions[index].Action,
                    g_RefTraceActions[index].Name);
            }
            return;

        default :
            PrintUsage( cmd );
            return;
        }
    }

    //
    // Snag the address and optional context and flags from the command line.
    //

    if (! GetExpressionEx(args, &address64, &args))
    {
        PrintUsage( cmd );
        return;
    }

    if (GetExpressionEx(args, &context64, &args))
        GetExpressionEx(args, &flags64, &args);

    address = (ULONG_PTR) address64;
    context = (ULONG_PTR) context64;
    flags = (ULONG_PTR) flags64;

    //
    // Read the log header.
    //

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

    dprintf(
        "%s: 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",
        cmd,
        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(
            "%s: log @ %p has invalid signature %08lx:\n",
            cmd,
            address,
            logHeader.Signature
            );
        return;
    }

    if (logHeader.EntrySize != sizeof(logEntry)
        || logHeader.TypeSignature != REF_TRACELOG_SIGNATURE
        )
    {
        dprintf(
            "%s: log @ %p is not a ref count log\n",
            cmd,
            address
            );
        return;
    }

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

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

    NumToDump = logHeader.LogSize;

    index  = max( 0, (logHeader.NextEntry + 1) - NumToDump );
    index2 = index % logHeader.LogSize;
    index1000 = index % 1000;

    pPrevFilePath = NULL;
    *filePath = '\0';

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

    //
    // Dump the log.
    //

    for ( ;
         index <= logHeader.NextEntry;
         index++,
         index2++,
         index1000++,
         entryAddress += sizeof(logEntry)
        )
    {
        if (CheckControlC())
        {
            break;
        }

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

        if (index1000 >= 1000)
            index1000 = 0;

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

        if ((*pfnMatchContext)(context, &logEntry))
        {
            if (logEntry.pFileName != pPrevFilePath)
            {
                if (ReadMemory(
                        (ULONG_PTR)logEntry.pFileName,
                        filePath,
                        sizeof(filePath),
                        &result
                        ))
                {
                    fileName = strrchr( filePath, '\\' );
                    
                    if (fileName != NULL)
                    {
                        fileName++;
                    }
                    else
                    {
                        fileName = filePath;
                    }

                    pPrevFilePath = logEntry.pFileName;
                }
                else
                {
                    sprintf(
                        filePath,
                        "%p",
                        logEntry.pFileName
                        );
                    
                    fileName = filePath;
                }
            }

            dprintf(
                "%s%4I64d: CPU=%lu Ctx=%p Act=%2lu %-30s Ref=%4d Src=%s:%lu\n",
                (NonMatch > 0) ? "\n" : "",
                index,
                (ULONG)logEntry.Processor,
                logEntry.pContext,
                (ULONG)logEntry.Action,
                Action2Name(logEntry.Action),
                logEntry.NewRefCount,
                fileName,
                (ULONG)logEntry.LineNumber
                );

            if (flags & 1)
            {
                GetSymbol(
                    logEntry.pCaller,
                    symbol1,
                    &offset1
                    );

                GetSymbol(
                    logEntry.pCallersCaller,
                    symbol2,
                    &offset2
                    );

                dprintf(
                    "      Process=%p Thread=%p\n"
                    "      Caller1=%p (%s+0x%p)\n"
                    "      Caller2=%p (%s+0x%p)\n",
                    logEntry.pProcess,
                    logEntry.pThread,
                    logEntry.pCaller,
                    symbol1,
                    offset1,
                    logEntry.pCallersCaller,
                    symbol2,
                    offset2
                    );
            }

            ++Dumped;
            NonMatch = 0;
        }
        else
        {
            if (index1000 == 0)
                dprintf("%I64d", index);
            
            if ((++NonMatch & 127) == 127)
                dprintf(".");
        }
    }

    if (context != 0)
        dprintf("%d entries dumped\n\n", Dumped);

}   // DumpRefTrace



//
//  Public functions.
//

DECLARE_API( ref )

/*++

Routine Description:

    Dumps the reference trace log at the specified address.

Arguments:

    None.

Return Value:

    None.

--*/
{
    SNAPSHOT_EXTENSION_DATA();

    DumpRefTrace(args, RefMatchContext, "ref");
} // ref


DECLARE_API( tref )

/*++

Routine Description:

    Dumps the trace log at the specified address.
    Filtering done by thread instead of context.

Arguments:

    None.

Return Value:

    None.

--*/

{
    SNAPSHOT_EXTENSION_DATA();

    DumpRefTrace(args, ThreadMatchContext, "tref");
} // ref