Bug 1216000

Summary: gcc: gdb fails to track tons of variables even in gcc's -Og binaries, shows "optimized out"
Product: [openSUSE] openSUSE Distribution Reporter: ell1e <el>
Component: DevelopmentAssignee: Michael Matz <matz>
Status: NEW --- QA Contact: E-mail List <qa-bugs>
Severity: Normal    
Priority: P5 - None    
Version: Leap 15.5   
Target Milestone: ---   
Hardware: Other   
OS: Other   
Whiteboard:
Found By: --- Services Priority:
Business Priority: Blocker: ---
Marketing QA Status: --- IT Deployment: ---

Description ell1e 2023-10-06 11:48:01 UTC
gdb fails to track tons of variables even in -Og or -O1 binaries, shows "optimized out" instead. I see this even in code like this:

        result = some_function_of_mine(
            "<img =\"<img >src=.png/>\"/>",
            &tagstart, &taglen, &suspiciousbroken,
            NULL, NULL
        );
        ck_assert(result == 12);

When the assert fails and it halts on the line of the assert itself, it can't even tell me the result value in that line itself because it's optimized out. Despite just halting there with SIGABRT, due to that very result value!

I don't know if this is a gcc or gdb at the end of the day, I assume gcc, but it's really a huge blocker for debugging. I run into this a lot, and it eats a lot of my time in development because I just can't figure out properly what's going on with half the values missing.

As a result, I just use -O0 now, which I believe defies the point of -Og as an option.

I'm seeing this with gcc (SUSE Linux) 12.3.0 on Leap 15.5 and gdb 12.1.
Comment 1 Richard Biener 2023-10-09 06:21:29 UTC
Both -Og and even more so -O1 can end up eliminating variables and thus make debugging harder.  -Og is "optimize but watch out for debugging", that's of
course more difficult to debug than -O0 "do not optimize".

So it works like designed - whether in your case I can't tell because you
didn't provide a testcase.  Note that the result of some_function_of_mine
is in %rax only, if that's not preserved somewhere else and the register
is clobbered (it doesn't have to be preserved across calls) then this is
the answer why it doesn't work when optimizing.  When not optimizing
'result' is placed on the stack.
Comment 2 ell1e 2023-10-09 08:25:25 UTC
I don't see how -Og has any use if even for asserts, I can't see simply the values in the contained assert. At the very least gcc should somehow make sure the asserted values remain accessible. In the given example, the only call that could have somehow clobbered it seems to me like it was the assert itself, that seems quite unfortunate.

Nevertheless, I don't know too much about the internals other than right now, I don't see how -Og is of any use if I end up needing to use -O0 anyway just to see the basic assert failing values in a normal program.
Comment 3 Richard Biener 2023-10-09 09:18:02 UTC
a simple testcase

extern void abort (void);
int __attribute__((noipa)) foo ()
{
  return 1;
} 

int main()
{
  int res = foo ();
  if (res != 0)
    abort ();
}

shows that EAX is overwritten in abort () itself:

Watchpoint 3: $eax

Old value = 1
New value = -966940928
0x00007ffff7e0128a in abort () from /lib64/libc.so.6

The main issue in this testcase (as well as in yours I guess) is that
'result' isn't used after the call so the compiler sees no need to preserve
its value somewhere.
Comment 4 ell1e 2023-10-09 12:02:45 UTC
I don't know what to add, and I just keep repeating myself. But with this behavior it seems to me as useful as just using -O1 and -O2 instead, which I assume wasn't the point of adding -Og originally. So I feel like a change should be suggested, to whatever place might have some good input on this.