/*++

Copyright (c) 2001-2001 Microsoft Corporation

Module Name:

    ownref.c

Abstract:

    Implements the ownref command.

Author:

    George V. Reilly (GeorgeRe)  23-Jan-2001

Environment:

    User Mode.

Revision History:

--*/


#include "precomp.h"

typedef struct _REF_OWNER_GLOBAL_CALLBACK_CONTEXT
{
    ULONG           Signature;
    LONG            Index;
    BOOLEAN         Verbose;
    PSTR            Prefix;
} REF_OWNER_GLOBAL_CALLBACK_CONTEXT, *PREF_OWNER_GLOBAL_CALLBACK_CONTEXT;

#define REF_OWNER_GLOBAL_CALLBACK_CONTEXT_SIGNATURE ((ULONG) 'xGOR')


BOOLEAN
DumpOwnerRefTraceLog(
    IN PLIST_ENTRY RemoteListEntry,
    IN PVOID Context
    )
{
    ULONG_PTR address;
    ULONG result;
    OWNER_REF_TRACELOG logHeader;
    PREF_OWNER_GLOBAL_CALLBACK_CONTEXT pCtxt
        = (PREF_OWNER_GLOBAL_CALLBACK_CONTEXT) Context;

    ASSERT(pCtxt->Signature == REF_OWNER_GLOBAL_CALLBACK_CONTEXT_SIGNATURE);

    address = (ULONG_PTR) CONTAINING_RECORD(
                                RemoteListEntry,
                                OWNER_REF_TRACELOG,
                                OwnerHeader.GlobalListEntry
                                );

    if (!ReadMemory(
            address,
            &logHeader,
            sizeof(logHeader),
            &result
            ))
    {
        return FALSE;
    }

    dprintf("OWNER_REF_TRACELOG[%d] @ %p\n", ++pCtxt->Index, address);

    return TRUE;
} // DumpOwnerRefTraceLog


#if 0

VOID
DumpOwnerRefTraceLogGlobalList(
    IN BOOLEAN              Verbose
    )
{
    REF_OWNER_GLOBAL_CALLBACK_CONTEXT Context;

    Context.Signature = REF_OWNER_GLOBAL_CALLBACK_CONTEXT_SIGNATURE ;
    Context.Verbose = Verbose;
    Context.Prefix = "";
    Context.Index  = 0;
"&http!g_OwnerRefTraceLogGlobalListHead"
    dprintf(
        "\n"
        "    OWNER_REF_TRACELOG @ %p: %d owners\n",
        RemoteAddress,
        plogHeader->OwnerHeader.OwnersCount
        );
    
    EnumLinkedList(
        (PLIST_ENTRY)REMOTE_OFFSET(
            RemoteAddress,
            OWNER_REF_TRACELOG,
            OwnerHeader.ListHead
            ),
        &DumpOwnerRefTraceLogOwnerCallback,
        &Context
        );
} // DumpOwnerRefTraceLogGlobalList

#endif


typedef struct _REF_OWNER_CALLBACK_CONTEXT
{
    ULONG           Signature;
    LONG            Index;
    BOOLEAN         Verbose;
    PSTR            Prefix;
    LONG            TotalRefs;
} REF_OWNER_CALLBACK_CONTEXT, *PREF_OWNER_CALLBACK_CONTEXT;

#define REF_OWNER_CALLBACK_CONTEXT_SIGNATURE ((ULONG) 'xCOR')


BOOLEAN
DumpOwnerRefTraceLogOwner(
    IN ULONG_PTR                   address,
    IN PREF_OWNER_CALLBACK_CONTEXT pCtxt
    )
{
    ULONG result;
    REF_OWNER RefOwner;
    LONG  index;
    ULONG index2;

    ASSERT(pCtxt->Signature == REF_OWNER_CALLBACK_CONTEXT_SIGNATURE);

    if (!ReadMemory(
            address,
            &RefOwner,
            sizeof(RefOwner),
            &result
            ))
    {
        return FALSE;
    }

    if (RefOwner.Signature != OWNER_REF_SIGNATURE)
    {
        dprintf(
            "Invalid REF_OWNER @ %p: signature = '%c%c%c%c'\n",
            address,
            DECODE_SIGNATURE(RefOwner.Signature)
            );

        return FALSE;
    }

    pCtxt->TotalRefs += RefOwner.RelativeRefCount;

    dprintf(
        "%s\tREF_OWNER[%3d] @ %p:"
        " pOwner=%p '%c%c%c%c',"
        " RelativeRefCount=%d,"
        " OwnedNextEntry=%d.\n"
        ,
        pCtxt->Verbose ? "\n" : "",
        pCtxt->Index++,
        address,
        RefOwner.pOwner,
        DECODE_SIGNATURE(RefOwner.OwnerSignature),
        RefOwner.RelativeRefCount,
        RefOwner.OwnedNextEntry
        );

    if (RefOwner.OwnedNextEntry == -1  ||  !pCtxt->Verbose)
        return TRUE;

    index  = max( 0, (RefOwner.OwnedNextEntry + 1) - OWNED_REF_NUM_ENTRIES );
    index2 = index % OWNED_REF_NUM_ENTRIES;

    for ( ;
         index <= RefOwner.OwnedNextEntry;
         index++, index2++
        )
    {
        if (CheckControlC())
        {
            break;
        }

        if (index2 >= OWNED_REF_NUM_ENTRIES)
            index2 = 0;

        dprintf(
            "\t\t%8ld: RefIndex=%6I64d, MonotonicId=%ld, Act=%s\n",
            index,
            RefOwner.RecentEntries[index2].RefIndex,
            RefOwner.RecentEntries[index2].MonotonicId,
            Action2Name(RefOwner.RecentEntries[index2].Action)
            );
     }

    return TRUE;
} // DumpOwnerRefTraceLogOwner


BOOLEAN
DumpOwnerRefTraceLogOwnerCallback(
    IN PLIST_ENTRY RemoteListEntry,
    IN PVOID Context
    )
{
    ULONG_PTR address = (ULONG_PTR) CONTAINING_RECORD(
                                RemoteListEntry,
                                REF_OWNER,
                                ListEntry
                                );
    PREF_OWNER_CALLBACK_CONTEXT pCtxt = (PREF_OWNER_CALLBACK_CONTEXT) Context;

    return DumpOwnerRefTraceLogOwner(address, pCtxt);
} // DumpOwnerRefTraceLogOwnerCallback



VOID
DumpOwnerRefTraceLogOwnersList(
    IN POWNER_REF_TRACELOG  plogHeader,
    IN ULONG_PTR            RemoteAddress,
    IN BOOLEAN              Verbose
    )
{
    REF_OWNER_CALLBACK_CONTEXT Context;

    Context.Signature = REF_OWNER_CALLBACK_CONTEXT_SIGNATURE ;
    Context.Verbose = Verbose;
    Context.Prefix = "";
    Context.Index  = 0;
    Context.TotalRefs = 0;

    dprintf(
        "\n"
        "    OWNER_REF_TRACELOG @ %p: %d owners\n",
        RemoteAddress,
        plogHeader->OwnerHeader.OwnersCount
        );
    
    EnumLinkedList(
        (PLIST_ENTRY) REMOTE_OFFSET(
            RemoteAddress,
            OWNER_REF_TRACELOG,
            OwnerHeader.ListHead
            ),
        &DumpOwnerRefTraceLogOwnerCallback,
        &Context
        );

    dprintf("\nTotal RefCount = %d\n\n", Context.TotalRefs);
    
} // DumpOwnerRefTraceLogOwnersList


VOID
DumpOwnerRefTraceLogData(
    IN POWNER_REF_TRACELOG  plogHeader,
    IN ULONG_PTR            RemoteAddress,
    IN BOOLEAN              Verbose,
    IN ULONG_PTR            context
    )
{
    OWNER_REF_TRACE_LOG_ENTRY   logEntry;
    ULONG_PTR                   entryAddress;
    CHAR                        filePath[MAX_PATH];
    PSTR                        fileName;
    PVOID                       pPrevFilePath;
    CHAR                        symbol1[MAX_SYMBOL_LENGTH];
    CHAR                        symbol2[MAX_SYMBOL_LENGTH];
    ULONG                       result;
    ULONG                       Dumped = 0;
    ULONG                       NonMatch = 0;
    ULONG                       NumToDump = plogHeader->TraceLog.LogSize;

    LONGLONG                    index = max( 0, ((plogHeader->TraceLog.NextEntry + 1)
                                    - NumToDump) );

    ULONGLONG                   index2 = index % plogHeader->TraceLog.LogSize;
    ULONGLONG                   index1000 = index % 1000;


    entryAddress = (ULONG_PTR) plogHeader->TraceLog.pLogBuffer +
            (ULONG_PTR)( index2 * sizeof(logEntry) );

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

    //
    // Dump the log.
    //

    dprintf(
        "\n"
        "    OWNER_REF_TRACELOG @ %p: dumping entries[%I64d-%I64d]",
        RemoteAddress,
        index,
        plogHeader->TraceLog.NextEntry
        );

    if (context != 0)
        dprintf(", filtering on owner=%p", context);
    
    dprintf("\n");

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

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

        if (index1000 >= 1000)
            index1000 = 0;

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

        if (context == 0 || context == (ULONG_PTR)logEntry.pOwner)
        {
            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: Own=%p Act=%2lu %-30s Ref=%4d Src=%s:%lu\n",
                (NonMatch > 0) ? "\n" : "",
                index,
                logEntry.pOwner,
                (ULONG)logEntry.Action,
                Action2Name(logEntry.Action),
                logEntry.NewRefCount,
                fileName,
                (ULONG)logEntry.LineNumber
                );

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

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

} // DumpOwnerRefTraceLogData



//
//  Public functions.
//

DECLARE_API( ownref )

/*++

Routine Description:

    Dumps the owner reference trace log at the specified address.

Arguments:

    None.

Return Value:

    None.

--*/

{
    ULONG_PTR address = 0;
    ULONG_PTR context = 0;
    ULONG64 address64 = 0, context64 = 0;
    ULONG result;
    OWNER_REF_TRACELOG logHeader;
    CHAR strSignature[MAX_SIGNATURE_LENGTH];
    BOOLEAN ListGlobal = FALSE; // CODEWORK
    BOOLEAN ListAllOwners = FALSE;
    BOOLEAN ListOneOwner = FALSE;
    BOOLEAN ListRefs = FALSE;
    BOOLEAN Verbose = FALSE;

    SNAPSHOT_EXTENSION_DATA();

    // Parse any leading flags
    while (*args == ' ' || *args == '\t')
    {
        args++;
    }

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

        switch (*args)
        {
        case 'g' :
            ListGlobal = TRUE;
            args++;
            break;

        case 'O' :
            ListAllOwners = TRUE;
            args++;
            break;

        case 'o' :
            ListOneOwner = TRUE;
            args++;
            break;

        case 'r' :
            ListRefs = TRUE;
            args++;
            break;

        case 'v' :
            Verbose = TRUE;
            args++;
            break;

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

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

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

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

    GetExpressionEx(args, &context64, &args);

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

    //
    // Read the log header.
    //

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

    if (ListGlobal)
    {
        dprintf("Sorry, -g option not yet implemented.\n"
                "Use: dl http!g_OwnerRefTraceLogGlobalListHead\n");
    }

    if (ListOneOwner)
    {
        REF_OWNER_CALLBACK_CONTEXT Context;

        Context.Signature = REF_OWNER_CALLBACK_CONTEXT_SIGNATURE ;
        Context.Verbose = TRUE;
        Context.Prefix = "";
        Context.Index  = -1;
        Context.TotalRefs = 0;

        DumpOwnerRefTraceLogOwner(address, &Context);

        return;
    }

    dprintf(
        "ownref: log @ %p\n"
        "    Signature     = %08lx '%c%c%c%c' (%s)\n"
        "    TypeSignature = %08lx (%s)\n"
        "    LogSize       = %lu\n"
        "    NextEntry     = %I64d\n"
        "    EntrySize     = %lu\n"
        "    LogBuffer     = %p\n"
        "    OwnersCount   = %d\n"
        "    MonotonicId   = %d\n",
        address,
        logHeader.TraceLog.Signature,
        DECODE_SIGNATURE(logHeader.TraceLog.Signature),
        logHeader.TraceLog.Signature == TRACE_LOG_SIGNATURE
            ? "OK"
            : logHeader.TraceLog.Signature == TRACE_LOG_SIGNATURE_X
                ? "FREED"
                : "INVALID",
        logHeader.TraceLog.TypeSignature,
        SignatureToString(
            logHeader.TraceLog.TypeSignature,
            OWNER_REF_TRACELOG_SIGNATURE,
            0,
            strSignature
            ),
        logHeader.TraceLog.LogSize,
        logHeader.TraceLog.NextEntry,
        logHeader.TraceLog.EntrySize,
        logHeader.TraceLog.pLogBuffer,
        logHeader.OwnerHeader.OwnersCount,
        logHeader.OwnerHeader.MonotonicId
        );

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

    if (logHeader.TraceLog.TypeSignature != OWNER_REF_TRACELOG_SIGNATURE)
    {
        dprintf(
            "ownref: log @ %p has invalid signature %08lx, '%c%c%c%c':\n",
            address,
            logHeader.TraceLog.TypeSignature,
            DECODE_SIGNATURE(logHeader.TraceLog.TypeSignature)
            );
        return;
    }

    if (logHeader.TraceLog.EntrySize != sizeof(OWNER_REF_TRACE_LOG_ENTRY)
        || logHeader.TraceLog.TypeSignature != OWNER_REF_TRACELOG_SIGNATURE)
    {
        dprintf(
            "ownref: log @ %p is not an owner ref count log\n",
            address
            );
        return;
    }

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


    if (Verbose || ListAllOwners)
        DumpOwnerRefTraceLogOwnersList(&logHeader, address, Verbose);

    if (Verbose || ListRefs)
        DumpOwnerRefTraceLogData(&logHeader, address, Verbose, context);

}   // ownref