Issues with getting call stack in release build

I am trying to get a call stack in a Windows executable. I tried several different ways to get the call stack. The following are some examples. Please note that I changed them a bit and removed the error handling so that they are easy to understand, so they cannot compile as they are. I think you understand.

A simple way:

const int max_entries = 10; void *entries[max_entries]; return CaptureStackBackTrace(0, max_entries, entries, 0); 

Low level method:

 const int max_entries = 10; void *entries[max_entries]; void **frame = 0; __asm { mov frame, ebp } unsigned int i = 0; while(frame && i < max_entries) { entries[i++] = frame[1]; frame = (void **)frame[0]; } 

Compatible way:

 void *entries[max_entries]; CONTEXT context; RtlCaptureContext(&context); STACKFRAME64 stack_frame; ZeroMemory(&stack_frame, sizeof(STACKFRAME64)); stack_frame.AddrPC.Offset = context.Eip; stack_frame.AddrPC.Mode = AddrModeFlat; stack_frame.AddrFrame.Offset = context.Ebp; stack_frame.AddrFrame.Mode = AddrModeFlat; stack_frame.AddrStack.Offset = context.Esp; stack_frame.AddrStack.Mode = AddrModeFlat; unsigned int num_frames = 0; while (true) { if (!StackWalk64(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &stack_frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) break; if (stack_frame.AddrPC.Offset == 0) break; entries[num_frames++] = reinterpret_cast<void *>(stack_frame.AddrPC.Offset); } 

My problem is that they work in an unoptimized build, but not with full optimization. What happens is that I get one broken record and then go out of my loops. In debugging, I get a full call stack, and when I look at the characters later, everything is correct.

I don’t understand how difficult it is to make this work in all assemblies when the debugger does this all the time. I can say that frame pointers will not be omitted in code generation. First, I create for debugging, and then change the optimization from only one to full optimization and rebuild to reproduce the call stack failure.

Any clues to the solution are welcome.

/ Jonas

+6
callstack winapi
source share
2 answers

I got this working using now the "compatible way". I use the following code to initialize the context:

 #define GET_CURRENT_CONTEXT(c, contextFlags) \ do { \ memset(&c, 0, sizeof(CONTEXT)); \ c.ContextFlags = contextFlags; \ __asm call x \ __asm x: pop eax \ __asm mov c.Eip, eax \ __asm mov c.Ebp, ebp \ __asm mov c.Esp, esp \ } while(0); CONTEXT context; GET_CURRENT_CONTEXT(context, CONTEXT_FULL); 

and then continue to fetch the stack using StackWalk64, as before.

 void *entries[max_entries]; STACKFRAME64 stack_frame; ZeroMemory(&stack_frame, sizeof(STACKFRAME64)); stack_frame.AddrPC.Offset = context.Eip; stack_frame.AddrPC.Mode = AddrModeFlat; stack_frame.AddrFrame.Offset = context.Ebp; stack_frame.AddrFrame.Mode = AddrModeFlat; stack_frame.AddrStack.Offset = context.Esp; stack_frame.AddrStack.Mode = AddrModeFlat; unsigned int num_frames = 0; while (true) { if (!StackWalk64(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(), &stack_frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) break; if (stack_frame.AddrPC.Offset == 0) break; entries[num_frames++] = reinterpret_cast<void *>(stack_frame.AddrPC.Offset); } 

I noticed that I forgot to clear the CONTEXT structure before sending it to RtlCaptureContext, so I tried to do it like this (because I would rather use the RtlCaptureContext function).

 CONTEXT context; memset(&context, 0, sizeof(CONTEXT)); context.ContextFlags = CONTEXT_FULL; RtlCaptureContext(&context); 

Now RtlCaptureContext is crashing, so I returned to using the GET_CURRENT_CONTEXT macro.

+2
source share

This is not an answer, but simply a “me too” report to clarify specific versions:

I see sporadic crashes inside RtlCaptureContext , but only on 32-bit Debug builds, and not on 32-bit release versions, and not on 64-bit Debug or Release builds. I am using VS2008 SP1 with dbghelp.dll fileVersion 6.12.2.633 from the debugging tools for Windows downloaded on April 25, 2011, and that dbghelp.dll was copied to the same directory as my EXE before the call.

This is compilation using the same version of the VS2008 SP1 compiler on the same 64-bit Windows XP machine with Windows XP (compiling both 32-bit and 64-bit native applications, no managed .NET code is in the mix).

The key above is sporadic . I have not determined the conditions under which it falls.

0
source share

All Articles