我实现了以下函数来获取 Windows 上的当前堆栈:
struct StackFrame
{
DWORD64 address;
std::string name;
std::string module;
};
std::vector<StackFrame> GetStackTrace()
{
DWORD machine = IMAGE_FILE_MACHINE_I386;
HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread();
STACKFRAME64 frame = {};
CONTEXT context = {};
if (SymInitialize(process, NULL, TRUE) == FALSE)
{
DBG_TRACE(__FUNCTION__ ": Failed to call SymInitialize.");
return std::vector<StackFrame>();
}
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
#ifdef _M_IX86
machine = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
machine = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rsp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
machine = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
std::vector<StackFrame> frames;
while (StackWalk64(machine, process, thread, &frame, &context , NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
{
StackFrame f;
f.address = frame.AddrPC.Offset;
DWORD64 moduleBase = SymGetModuleBase64(process, frame.AddrPC.Offset);
char moduelBuff[MAX_PATH];
if (moduleBase && GetModuleFileNameA((HINSTANCE)moduleBase, moduelBuff, MAX_PATH))
{
f.module = basename(moduelBuff);
}
else
{
f.module = "Unknown Module";
}
DWORD64 offset = 0;
char symbolBuff[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
PSYMBOL_INFO symbol = (PSYMBOL_INFO)symbolBuff;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
if (SymFromAddr(process, frame.AddrPC.Offset, &offset, symbol))
{
f.name = symbol->Name;
}
else
{
DWORD error = GetLastError();
DBG_TRACE(__FUNCTION__ ": Failed to resolve address 0x%X: %u\n", frame.AddrPC.Offset, error);
f.name = "Unknown Function";
}
frames.push_back(f);
}
SymCleanup(process);
return frames;
}
堆栈遍历似乎按预期工作,我得到的帧数与调试器中的帧数差不多。 SymGetModuleBase64
和 GetModuleFileName
也可以正常工作以解析模块名称。
但是对 SymFromAddr
的调用总是给我一个模块的相同功能。每个地址都不同,测试一些地址,它们看起来是正确的。
PDB 是使用/Zi 和/DEBUG 生成的。
知道我该怎么做才能让它正常工作吗?
最佳答案
代码没问题,现在已经成功运行多年了。参见 dbg.h实现正常运行。
事实证明环境很无聊。一些模块以奇怪的方式乱七八糟。在其他情况下,代码多年来一直完美运行。
关于winapi - StackWalk64 似乎有效,但 SymFromAddr 返回虚假名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23222873/