/*++ Copyright (c) 1995-1996 Microsoft Corporation Module Name : wamxcf.cxx Abstract: WAM Exception Filter and stack walking code from MTS Author: Andrei Kozlov ( AKozlov ) 23-Sep-98 Environment: User Mode - Win32 Project: WAM DLL --*/ /************************************************************ * Include Headers ************************************************************/ # include #include # include "wamxinfo.hxx" # include "WReqCore.hxx" # include "setable.hxx" # include "gip.h" # include "WamW3.hxx" // MIDL-generated # include "iwr.h" // =================================================================== // Stack trace classes by Don McCrady of MTS class Symbol { friend class StackWalker; private: Symbol(const char* moduleName, const char* symbolName, UINT_PTR displacement); void Append(Symbol*); public: ~Symbol(); const char* moduleName() const { return _moduleName; } const char* symbolName() const { return _symbolName; } UINT_PTR displacement() const { return _displacement; } void AppendDisplacement(char * sz) { char szDisp[16]; wsprintfA(szDisp, " + 0x%X", _displacement); lstrcatA(sz, szDisp); } Symbol* next() const { return _next; } private: char* _moduleName; char* _symbolName; UINT_PTR _displacement; Symbol* _next; }; class StackWalker { public: StackWalker(HANDLE hProcess); ~StackWalker(); Symbol* ResolveAddress(UINT_PTR addr); Symbol* CreateStackTrace(CONTEXT*); BOOL GetCallStack(Symbol * symbol, int nChars, char * sz); int GetCallStackSize(Symbol* symbol); private: static UINT_PTR __stdcall GetModuleBase(HANDLE hProcess, UINT_PTR address); static UINT_PTR LoadModule(HANDLE hProcess, UINT_PTR address); private: typedef BOOL (__stdcall *SymGetModuleInfoFunc)(HANDLE hProcess, UINT_PTR dwAddr, PIMAGEHLP_MODULE ModuleInfo); typedef BOOL (__stdcall *SymGetSymFromAddrFunc)(HANDLE hProcess, UINT_PTR dwAddr, UINT_PTR * pdwDisplacement, PIMAGEHLP_SYMBOL Symbol); typedef UINT_PTR (__stdcall *SymLoadModuleFunc)(HANDLE hProcess, HANDLE hFile, PSTR ImageName, PSTR ModuleName, UINT_PTR BaseOfDll, UINT_PTR SizeOfDll); typedef BOOL (__stdcall *StackWalkFunc)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME StackFrame, LPVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE TranslateAddress); typedef BOOL (__stdcall *UndecorateSymbolNameFunc)(LPSTR DecName, LPSTR UnDecName, DWORD UnDecNameLength, DWORD Flags); private: HMODULE _imageHlpDLL; HANDLE _hProcess; EXCEPTION_POINTERS m_exceptionpts; static SymGetModuleInfoFunc _SymGetModuleInfo; static SymGetSymFromAddrFunc _SymGetSymFromAddr; static SymLoadModuleFunc _SymLoadModule; static StackWalkFunc _StackWalk; static UndecorateSymbolNameFunc _UndecorateSymbolName; static PFUNCTION_TABLE_ACCESS_ROUTINE _SymFunctionTableAccess; }; // ====================================================================== // Exception handling and stack traces // ====================================================================== char * mystrdup(const char * sz) { int nLen = lstrlenA(sz) + 1; char * tmp = (char *)malloc(nLen); if (tmp) { lstrcpyA(tmp, sz); } return tmp; } StackWalker::SymGetModuleInfoFunc StackWalker::_SymGetModuleInfo; StackWalker::SymGetSymFromAddrFunc StackWalker::_SymGetSymFromAddr; StackWalker::SymLoadModuleFunc StackWalker::_SymLoadModule; StackWalker::StackWalkFunc StackWalker::_StackWalk; StackWalker::UndecorateSymbolNameFunc StackWalker::_UndecorateSymbolName; PFUNCTION_TABLE_ACCESS_ROUTINE StackWalker::_SymFunctionTableAccess; StackWalker::StackWalker(HANDLE hProcess) : _imageHlpDLL(NULL), _hProcess(hProcess) { _imageHlpDLL = LoadLibrary("imagehlp.dll"); if (_imageHlpDLL != NULL) { // Get commonly used Sym* functions. if (_StackWalk == NULL) { // If one of them are null, assume // they all are. Benign race here. _StackWalk = (StackWalkFunc)GetProcAddress(_imageHlpDLL, "StackWalk"); if (_StackWalk == NULL) return; _SymGetModuleInfo = (SymGetModuleInfoFunc)GetProcAddress(_imageHlpDLL, "SymGetModuleInfo"); if (_SymGetModuleInfo == NULL) return; _SymGetSymFromAddr = (SymGetSymFromAddrFunc)GetProcAddress(_imageHlpDLL, "SymGetSymFromAddr"); if (_SymGetSymFromAddr == NULL) return; _SymLoadModule = (SymLoadModuleFunc)GetProcAddress(_imageHlpDLL, "SymLoadModule"); if (_SymLoadModule == NULL) return; _UndecorateSymbolName = (UndecorateSymbolNameFunc)GetProcAddress(_imageHlpDLL, "UnDecorateSymbolName"); if (_UndecorateSymbolName == NULL) return; _SymFunctionTableAccess = (PFUNCTION_TABLE_ACCESS_ROUTINE)GetProcAddress(_imageHlpDLL, "SymFunctionTableAccess"); if (_SymFunctionTableAccess == NULL) return; } // Sym* functions that we're only going to use locally. typedef BOOL (__stdcall *SymInitializeFunc)(HANDLE hProcess, LPSTR path, BOOL invadeProcess); typedef DWORD (__stdcall *SymSetOptionsFunc)(DWORD); SymInitializeFunc SymInitialize = (SymInitializeFunc)GetProcAddress(_imageHlpDLL, "SymInitialize"); if (SymInitialize == NULL) return; SymSetOptionsFunc SymSetOptions = (SymSetOptionsFunc)GetProcAddress(_imageHlpDLL, "SymSetOptions"); if (SymSetOptions == NULL) return; if (SymInitialize(hProcess, NULL, FALSE)) SymSetOptions(0); } } StackWalker::~StackWalker() { if (_imageHlpDLL != NULL) { typedef BOOL (__stdcall *SymCleanupFunc)(HANDLE hProcess); SymCleanupFunc SymCleanup = (SymCleanupFunc)GetProcAddress(_imageHlpDLL, "SymCleanup"); if (SymCleanup != NULL) SymCleanup(_hProcess); FreeLibrary(_imageHlpDLL); } } UINT_PTR StackWalker::LoadModule(HANDLE hProcess, UINT_PTR address) { MEMORY_BASIC_INFORMATION mbi; if (VirtualQueryEx(hProcess, (void*)address, &mbi, sizeof mbi)) { if (mbi.Type & MEM_IMAGE) { char module[MAX_PATH]; DWORD cch = GetModuleFileNameA((HINSTANCE)mbi.AllocationBase, module, MAX_PATH); // Ignore the return code since we can't do anything with it. (void)_SymLoadModule(hProcess, NULL, ((cch) ? module : NULL), NULL, (ULONG_PTR)mbi.AllocationBase, 0); return (UINT_PTR)mbi.AllocationBase; } } return 0; } Symbol* StackWalker::ResolveAddress(UINT_PTR addr) { if (_imageHlpDLL == NULL) return NULL; // Find out what module the address lies in. char* module = NULL; IMAGEHLP_MODULE moduleInfo; moduleInfo.SizeOfStruct = sizeof moduleInfo; if (_SymGetModuleInfo(_hProcess, addr, &moduleInfo)) { module = moduleInfo.ModuleName; } else { // First attempt failed, load the module info. LoadModule(_hProcess, addr); if (_SymGetModuleInfo(_hProcess, addr, &moduleInfo)) module = moduleInfo.ModuleName; } char* symbolName = NULL; char undecoratedName[512]; IMAGEHLP_SYMBOL* symbolInfo = (IMAGEHLP_SYMBOL*)_alloca(sizeof(IMAGEHLP_SYMBOL) + 512); symbolInfo->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL) + 512; symbolInfo->MaxNameLength = 512; UINT_PTR displacement = 0; if (_SymGetSymFromAddr(_hProcess, addr, &displacement, symbolInfo)) { DWORD flags = UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS | UNDNAME_NO_FUNCTION_RETURNS | UNDNAME_NO_MEMBER_TYPE; if (_UndecorateSymbolName(symbolInfo->Name, undecoratedName, 512, flags)) symbolName = undecoratedName; else symbolName = symbolInfo->Name; } else { displacement = addr - moduleInfo.BaseOfImage; } return new Symbol(module, symbolName, displacement); } UINT_PTR __stdcall StackWalker::GetModuleBase(HANDLE hProcess, UINT_PTR address) { IMAGEHLP_MODULE moduleInfo; moduleInfo.SizeOfStruct = sizeof moduleInfo; if (_SymGetModuleInfo(hProcess, address, &moduleInfo)) return moduleInfo.BaseOfImage; else return LoadModule(hProcess, address); } Symbol* StackWalker::CreateStackTrace(CONTEXT* context) { if (_imageHlpDLL == NULL) return NULL; HANDLE hThread = GetCurrentThread(); DWORD dwMachineType; STACKFRAME frame = {0}; frame.AddrPC.Mode = AddrModeFlat; #if defined(_M_IX86) dwMachineType = IMAGE_FILE_MACHINE_I386; frame.AddrPC.Offset = context->Eip; // Program Counter frame.AddrStack.Offset = context->Esp; // Stack Pointer frame.AddrStack.Mode = AddrModeFlat; frame.AddrFrame.Offset = context->Ebp; // Frame Pointer #elif defined(_M_AMD64) dwMachineType = IMAGE_FILE_MACHINE_AMD64; frame.AddrPC.Offset = context->Rip; // Program Counter #elif defined(_M_IA64) dwMachineType = IMAGE_FILE_MACHINE_IA64; frame.AddrPC.Offset = CONTEXT_TO_PROGRAM_COUNTER(context); #elif #error("Unknown Target Machine"); #endif // Walk the stack... Symbol* prev = NULL; Symbol* head = NULL; for (;;) { if (!_StackWalk(dwMachineType, _hProcess, hThread, &frame, &context, NULL, (PFUNCTION_TABLE_ACCESS_ROUTINE)_SymFunctionTableAccess, (PGET_MODULE_BASE_ROUTINE)GetModuleBase, NULL)) break; if (frame.AddrPC.Offset == 0) break; Symbol* sym = ResolveAddress(frame.AddrPC.Offset); if (sym == NULL) break; // Append this symbol to the previous one, if any. if (prev == NULL) { prev = sym; head = sym; } else { prev->Append(sym); prev = sym; } } return head; } int StackWalker::GetCallStackSize(Symbol* symbol) { int nSize = 2; // Start with a "\r\n". const char* module = NULL; const char* symbolName = NULL; Symbol * sym = symbol; while (sym != NULL) { module = sym->moduleName(); symbolName = sym->symbolName(); nSize += lstrlenA(module); nSize += lstrlenA(symbolName); nSize += 32; // displacement, spaces, etc. sym = sym -> next(); } return nSize; } BOOL StackWalker::GetCallStack(Symbol * symbol, int nChars, char * sz) { if (!symbol || !nChars) return FALSE; Symbol* sym = symbol; const char* module = NULL; const char* symbolName = NULL; ZeroMemory(sz, nChars); lstrcpy(sz, "\r\n"); // Start with a CR-LF. Symbol* tmp = NULL; while (sym != NULL) { module = sym->moduleName(); symbolName = sym->symbolName(); if (module != NULL) { strcat(sz, module); if (symbolName != NULL) strcat(sz, "!"); } if (symbolName != NULL) strcat(sz, symbolName); sym->AppendDisplacement(sz); lstrcat(sz, "\r\n"); tmp = sym; sym = sym->next(); delete tmp; } return TRUE; } Symbol::Symbol(const char* moduleName, const char* symbolName, UINT_PTR displacement) : _moduleName(NULL), _symbolName(NULL), _displacement(displacement), _next(NULL) { if (moduleName != NULL) _moduleName = mystrdup(moduleName); if (symbolName != NULL) _symbolName = mystrdup(symbolName); } Symbol::~Symbol() { free(_moduleName); free(_symbolName); } void Symbol::Append(Symbol* sym) { _next = sym; } // // WAM Exception Filter -- walk the stack and log event // DWORD WAMExceptionFilter( EXCEPTION_POINTERS *xp, DWORD dwEventId, WAM_EXEC_INFO *pWamExecInfo ) { CHAR * pszStack = NULL; // // Don't handle breakpoints // if (xp->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) return EXCEPTION_CONTINUE_SEARCH; StackWalker walker( GetCurrentProcess() ); Symbol* symbol = walker.CreateStackTrace( xp->ContextRecord ); if( symbol ) { if (symbol != NULL) { int stackBufLen = walker.GetCallStackSize(symbol); pszStack = (TCHAR*)_alloca(stackBufLen * sizeof pszStack[0]); walker.GetCallStack(symbol, stackBufLen, pszStack); } } pWamExecInfo->LogEvent( dwEventId, (unsigned char *) pszStack ); return EXCEPTION_EXECUTE_HANDLER; }