#include #include "stackwlk.hxx" char * mystrdup(const char * sz) { int nLen = lstrlenA(sz) + 1; char * tmp = new char[nLen]; 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(L"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); } } DWORD_PTR StackWalker::LoadModule(HANDLE hProcess, DWORD_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, (DWORD_PTR) mbi.AllocationBase, 0); return (DWORD_PTR) mbi.AllocationBase; } } return 0; } Symbol* StackWalker::ResolveAddress(DWORD_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; DWORD_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); } DWORD_PTR __stdcall StackWalker::GetModuleBase(HANDLE hProcess, DWORD_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_MRX000) dwMachineType = IMAGE_FILE_MACHINE_R4000; frame.AddrPC.Offset = context->Fir; // Program Counter #elif defined(_M_ALPHA) dwMachineType = IMAGE_FILE_MACHINE_ALPHA; frame.AddrPC.Offset = (DWORD_PTR) context->Fir; // Program Counter frame.AddrStack.Offset = (DWORD_PTR) context->IntSp; // Stack Pointer frame.AddrFrame.Offset = (DWORD_PTR) context->IntFp; // Frame Pointer #elif defined(_M_PPC) dwMachineType = IMAGE_FILE_MACHINE_POWERPC; frame.AddrPC.Offset = context->Iar; // Program Counter #elif defined(_M_IA64) // BUGBUG: check for correctness dwMachineType = IMAGE_FILE_MACHINE_IA64; frame.AddrPC.Offset = context->StIIP; // Program Counter frame.AddrStack.Offset = context->IntSp; //Stack Pointer frame.AddrStack.Mode = AddrModeFlat; // No Frame pointer information for IA64 (per Intel folks) //frame.AddrFrame.Offset = context->Ebp; // Frame Pointer #elif defined(_M_AXP64) // BUGBUG: check for correctness dwMachineType = IMAGE_FILE_MACHINE_AXP64; frame.AddrPC.Offset = (DWORD_PTR) context->Fir; // Program Counter frame.AddrStack.Offset = (DWORD_PTR) context->IntSp; // Stack Pointer frame.AddrFrame.Offset = (DWORD_PTR) context->IntFp; // Frame Pointer #elif defined(_M_AMD64) dwMachineType = IMAGE_FILE_MACHINE_AMD64; frame.AddrPC.Offset = (ULONG64) context->Rip; // Program Counter frame.AddrStack.Offset = (ULONG64) context->Rsp; // Stack Pointer frame.AddrFrame.Offset = (ULONG64) context->Rbp; // Frame Pointer #else #error Unknown Target Machine #endif // These variables are used to count the number of consecutive frames // with the exact same PC returned by StackWalk(). On the Alpha infinite // loops (and infinite lists!) were being caused by StackWalk() never // returning FALSE (see Raid Bug #8354 for details). const DWORD dwMaxNumRepetitions = 40; DWORD dwRepetitions = 0; ADDRESS addrRepeated = {0, 0, AddrModeFlat}; // Walk the stack... Symbol* prev = NULL; Symbol* head = NULL; for (;;) { if (!_StackWalk(dwMachineType, _hProcess, hThread, &frame, &context, NULL, _SymFunctionTableAccess, GetModuleBase, NULL)) break; if (frame.AddrPC.Offset == 0) break; // Check for repeated addresses; if dwMaxNumRepetitions are found, // then we break out of the loop and exit the stack walk if (addrRepeated.Offset == frame.AddrPC.Offset && addrRepeated.Mode == frame.AddrPC.Mode) { dwRepetitions ++; if (dwRepetitions == dwMaxNumRepetitions) { break; } } else { dwRepetitions = 0; addrRepeated.Offset = frame.AddrPC.Offset; addrRepeated.Mode = frame.AddrPC.Mode; } // There have been reports of StackWalk returning an offset of // -1, which SymLoadModule later av's on. If this happens, // we simply skip that frame. if (frame.AddrPC.Offset == -1) continue; 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; } SIZE_T StackWalker::GetCallStackSize(Symbol* symbol) { SIZE_T 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, SIZE_T nChars, WCHAR * sz, SIZE_T nMaxLines) { if (!symbol || !nChars) return FALSE; Symbol* sym = symbol; // for (int i=0;i<3;i++) // { // Symbol* tmp = sym; // sym = sym->next(); // delete tmp; // if (!sym) // break; // } const char* module = NULL; const char* symbolName = NULL; char * szStack = (char * )PrivMemAlloc(nChars); ZeroMemory(szStack, nChars); lstrcpyA(szStack, "\r\n"); // Start with a CR-LF. Symbol* tmp = NULL; while (sym != NULL && nMaxLines-- > 0) { module = sym->moduleName(); symbolName = sym->symbolName(); if (module != NULL) { lstrcatA(szStack, module); if (symbolName != NULL) lstrcatA(szStack, "!"); } if (symbolName != NULL) lstrcatA(szStack, symbolName); sym -> AppendDisplacement(szStack); lstrcatA(szStack, "\r\n"); tmp = sym; sym = sym->next(); delete tmp; } SIZE_T nLen = lstrlenA(szStack); nLen++; MultiByteToWideChar(CP_ACP, 0, szStack, (int) nLen, sz, (int) nLen); PrivMemFree(szStack); return TRUE; } Symbol::Symbol(const char* moduleName, const char* symbolName, DWORD_PTR displacement) : _moduleName(NULL), _symbolName(NULL), _displacement(displacement), _next(NULL) { if (moduleName != NULL) _moduleName = mystrdup(moduleName); if (symbolName != NULL) _symbolName = mystrdup(symbolName); } Symbol::~Symbol() { delete [] _moduleName; delete [] _symbolName; } void Symbol::Append(Symbol* sym) { _next = sym; } #if 0 #include DWORD filter(EXCEPTION_POINTERS* exp) { StackWalker resolver(GetCurrentProcess()); Symbol* symbol = resolver.CreateStackTrace(exp->ContextRecord); if (symbol == NULL) { cout << "Couldn't get stack trace" << endl; } else { cout << "Stack trace:" << endl; Symbol* sym = symbol; while (sym != NULL) { const char* module = sym->moduleName(); const char* symbolName = sym->symbolName(); if (module != NULL) { cout << module; if (symbolName != NULL) cout << '!'; } if (symbolName != NULL) cout << symbolName; cout << "+0x" << hex << sym->displacement() << dec << endl; Symbol* tmp = sym; sym = sym->next(); delete tmp; } } return EXCEPTION_EXECUTE_HANDLER; } int bar(int x, int* p) { *p = x; return 5; } void foo(int* p) { bar(5, p); } int main() { __try { int* p = (int*)0xdeadbeef; foo(p); } __except (filter(GetExceptionInformation())) { } return 0; } #endif