[Bug debug/105007] New: Missing variables at -O2/O3 likely caused by incorrect debug info after inlining

assaiante at diag dot uniroma1.it gcc-bugzilla@gcc.gnu.org
Mon Mar 21 19:43:07 GMT 2022


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105007

            Bug ID: 105007
           Summary: Missing variables at -O2/O3 likely caused by incorrect
                    debug info after inlining
           Product: gcc
           Version: 12.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: debug
          Assignee: unassigned at gcc dot gnu.org
          Reporter: assaiante at diag dot uniroma1.it
  Target Milestone: ---

In this minimized C example, variables i and l_3, defined within the scope of
the inlined function foo, are associated with DWARF symbols having possibly
wrong location definition, which causes variables to not be available during
debugging (in the example, the variables are used as call arguments to a
function defined in an external module). We observe the bug at -O2 and -O3 with
the root cause possibly being inlining. Please find below a detailed analysis
for -O2 (with -O3 DWARF info looks identical) on x64, including a comparison
with past gcc versions where the bug is sometimes not present.

Furthermore, once we make both variables appear by disabling some
optimizations, the program reveals a possible bug in gdb, which ends up
displaying an incorrect value for variable i while lldb shows the correct one.
We are filing a separate bug report, linking it in a comment below, as the
behavior is quite articulated.

$ cat a.c
void foo()
{ 
   int l_3 = 5, i = 0;
   for (; i < 8; i++)
       ;
   test(l_3, i);
}
int main()
{
   foo();
}
$ cat lib.c
#include <stdio.h>

void test(int l_3, int i) {
   printf("%d %d", l_3, i);
}

GCC and GDB version (GCC commit id: 500d3f0a302):
$ gcc --version
gcc (GCC) 12.0.0 20211227 (experimental)
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gdb --version
GNU gdb (GDB) 11.2
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

GDB trace:
$ gcc -O2 -g a.c -o opt
$ gdb -q opt
Reading symbols from opt...
(gdb) b 6
Breakpoint 1 at 0x400410: /home/stepping/2/reduce/a.c:6. (2 locations)
(gdb) r
Starting program: /home/stepping/2/reduce/opt

Breakpoint 1, main () at a.c:6
6           test(l_3, i);
(gdb) info loc
No locals.

ASM at -O2:
0000000000400410 <main>:
 400410:       48 83 ec 08             sub    $0x8,%rsp
 400414:       be 08 00 00 00          mov    $0x8,%esi
 400419:       bf 05 00 00 00          mov    $0x5,%edi
 40041e:       31 c0                   xor    %eax,%eax
 400420:       e8 1b 01 00 00          callq  400540 <test>
 400425:       31 c0                   xor    %eax,%eax
 400427:       48 83 c4 08             add    $0x8,%rsp
 40042b:       c3                      retq    
 40042c:       0f 1f 40 00             nopl   0x0(%rax)

DWARF info at -O2:
0x00000065:     DW_TAG_inlined_subroutine
                 DW_AT_abstract_origin (0x000000ac "foo")
                 DW_AT_entry_pc        (0x0000000000400410)
                 DW_AT_unknown_2138    (0x02)
                 DW_AT_ranges  (0x0000000c
                    [0x0000000000400410, 0x0000000000400410)
                    [0x0000000000400414, 0x0000000000400425))
                 DW_AT_call_file       ("/home/stepping2/reduce/a.c")
                 DW_AT_call_line       (10)
                 DW_AT_call_column     (0x05)

0x0000007a:       DW_TAG_lexical_block
                   DW_AT_ranges        (0x0000000c
                      [0x0000000000400410, 0x0000000000400410)
                      [0x0000000000400414, 0x0000000000400425))

0x0000007f:         DW_TAG_variable
                     DW_AT_abstract_origin     (0x000000b9 "l_3")

0x00000084:         DW_TAG_variable
                     DW_AT_abstract_origin     (0x000000c3 "i")
                     DW_AT_location    (0x0000000e:  
                        [0x0000000000400410, 0x0000000000400410): DW_OP_lit0,
DW_OP_stack_value)
                     DW_AT_unknown_2137        (0x0000000c)

0x00000091:         DW_TAG_call_site
                     DW_AT_call_return_pc      (0x0000000000400425)
                     DW_AT_call_origin (0x0000002a)

0x0000009e:           DW_TAG_call_site_parameter
                       DW_AT_location  (DW_OP_reg5 RDI)
                       DW_AT_call_value        (DW_OP_lit5)

0x000000a3:           DW_TAG_call_site_parameter
                       DW_AT_location  (DW_OP_reg4 RSI)
                       DW_AT_call_value        (DW_OP_lit8)

...

0x000000da:   DW_TAG_subprogram
               DW_AT_abstract_origin   (0x000000ac "foo")
               DW_AT_low_pc    (0x0000000000400520)
               DW_AT_high_pc   (0x0000000000400531)
               DW_AT_frame_base        (DW_OP_call_frame_cfa)
               DW_AT_call_all_calls    (true)

0x000000f1:     DW_TAG_variable
                 DW_AT_abstract_origin (0x000000b9 "l_3")

0x000000f6:     DW_TAG_variable
                 DW_AT_abstract_origin (0x000000c3 "i")
                 DW_AT_location        (0x0000001e:  
                    [0x0000000000400520, 0x0000000000400520): DW_OP_lit0,
DW_OP_stack_value)
                 DW_AT_unknown_2137    (0x0000001c)

0x00000103:     DW_TAG_call_site
                 DW_AT_call_return_pc  (0x0000000000400531)
                 DW_AT_call_tail_call  (true)
                 DW_AT_call_origin     (0x0000002a)

0x00000110:       DW_TAG_call_site_parameter
                   DW_AT_location      (DW_OP_reg5 RDI)
                   DW_AT_call_value    (DW_OP_lit5)

0x00000115:       DW_TAG_call_site_parameter
                   DW_AT_location      (DW_OP_reg4 RSI)
                   DW_AT_call_value    (DW_OP_lit8)

>From an initial assessment, there may be multiple factors at play behind the
unavailability of variables i and l_3:
- the empty range in the location definition for variable i;
- the missing location definition for variable l_3 together with the missing
const value attribute in the variable DIE in the subprogram DIE of function
foo;
- the empty ranges in both the inlined subroutine location definition and the
lexical block location definition nested in the inlined subroutine.

Through some testing we found out that the optimizations that make the
variables disappear are related to inlining (-fearly-inlining, -finline, …) at
both -O2 and -O3 but include also -ftree-dce at -O2 (since foo does not get
inlined under -fno-tree-dce).

If we disable either inlining or -ftree-dce, variables l_3 and i appear in the
current frame. However, a wrong value for i is displayed at the call site (0
instead of 8, see also the gdb bug report). When compiling with -fno-tree-dce
lldb reports the correct value, while when disabling inlining it reports i as
not available.


ASM with -fno-tree-dce at -O2:
0000000000400410 <main>:
 400410:       48 83 ec 08             sub    $0x8,%rsp
 400414:       31 c0                   xor    %eax,%eax
 400416:       e8 05 01 00 00          callq  400520 <foo>
 40041b:       31 c0                   xor    %eax,%eax
 40041d:       48 83 c4 08             add    $0x8,%rsp
 400421:       c3                      retq    
 400422:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 400429:       00 00 00  
 40042c:       0f 1f 40 00             nopl   0x0(%rax)

...

0000000000400520 <foo>:
 400520:       be 08 00 00 00          mov    $0x8,%esi
 400525:       bf 05 00 00 00          mov    $0x5,%edi
 40052a:       31 c0                   xor    %eax,%eax
 40052c:       e9 0f 00 00 00          jmpq   400540 <test>
 400531:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 400538:       00 00 00  
 40053b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

DEBUG info with -fno-tree-dce at -O2:
0x00000070:   DW_TAG_subprogram
               DW_AT_external  (true)
               DW_AT_name      ("foo")
               DW_AT_decl_file ("/home/stepping/2/reduce/a.c")
               DW_AT_decl_line (1)
               DW_AT_decl_column       (0x06)
               DW_AT_low_pc    (0x0000000000400520)
               DW_AT_high_pc   (0x0000000000400531)
               DW_AT_frame_base        (DW_OP_call_frame_cfa)
               DW_AT_call_all_calls    (true)

0x0000008a:     DW_TAG_variable
                 DW_AT_name    ("l_3")
                 DW_AT_decl_file       ("/home/stepping/2/reduce/a.c")
                 DW_AT_decl_line       (3)
                 DW_AT_decl_column     (0x09)
                 DW_AT_type    (0x00000039 "int")
                 DW_AT_const_value     (0x05)

0x00000097:     DW_TAG_variable
                 DW_AT_name    ("i")
                 DW_AT_decl_file       ("/home/stepping/2/reduce/a.c")
                 DW_AT_decl_line       (3)
                 DW_AT_decl_column     (0x12)
                 DW_AT_type    (0x00000039 "int")
                 DW_AT_location        (0x0000001e:  
                    [0x0000000000400520, 0x0000000000400520): DW_OP_lit0,
DW_OP_stack_value
                    [0x0000000000400520, 0x0000000000400520): DW_OP_lit1,
DW_OP_stack_value
                    [0x0000000000400520, 0x0000000000400520): DW_OP_lit2,
DW_OP_stack_value
                    [0x0000000000400520, 0x0000000000400520): DW_OP_lit3,
DW_OP_stack_value
                    [0x0000000000400520, 0x0000000000400520): DW_OP_lit4,
DW_OP_stack_value
                    [0x0000000000400520, 0x0000000000400520): DW_OP_lit5,
DW_OP_stack_value
                    [0x0000000000400520, 0x0000000000400520): DW_OP_lit6,
DW_OP_stack_value
                    [0x0000000000400520, 0x0000000000400520): DW_OP_lit7,
DW_OP_stack_value
                    [0x0000000000400520, 0x0000000000400531): DW_OP_lit8,
DW_OP_stack_value)
                 DW_AT_GNU_locviews    (0x0000000c)

We tested the program with older gcc versions. Within each version, the
behavior at -O2 and -O3 is consistent: same generated DWARF info and debugging
experience w.r.t. visible variables.  

gcc-6: both variables are visible and with correct values
ggc-7: both variables are visible with i's value being incorrect (not only in
gdb, also lldb shows 0 instead of 8)
gcc-{8-9-10-11}: neither variable is visible (just like in the 500d3f0a302 git
version that we tested)  

We are discussing gcc-7 below as we found some interesting details.

ASM from gcc-7 at -O2:
0000000000000560 <main>:
560:   48 83 ec 08             sub    $0x8,%rsp
564:   be 08 00 00 00          mov    $0x8,%esi
569:   bf 05 00 00 00          mov    $0x5,%edi
56e:   31 c0                   xor    %eax,%eax
570:   e8 3b 01 00 00          callq  6b0 <test>
575:   31 c0                   xor    %eax,%eax
577:   48 83 c4 08             add    $0x8,%rsp
57b:   c3                      retq    
57c:   0f 1f 40 00             nopl   0x0(%rax)

DWARF info generated for function foo:
DW_TAG_inlined_subroutine
                 DW_AT_abstract_origin (0x0000009e "foo")
                 DW_AT_low_pc  (0x0000000000000564)
                 DW_AT_high_pc (0x0000000000000575)
                 DW_AT_call_file       ("/home/stepping/2/reduce/a.c")
                 DW_AT_call_line       (10)

0x00000061:       DW_TAG_lexical_block
                   DW_AT_low_pc        (0x0000000000000564)
                   DW_AT_high_pc       (0x0000000000000575)

0x00000072:         DW_TAG_variable
                     DW_AT_abstract_origin     (0x000000e7 "l_3")


0x00000077:         DW_TAG_variable
                     DW_AT_abstract_origin     (0x000000ed "i")

0x0000007c:         DW_TAG_GNU_call_site
                     DW_AT_low_pc      (0x0000000000000575)
                     DW_AT_abstract_origin     (0x0000010c "test")



0x00000089:           DW_TAG_GNU_call_site_parameter
                       DW_AT_location  (DW_OP_reg5 RDI)
                       DW_AT_GNU_call_site_value       (DW_OP_lit5)

0x0000008e:           DW_TAG_GNU_call_site_parameter
                       DW_AT_location  (DW_OP_reg4 RSI)
                       DW_AT_GNU_call_site_value       (DW_OP_lit8)

...

0x000000cc:   DW_TAG_subprogram
               DW_AT_abstract_origin   (0x0000009e "foo")
               DW_AT_low_pc    (0x0000000000000690)
               DW_AT_high_pc   (0x00000000000006a1)
               DW_AT_frame_base        (DW_OP_call_frame_cfa)
               DW_AT_GNU_all_call_sites        (true)
               DW_AT_sibling   (0x0000010c)

0x000000e7:     DW_TAG_variable
                 DW_AT_abstract_origin (0x000000aa "l_3")
                 DW_AT_const_value     (0x05)

0x000000ed:     DW_TAG_variable
                 DW_AT_abstract_origin (0x000000b5 "i")
                 DW_AT_const_value     (0x00)

0x000000f3:     DW_TAG_GNU_call_site
                 DW_AT_low_pc  (0x00000000000006a1)
                 DW_AT_GNU_tail_call   (true)
                 DW_AT_abstract_origin (0x0000010c "test")

0x00000100:       DW_TAG_GNU_call_site_parameter
                   DW_AT_location      (DW_OP_reg5 RDI)
                   DW_AT_GNU_call_site_value   (DW_OP_lit5)

0x00000105:       DW_TAG_GNU_call_site_parameter
                   DW_AT_location      (DW_OP_reg4 RSI)
                   DW_AT_GNU_call_site_value   (DW_OP_lit8)

Differently from the latest gcc version that we tested, with gcc 7 in the
subprogram DIE of the foo function there is the attribute const value defined
for variable l_3 that makes the variable's value available. We can also see
that there is the attribute const value defined for variable i that makes the
variable have a wrong value during debugging.


More information about the Gcc-bugs mailing list