435 lines
12 KiB
C++
435 lines
12 KiB
C++
|
#include <ole2int.h>
|
||
|
#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 <iostream.h>
|
||
|
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
|