Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
How to read variables optimized out in GDB? (luajit.io)
93 points by kingluo on Oct 11, 2022 | hide | past | favorite | 20 comments


This is "How to read variables optimized out in GDB sometimes", lest you might be misled to think that the value you are looking for is always hanging around somewhere.

Sometimes, it's just gone, literally optimized out completely. Consider this piece of code:

    bool foo(void) {
      int v = some_system_call();
      bool b = v > 500;
      some_function(b);
      ... /\* rest of code not using v \*/
    }
There is no reason for the original value of v to stick around any longer than until the comparison with 500 has been made.

Even though the syntactic scope of "int v" tells you that v is valid for the rest of the function body, and indeed it is while you are writing the source code, an optimizing compiler can and should make use of the fact that you are not actually using it anymore.


The article actually suggests using the record command in that case.


Won't help with coredumps, or generally if you can't reproduce.


I got great mileage out of:

-fno-eliminate-unused-debug-symbols

-fno-eliminate-unused-debug-types

https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug.html https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html

this post seems way more hardcore and beyond my experience though.


This isn't something limited to GDB but I've always wondered why no one seems to have bothered to come up with a solution after all these years so that it can actually know that the variable is in a register at that point.


"Optimized out" doesn't just cover a variable simply being in a register. The code might have been adjusted such that the variable's value isn't stored anywhere any more, or some derivative value is stored instead. So in the general case, it's quite a complex problem.


So in the general case, it's quite a complex problem.

I'm not talking about "the general case". I'm referring to all the cases where the compiler obviously should know where the value is, yet the debugger somehow doesn't.


> I'm referring to all the cases where the compiler obviously should know where the value is, yet the debugger somehow doesn't.

Soooommmee of this can be explained by the design decisions in the compiler not lending themselves to working well with debug-info, as explained here [0] (shamless plug), from about 3 minutes in there's an example of a scenario where a variable location has to be discarded out of conservative precaution (4:40 to 7:00), rather than because it's definitely been optimised out.

[0] https://www.youtube.com/watch?v=yxuwfNnp064&t=45s


But there are plenty of cases where a debugger can tell you the value of variables which are definitely in registers, such as with function arguments.

So this complaint really is about cases related to the optimizer which, as mentioned, is a very complex issue.


There are solutions, but with tradeoffs. In theory DWARF debugging metadata is Turing complete, so you could bundle an exact copy of the unoptimized program and fall back in that when someone requests something from the debugger. The JVM actually exposes this kind of level of access as API and it goes through astonishing amounts of effort to make sure that when you’re debugging the code gets shunted into the paths where this information is available rather than the highly optimized view that is normally executing.

In practice, well, things are complicated. Maintaining sane debug information across optimizations is pretty hard, and nobody really holds compiler author’s feet to the fire to make it happen. There’s a lot of low hanging fruit that should be fixed (for example an index→pointer conversion should be easy to undo, even though most compilers don’t generate the debug information for this). Likewise variables that just happen to be alive by chance should be made available. But I doubt we’ll ever get good support for things which have been fully optimized, though I think most programmers will forgive this.


Assuming you're referring to the articles first example, I suspect optimised-out is the correct outcome in that situation. The program is stopped at the start of one function, and the author is looking one stack frame up at this code:

     0x0000564ab5d178c4 <+52>:    callq  *%rax
  => 0x0000564ab5d178c6 <+54>:    mov    %rbp,%rdi
%rsi indeed hasn't been clobbered when the call executes, but it becomes liable to be clobbered during the execution of the callee. By the next instruction (at +54) there's no guarantee that %rsi contains the correct value, thus it's best reported as optimised out. The author is handily stopped at a point in time between the two instructions displayed above (in a lower stack frame), where the correct value happens to be in %rsi, but this is not guaranteed to be always true.


Yes, if there is a call before the instruction, you should check if it changes the register you want. Particular case is particular analysis.


Having spent some time looking at DWARF data closely, I get the impression that there is an insane amount of information generated and the format can handle way way more complex scenarios than a simple "X is in register Y" - they got a full state machine and all in there. But the tools available.. simply do not consume it? I'm not entirely sure what the disconnect is.


I guess the problem is that variable is in the register only in the beginning of the function. It might overwritten later, that's why the author checks the assembly code.

I haven't looked at DWARF for ages. Can it describe the situation that a variable is valid (in some register) in the beginning of a function, but then goes "out of scope " in the middle of the function?


> I haven't looked at DWARF for ages. Can it describe the situation that a variable is valid (in some register) in the beginning of a function, but then goes "out of scope " in the middle of the function?

Yes, with DW_OP_entry_value used in location expressions to indicate that the variable value is in a certain location on entry to the function. However -- debuggers typically don't store all register values on entry to all functions, so usually the stack frame up needs to be instrumented with call site information indicating where the debugger can find longer-term stored copies of the arguments.

In TFA you can see that happening correctly wherever there's a "@entry" annotation from gdb for an argument, in some cases there isn't a longer-term copy of the value further up the stack, in which case the variable remains optimised out.


The tools are perfectly capable of consuming it. Optimizers just don't emit it.


Great article. Similar tricks are available in any interactive-debugger worth its salt, not just gdb.

So you can do similar tricks in LLDB, Visual Studio, and many others.


When this made it into gdb some years ago, I wanted to print the optimized away values nevertheless with a ? prefix. This was easy. But then gdb got carried away and reworked everything in C++, and I could not print those values anymore.

https://github.com/rurban/binutils-gdb/commits/users/rurban/...


optimized out is my number one pet peeve in my job.


set disassembly-flavor intel




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: