0

This question is a follow up of this one where the consensus was that the value of *this* pointer is not correct because of optimization but what does not correct means?

  1. That we can't trust the value of this pointer so we should throw it out all together when we debug a release build?
  2. That this pointer may change its value as we debug a release build and that's okay because its optimized code, it may not mean any harm?
  3. this pointer still points to a valid memory block of the object and the fact it changes is cause of concern even when its optimized code.
  4. There is mismatch between the actual code and the optimized generated code, this is why the debugger may show incorrect values or values of variables may change abruptly? I could understand if debugger shows incorrect values but why should these values change?

How do we debug optimized code? A lof of times for exmaple with WinDbg we peek into pointers, objects layouts, stack pointers even instruction pointers..does it mean we can't rely on these values because it's an optimized code? What can we rely on and what not?

Community
  • 1
  • 1
zar
  • 11,361
  • 14
  • 96
  • 178
  • This is the same question we've seen two days ago: http://stackoverflow.com/questions/39105633/why-this-pointer-changes-its-value-while-stepping-through-the-code. What makes you think we want to see it again? – SergeyA Aug 25 '16 at 17:45
  • 1
    @SergeyA it is follow up of that, I was suggested to put a follow up question. – zar Aug 25 '16 at 17:46
  • A debugged or working compiler generates output based on what you fed it. If you are producing optimized code then you dont really have any business stepping through the high level representation, I would abandon any debugger that lets you do that. you should at best be stepping through the machine/assembly or why are you stepping at all? Debuggers are not an accurate representation of your code flow at speed, what works in a debugger is not necessarily expected to work at speed. Why debug twice? – old_timer Aug 25 '16 at 18:10
  • @dwelch that really is the context of question, than how do we debug release code with such limitation? I don't think we can jump 100% in assembly to debug the issue, it has to be traced back with source code if that if that is somewhat loose. – zar Aug 25 '16 at 18:27
  • 2
    @dwelch: Debugging on Windows is different from what you are used to. The debugger uses PDBs (Program Database files) to aid it in matching memory locations and registers to symbolic information and source code references. Using the [/Zo](https://msdn.microsoft.com/en-us/library/dn785163.aspx) compiler switch (VS 2013 Update 3 and later) produces radically improved PDBs, even for optimized builds. None of this winds up in the executable either (as is the case with GCC). Things only start to get interesting when moving to x64... – IInspectable Aug 25 '16 at 18:52
  • @zar: it does not have to be traced back to the source. The CPU is not interested in your source. The RAM also not. Neither is the screen. Nor the disk. The user also not. Nobody except you seems to have the need to be able to get from machine language back to the source. – Thomas Weller Aug 25 '16 at 19:39
  • @zar: BTW: some people can write assembler programs. For them, the assembly language **is** the source. They can read it with the `u`, `uf` and `ub` commands. High level languages - you never know what they do. Only assembly is the true language. – Thomas Weller Aug 25 '16 at 19:40
  • @IInspectable This is not some new windows technology, attempting to match the code to the source. You are just further reinforcing my point. It got interesting as soon as we had optimizers, then parallel and out of order execution made it worse, 64 bit is really no change to the problem by itself. – old_timer Aug 25 '16 at 19:47
  • @dwelch: Information from the optimizer is dumped into the PDB. I take it you haven't thoroughly looked into debugging on Windows, certainly not anything halfway recent. COMDAT folding is still somewhat problematic, but you should definitely give it try. At least if you are commenting as if you knew. – IInspectable Aug 25 '16 at 19:58
  • It may be that you have a bug in your code that is being hidden by the debugger in debug mode that the optimized code is hiding. That or a bug in the compiler/optimizer. Or a bug in the debugger or the toolchain that leaves information for the debugger as to how to step through the code. – old_timer Aug 25 '16 at 20:01
  • Does the program fail when not run in the debugger? – old_timer Aug 25 '16 at 20:01
  • @dwelch Yes, but it passes through this function fine and fails in next function. I am trying to understand why *this* pointer jumps around in this function in release build. – zar Aug 25 '16 at 20:11
  • Either take a printf debugging approach (which can change the results give the illusion that it is fixed) or for the most part this is all about inspection. Inspect your code if you firmly believe it is not you then build and inspect the assembly (disassembly is preferred easier to read and is the actual output) for debug and for optimized and see what the compiler is doing for each you may find that it is building wrong. Compilers have bugs that is in part why they keep coming out with new versions. – old_timer Aug 25 '16 at 20:15
  • This question is a disrespectful waste of our time. The title asks one thing and then the contents repeat on and on and on your original question. – conio Aug 28 '16 at 21:19

1 Answers1

1

IIRC, in VC++ this is passed to functions as register ECX, so the debugger just assumes that ECX is this, just a cast away. That is this is the same as (CADOCommand*)ECX.

In debug builds that is fine because registers are not reused, but in release builds ECX is reused as any other register. Thus the debugger loses track of the actual this or anything pointed to by it. The code will still be correct, of course, only the debugger is affected.

Actually ECX must be overwritten each time a member function of other class is called. For example:

m_pCommand.CreateInstance(__uuidof(Command));

Will be compiled to something like:

PUSH ECX ; push this into the stack
MOV ECX, &m_pCommand
PUSH __uuidof(command) ; a constant maybe?
CALL CCommand::CreateInstance
POP ECX  ; restore this

Note how, if the compiler determines that the current this is no longer needed, then it may omit the PUSH/POP ECX.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • One hand it make sense but on the other hand it make feels like debug and release code are generated and operated so differently? *this* is a hidden parameter to all member functions so I don't understand why would assembly implementation be that different for debug and release build as if the release build don't need it? – zar Aug 25 '16 at 18:18
  • @zar: About the debug vs release, you'll have to trust that the compiler will generate equivalent code, inside the limits of the C++ language (that is your responsibility). About the hidden parameter thing, the passing of parameters is the same in debug and release modes (else they couldn't interact). The difference is in considering `this` vs any other local variable, parameters included, because local variables usually live in the stack, while `this` lives in a register. – rodrigo Aug 25 '16 at 18:30
  • That make sense that *this* will live in register and locals on stack, that clears differentiation between these two but why would release build not keep track of *this* the same way as debug is not clear. Like you said the code generated should be equivalent. – zar Aug 25 '16 at 18:33
  • 1
    @zar: When I say that the generated code is equivalent I mean equivalent on visible behavior, and debugger wathes are not considered visible. The compiler may reuse `ECX` for any other purpose, included calling other member functions. If it determines that `this` is no longer needed for the current function it may even forget about it. Local variables, however, are located from the frame stack pointer, `EBP` or `ESP`, and those registers are used just for this purpose so they are much easier to predict. – rodrigo Aug 25 '16 at 18:38
  • I debugged the debug version to test and if your theory was true then ECX will have the same value as `this` right but they are not and when I step through the code, ECX changes as well (again in debug). Am I making a mistake comparing the two? – zar Aug 25 '16 at 19:04
  • @zar: Well, `this` is in `ECX` at the beginning of the function, then maybe it is moved away, that I don't know. BTW, take a look at this great answer: http://stackoverflow.com/a/5253321/865874 – rodrigo Aug 25 '16 at 20:19
  • They are close at start but not identical, there is offset of 14. ecx=0018d398 and this = 0x0018d384 – zar Aug 25 '16 at 20:44
  • @zar Difficult to say without seeing the code... maybe your function is inlined, so there is no `this` to begin with. – rodrigo Aug 25 '16 at 21:03