3

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?

Pascal
  • 1,288
  • 8
  • 13
rioki
  • 5,988
  • 5
  • 32
  • 55

1 Answers1

1

The code is ok and successfully runs form years now. See dbg.h for a functioning implementation.

It turned out the environment was just borked. Some module was messing the around in weird ways. In other contexts the code has runs flawlessly for years now.

rioki
  • 5,988
  • 5
  • 32
  • 55