I implemented the following function to get the current stack on 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;
}
The stack walk appears to work as expected, I get about as many frames as in the debugger. Also the SymGetModuleBase64
and GetModuleFileName
work properly to resolve module names.
But the call to SymFromAddr
always gives me the same function for one module. Each address is different and testing some address, they appear correct.
The PDBs are generated with /Zi and /DEBUG.
Any idea what I can do to get it to work properly?