views:

1376

answers:

3

How do I obtain a stack trace of addresses on Windows without using dbghelp.dll?

I don't need to know what the symbols or function names associated with the addresses, I just want the list of addresses -- something similar to backtrace of *nix systems.

Thanks!

+3  A: 

Check out the CaptureStackBackTrace() function, which is in Kernel32.dll. This should do all that you need.

Captures a stack back trace by walking up the stack and recording the information for each frame.

USHORT WINAPI CaptureStackBackTrace(
  __in       ULONG FramesToSkip,
  __in       ULONG FramesToCapture,
  __out      PVOID *BackTrace,
  __out_opt  PULONG BackTraceHash
);
Kris Kumler
Looks good. But I'm using VS 2005, and CaptureStackBackTrace no RtlCaptureBackTrace isn't defined in winbase.h.I'll try manually including the function prototype for RtlCaptureBackTrace(...) and see if that will work for me. Thanks!
Uhall
Way cool! I've written stack tracing code ateast 3 times and had no idea this exists. Thank you!
shoosh
@Uhall: You can try installing a newer Windows SDK. The minimum supported client is listed as Windows XP although the MSDN page does list that it was included in the Windows SDK starting with Windows Vista.
Kris Kumler
+1  A: 

If you want to do this extremely non-portably, you can just read the EBP register and walk the stack yourself. This will only work for the x86 architecture, and it also assumes that the C runtime you're using properly initializes EBP to 0 before calling the first function.

uint32_t read_ebp(void)
{
    uint32_t my_ebp;
    __asm
    {
        mov ebp, my_ebp
    }

    return my_ebp;
}

void backtrace(void)
{
    uint32_t ebp = read_ebp();

    printf("backtrace:\n");

    while(ebp != 0)
    {
        printf("0x%08x\n", ebp);
        ebp = ((uint32_t *)ebp)[1];
    }
}
Adam Rosenfield
With release builds this will only give you the first few frames. For this to actually work reliably you need to set "Omit Frame Pointers"=No.
shoosh
destination comes first, so you need to mov my_ebp, ebp.. other than that it's pretty cool
Daniel
+1  A: 

Previous variant doesn't work for me(msvc 6), so:

unsigned long prev;
unsigned long addr;
__asm { mov prev, ebp }
while(addr!=0) { 
  addr = ((unsigned long *)prev)[1]; 
  printf("0x%08x\n", addr); 
  prev = ((unsigned long *)prev)[0]; 
}
Adam, thanks for highlighting the way!