455 lines
11 KiB
C
455 lines
11 KiB
C
/*++
|
||
|
||
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
|