7

In gdb how can I discover the value of a variable which displays as optimized out? Presumably the value is being stored in a register, how can I find out which one? Given this simple program (copied from the website named below).

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int foo = rand();
    printf("foo is %d\n", foo++);
    return foo;
}

Compile with optimization.

gcc -O3 -g optimized.c
gdb a.out

Run GDB

(gdb) print foo
$1 = <optimized out>

What is going on? The variable foo must exist somewhere but how do I find its value or where it is stored?

I found a page which explains in detail how the DWARF debug information works for optimized code https://undo.io/resources/gdb-watchpoint/build-for-debug-in-gdb. Using the suggestions there I could eventually find the register assigned to foo but it's rather painful.

How can I get gdb to tell me which register to watch?

I tried compiling with -g3 but that didn't help.

I tried a few commands in gdb which also didn't help.

ptype foo
info locals
print &foo
ks1322
  • 33,961
  • 14
  • 109
  • 164
WorldGeek
  • 71
  • 1
  • 2
  • 2
    You are optimising it out. You can try compiling with -O0, then gdb will obey. – Siddharth Nov 27 '19 at 13:51
  • _"...The variable foo must exist somewhere but how do I find its value or where it is stored?..."_: you are going to need to use the assembly view of your code and read the docs on your platforms calling conventions. – Richard Critten Nov 27 '19 at 14:00
  • "The variable foo must exist somewhere" for a *very* loose definition of "must". There is no requirement for such a thing anywhere. – n. m. could be an AI Nov 27 '19 at 14:22
  • Could not reproduce ``. What version of gcc did you use? – ks1322 Nov 27 '19 at 15:10

3 Answers3

2

The variable foo must exist somewhere

There is no requirement that it should exist. C++ operates on the as-if principle. The program works as if the variable exists. If the compiler can get away with throwing your variable out of the window, while adhering to the as-if principle, expect it to do so eagerly. This is the very essence of optimisation. If you don't like it, don't use optimisation. For extra double strong protection, declare the variable of interest volatile.

how do I find its value or where it is stored

It does not have to be stored anywhere. In this case you can find its value by looking at the output of the program and/or examining its exit status. There is no general method to deternine which register an optimised-out variable gets assigned. There may or may not be such a register, depending on the exact details of your program. The value and the computation that leads to it might well be optimised away together with the storage. (Not in this case because the value is visible from the outside).

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
1

The variable foo must exist somewhere

Not necessarily. The compiler may instead store some value derived from the variable (e.g. foo+1 or foo-23) if it's convenient.

In this particular case, since foo is being passed to printf, you can discover it in a register or on the stack if you know the calling convention for your particular platform.

but how do I find its value or where it is stored?

Debugging optimized code currently requires significant skill, knowledge of the ABI and the assembly instructions used.

The problem is that compilers rarely accurately track values through their in-register and in-memory locations for debugging purposes, because doing so is quite hard.

Debuggers also have to correctly interpret the debugging info which compilers produce, and that isn't an easy or bug-free process either.

GCC recently had a lot of improvement in that area. Using gcc (GCC) 9.2.1 20190827 (Red Hat 9.2.1-1) from Fedora 31 on Linux x86_64, I observe this:

(gdb) start
Temporary breakpoint 1 at 0x401050: file t.c, line 6.
Starting program: /tmp/a.out 
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.30-5.fc31.x86_64

Temporary breakpoint 1, main () at t.c:6
6       int foo = rand();
(gdb) n
7       printf("foo is %d\n", foo++);
(gdb) p foo
$1 = 1804289384
(gdb) p &foo
Can't take address of "foo" which isn't an lvalue.
(gdb) c
Continuing.
foo is 1804289383
[Inferior 1 (process 2696) exited with code 0150]
(gdb) q

Here you can see that gcc described the location of foo in a register (or some other location that isn't memory), and GDB was able to print almost the value of foo.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • not all compiler have this deficiency. MSVC, for example emits register-lifting information for symbols in optimized code so the debugger _can_ display values even when they don't exist in memory. it also emits significantly better code-motion information so it's much easier to set breakpoints in optimized code. it's not perfect, but it's not completely useless like gcc. – Spongman Mar 25 '21 at 18:08
0

Not a general answer, but should work with your example.

Also as Employed Russian mentioned in his answer, debugging optimized code can become very tricky!

However, since foo is initialized with rand() it actually should not be fully optimized away. (Also since you are mentioning gdb I asume you are using some Linux?! - actually should be irrelevant) foo should be optimized into some registers. cdecl specifies returning integers via EAX register on x86 Platforms. Thus, while running your application, inspect the registers.

  1. break main
  2. (optional) enable assembler view: layout asm
  3. next until rand() has returned and initialized foo. At this point you should see some indications which registers are used in the assembler view.
  4. info registers eax should give you the value of foo.

With your code and compiler options I am getting the following result. enter image description here

user1810087
  • 5,146
  • 1
  • 41
  • 76