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

Message ID 1513776096-19313-1-git-send-email-roirand@adacore.com
State New, archived
Headers

Commit Message

Xavier Roirand Dec. 20, 2017, 1:21 p.m. UTC
  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
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.

gdb/ChangeLog:

        * dwarf2read.c (scan_partial_symbols, add_partial_symbol)
        (add_partial_subprogram, load_partial_dies): Add
        DW_TAG_inlined_subroutine handling.

gdb/testsuite/ChangeLog:

        * gdb.ada/bp_inlined_func: New testcase.
---
 gdb/dwarf2read.c                              | 21 ++++++----
 gdb/testsuite/gdb.ada/bp_inlined_func.exp     | 55 +++++++++++++++++++++++++++
 gdb/testsuite/gdb.ada/bp_inlined_func/b.adb   | 28 ++++++++++++++
 gdb/testsuite/gdb.ada/bp_inlined_func/b.ads   | 19 +++++++++
 gdb/testsuite/gdb.ada/bp_inlined_func/c.adb   | 27 +++++++++++++
 gdb/testsuite/gdb.ada/bp_inlined_func/c.ads   | 19 +++++++++
 gdb/testsuite/gdb.ada/bp_inlined_func/foo.adb | 23 +++++++++++
 7 files changed, 184 insertions(+), 8 deletions(-)
 create mode 100644 gdb/testsuite/gdb.ada/bp_inlined_func.exp
 create mode 100644 gdb/testsuite/gdb.ada/bp_inlined_func/b.adb
 create mode 100644 gdb/testsuite/gdb.ada/bp_inlined_func/b.ads
 create mode 100644 gdb/testsuite/gdb.ada/bp_inlined_func/c.adb
 create mode 100644 gdb/testsuite/gdb.ada/bp_inlined_func/c.ads
 create mode 100644 gdb/testsuite/gdb.ada/bp_inlined_func/foo.adb
  

Comments

Simon Marchi Dec. 21, 2017, 2:35 a.m. UTC | #1
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 don't see anything wrong with the change, but I'm far from an expert 
in that area :)

Simon
  
Joel Brobecker Dec. 21, 2017, 11:03 a.m. UTC | #2
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.

This is of course predicated on the assumption that the compiler
generates the DW_TAG_inline_subroutine DIE as a child of
the DW_TAG_subprogram DIE. Xavier tells me he tried it, and
verified that this was the case.

In your scenario, a number of things might be different, and
might explain why it works. For instance, you might have
expanded the partial symtab where your inlined instance lives
as a side-effect of some earlier command, for instance. Or
maybe the inlined instances are described differently with
your compiler...

> I don't see anything wrong with the change, but I'm far from an expert
> in that area :)

I used to know this fairly well, and the patch does seem
relatively logical, but it's been a while, hence the caution
and the delay before pushing ;-).
  
Simon Marchi Dec. 21, 2017, 10:51 p.m. UTC | #3
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
  
Joel Brobecker Dec. 22, 2017, 2:58 a.m. UTC | #4
> 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.

That's the abstract instance of the inlined subprogram. You might
notice that it has a DW_AT_inline attribute, and nothing else.
This DIE is there as the parent for the DIE(s) that represent
the instance(s) where the function was actually 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

This DIE is the one that represents one concrete instance
of the inlined function. You can see that it has a
DW_AT_abstract_origin attribute which points to the abstract
instance (so we can "inherit" some of the attributes that
are common to all concrete instances), and then the attributes
specific to this inlined instance, such as location, and
more important of all, the low/high_pc.
  
Joel Brobecker Jan. 3, 2018, 4:44 a.m. UTC | #5
Hi Xavier,

> gdb/ChangeLog:
> 
>         * dwarf2read.c (scan_partial_symbols, add_partial_symbol)
>         (add_partial_subprogram, load_partial_dies): Add
>         DW_TAG_inlined_subroutine handling.
> 
> gdb/testsuite/ChangeLog:
> 
>         * gdb.ada/bp_inlined_func: New testcase.

The compiler is detecting a bug in your code:

> @@ -9412,7 +9415,7 @@ add_partial_subprogram (struct partial_die_info *pdi,
>  			CORE_ADDR *lowpc, CORE_ADDR *highpc,
>  			int set_addrmap, struct dwarf2_cu *cu)
>  {
> -  if (pdi->tag == DW_TAG_subprogram)
> +  if (pdi->tag == DW_TAG_subprogram || DW_TAG_inlined_subroutine)
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

It should be "|| pdi->tag == DW_TAG_inlined_subroutine".
Can you re-send a fixed version, please?
  

Patch

diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 484cbce..694f3fc 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -8961,11 +8961,13 @@  scan_partial_symbols (struct partial_die_info *first_die, CORE_ADDR *lowpc,
 
       if (pdi->name != NULL || pdi->tag == DW_TAG_namespace
 	  || pdi->tag == DW_TAG_module || pdi->tag == DW_TAG_enumeration_type
-	  || pdi->tag == DW_TAG_imported_unit)
+	  || pdi->tag == DW_TAG_imported_unit
+	  || pdi->tag == DW_TAG_inlined_subroutine)
 	{
 	  switch (pdi->tag)
 	    {
 	    case DW_TAG_subprogram:
+	    case DW_TAG_inlined_subroutine:
 	      add_partial_subprogram (pdi, lowpc, highpc, set_addrmap, cu);
 	      break;
 	    case DW_TAG_constant:
@@ -9198,6 +9200,7 @@  add_partial_symbol (struct partial_die_info *pdi, struct dwarf2_cu *cu)
 
   switch (pdi->tag)
     {
+    case DW_TAG_inlined_subroutine:
     case DW_TAG_subprogram:
       addr = gdbarch_adjust_dwarf2_addr (gdbarch, pdi->lowpc + baseaddr);
       if (pdi->is_external || cu->language == language_ada)
@@ -9395,12 +9398,12 @@  add_partial_module (struct partial_die_info *pdi, CORE_ADDR *lowpc,
     scan_partial_symbols (pdi->die_child, lowpc, highpc, set_addrmap, cu);
 }
 
-/* Read a partial die corresponding to a subprogram and create a partial
-   symbol for that subprogram.  When the CU language allows it, this
-   routine also defines a partial symbol for each nested subprogram
-   that this subprogram contains.  If SET_ADDRMAP is true, record the
-   covered ranges in the addrmap.  Set *LOWPC and *HIGHPC to the lowest
-   and highest PC values found in PDI.
+/* Read a partial die corresponding to a subprogram or an inlined
+   subprogram and create a partial symbol for that subprogram.
+   When the CU language allows it, this routine also defines a partial
+   symbol for each nested subprogram that this subprogram contains.
+   If SET_ADDRMAP is true, record the covered ranges in the addrmap.
+   Set *LOWPC and *HIGHPC to the lowest and highest PC values found in PDI.
 
    PDI may also be a lexical block, in which case we simply search
    recursively for subprograms defined inside that lexical block.
@@ -9412,7 +9415,7 @@  add_partial_subprogram (struct partial_die_info *pdi,
 			CORE_ADDR *lowpc, CORE_ADDR *highpc,
 			int set_addrmap, struct dwarf2_cu *cu)
 {
-  if (pdi->tag == DW_TAG_subprogram)
+  if (pdi->tag == DW_TAG_subprogram || DW_TAG_inlined_subroutine)
     {
       if (pdi->has_pc_info)
         {
@@ -9460,6 +9463,7 @@  add_partial_subprogram (struct partial_die_info *pdi,
 	{
 	  fixup_partial_die (pdi, cu);
 	  if (pdi->tag == DW_TAG_subprogram
+	      || pdi->tag == DW_TAG_inlined_subroutine
 	      || pdi->tag == DW_TAG_lexical_block)
 	    add_partial_subprogram (pdi, lowpc, highpc, set_addrmap, cu);
 	  pdi = pdi->die_sibling;
@@ -18223,6 +18227,7 @@  load_partial_dies (const struct die_reader_specs *reader,
 	  && abbrev->tag != DW_TAG_constant
 	  && abbrev->tag != DW_TAG_enumerator
 	  && abbrev->tag != DW_TAG_subprogram
+	  && abbrev->tag != DW_TAG_inlined_subroutine
 	  && abbrev->tag != DW_TAG_lexical_block
 	  && abbrev->tag != DW_TAG_variable
 	  && abbrev->tag != DW_TAG_namespace
diff --git a/gdb/testsuite/gdb.ada/bp_inlined_func.exp b/gdb/testsuite/gdb.ada/bp_inlined_func.exp
new file mode 100644
index 0000000..299d25a
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/bp_inlined_func.exp
@@ -0,0 +1,55 @@ 
+# Copyright 2017 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+load_lib "ada.exp"
+
+standard_ada_testfile foo
+
+if {[gdb_compile_ada "${srcfile}" "${binfile}" executable [list debug ]] != "" } {
+  return -1
+}
+
+# Test inserting breakpoint on read_small inlined function.
+
+clean_restart ${testfile}
+
+if ![runto_main] then {
+   fail "Cannot run to main, testcase aborted"
+   return 0
+}
+
+gdb_test "break read_small" \
+         "Breakpoint $decimal at $hex: read_small\\. \\(4 locations\\)" \
+         "set breakpoint at read_small"
+
+gdb_test "continue" \
+         "Breakpoint $decimal, b\\.doit \\(\\).*" \
+         "Hitting first call of read_small"
+
+gdb_test "continue" \
+         "Breakpoint $decimal, foo \\(\\).*" \
+         "Hitting second call of read_small"
+
+gdb_test "continue" \
+         "Breakpoint $decimal, c\\.c_doit \\(\\).*" \
+         "Hitting third call of read_small"
+
+gdb_test "continue" \
+         "Breakpoint $decimal, c\\.c_doit2 \\(\\).*" \
+         "Hitting fourth call of read_small"
+
+gdb_test "continue" \
+         "Continuing\..*$inferior_exited_re.*" \
+         "continuing to program completion"
diff --git a/gdb/testsuite/gdb.ada/bp_inlined_func/b.adb b/gdb/testsuite/gdb.ada/bp_inlined_func/b.adb
new file mode 100644
index 0000000..49a5374
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/bp_inlined_func/b.adb
@@ -0,0 +1,28 @@ 
+--  Copyright 2017 Free Software Foundation, Inc.
+--
+--  This program is free software; you can redistribute it and/or modify
+--  it under the terms of the GNU General Public License as published by
+--  the Free Software Foundation; either version 3 of the License, or
+--  (at your option) any later version.
+--
+--  This program is distributed in the hope that it will be useful,
+--  but WITHOUT ANY WARRANTY; without even the implied warranty of
+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--  GNU General Public License for more details.
+--
+--  You should have received a copy of the GNU General Public License
+--  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package body B is
+  Total : Natural := 0;
+  procedure Read_Small is
+  begin
+    Total := Total + 1; -- BREAK
+  end Read_Small;
+
+  procedure Doit is
+  begin
+     Read_Small;
+     null;
+  end Doit;
+end B;
diff --git a/gdb/testsuite/gdb.ada/bp_inlined_func/b.ads b/gdb/testsuite/gdb.ada/bp_inlined_func/b.ads
new file mode 100644
index 0000000..2987c97
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/bp_inlined_func/b.ads
@@ -0,0 +1,19 @@ 
+--  Copyright 2017 Free Software Foundation, Inc.
+--
+--  This program is free software; you can redistribute it and/or modify
+--  it under the terms of the GNU General Public License as published by
+--  the Free Software Foundation; either version 3 of the License, or
+--  (at your option) any later version.
+--
+--  This program is distributed in the hope that it will be useful,
+--  but WITHOUT ANY WARRANTY; without even the implied warranty of
+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--  GNU General Public License for more details.
+--
+--  You should have received a copy of the GNU General Public License
+--  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package B is
+  procedure Read_Small with Inline_Always;
+  procedure Doit;
+end B;
diff --git a/gdb/testsuite/gdb.ada/bp_inlined_func/c.adb b/gdb/testsuite/gdb.ada/bp_inlined_func/c.adb
new file mode 100644
index 0000000..12784ea
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/bp_inlined_func/c.adb
@@ -0,0 +1,27 @@ 
+--  Copyright 2017 Free Software Foundation, Inc.
+--
+--  This program is free software; you can redistribute it and/or modify
+--  it under the terms of the GNU General Public License as published by
+--  the Free Software Foundation; either version 3 of the License, or
+--  (at your option) any later version.
+--
+--  This program is distributed in the hope that it will be useful,
+--  but WITHOUT ANY WARRANTY; without even the implied warranty of
+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--  GNU General Public License for more details.
+--
+--  You should have received a copy of the GNU General Public License
+--  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+with B;
+package body C is
+  procedure C_Doit is
+  begin
+    B.Read_Small;
+    C_Doit2;
+  end;
+  procedure C_Doit2 is
+  begin
+    B.Read_Small;
+  end;
+end C;
diff --git a/gdb/testsuite/gdb.ada/bp_inlined_func/c.ads b/gdb/testsuite/gdb.ada/bp_inlined_func/c.ads
new file mode 100644
index 0000000..fd6dfc6
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/bp_inlined_func/c.ads
@@ -0,0 +1,19 @@ 
+--  Copyright 2017 Free Software Foundation, Inc.
+--
+--  This program is free software; you can redistribute it and/or modify
+--  it under the terms of the GNU General Public License as published by
+--  the Free Software Foundation; either version 3 of the License, or
+--  (at your option) any later version.
+--
+--  This program is distributed in the hope that it will be useful,
+--  but WITHOUT ANY WARRANTY; without even the implied warranty of
+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--  GNU General Public License for more details.
+--
+--  You should have received a copy of the GNU General Public License
+--  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package C is
+  procedure C_Doit;
+  procedure C_Doit2;
+end C;
diff --git a/gdb/testsuite/gdb.ada/bp_inlined_func/foo.adb b/gdb/testsuite/gdb.ada/bp_inlined_func/foo.adb
new file mode 100644
index 0000000..bf44275
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/bp_inlined_func/foo.adb
@@ -0,0 +1,23 @@ 
+--  Copyright 2017 Free Software Foundation, Inc.
+--
+--  This program is free software; you can redistribute it and/or modify
+--  it under the terms of the GNU General Public License as published by
+--  the Free Software Foundation; either version 3 of the License, or
+--  (at your option) any later version.
+--
+--  This program is distributed in the hope that it will be useful,
+--  but WITHOUT ANY WARRANTY; without even the implied warranty of
+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--  GNU General Public License for more details.
+--
+--  You should have received a copy of the GNU General Public License
+--  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+with B; use B;
+with C;
+procedure FOO is
+begin
+  Doit;
+  B.Read_Small;
+  C.C_Doit;
+end FOO;