/*++

Copyright (c) 2001-2001 Microsoft Corporation

Module Name:

    ownerref.cxx

Abstract:

    This module implements a reference count tracing facility.

Author:

    Keith Moore (keithmo)       10-Jun-1998

Revision History:

--*/


#include "precomp.h"


// Globals
LIST_ENTRY      g_OwnerRefTraceLogGlobalListHead;
UL_SPIN_LOCK    g_OwnerRefTraceLogGlobalSpinLock;
LONG            g_OwnerRefTraceLogGlobalCount;


VOID
UlInitializeOwnerRefTraceLog(
    VOID)
{
    InitializeListHead(&g_OwnerRefTraceLogGlobalListHead);
    UlInitializeSpinLock( &g_OwnerRefTraceLogGlobalSpinLock,
                          "OwnerRefTraceLogGlobal" );
    g_OwnerRefTraceLogGlobalCount = 0;
} // UlInitializeOwnerRefTraceLog


VOID
UlTerminateOwnerRefTraceLog(
    VOID)
{
    ASSERT( IsListEmpty(&g_OwnerRefTraceLogGlobalListHead) );
    ASSERT( 0 == g_OwnerRefTraceLogGlobalCount );
} // UlTerminateOwnerRefTraceLog



#if ENABLE_OWNER_REF_TRACE

POWNER_REF_TRACELOG
CreateOwnerRefTraceLog(
    IN ULONG LogSize,
    IN ULONG ExtraBytesInHeader
    )
{
    POWNER_REF_TRACELOG pOwnerRefTraceLog = NULL;

    PTRACE_LOG pTraceLog
        = CreateTraceLog(
                OWNER_REF_TRACELOG_SIGNATURE,
                LogSize,
                sizeof(OWNER_REF_TRACELOG)
                    - FIELD_OFFSET(OWNER_REF_TRACELOG, OwnerHeader)
                    + ExtraBytesInHeader,
                sizeof(OWNER_REF_TRACE_LOG_ENTRY)
                );

    if (pTraceLog != NULL)
    {
        pOwnerRefTraceLog = (POWNER_REF_TRACELOG) pTraceLog;

        ASSERT(pOwnerRefTraceLog->TraceLog.TypeSignature
                    == OWNER_REF_TRACELOG_SIGNATURE);

        InitializeListHead(&pOwnerRefTraceLog->OwnerHeader.ListHead);
        UlInitializeSpinLock( &pOwnerRefTraceLog->OwnerHeader.SpinLock,
                              "OwnerRefTraceLog" );
        pOwnerRefTraceLog->OwnerHeader.MonotonicId = 0;
        pOwnerRefTraceLog->OwnerHeader.OwnersCount = 0;

        // insert into global list

        ExInterlockedInsertTailList(
            &g_OwnerRefTraceLogGlobalListHead,
            &pOwnerRefTraceLog->OwnerHeader.GlobalListEntry,
            KSPIN_LOCK_FROM_UL_SPIN_LOCK(&g_OwnerRefTraceLogGlobalSpinLock)
            );

        InterlockedIncrement(&g_OwnerRefTraceLogGlobalCount);
    }

    return pOwnerRefTraceLog;
}   // CreateOwnerRefTraceLog


PREF_OWNER
NewRefOwner(
    POWNER_REF_TRACELOG pOwnerRefTraceLog,
    PVOID               pOwner,
    IN PPREF_OWNER      ppRefOwner,
    IN ULONG            OwnerSignature
    )
{
    PREF_OWNER pRefOwner;

    ASSERT( UlDbgSpinLockOwned( &pOwnerRefTraceLog->OwnerHeader.SpinLock ) );

    pRefOwner = UL_ALLOCATE_STRUCT(
                    NonPagedPool,
                    REF_OWNER,
                    UL_OWNER_REF_POOL_TAG
                    );

    if (pRefOwner != NULL)
    {
        int i;

        pRefOwner->Signature = OWNER_REF_SIGNATURE;
        pRefOwner->OwnerSignature = OwnerSignature;

        InsertTailList(
            &pOwnerRefTraceLog->OwnerHeader.ListHead,
            &pRefOwner->ListEntry
            );

        ++pOwnerRefTraceLog->OwnerHeader.OwnersCount;

        pRefOwner->pOwner = pOwner;
        pRefOwner->pOwnerRefTraceLog = pOwnerRefTraceLog;
        pRefOwner->RelativeRefCount = 0;
        pRefOwner->OwnedNextEntry = -1;

        for (i = 0;  i < OWNED_REF_NUM_ENTRIES;  ++i)
        {
            pRefOwner->RecentEntries[i].RefIndex    = -1;
            pRefOwner->RecentEntries[i].MonotonicId = -1;
            pRefOwner->RecentEntries[i].Action      = -1;
        }

        *ppRefOwner = pRefOwner;
    }

    return pRefOwner;
} // NewRefOwner


VOID
InsertRefOwner(
    IN POWNER_REF_TRACELOG  pOwnerRefTraceLog,
    IN PVOID                pOwner,
    IN PPREF_OWNER          ppRefOwner,
    IN ULONG                OwnerSignature,
    IN LONGLONG             RefIndex,
    IN LONG                 MonotonicId,
    IN LONG                 IncrementValue,
    IN USHORT               Action
    )
{
    PREF_OWNER  pRefOwner = NULL;
    KIRQL       OldIrql;

    ASSERT(RefIndex >= 0);

    UlAcquireSpinLock(&pOwnerRefTraceLog->OwnerHeader.SpinLock, &OldIrql);

    pRefOwner = *ppRefOwner;

    if (pRefOwner == NULL)
    {
        pRefOwner = NewRefOwner(
                         pOwnerRefTraceLog,
                         pOwner,
                         ppRefOwner,
                         OwnerSignature
                         );
    }

    if (pRefOwner != NULL)
    {
        ULONG Index;

        ASSERT(pRefOwner->Signature == OWNER_REF_SIGNATURE);
        ASSERT(pRefOwner->pOwner == pOwner);
        ASSERT(pRefOwner->OwnerSignature == OwnerSignature);
        ASSERT(pRefOwner->pOwnerRefTraceLog == pOwnerRefTraceLog);

        Index = ((ULONG) ++pRefOwner->OwnedNextEntry) % OWNED_REF_NUM_ENTRIES;

        pRefOwner->RecentEntries[Index].RefIndex    = RefIndex;
        pRefOwner->RecentEntries[Index].MonotonicId = MonotonicId;
        pRefOwner->RecentEntries[Index].Action      = Action;

        if (IncrementValue > 0)
            ++pRefOwner->RelativeRefCount;
        else if (IncrementValue < 0)
            --pRefOwner->RelativeRefCount;
        // else do nothing if IncrementValue == 0.

        ASSERT(pRefOwner->RelativeRefCount >= 0);
    }

    UlReleaseSpinLock(&pOwnerRefTraceLog->OwnerHeader.SpinLock, OldIrql);

} // InsertRefOwner


LONGLONG
WriteOwnerRefTraceLog(
    IN POWNER_REF_TRACELOG  pOwnerRefTraceLog,
    IN PVOID                pOwner,
    IN PPREF_OWNER          ppRefOwner,
    IN ULONG                OwnerSignature,
    IN USHORT               Action,
    IN LONG                 NewRefCount,
    IN LONG                 MonotonicId,
    IN LONG                 IncrementValue,
    IN PVOID                pFileName,
    IN USHORT               LineNumber
    )
{
    OWNER_REF_TRACE_LOG_ENTRY entry;
    LONGLONG RefIndex;

    ASSERT(NULL != ppRefOwner);
    ASSERT(Action < (1 << REF_TRACE_ACTION_BITS));

    if (NULL == pOwnerRefTraceLog)
        return -1;

    entry.NewRefCount = NewRefCount;
    entry.pOwner = pOwner;
    entry.pFileName = pFileName;
    entry.LineNumber = LineNumber;
    entry.Action = Action;

    RefIndex = WriteTraceLog( &pOwnerRefTraceLog->TraceLog, &entry );

    InsertRefOwner(
        pOwnerRefTraceLog,
        pOwner,
        ppRefOwner,
        OwnerSignature,
        RefIndex,
        MonotonicId,
        IncrementValue,
        Action
        );

    return RefIndex;
} // WriteOwnerRefTraceLog


VOID
DestroyOwnerRefTraceLog(
    IN POWNER_REF_TRACELOG pOwnerRefTraceLog
    )
{
    if (pOwnerRefTraceLog != NULL)
    {
        KIRQL       OldIrql;
        PLIST_ENTRY pEntry;
        int         i = 0;

        UlAcquireSpinLock(&pOwnerRefTraceLog->OwnerHeader.SpinLock, &OldIrql);

        for (pEntry =   pOwnerRefTraceLog->OwnerHeader.ListHead.Flink;
             pEntry != &pOwnerRefTraceLog->OwnerHeader.ListHead;
             ++i)
        {
            PREF_OWNER pRefOwner =
                CONTAINING_RECORD(pEntry, REF_OWNER, ListEntry);

            pEntry = pEntry->Flink; // save before deletion

            ASSERT(pRefOwner->Signature == OWNER_REF_SIGNATURE);
            ASSERT(pRefOwner->RelativeRefCount == 0);

            UL_FREE_POOL_WITH_SIG(pRefOwner, UL_OWNER_REF_POOL_TAG);
        }

        UlReleaseSpinLock(&pOwnerRefTraceLog->OwnerHeader.SpinLock, OldIrql);

        ASSERT(i == pOwnerRefTraceLog->OwnerHeader.OwnersCount);

        // Remove log from global list

        UlAcquireSpinLock(&g_OwnerRefTraceLogGlobalSpinLock, &OldIrql);
        RemoveEntryList(&pOwnerRefTraceLog->OwnerHeader.GlobalListEntry);
        UlReleaseSpinLock(&g_OwnerRefTraceLogGlobalSpinLock, OldIrql);
        InterlockedDecrement(&g_OwnerRefTraceLogGlobalCount);

        DestroyTraceLog( (PTRACE_LOG) pOwnerRefTraceLog );
    }
}   // DestroyOwnerRefTraceLog



VOID
ResetOwnerRefTraceLog(
    IN POWNER_REF_TRACELOG pOwnerRefTraceLog
    )
{
    // CODEWORK: reset OwnerHeader?
    if (pOwnerRefTraceLog != NULL)
    {
        ResetTraceLog(&pOwnerRefTraceLog->TraceLog);
    }
} // ResetOwnerRefTraceLog

#endif  // ENABLE_OWNER_REF_TRACE