
/*++



Copyright (c) 1991  Microsoft Corporation

Module Name:

    RegLeak.h

Abstract:

    This module contains helper functions for tracking
n    win32 registry leaks

Author:

    Adam Edwards (adamed) 06-May-1998

--*/


#ifdef LOCAL
#ifdef LEAK_TRACK

#include "ntverp.h"
#include <rpc.h>
#include "regrpc.h"
#include "localreg.h"
#include "regclass.h"
#include "stkwalk.h"
#include "regleak.h"
#include <malloc.h>

RegLeakTable gLeakTable;
RegLeakTraceInfo g_RegLeakTraceInfo;


void     TrackObjectDataPrint(TrackObjectData* pKeyData)
{
    NTSTATUS       Status;
    SKeySemantics  keyinfo;
    UNICODE_STRING EmptyString = {0, 0, 0};
    BYTE           rgNameBuf[REG_MAX_CLASSKEY_LEN + REG_CHAR_SIZE + sizeof(KEY_NAME_INFORMATION)];

    DbgPrint("WINREG: Tracked key data for object 0x%x\n", pKeyData->hKey);

    //
    // Set buffer to store info about this key
    //
    keyinfo._pFullPath = (PKEY_NAME_INFORMATION) rgNameBuf;
    keyinfo._cbFullPath = sizeof(rgNameBuf);

    //
    // get information about this key
    //
    Status = BaseRegGetKeySemantics(pKeyData->hKey, &EmptyString, &keyinfo);

    if (!NT_SUCCESS(Status)) {
        DbgPrint("WINREG: Unable to retrieve object name error 0x%x\n", Status);
    } else {
        DbgPrint("WINREG: Name: %S\n", keyinfo._pFullPath->Name);
    }

    BaseRegReleaseKeySemantics(&keyinfo);

    DbgPrint("Frames %d", pKeyData->dwStackDepth);

    {
        DWORD iFrame;

        for (iFrame = 0; iFrame < pKeyData->dwStackDepth; iFrame++) 
        {
            DbgPrint("WINREG: Frame %d = 0x%x\n", iFrame, pKeyData->rgStack[iFrame]);
        }
    }
    DbgPrint("\n");

}

NTSTATUS TrackObjectDataInit(TrackObjectData* pKeyData, PVOID* rgStack, DWORD dwMaxStackDepth, HKEY hKey)
{
    RtlZeroMemory(pKeyData, sizeof(*pKeyData));
    
    pKeyData->hKey = REG_CLASS_RESET_SPECIAL_KEY(hKey);
  
    pKeyData->dwStackDepth = dwMaxStackDepth;
    pKeyData->rgStack = rgStack;

    return STATUS_SUCCESS;
}

NTSTATUS TrackObjectDataClear(TrackObjectData* pKeyData)
{
    if (pKeyData->rgStack) {
        RtlFreeHeap(RtlProcessHeap(), 0, pKeyData->rgStack);
        pKeyData->rgStack = NULL;
    }

    return STATUS_SUCCESS;
}

NTSTATUS RegLeakTableInit(RegLeakTable* pLeakTable, DWORD dwFlags)
{
    NTSTATUS Status;

    RtlZeroMemory(pLeakTable, sizeof(*pLeakTable));
    
    pLeakTable->dwFlags = dwFlags;

    Status = RtlInitializeCriticalSection(
                    &(pLeakTable->CriticalSection));

    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    //
    // Remember that we have initialized this critical section
    // so we can remember to delete it.
    //

    pLeakTable->bCriticalSectionInitialized = TRUE;

    Status = RtlInitializeCriticalSection(
                    &(g_RegLeakTraceInfo.StackInitCriticalSection));

    if (!NT_SUCCESS(Status)) {
        return Status;
    }
    
    return STATUS_SUCCESS;
}


NTSTATUS RegLeakTableClear(RegLeakTable* pLeakTable)
{
    NTSTATUS Status;

#if defined(DBG) // LEAK_TRACK
    DbgPrint("WINREG: Leak data for process id 0x%x\n", NtCurrentTeb()->ClientId.UniqueProcess);
    DbgPrint("WINREG: Keys Leaked 0x%x\n", pLeakTable->cKeys);
#endif // LEAK_TRACK

    Status = RtlDeleteCriticalSection(
        &(pLeakTable->CriticalSection));

    ASSERT(NT_SUCCESS(Status));

    Status = RtlDeleteCriticalSection(
        &(g_RegLeakTraceInfo.StackInitCriticalSection));

        
    ASSERT(NT_SUCCESS(Status));
#if DBG
    if ( !NT_SUCCESS( Status ) ) {
        DbgPrint( "WINREG: RtlDeleteCriticalSection() in EnumTableClear() failed. Status = %lx \n", Status );
    }
#endif

    {
        DWORD cKeys;

        cKeys = 0;

        for (;;) 
        {
            if (!(pLeakTable->pHead)) {
                break;
            }
            TrackObjectDataPrint(pLeakTable->pHead);

            cKeys++;

            (void) RegLeakTableRemoveKey(pLeakTable, pLeakTable->pHead->hKey);
        }

#if defined(DBG) // LEAK_TRACK
        DbgPrint("WINREG: 0x%x total keys leaked\n", cKeys);
#endif // LEAK_TRACK
    }

    return STATUS_SUCCESS;
}

NTSTATUS RegLeakTableAddKey(RegLeakTable* pLeakTable, HKEY hKey)
{
    NTSTATUS           Status;
    TrackObjectData*   pNewData;
    PVOID*             rgStack;
    DWORD              dwMaxStackDepth;

    rgStack = NULL;
    dwMaxStackDepth = 0;

    hKey = REG_CLASS_RESET_SPECIAL_KEY(hKey);

    if (!RegLeakTableIsTrackedObject(pLeakTable, hKey)) {
        return STATUS_SUCCESS;
    }

    (void) GetLeakStack(
        &rgStack,
        &dwMaxStackDepth,
        g_RegLeakTraceInfo.dwMaxStackDepth);

    Status = RtlEnterCriticalSection(&(pLeakTable->CriticalSection));
    
    ASSERT( NT_SUCCESS( Status ) );
    if ( !NT_SUCCESS( Status ) ) {
#if DBG
        DbgPrint( "WINREG: RtlEnterCriticalSection() in EnumTableRemoveKey() failed. Status = %lx \n", Status );
#endif
        return Status;
    }
    
    pNewData = RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(*pNewData));

    if (!pNewData) {
        Status = STATUS_NO_MEMORY;
        goto cleanup;
    }

    Status = TrackObjectDataInit(pNewData, rgStack, dwMaxStackDepth, hKey);

    if (!NT_SUCCESS(Status)) {
        goto cleanup;
    }

    if (!RegLeakTableIsEmpty(pLeakTable)) {
        
        pNewData->Links.Flink = (PLIST_ENTRY) pLeakTable->pHead;
        pLeakTable->pHead->Links.Blink = (PLIST_ENTRY) pNewData;

    }

    pLeakTable->pHead = pNewData;

    pLeakTable->cKeys++;

cleanup:    
    {
        NTSTATUS Status;

        Status = RtlLeaveCriticalSection(&(pLeakTable->CriticalSection));

        ASSERT( NT_SUCCESS( Status ) );
#if DBG
        if ( !NT_SUCCESS( Status ) ) {
            DbgPrint( "WINREG: RtlLeaveCriticalSection() in EnumTableClear() failed. Status = %lx \n", Status );
        }
#endif
    }

    return Status;
}

NTSTATUS RegLeakTableRemoveKey(RegLeakTable* pLeakTable, HKEY hKey)
{
    NTSTATUS         Status;
    TrackObjectData* pData;

    Status = RtlEnterCriticalSection(&(pLeakTable->CriticalSection));
    
    ASSERT( NT_SUCCESS( Status ) );
    if ( !NT_SUCCESS( Status ) ) {
#if DBG
        DbgPrint( "WINREG: RtlEnterCriticalSection() in EnumTableRemoveKey() failed. Status = %lx \n", Status );
#endif
        return Status;
    }
    

    hKey = REG_CLASS_RESET_SPECIAL_KEY(hKey);

    for (pData = pLeakTable->pHead; 
         pData != NULL;
         pData = (TrackObjectData*) pData->Links.Flink)
    {
        if (hKey == pData->hKey) {
            
            PLIST_ENTRY pFlink;
            PLIST_ENTRY pBlink;
            
            pBlink = pData->Links.Blink;
            pFlink = pData->Links.Flink;

            if (pBlink) {
                pBlink->Flink = pFlink;
            }

            if (pFlink) {
                pFlink->Blink = pBlink;
            }

            if (pData == pLeakTable->pHead) {
                pLeakTable->pHead = (TrackObjectData*) pFlink;
            }

            (void) TrackObjectDataClear(pData);

            RtlFreeHeap(RtlProcessHeap(), 0, pData);

            pLeakTable->cKeys--;

            goto cleanup;
        }
    }

    Status = STATUS_OBJECT_NAME_NOT_FOUND;

cleanup:    

    {
        NTSTATUS Status;

        Status = RtlLeaveCriticalSection(&(pLeakTable->CriticalSection));

        ASSERT( NT_SUCCESS( Status ) );
#if DBG
        if ( !NT_SUCCESS( Status ) ) {
            DbgPrint( "WINREG: RtlLeaveCriticalSection() in EnumTableClear() failed. Status = %lx \n", Status );
        }
#endif
    }

    return Status;
}

BOOL RegLeakTableIsEmpty(RegLeakTable* pLeakTable)
{
    return pLeakTable->pHead == NULL;
}

BOOL RegLeakTableIsTrackedObject(RegLeakTable* pLeakTable, HKEY hKey)
{
    NTSTATUS       Status;
    SKeySemantics  keyinfo;
    UNICODE_STRING EmptyString = {0, 0, 0};
    BYTE           rgNameBuf[REG_MAX_CLASSKEY_LEN + REG_CHAR_SIZE + sizeof(KEY_NAME_INFORMATION)];
    BOOL           fTrackObject;

    fTrackObject = FALSE;

    if (LEAK_TRACK_FLAG_ALL == pLeakTable->dwFlags) {
        return TRUE;
    }

    if (LEAK_TRACK_FLAG_NONE == pLeakTable->dwFlags) {
        return FALSE;
    }
    
    //
    // Set buffer to store info about this key
    //
    keyinfo._pFullPath = (PKEY_NAME_INFORMATION) rgNameBuf;
    keyinfo._cbFullPath = sizeof(rgNameBuf);

    //
    // get information about this key
    //
    Status = BaseRegGetKeySemantics(hKey, &EmptyString, &keyinfo);

    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    if (LEAK_TRACK_FLAG_USER & pLeakTable->dwFlags) {

        WCHAR UserChar;

        UserChar = keyinfo._pFullPath->Name[REG_CLASSES_FIRST_DISTINCT_ICH];

        if ((L'U' == UserChar) || (L'u' == UserChar)) {
            fTrackObject = TRUE;
        }
    }

    BaseRegReleaseKeySemantics(&keyinfo);

    return fTrackObject;

}

NTSTATUS TrackObject(HKEY hKey)
{
    return RegLeakTableAddKey(&gLeakTable, hKey);
}

#define WINLOGON_KEY            L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"
#define LEAKTRACK_VALUE         L"LeakTrack"
#define DEFAULT_VALUE_SIZE          128

void ReadRegLeakTrackInfo()
{
    LPTSTR                          lpWinlogonKey;
    LONG                            error;
    OBJECT_ATTRIBUTES               Attributes;
    NTSTATUS                        Status;
    HKEY                            hKey;
    UNICODE_STRING                  uWinlogonPath;
    UNICODE_STRING                  uValueName;

    KEY_VALUE_INFORMATION_CLASS     KeyValueInformationClass;
    PVOID                           KeyValueInformation;

    BYTE                            PrivateKeyValueInformation[ sizeof( KEY_VALUE_PARTIAL_INFORMATION) +
                                        DEFAULT_VALUE_SIZE ];
    ULONG                           BufferLength;
    ULONG                           ResultLength;

    //
    // Look in the registry whether tracking is enabled uder
    // \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion\Winlogon
    //

    memset(&g_RegLeakTraceInfo, 0, sizeof(g_RegLeakTraceInfo));

    g_RegLeakTraceInfo.bEnableLeakTrack = 0;
 
    RtlInitUnicodeString(&uWinlogonPath, WINLOGON_KEY);
 
    InitializeObjectAttributes(&Attributes,
                              &uWinlogonPath,
                              OBJ_CASE_INSENSITIVE,
                              NULL,
                              NULL);


    Status = NtOpenKey( &hKey,
                      KEY_READ,
                      &Attributes );


    if (NT_SUCCESS(Status)) {

        RtlInitUnicodeString(&uValueName, LEAKTRACK_VALUE);

        KeyValueInformationClass = KeyValuePartialInformation;

        KeyValueInformation = PrivateKeyValueInformation;
        BufferLength = sizeof( PrivateKeyValueInformation );

        Status = NtQueryValueKey( hKey,
                                  &uValueName,
                                  KeyValueInformationClass,
                                  KeyValueInformation,
                                  BufferLength,
                                  &ResultLength );


       //
       // if it succeeded and the datalength is greater than zero
       // check whether it is non-zero
       //

       if ((NT_SUCCESS(Status)) && 
           (((PKEY_VALUE_PARTIAL_INFORMATION )KeyValueInformation )->DataLength)) {

            if (((( PKEY_VALUE_PARTIAL_INFORMATION )KeyValueInformation)->Data) && 
            (*((( PKEY_VALUE_PARTIAL_INFORMATION )KeyValueInformation)->Data)))
                g_RegLeakTraceInfo.bEnableLeakTrack = 1;
       }

       NtClose(hKey);

    }
//    g_RegLeakTraceInfo.bEnableLeakTrack = GetProfileInt(TEXT("RegistryLeak"), TEXT("Enable"), 0);
}


BOOL InitializeLeakTrackTable()
{
    ReadRegLeakTrackInfo();
    
    if (g_RegLeakTraceInfo.bEnableLeakTrack)
        return NT_SUCCESS(RegLeakTableInit(&gLeakTable, LEAK_TRACK_FLAG_USER));
    else
        return TRUE;
}

BOOL CleanupLeakTrackTable()
{
    BOOL fSuccess;

    if (!g_RegLeakTraceInfo.bEnableLeakTrack)
        return TRUE;

    //
    // if leak_tracking is not enabled, quit quickly.
    //
    
    fSuccess = NT_SUCCESS(RegLeakTableClear(&gLeakTable));

    (void) StopDebug();

    return fSuccess;
}

NTSTATUS UnTrackObject(HKEY hKey)
{
    return RegLeakTableRemoveKey(&gLeakTable, hKey);
}

NTSTATUS GetLeakStack(PVOID** prgStack, DWORD* pdwMaxDepth, DWORD dwMaxDepth)
{

    PCALLER_SYM pStack;
    DWORD       dwDepth;

    pStack = (PCALLER_SYM) RtlAllocateHeap(
        RtlProcessHeap(),
        0,
        dwMaxDepth * sizeof(*pStack));

    if (!pStack) {
        return STATUS_NO_MEMORY;
    }

    RtlZeroMemory(pStack, sizeof(*pStack) * dwMaxDepth);

   *prgStack = RtlAllocateHeap(
        RtlProcessHeap(),
        0,
        dwMaxDepth * sizeof(*(*prgStack)));

    if (!*prgStack) {
        RtlFreeHeap(RtlProcessHeap(),
                    0,
                    pStack);

        return STATUS_NO_MEMORY;
    }

    RtlZeroMemory(*prgStack, sizeof(*(*prgStack)) * dwMaxDepth);

    GetCallStack(
        pStack,
        4,
        dwMaxDepth,
        FALSE);

    for (dwDepth = 0; dwDepth < dwMaxDepth; dwDepth++) 
    {
        if (!(pStack[dwDepth].Addr)) {
            break;
        }

        (*prgStack)[dwDepth] = pStack[dwDepth].Addr;

    }

    *pdwMaxDepth = dwDepth;

    RtlFreeHeap(
        RtlProcessHeap(),
        0,
        pStack);

    return STATUS_SUCCESS;
}


#endif // DBG
#endif // LOCAL
