On 07/17/2015 03:43 AM, Nikolai Bozhenov wrote:
Hello!

It is certainly true that debugging an optimized code is an inherently
difficult task. Though, I wonder if the compiler could make such
debugging experience slightly less surprising.

Consider the following example:

     1 extern void bar(int *i1, int *i2, int *i3);
     2
     3 int __attribute__((noinline)) foo(int i1, int i2) {
     4   int a, b, c;
     5   a = i1 << i2;
     6   b = (i1 + i2) * i1;
     7   c = (b + i1);
     8   bar(&a, &b, &c);
     9 }
    10
    11 int main() {
    12   foo(42, 12);
    13 }

Let's compile it:

    $ gcc-trunk tst.c -g -fvar-tracking-uninit -O2

After hitting a breakpoint at line 8 (the last line of the function
foo) I have some random (and very confusing) values displayed in gdb
for all three variables a, b and c. This is because GCC allocates
these three variables on the stack (their addresses are taken) and
creates for them DWARF entries like this:

    <2><a8>: Abbrev Number: 8 (DW_TAG_variable)
       <a9>   DW_AT_name        : a
       <ab>   DW_AT_decl_file   : 1
       <ac>   DW_AT_decl_line   : 4
       <ad>   DW_AT_type        : <0x64>
       <b1>   DW_AT_location    : 2 byte block: 91 64 (DW_OP_fbreg: -28)

This (incorrectly) says that the variable is at the specified location
for the entire scope of the function.  This should be a location list,
which specifies the live range for the variable.  At the breakpoint,
this location list would/should have no location for the variable.

A related issue is where the breakpoint is taken.  GCC sets breakpoints
at the first instruction generated for a statement, which in this case,
appears to be before any of the arguments to bar are evaluated.  A
possibly better location would be after arguments are evaluated, before
the call is executed.


That is, actual values for variables are supposed to reside in fixed
stack slots throughout the whole function. But in fact, by the time
the breakpoint is hit none of the values is stored on the stack (the
last line of the function, evaluation of the first argument for the
call):

    Dump of assembler code for function foo:
       0x00000000004004c0 <+0>:     mov    %esi,%ecx
       0x00000000004004c2 <+2>:     add    %edi,%esi
       0x00000000004004c4 <+4>:     sub    $0x18,%rsp
       0x00000000004004c8 <+8>:     imul   %edi,%esi
       0x00000000004004cb <+11>:    mov    %edi,%eax
    => 0x00000000004004cd <+13>:    lea    0xc(%rsp),%rdx
       0x00000000004004d2 <+18>:    shl    %cl,%eax
       0x00000000004004d4 <+20>:    mov    %eax,0x4(%rsp)
       0x00000000004004d8 <+24>:    mov    %esi,0x8(%rsp)
       0x00000000004004dc <+28>:    add    %edi,%esi
       0x00000000004004de <+30>:    lea    0x4(%rsp),%rdi
       0x00000000004004e3 <+35>:    mov    %esi,0xc(%rsp)
       0x00000000004004e7 <+39>:    lea    0x8(%rsp),%rsi
       0x00000000004004ec <+44>:    callq  0x4004f6 <bar>
       0x00000000004004f1 <+49>:    add    $0x18,%rsp
       0x00000000004004f5 <+53>:    retq
    End of assembler dump.

By contrast, If I didn't take addresses of these variables, they would
be <optimized out> until we can determine their correct values. I
believe that such behavior is much better than displaying wrong values
for variables. Also, GCC seems to be able to produce DW_OP_GNU_uninit
operations to mark uninitialized variables (when -fvar-tracking-uninit
is used) but I've never seen it in generated debug info.

At first glance the problem seems to have something to do with the
fact that there's no gimple_debug/BIND and debug_insn/var_location
instructions for such stack variables in internal representation.
There are only statements like this:

    gimple_assign <lshift_expr, a.0_3, i1_1(D), i2_2(D), NULL>
    # .MEM_5 = VDEF <.MEM_4(D)>
    gimple_assign <ssa_name, a, a.0_3, NULL, NULL>

and *no* BIND statement like gimple_debug BIND <a, a.0_3>

Furthermore, in dwarf2out.c there are a lot of calls to
mem_loc_descriptor where var_init_status is unconditionally set to
VAR_INIT_STATUS_INITIALIZED. And it looks like currently all values
in memory are considered to be initialized by default. And only in
few places NOTE_VAR_LOCATION_STATUS is taken into account (anyway,
there's no such notes for variables a, b, c in this case).

So, here are my questions:
1) Wouldn't it be nice if GCC marked such stack variables either as
    uninitialized (with DW_OP_GNU_uninit) or as <optimized out>?
2) Is it feasible to implement such tracking for variables?
3) What exactly is GCC supposed to track with -fvar-tracking-uninit
    option?


Thanks,
Nikolai



--
Michael Eager    ea...@eagercon.com
1960 Park Blvd., Palo Alto, CA 94306  650-325-8077

Reply via email to