Avinash
Avinash

Reputation: 13257

Stack Walker for x64 Windows

I am trying to print the stack trace for my application. I cannot use StackWalk64 as my application is release with optimization disabled. For x86 we were using strace library somebody written on codeprex But I did not find anything similar for x64. Following is the code I found on web for x86.

#include <Windows.h>
#include <DbgHelp.h>
#include <stdio.h>

#define INVALID_FP_RET_ADDR_VALUE 0x00000000
BOOL g_fSymInit;
HANDLE g_hProcess;
BOOL DisplaySymbolDetails(DWORD dwAddress)
{
    DWORD64 displacement = 0;
    ULONG64 buffer[(sizeof(SYMBOL_INFO) +
        MAX_SYM_NAME*sizeof(TCHAR) +
        sizeof(ULONG64) - 1) /
        sizeof(ULONG64)];
    PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
    pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    pSymbol->MaxNameLen = MAX_SYM_NAME;
    if (SymFromAddr(g_hProcess,dwAddress,&displacement,pSymbol))
    {
        // Try to get the Module details
        IMAGEHLP_MODULE64 moduleinfo;
        moduleinfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
        if (SymGetModuleInfo64(g_hProcess,pSymbol->Address,&moduleinfo))
        {
            printf("%s!",moduleinfo.ModuleName);
        }
        else
        {
            printf("<ErrorModuleInfo_%d>!", GetLastError());
        }
        // now print the function name
        if (pSymbol->MaxNameLen > 0)
        {
            printf("%s",pSymbol->Name);
        }
        else
        {
            printf("<Unknown_Function>");
        }
    }
    else
    {
        printf(" <Unable to get symbol details_%d>", GetLastError());
    }
    return TRUE;
}
bool WalkTheStack()
{
    DWORD _ebp = INVALID_FP_RET_ADDR_VALUE;
    DWORD dwIPOfCurrentFunction = (DWORD)&WalkTheStack;
    // Get the current Frame pointer
    __asm
    {
        mov [_ebp], ebp
    }
    // We cannot walk the stack (yet!) without a frame pointer
    if (_ebp == INVALID_FP_RET_ADDR_VALUE)
        return false;
    printf("CurFP\t\t\tRetAddr\n");
    DWORD *pCurFP = (DWORD *)_ebp;
    BOOL fFirstFP = TRUE;
    while (pCurFP != INVALID_FP_RET_ADDR_VALUE)
    {
        // pointer arithmetic works in terms of type pointed to. Thus,
        // "+1" below is equivalent of 4 bytes since we are doing DWORD
        // math.
        DWORD pRetAddrInCaller = (*((DWORD *)(pCurFP + 1)));
        printf("%p\t\t%p ",pCurFP, (DWORD *)pRetAddrInCaller);
        if (g_fSymInit)
        {
            if (fFirstFP)
            {
                fFirstFP = FALSE;
            }
            DisplaySymbolDetails(dwIPOfCurrentFunction);
            // To get the name of the next function up the stack,
            // we use the return address of the current frame
            dwIPOfCurrentFunction = pRetAddrInCaller;
        }
        printf("\n");
        if (pRetAddrInCaller == INVALID_FP_RET_ADDR_VALUE)
        {
            // StackWalk is over now...
            break;
        }
        // move up the stack to our caller
        DWORD pCallerFP = *((DWORD *)pCurFP);
        pCurFP = (DWORD *)pCallerFP;
    }
    return true;
}

int main ( int argc, char **argv) {
    g_fSymInit = FALSE;
    g_hProcess = GetCurrentProcess();
    if (!SymInitialize(g_hProcess, NULL,TRUE)) {
        printf("Unable to initialize symbols!\n\n");    
    } else {
        g_fSymInit = TRUE;
    }
    SymSetOptions(SYMOPT_UNDNAME|SYMOPT_INCLUDE_32BIT_MODULES|SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
    WalkTheStack();
    return 0;
}

What changes would be required to make it work on x64

Upvotes: 1

Views: 899

Answers (1)

Max DeLiso
Max DeLiso

Reputation: 1244

What you want to do is unroll the stack. Rather than fix that ugly mess I'll just tell you the general principles involved. On x86 and x86_64 the ebp/rsp and esp/rsp registers form an implicit linked list of memory locations. Each esp/rsp points to the top of the current stack frame, and each ebp/rbp points to the bottom of the previous stack frame. Armed with this knowledge, it's fairly trivial to walk through the frames.

Upvotes: 1

Related Questions