Bug 1092144

Summary: only a single inlined_subroutine DIE is emitted during loop unrolling
Product: Red Hat Developer Toolset Reporter: Jonathan Lebon <jlebon>
Component: gccAssignee: Jakub Jelinek <jakub>
Status: CLOSED WORKSFORME QA Contact: qe-baseos-tools-bugs
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: DTS 5.0 RHEL 7CC: jakub, jason, law, mcermak, mjw, mnewsome, mpolacek, sipoyare
Target Milestone: rcKeywords: Bugfix
Target Release: 6.0   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2020-12-08 19:38:10 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1110700, 1191021    

Description Jonathan Lebon 2014-04-28 19:57:45 UTC
Description of problem:

When GCC does a single unrolling of a loop in which a function call has been inlined, an inlined_subroutine DIE is emitted only for the first inlining, not for the second one in the loop. As a result, DWARF consumers such as SystemTap/GDB only fire once at startup when probing the inlined function, rather than at every iteration.

Version-Release number of selected component (if applicable):

$ uname -r
3.10.0-121.el7.x86_64
$ rpm -q gcc
gcc-4.8.2-16.el7.x86_64

How reproducible:

All the time

Steps to Reproduce:

$ cat tmp.c
#include <stdio.h>
#include <unistd.h>

inline int ibar (void) {
  usleep(250000);
  return printf("printing\n");
}

int main (int argc, char *argv[]) {
  int j = 0;
  for (;;) {
    j += ibar();
    if (argc > 1)
      return;
  }
  return j;
}
$ gcc tmp.c -g -O -o tmp
$

Actual results:

$ gdb tmp
(gdb) break ibar
Breakpoint 1 at 0x4005c0: ibar. (2 locations)
(gdb) run
Starting program: /notnfs/jlebon/codebase/systemtap/systemtap/testsuite/systemtap.unprivileged/tmp

Breakpoint 1, main (argc=1, argv=0x7fffffffe298) at tmp.c:12
12          j += ibar();
(gdb) c
Continuing.
printing
printing
printing
^C
Program received signal SIGINT, Interrupt.
(gdb) q
$

Expected results:

GDB breaks at every iteration rather than only the first one.

Additional info:

The loop is unrolled once:

$ objdump -d tmp | grep '<main>' -A 18
00000000004005e2 <main>:
  4005e2:       53                      push   %rbx
  4005e3:       89 fb                   mov    %edi,%ebx
  4005e5:       bf 90 d0 03 00          mov    $0x3d090,%edi
  4005ea:       e8 d1 fe ff ff          callq  4004c0 <usleep@plt>
  4005ef:       bf ba 06 40 00          mov    $0x4006ba,%edi
  4005f4:       e8 87 fe ff ff          callq  400480 <puts@plt>
  4005f9:       83 fb 01                cmp    $0x1,%ebx
  4005fc:       7f 16                   jg     400614 <main+0x32>
  4005fe:       bf 90 d0 03 00          mov    $0x3d090,%edi
  400603:       e8 b8 fe ff ff          callq  4004c0 <usleep@plt>
  400608:       bf ba 06 40 00          mov    $0x4006ba,%edi
  40060d:       e8 6e fe ff ff          callq  400480 <puts@plt>
  400612:       eb ea                   jmp    4005fe <main+0x1c>
  400614:       b8 00 00 00 00          mov    $0x0,%eax
  400619:       5b                      pop    %rbx
  40061a:       c3                      retq
  40061b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

We can see ibar is inlined once outside the loop at 4005e5, and once in the loop at 4005fe. However, the DWARF info only has a single inlined_subroutine DIE with entry_pc pointing to 4005e5:

$ readelf -w tmp
...
 <1><29d>: Abbrev Number: 14 (DW_TAG_subprogram)
    <29e>   DW_AT_external    : 1
    <29e>   DW_AT_name        : (indirect string, offset: 0x36): ibar
    <2a2>   DW_AT_decl_file   : 1
    <2a3>   DW_AT_decl_line   : 4
    <2a4>   DW_AT_prototyped  : 1
    <2a4>   DW_AT_type        : <0x62>
    <2a8>   DW_AT_inline      : 3       (declared as inline and inlined)
...
 <2><343>: Abbrev Number: 22 (DW_TAG_inlined_subroutine)
    <344>   DW_AT_abstract_origin: <0x29d>
    <348>   DW_AT_entry_pc    : 0x4005e5
    <350>   DW_AT_ranges      : 0x0
    <354>   DW_AT_call_file   : 1
    <355>   DW_AT_call_line   : 12

Note however that the associated ranges do indicate the two locations:

$ readelf -w tmp
...
Contents of the .debug_ranges section:

    Offset   Begin    End
    00000000 00000000004005e5 00000000004005f9
    00000000 00000000004005fe 0000000000400614
    00000000 <End of list>

Shouldn't these be separate DW_TAG_inlined_subroutine DIEs instead?

Comment 2 RHEL Program Management 2014-05-13 05:47:12 UTC
This request was not resolved in time for the current release.
Red Hat invites you to ask your support representative to
propose this request, if still desired, for consideration in
the next release of Red Hat Enterprise Linux.

Comment 11 Marek Polacek 2019-12-18 17:46:05 UTC
This now works in DTS 9, even with -O3:  (Actually, even DTS 3 works as expected.)

(gdb) b ibar
Breakpoint 1 at 0x401145: file tmp.c, line 12.
(gdb) r
Starting program: /root/tmp 
Missing separate debuginfos, use: debuginfo-install glibc-2.17-307.el7.x86_64

Breakpoint 1, ibar () at tmp.c:12
12	    j += ibar();
(gdb) c
Continuing.
printing

Breakpoint 1, ibar () at tmp.c:12
12	    j += ibar();
(gdb) c
Continuing.
printing

Breakpoint 1, ibar () at tmp.c:12
12	    j += ibar();
(gdb) c
Continuing.
printing

Breakpoint 1, ibar () at tmp.c:12
12	    j += ibar();


However, there still is just one DW_TAG_inlined_subroutine:

<2><3c9>: Abbrev Number: 21 (DW_TAG_inlined_subroutine)
    <3ca>   DW_AT_abstract_origin: <0x450>
    <3ce>   DW_AT_entry_pc    : 0x401080
    <3d6>   DW_AT_GNU_entry_view: 2
    <3d7>   DW_AT_ranges      : 0x0
    <3db>   DW_AT_call_file   : 1
    <3dc>   DW_AT_call_line   : 12
    <3dd>   DW_AT_call_column : 10

So I'm not sure if we should move forward with this.

Comment 12 Marek Polacek 2020-01-14 17:03:28 UTC
Jonathan, would you be OK with us moving forward with this bug?  I.e., mark is as fixed, given Comment 11.

Comment 13 Marek Polacek 2020-01-14 18:57:14 UTC
DTS 10.0 meanwhile.

Comment 14 Jonathan Lebon 2020-01-14 20:49:31 UTC
Hmm, interesting that it doesn't unroll the loop anymore. It might just be that the sample I've provided no longer triggers loop unrolling, and that the underlying issue of breakpoints on unrolled loops is still present. My suggestion is to try to find another minimal scenario where loop unrolling is done and trying out the gdb steps again.