winapi - StackWalk64 似乎有效,但 SymFromAddr 返回虚假名称

标签 winapi dbghelp

我实现了以下函数来获取 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;
}

堆栈遍历似乎按预期工作,我得到的帧数与调试器中的帧数差不多。 SymGetModuleBase64GetModuleFileName 也可以正常工作以解析模块名称。

但是对 SymFromAddr 的调用总是给我一个模块的相同功能。每个地址都不同,测试一些地址,它们看起来是正确的。

PDB 是使用/Zi 和/DEBUG 生成的。

知道我该怎么做才能让它正常工作吗?

最佳答案

代码没问题,现在已经成功运行多年了。参见 dbg.h实现正常运行。

事实证明环境很无聊。一些模块以奇怪的方式乱七八糟。在其他情况下,代码多年来一直完美运行。

关于winapi - StackWalk64 似乎有效,但 SymFromAddr 返回虚假名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23222873/

相关文章:

c++ - 如何正确传递窗口句柄?

c++ - 哪种方法更好地获取静态链接 DLL 的模块句柄 (HMODULE) - 使用 GetModuleHandleEx() 或 LoadLibrary()?

c - 从已卸载模块的 pdb 中提取结构信息

c++ - 如何通过 DbgHelp 获取局部变量的值

windows - 如何从小型转储中提取堆栈跟踪?

c - 如何使用 SymLoadModuleEx 加载 PDB 文件?

c++ - 使用 SetFileInformationByHandle 移动文件

c - 如何在 Windows 上删除 c 中的 .zip 文件? (错误: Directory not empty)

delphi - 如何将delphi窗体的宽度和高度像素转换为对话框单位?

c++ - 在哪里可以找到 Windows 进程中已卸载模块的列表?