windows-nt/Source/XPSP1/NT/com/ole32/common/stackwlk.cxx
2020-09-26 16:20:57 +08:00

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