views:

135

answers:

3

When a 64 bit VC 2005 application is compiled with optimization turned on, it is not possible to see all local variables in a crash dump file. In many cases, the local variables or parameters are stored in registers instead of on the stack. Subsequent calls to other functions, such as error handling functions, will sometimes overwrite those values. This makes it difficult to trace the cause of the problem. Is there a way to force local variables and/or parameters to the stack at run time?

Turning off the optimization is one way but that makes application slow, and it is generally not a good idea for the release build. I am hoping that there is a run time call that will dump all local variables and/or registry somewhere. If there is such function, we can then call the function before calling the error logging function and will hopefully be able to see more local variable on the stack.

-- Alex

A: 

If you take the address of a variable, the compiler will be forced to allocate space for it on the stack, so you could write a little macro.

#ifdef DEBUG
 #define FORCE_ON_STACK(var)  void * p##var##_dummy = (void *)&var
#else
 #define FORCE_ON_STACK(var)  (void)0 // eat the ;
#endif

The compiler still might be lazy about keeping the value on the stack in sync with the registers in some cases. Also keeping arguments in registers is an important optimization which this will at least partly defeat, so this will impact performance somewhat.

John Knoeller
A: 

Is there a way to force local variables and/or parameters to the stack at run time?

Such a thing is completely impractical; it would require re-compiling the code. John Knoeller's answer is your best bet, otherwise you need to break at each function entry and record the register state.

Nathan Kidd
+2  A: 

I think you're seeing something else. Most common calling conventions on x86 pass arguments the function arguments on the stack. But the x64 calling convention is different, it is similar to __fastcall on x86, it passes the first 4 function arguments in registers (rcx, rdx, r8 and r9). If the function is non-trivial, the compiler generates code to save those registers on the stack frame right away.

Unfortunately, the debugger isn't smart enough to know about the saved register locations. It displays the register value in the call stack, a value that almost always has changed. You can technically dig the argument value out of the stack frame yourself, but you really, really want to have to do this. In optimized code it is an offset from rsp, not rbp, and the stack pointer value has changed as well.

I haven't found any good workaround for this yet. Looking forward to improvements in the VS2010 debugger, no idea if this was addressed.

Hans Passant
Thanks for the explanation. The problem is that (with optimizer turned on) the compiler does not generate code to save the registers (used for local variables) if the variables are not used further in the function. \For example, in an unexpected error situation in our application, we call into an error handling function that generates a crash dump, and then returns. Since there is no further execution is the current function, the call into the error handler will certainly wipe out most local variables stored in the registers.
Alex W
My idea is to macro the call to the error handler function to preserve all registers (in global variables) before calling the error handler function. Then I will be able to dig out the values of the variable from the preserved register values. If there is not a single function to do that, what are the assembly to retrieve each individual register value?
Alex W
That's a different kind of debugging problem. Local variables getting stored in registers and not saved back happens in x86 optimized code as well. Definitely easier to deal with if you switch to assembly code view.
Hans Passant
Well, it does not happen quite as often in 32bit app though. In 64bit app, because of the additional registers, the compiler is really aggressive in using the registers for local temporary variables. I have looked into the registers and stack trying to dig them out but more often than not, they are no longer there.
Alex W