/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    stkwalk.c

Abstract:

    This module contains memory debug routines for catching memory leaks and memory
    overwrites.

Author:
    Stolen from dbgmem.c
    Jim Stewart/Ramesh Pabbati    January 8, 1996

    Fixed up for regleaks
    UShaji                        Dec 11th,  1998

Revision History:

--*/

#ifdef LOCAL
#ifdef LEAK_TRACK

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#include <windows.h>
#include <stdio.h>
#include<imagehlp.h>
#include "regleak.h"
#include "stkwalk.h"
DWORD   MachineType;            // the architecutre we are on
HANDLE  OurProcess;             // the process that we are running as a part of



// typedefs from imagehlp.dll

typedef BOOL (WINAPI * PFNSYMINITIALIZE)(HANDLE hProcess,
                                         PSTR UserSearchPath,
                                         BOOL fInvadeProcess);

typedef BOOL (WINAPI * PFNSYMCLEANUP)(HANDLE hProcess);

typedef BOOL (WINAPI * PFNSTACKWALK)(DWORD MachineType,
                                  HANDLE hProcess,
                                  HANDLE hThread,
                                  LPSTACKFRAME StackFrame,
                                  PVOID ContextRecord,
                                  PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
                                  PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
                                  PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
                                  PTRANSLATE_ADDRESS_ROUTINE TranslateAddress);

typedef BOOL (WINAPI * PFNSYMGETSYMFROMADDR)(HANDLE hProcess,
                                             DWORD_PTR Address,
                                             PDWORD_PTR Displacement,
                                             PIMAGEHLP_SYMBOL Symbol);


typedef DWORD_PTR (WINAPI * PFNSYMGETMODULEBASE)(HANDLE hProcess,
                                          DWORD_PTR dwAddr);


typedef PVOID (WINAPI * PFNSYMFUNCTIONTABLEACCESS)(HANDLE hProcess,
                                                DWORD_PTR AddrBase);


// imagehlp function pointers

PFNSYMINITIALIZE            g_pfnSymInitialize=NULL;
PFNSYMCLEANUP               g_pfnSymCleanup=NULL;
PFNSTACKWALK                g_pfnStackWalk=NULL;
PFNSYMGETSYMFROMADDR        g_pfnSymGetSymFromAddr=NULL;
PFNSYMFUNCTIONTABLEACCESS   g_pfnSymFunctionTableAccess=NULL;
PFNSYMGETMODULEBASE         g_pfnSymGetModuleBase=NULL;

HINSTANCE                   g_hImagehlpInstance=NULL;


BOOL fDebugInitialised = FALSE;


BOOL
InitDebug(
    );


DWORD GetStack(
    IN EXCEPTION_POINTERS *exp,
    IN PCALLER_SYM   Caller,
    IN int           Skip,
    IN int           cFind,
    IN int           fResolveSymbols
    );

BOOL LoadImageHLP()
{

   g_hImagehlpInstance = LoadLibrary ("imagehlp.dll");

   if (!g_hImagehlpInstance) {
        return FALSE;
   }


   g_pfnSymInitialize = (PFNSYMINITIALIZE) GetProcAddress (g_hImagehlpInstance,
                                                           "SymInitialize");
   if (!g_pfnSymInitialize) {
        return FALSE;
   }

   g_pfnSymCleanup = (PFNSYMCLEANUP) GetProcAddress (g_hImagehlpInstance,
                                                           "SymCleanup");
   if (!g_pfnSymCleanup) {
        return FALSE;
   }


   g_pfnStackWalk = (PFNSTACKWALK) GetProcAddress (g_hImagehlpInstance,
                                                           "StackWalk");
   if (!g_pfnStackWalk) {
        return FALSE;
   }


   g_pfnSymGetSymFromAddr = (PFNSYMGETSYMFROMADDR) GetProcAddress (g_hImagehlpInstance,
                                                           "SymGetSymFromAddr");
   if (!g_pfnSymGetSymFromAddr) {
        return FALSE;
   }


   g_pfnSymFunctionTableAccess = (PFNSYMFUNCTIONTABLEACCESS) GetProcAddress (g_hImagehlpInstance,
                                                           "SymFunctionTableAccess");
   if (!g_pfnSymFunctionTableAccess) {
        return FALSE;
   }


   g_pfnSymGetModuleBase = (PFNSYMGETMODULEBASE) GetProcAddress (g_hImagehlpInstance,
                                                           "SymGetModuleBase");
   if (!g_pfnSymGetModuleBase) {
        return FALSE;
   }

   return TRUE;
}


BOOL
InitDebug(
    )
/*++

Description:

    This routine initializes the debug memory functionality.

Arguments:

    none

Return Value:

    BOOL - pass or fail

--*/
{
    BOOL        status;
    SYSTEM_INFO SysInfo;

    if (fDebugInitialised)
        return TRUE;

    status = RtlEnterCriticalSection(&(g_RegLeakTraceInfo.StackInitCriticalSection));
    ASSERT( NT_SUCCESS( status ) );

    if (fDebugInitialised)
        return TRUE;

    OurProcess = GetCurrentProcess();



    g_RegLeakTraceInfo.szSymPath = (LPTSTR) RtlAllocateHeap(
                                                            RtlProcessHeap(),
                                                            0,
                                                            SYM_PATH_MAX_SIZE*sizeof(TCHAR));


    if (!g_RegLeakTraceInfo.szSymPath) {
        // looks like machine already doesn't have enough memory
        // disable leak tracking
        g_RegLeakTraceInfo.bEnableLeakTrack = 0;
        return FALSE;
    }

    g_RegLeakTraceInfo.dwMaxStackDepth = GetProfileInt(TEXT("RegistryLeak"), TEXT("StackDepth"), MAX_LEAK_STACK_DEPTH);
    GetProfileString(TEXT("RegistryLeak"), TEXT("SymbolPath"), TEXT(""), g_RegLeakTraceInfo.szSymPath, SYM_PATH_MAX_SIZE);


    if (!(*g_RegLeakTraceInfo.szSymPath)) {

        RtlFreeHeap(
            RtlProcessHeap(),
            0,
            g_RegLeakTraceInfo.szSymPath);

            g_RegLeakTraceInfo.szSymPath = NULL;
    }


    if (!LoadImageHLP()) {
        g_RegLeakTraceInfo.bEnableLeakTrack = FALSE;
        status = RtlLeaveCriticalSection(&(g_RegLeakTraceInfo.StackInitCriticalSection));
        return FALSE;
    }

    GetSystemInfo( &SysInfo );
    switch (SysInfo.wProcessorArchitecture) {

    default:
    case PROCESSOR_ARCHITECTURE_INTEL:
        MachineType = IMAGE_FILE_MACHINE_I386;
        break;

    case PROCESSOR_ARCHITECTURE_MIPS:
        //
        // note this may not detect R10000 machines correctly
        //
        MachineType = IMAGE_FILE_MACHINE_R4000;
        break;

    case PROCESSOR_ARCHITECTURE_ALPHA:
        MachineType = IMAGE_FILE_MACHINE_ALPHA;
        break;

    case PROCESSOR_ARCHITECTURE_PPC:
        MachineType = IMAGE_FILE_MACHINE_POWERPC;
        break;

    }


    // symbols from Current directory/Environment variable _NT_SYMBOL_PATH
    // Environment variable _NT_ALTERNATE_SYMBOL_PATH or Environment variable SYSTEMROOT

    status = g_pfnSymInitialize ( OurProcess, g_RegLeakTraceInfo.szSymPath, FALSE );

    fDebugInitialised = TRUE;

    status = RtlLeaveCriticalSection(&(g_RegLeakTraceInfo.StackInitCriticalSection));
    return( TRUE );
}

BOOL
StopDebug()
{
    if (fDebugInitialised) {

        BOOL fSuccess;

        fSuccess = g_pfnSymCleanup(OurProcess);

        fDebugInitialised = FALSE;

        FreeLibrary(g_hImagehlpInstance);

        if (g_RegLeakTraceInfo.szSymPath) {
            RtlFreeHeap(
                RtlProcessHeap(),
                0,
                g_RegLeakTraceInfo.szSymPath);
        }

        return fSuccess;
    }
    return TRUE;
}

BOOL
ReadMem(
    IN HANDLE   hProcess,
    IN LPCVOID  BaseAddr,
    IN LPVOID   Buffer,
    IN DWORD    Size,
    IN LPDWORD  NumBytes )
/*++

Description:

    This is a callback routine that StackWalk uses - it just calls teh system ReadProcessMemory
    routine with this process's handle

Arguments:


Return Value:

    none

--*/

{
    BOOL    status;
    SIZE_T  RealNumberBytesRead;

    status = ReadProcessMemory( GetCurrentProcess(),BaseAddr,Buffer,Size,&RealNumberBytesRead );
    *NumBytes = (DWORD)RealNumberBytesRead;

    return( status );
}


VOID
GetCallStack(
    IN PCALLER_SYM   Caller,
    IN int           Skip,
    IN int           cFind,
    IN int           fResolveSymbols
    )
/*++

Description:

    This routine walks te stack to find the return address of caller. The number of callers
    and the number of callers on top to be skipped can be specified.

Arguments:

    pdwCaller       array of DWORD to return callers
                    return addresses
    Skip            no. of callers to skip
    cFInd           no. of callers to find

Return Value:

    none

--*/
{

    if (!g_RegLeakTraceInfo.bEnableLeakTrack) {
        return;
    }

    if (!InitDebug()) {
        return;
    }

    __try {
        memset(Caller, 0, cFind * sizeof(CALLER_SYM));
        RaiseException(MY_DBG_EXCEPTION, 0, 0, NULL);
        // raise an exception to get the exception record to start the stack walk
        //
    }
    __except(GetStack(GetExceptionInformation(), Caller, Skip, cFind, fResolveSymbols)) {
    }
}

DWORD GetStack(
    IN EXCEPTION_POINTERS *exp,
    IN PCALLER_SYM   Caller,
    IN int           Skip,
    IN int           cFind,
    IN int           fResolveSymbols
    )
{
    BOOL             status;
    CONTEXT          ContextRecord;
    PUCHAR           Buffer[sizeof(IMAGEHLP_SYMBOL)-1 + MAX_FUNCTION_INFO_SIZE]; // symbol info
    PIMAGEHLP_SYMBOL Symbol = (PIMAGEHLP_SYMBOL)Buffer;
    STACKFRAME       StackFrame;
    INT              i;
    DWORD            Count;

    memcpy(&ContextRecord, exp->ContextRecord, sizeof(CONTEXT));

    ZeroMemory( &StackFrame,sizeof(STACKFRAME) );
    StackFrame.AddrPC.Segment = 0;
    StackFrame.AddrPC.Mode = AddrModeFlat;

#ifdef _M_IX86
    StackFrame.AddrFrame.Offset = ContextRecord.Ebp;
    StackFrame.AddrFrame.Mode = AddrModeFlat;

    StackFrame.AddrStack.Offset = ContextRecord.Esp;
    StackFrame.AddrStack.Mode = AddrModeFlat;

    StackFrame.AddrPC.Offset = (DWORD)ContextRecord.Eip;
#elif defined(_M_MRX000)
    StackFrame.AddrPC.Offset = (DWORD)ContextRecord.Fir;
#elif defined(_M_ALPHA)
    StackFrame.AddrPC.Offset = (DWORD)ContextRecord.Fir;
#elif defined(_M_PPC)
    StackFrame.AddrPC.Offset = (DWORD)ContextRecord.Iar;
#endif

    Count = 0;
    for (i=0;i<cFind+Skip ;i++ ) {
        status = g_pfnStackWalk( MachineType,
            OurProcess,
            GetCurrentThread(),
            &StackFrame,
            (PVOID)&ContextRecord,
            (PREAD_PROCESS_MEMORY_ROUTINE)ReadMem,
            g_pfnSymFunctionTableAccess,
            g_pfnSymGetModuleBase,
            NULL );


        if (status) {
            if ( i >= Skip) {
                DWORD   Displacement;

                ZeroMemory( Symbol,sizeof(IMAGEHLP_SYMBOL)-1 + MAX_FUNCTION_INFO_SIZE );
                Symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
                Symbol->Address = StackFrame.AddrPC.Offset;
                Symbol->MaxNameLength = MAX_FUNCTION_INFO_SIZE-1;
                Symbol->Flags = SYMF_OMAP_GENERATED;

                if (fResolveSymbols)
                    status = g_pfnSymGetSymFromAddr( OurProcess,StackFrame.AddrPC.Offset,(DWORD_PTR*)&Displacement,Symbol );

                //
                // save the name of the function and the displacement into it for later printing
                //

                Caller[Count].Addr = (PVOID)StackFrame.AddrPC.Offset;

                if (status) {
                    strcpy( Caller[Count].Buff,Symbol->Name );
                    Caller[Count].Displacement = Displacement;
                }
                Count++;
            }

        } else {
            break;
        }
    }

    return EXCEPTION_CONTINUE_EXECUTION;
    // done with exceptions
}

#endif // LEAK_TRACK
#endif // LOCAL
