[RFA/DWARF v2] Fix breakpoint add on inlined function using function name.

Simon Marchi simon.marchi@polymtl.ca
Thu Dec 21 22:52:00 GMT 2017


On 2017-12-21 06:03, Joel Brobecker wrote:
> Hi Simon,
> 
> On Wed, Dec 20, 2017 at 09:35:34PM -0500, Simon Marchi wrote:
>> On 2017-12-20 08:21, Xavier Roirand wrote:
>> > So far only approved by Joel B.
>> >
>> > Using this Ada example:
>> >
>> >   package B is
>> >     procedure Read_Small with Inline_Always;
>> >   end B;
>> >
>> >   package body B is
>> >     Total : Natural := 0;
>> >     procedure Read_Small is
>> >     begin
>> >       Total := Total + 1;
>> >     end Read_Small;
>> >   end B;
>> >
>> > and
>> >
>> >   with B;
>> >
>> >   procedure M is
>> >   begin
>> >     B.Read_Small;
>> >   end M;
>> >
>> > % gnatmake -g -O0 -m m.adb -cargs -gnatn
>> > % gdb m
>> >
>> > Inserting a breakpoint on Read_Small inlined function does not work:
>> >
>> > (gdb) b read_small
>> > Breakpoint 1 at 0x40250e: file b.adb, line 5.
>> > (gdb) info b
>> > Num     Type           Disp Enb Address            What
>> > 1       breakpoint     keep y   0x000000000040250e in b.doit at b.adb:5
>> > (gdb)
>> >
>> > In this exemple we should have two breakpoints set, one in package B and
>> 
>> "example"
>> 
>> > the other one in the inlined instance inside procedure M), like below:
>> >
>> > (gdb) b read_small
>> > Breakpoint 1 at 0x40250e: b.adb:5. (2 locations)
>> > (gdb) info b
>> > Num     Type           Disp Enb Address            What
>> > 1       breakpoint     keep y   <MULTIPLE>
>> > 1.1                         y     0x000000000040250e in b.doit at
>> > b.adb:5
>> > 1.2                         y     0x0000000000402540 in m at b.adb:5
>> > (gdb)
>> >
>> > Looking at the DWARF info for inlined instance of Read_Small:
>> >
>> > <1><1526>: Abbrev Number: 2 (DW_TAG_subprogram)
>> >     <1527>   DW_AT_name        : ([...], offset: 0x1e82): b__read_small
>> >     <152b>   DW_AT_decl_file   : 2
>> >     <152c>   DW_AT_decl_line   : 3
>> >     <152d>   DW_AT_inline      : 3      (declared as inline and inlined)
>> > [...]
>> >  <2><1547>: Abbrev Number: 4 (DW_TAG_inlined_subroutine)
>> >     <1548>   DW_AT_abstract_origin: <0x1526>
>> >     <154c>   DW_AT_low_pc      : 0x402552
>> >     <1554>   DW_AT_high_pc     : 0x2b
>> >     <155c>   DW_AT_call_file   : 1
>> >     <155d>   DW_AT_call_line   : 5
>> >  <2><155e>: Abbrev Number: 0
>> >
>> > During the parsing of DWARF info in order to produce partial DIE linked
>> > list, the DW_TAG_inlined_subroutine were skipped thus not present in the
>> > final partial dies.
>> > Taking DW_TAG_inlined_subroutine in account during the parsing process
>> > fixes the problem.
>> 
>> Do you have some insights as to why we don't see the same problem in 
>> other
>> languages such as C?  I tried to make a test case in C similar to 
>> yours.  I
>> made a shared library that contained a function, with both the 
>> standalone
>> instance and an inlined instance.  In the beginning, we have only 
>> loaded
>> partial symbols for the library.  I tried placing a breakpoint on the
>> function, and got the expected two locations.  What seems to happen is 
>> that
>> placing the breakpoint triggers the read of the full symbols, which
>> eventually finds the inline instance.  Do you have any idea what's 
>> different
>> about Ada?
> 
> I think we'll need to look more deeply at your example to figure out
> what is going on.
> 
> First, just to make sure we understand the lookup process:
> When trying to search for a symbol, we first search through all
> the partial symtabs to see if they contain partial symbols that
> match our search. From there, we expand those partial symtabs into
> full symtabs, and only then do we search the full symtabs for
> symbols that match, and that's the list we return.
> 
> In our case, here is the reasoning: Imagine we have the following
> C pseudo-code:
> 
>    /* inc.h */
>    inline int increment (int i);
> 
>    /* inc.c */
>    int
>    increment (int i)
>    {
>      return i + 1;
>    }
> 
>    /* foo.c */
>    #include "inc.h"
> 
>    int
>    main (void)
>    {
>      return increment (0);
>    }
> 
> For function increment, we would assume that the compiler would first
> generate a DW_TAG_subprogram DIE, and that this DIE would be a child
> of inc.c's compilation unit.
> 
> Looking at the debugging information for foo.c's unit, we would see
> the DW_TAG_subprogram DIE for the function "main".  And if the compiler
> were to elect to inline increment, we would see one of the children of
> main's DW_TAG_subprogram be a DW_TAG_inline_subroutine DIE.
> 
> Now, let's take a look at what happens when the dwarf2read module
> processes the DWARF, and transforms a DW_TAG_subprogram DIE into
> a partial symbol: First, it processes the DIE's attributes. So far
> so good, but then you see that it processes the DIE's children
> only in the case of Ada. See dwarf2read.c::add_partial_subprogram:
> 
>   if (cu->language == language_ada)
>     {
>       pdi = pdi->die_child;
>       while (pdi != NULL)
>         {
>           fixup_partial_die (pdi, cu);
>           if (pdi->tag == DW_TAG_subprogram
>               || pdi->tag == DW_TAG_lexical_block)
>             add_partial_subprogram (pdi, lowpc, highpc, set_addrmap, 
> cu);
>           pdi = pdi->die_sibling;
>         }
>     }
> 
> The reason why we do it for Ada is that nested subprograms are
> common-place in Ada, and users expect to be able to be able to
> break on those subprograms without having to be inside the function
> itself to do so. In other words, we don't treat the symbol as
> being local to the enclosing subprogram.
> 
> With that in mind, what happens in the C case while dwarf2read
> processes the main's DW_TAG_subprogram: It adds "main"'s partial
> symbol, but stops there instead of looking at its children.
> The net result is that the inline instance of our "increment"
> function never makes it to the partial symtabs/symbols. This
> impacts our partial symtab scan, as we now only have one partial
> symbol for increment in inc.c's partial symtab which matches.
> So, that's the only partial symtab selected for expansion.
> This then potentially affects the next step, which is the full
> symtab scan, and if "foo.c"'s partial symtab hasn't be expanded
> for some reasons unrelated to our lookup, we'll end up only
> finding one match, the non-inlined one.

Why doesn't the same happen in the Ada case?  The debug info in the Ada 
program looks similar, so a partial symbol should be created for this 
DIE:

  <1><77b>: Abbrev Number: 2 (DW_TAG_subprogram)
     <77c>   DW_AT_external    : 1
     <77c>   DW_AT_name        : (indirect string, offset: 0xae3): 
b__read_small
     <780>   DW_AT_decl_file   : 2
     <781>   DW_AT_decl_line   : 3
     <782>   DW_AT_inline      : 3       (declared as inline and inlined)

Then, when we set the breakpoint, I would expect GDB to find the partial 
symbol for read_small in this objfile, expand it to full symbols, and 
then find the inlined instance, just as it does for C.  That's the 
difference I don't get.

Simon



More information about the Gdb-patches mailing list