7

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!

Cheeso
  • 189,189
  • 101
  • 473
  • 713
Uhall
  • 5,673
  • 7
  • 24
  • 19
  • Why would you want to do this without DbgHelp.dll. DbgHelp.dll is builtin to Windows, since Windows 2000. Also check out Jochen Kalmbach's Stackwalker: http://www.codeproject.com/KB/threads/StackWalker.aspx – Cheeso Oct 25 '09 at 02:50

3 Answers3

9

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
  • 6,307
  • 3
  • 24
  • 27
  • 1
    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 Dec 24 '08 at 23:47
  • Way cool! I've written stack tracing code ateast 3 times and had no idea this exists. Thank you! – shoosh Dec 25 '08 at 03:54
  • @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 Dec 29 '08 at 16:24
2

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
  • 390,455
  • 97
  • 512
  • 589
  • 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 Dec 25 '08 at 03:56
  • destination comes first, so you need to mov my_ebp, ebp.. other than that it's pretty cool – Daniel Sloof Mar 19 '09 at 17:59
1

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!
user80143
  • 11
  • 1