/*++ 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 #include #include #include #include #include #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= 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