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
|