Extension for addr2line to unwind inline scopes

Fred Fish fnf@ninemoons.com
Mon Jan 17 15:41:00 GMT 2005


Sometimes application programmers like to include the ability to show
a call stack traceback to the user when they detect an internal fault.
One way to do this is to walk back up the call stack, calling the
external program addr2line with the PC for each frame.  The man page
for addr2line specifically mentions using addr2line this way:

  In the second, addr2line reads hexadecimal addresses from standard
  input, and prints the file name and line number for each address on
  standard output.  In this mode, addr2line may be used in a pipe to
  convert dynamically chosen addresses.

Problem with this though is that execution of inlined functions is
ignored since they share their caller's stack frame.  Consider the
example program:

  inline void bar (void)
  {
    printf ("Called bar");
  }
  
  inline void foo (void)
  {
    bar ();
    printf ("Called foo");
  }
  
  main ()
  {
    foo ();
  }

Which when compiled with a gcc built from the current development
sources produces the following (partial) line table information:

  Special opcode 233: advance Address by 16 to 0x804839c and Line by 4 to 13
  Special opcode 89: advance Address by 6 to 0x80483a2 and Line by 0 to 13
  Advance Line by -10 to 3
  Special opcode 89: advance Address by 6 to 0x80483a8 and Line by 0 to 3
  Special opcode 151: advance Address by 10 to 0x80483b2 and Line by 6 to 9
  Special opcode 221: advance Address by 15 to 0x80483c1 and Line by 6 to 15

I've annotated a disassembly of main() with a line number prefix:

  13  0804839c <main>:
  13   804839c:       55                      push   %ebp
  13   804839d:       89 e5                   mov    %esp,%ebp
  13   804839f:       83 ec 08                sub    $0x8,%esp
  13   80483a2:       83 e4 f0                and    $0xfffffff0,%esp
  13   80483a5:       83 ec 1c                sub    $0x1c,%esp
   3   80483a8:       68 a4 84 04 08          push   $0x80484a4
   3   80483ad:       e8 fe fe ff ff          call   80482b0 <printf@plt>
   9   80483b2:       c7 04 24 af 84 04 08    movl   $0x80484af,(%esp)
   9   80483b9:       e8 f2 fe ff ff          call   80482b0 <printf@plt>
   9   80483be:       83 c4 10                add    $0x10,%esp
  15   80483c1:       c9                      leave  
  15   80483c2:       c3                      ret    
  15   80483c3:       90                      nop    
    
Current released versions of addr2line with find misleading info for
address 80483a8:

  $ /usr/bin/addr2line -e x1 -f 80483a8
  main
  /build/sourceware/binutils/i686-pc-linux-gnu/binutils/x1.c:3

A recent fix to addr2line improves that by making the function name
and line consistent:

  $ ./addr2line -e x1 -f 80483a8
  bar
  /build/sourceware/binutils/i686-pc-linux-gnu/binutils/x1.c:3

However if this was used as part of a frame traceback as described
above, the user would never see that bar was called by foo, which was
called by main.

As an experiment, I've modified bfd_find_nearest_line() so that when
it parses the DWARF info for an inlined function, it remembers a PC
from the caller of that inlined function, and makes it available via a
separate BFD call.  This allows the caller of bfd_find_nearest_line()
to loop until it's "unwound" all the inlined functions.  I also
modified addr2line to do this.  With these modifications in place, the
above example produces:

  $ ./addr2line -e x1 -f 80483a8
  bar
  /build/sourceware/binutils/i686-pc-linux-gnu/binutils/x1.c:3
  foo
  /build/sourceware/binutils/i686-pc-linux-gnu/binutils/x1.c:9
  main

Note I'm not proposing that any of these changes be adopted "as is",
just presenting them as a possible starting point for discussion.  To
adopt these patches at least the following issues would need to be
resolved:

(1) The _bfd_dwarf2_find_saved_nearest_scope() entry point to fetch
the most recent saved PC would need to have the BFD infrastructure
added to make it a generic BFD call.

(2) The PC that get's saved isn't necessarily the most recent
instruction executed in the caller of the inlined function.  It is
merely one which when fed back into bfd_find_nearest_line() will get
us to the caller of the inlined function.

(3) It would probably be best to add a new option to addr2line that
tells it to produce multiple outputs for a single address that is
found to be inside an inlined function, otherwise existing users of
addr2line might break.

(4) The DWARF info produced by current versions of gcc does not make
use of the DW_AT_ranges attribute to indentify discontiguous address
ranges of the inlined subroutine and it's caller.  Instead it uses
just DW_AT_low_pc and DW_AT_high_pc, which can produce overlapping
ranges.  I've somewhat hacked around this problem in the
find_nearest_line code.

(5) The DWARF parser in BFD's dwarf2.c is pretty much goal oriented
towards supporting just bfd_find_nearest_line().  It might be best to
reorganize this code to make it easier to add other BFD functions to
extract info from the DWARF bits.  On the other hand, perhaps BFD
really isn't the place for this code anyway.

(6) If we have to add another bfd entry point anyway, perhaps a better
idea is a variant of bfd_find_nearest_line() that returns a PC from
the next higher inlining scope directly.

I've attached the patches I'm currently experimenting with.

-Fred

-------------- next part --------------
A non-text attachment was scrubbed...
Name: binutils.diff
Type: text/x-diff
Size: 8686 bytes
Desc: not available
URL: <https://sourceware.org/pipermail/binutils/attachments/20050117/8e609e85/attachment.bin>


More information about the Binutils mailing list