diff mbox

[1/2] Report call site for inlined functions

Message ID 1499740601-15957-1-git-send-email-keiths@redhat.com
State New
Headers show

Commit Message

Keith Seitz July 11, 2017, 2:36 a.m. UTC
Currently, "info break" can show some (perhaps) unexpected results when
setting a breakpoint on an inlined function:

(gdb) list
1	#include <stdio.h>
2
3	static inline void foo()
4	{
5	        printf("Hello world\n");
6	}
7
8	int main()
9	{
10	        foo();
11	        return 0;
12	}
13
(gdb) b foo
Breakpoint 1 at 0x400434: file 1228556.c, line 5.
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400434 in main at 1228556.c:5

GDB reported that we understood what "foo" was, but we then report that the
breakpoint is actually set in main. While that is literally true, we can
do a little better.

I have chosen to record the actual symbol that we found during the parse
in the SaL.  Later that information is copied into the bp_location.  From
there, print_breakpoint_location can use this information to ascertain
that the symbol is really an inlined function, and it can report the
real symbol (foo) and inform the user of the actual call site (where the
breakpoint is really set):

(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400434 in foo at 1228556.c:3
                                                   inlined in main
                                                   at 1228556.c:10

I have reported this information through to MI so that UI writers can inform
their users as well:

(gdb) interpreter-exec mi -break-info
^done,BreakpointTable={nr_rows="1",nr_cols="6",hdr=[{width="7",alignment="-1",col_name="number",colhdr="Num"},{width="14",alignment="-1",col_name="type",colhdr="Type"},{width="4",alignment="-1",col_name="disp",colhdr="Disp"},{width="3",alignment="-1",col_name="enabled",colhdr="Enb"},{width="18",alignment="-1",col_name="addr",colhdr="Address"},{width="40",alignment="2",col_name="what",colhdr="What"}],body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000400434",func="foo",file="1228556.c",fullname="/home/keiths/tmp/1228556.c",line="3",call-site-func="main",call-site-file="1228556.c",call-site-fullname="/home/keiths/tmp/1228556.c",call-site-line="10",thread-groups=["i1"],times="0",original-location="foo"}]}

Here you can see the new call-site-func, call-site-file, call-site-fullname,
and call-site-line.

gdb/ChangeLog:
2017-MM-DD  Keith Seitz  <keiths@redhat.com>

	* breakpoint.c (print_breakpoint_location): Print out call site
	information for inlined functions.
	(add_location_to_breakpoint): Save sal->symbol.
	* breakpoint.h (struct bp_location)<symbol>: New field.
	* dwarf2read.c (dwarf2_file_symtab): New function.
	(read_func_scope): For inlined functions, allocate an inlined_symbol.
	(new_symbol_full): Move call-file/dec_file handling to
	dwarf2_file_symtab and use it.
	* symtab.c (find_function_start_sal): Save the symbol into the SaL.
	(allocate_inlined_symbol): New function.
	* symtab.h (struct inlined_symbol): New structure.
	(struct symtab_and_line)<symbol>: New field.
	(allocate_inlined_symbol): Declare.

gdb/doc/ChangeLog:
2017-MM-DD  Keith Seitz  <keiths@redhat.com>

	* gdb.texinfo (GDB/MI Breakpoint Information): Mention call-site-func,
	call-site-file, call-site-fullname, and call-site-line.

gdb/testsuite/ChangeLog:
2017-MM-DD  Keith Seitz  <keiths@redhat.com>

	* gdb.opt/inline-break.exp (break_info_1): New procedure.
	Test "info break" for every inlined function breakpoint.
	Test output of -break-info.
---
 gdb/breakpoint.c                       |  70 +++++++++++++++++++----
 gdb/breakpoint.h                       |   5 ++
 gdb/doc/gdb.texinfo                    |   8 +++
 gdb/dwarf2read.c                       |  72 ++++++++++++++++-------
 gdb/symtab.c                           |  14 +++++
 gdb/symtab.h                           |  20 +++++++
 gdb/testsuite/gdb.opt/inline-break.exp | 101 +++++++++++++++++++++++++++++++++
 7 files changed, 260 insertions(+), 30 deletions(-)

Comments

Eli Zaretskii July 11, 2017, 2:24 p.m. UTC | #1
> From: Keith Seitz <keiths@redhat.com>
> Date: Mon, 10 Jul 2017 19:36:40 -0700
> 
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index c167a86..58cdc1a 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -26755,6 +26755,14 @@ known.  If not known, this field is not present.
>  The line number at which this breakpoint appears, if known.
>  If not known, this field is not present.
>  
> +@item call-site-func
> +@item call-site-file
> +@item call-site-fullname
> +@item call-site-line

All but the first of these should be @itemx.

Does this warrant a NEWS entry?

OK for the documentation part, thanks.
Jan Kratochvil July 17, 2017, 7:23 p.m. UTC | #2
Hi Keith,

with attached file on Fedora 25 x86_64 'gcc -g' (not any -O):

------------------------------------------------------------------------------
(gdb) b g
Breakpoint 1 at 0x4004ce: file inlined5.c, line 12.
(gdb) r
Starting program: inlined5 

Breakpoint 1, g (a=1) at inlined5.c:12
12	  f (a);
(gdb) bt
#0  g (a=1) at inlined5.c:12
#1  h (a=1) at inlined5.c:20
#2  main () at inlined5.c:28
(gdb) info breakpoints 
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000004004ce in g at inlined5.c:10 inlined in main 
                                                   at inlined5.c:20
	breakpoint already hit 1 time
(gdb) 
------------------------------------------------------------------------------

I think g is inlined into h which is inlined into main.  Personally I think it
would be enough to say "in g" as the rest is a toolchain implementation detail.
But if it tries to describe the code layout then it should match the reality.

The MI attributes would also need to be adjusted for multi-level inlines.


Jan
int e;

static __attribute__ ((__noinline__)) void
f (int a)
{
  e = a;
}

static __attribute__ ((__always_inline__)) void
g (int a)
{
  f (a);

  e = a;
}

static __attribute__ ((__always_inline__)) void
h (int a)
{
  g (a);

  e = a;
}

int
main (void)
{
  h (1);

  return 0;
}
Pedro Alves July 18, 2017, 7:05 p.m. UTC | #3
On 07/11/2017 03:36 AM, Keith Seitz wrote:

> I have chosen to record the actual symbol that we found during the parse
> in the SaL.  Later that information is copied into the bp_location.  

At some point I think we'll just make bp_location hold a SaL.  :-)

> From
> there, print_breakpoint_location can use this information to ascertain
> that the symbol is really an inlined function, and it can report the
> real symbol (foo) and inform the user of the actual call site (where the
> breakpoint is really set):
> 
> (gdb) i b
> Num     Type           Disp Enb Address            What
> 1       breakpoint     keep y   0x0000000000400434 in foo at 1228556.c:3
>                                                    inlined in main
>                                                    at 1228556.c:10
> 
> I have reported this information through to MI so that UI writers can inform
> their users as well:
> 
> (gdb) interpreter-exec mi -break-info
> ^done,BreakpointTable={nr_rows="1",nr_cols="6",hdr=[{width="7",alignment="-1",col_name="number",colhdr="Num"},{width="14",alignment="-1",col_name="type",colhdr="Type"},{width="4",alignment="-1",col_name="disp",colhdr="Disp"},{width="3",alignment="-1",col_name="enabled",colhdr="Enb"},{width="18",alignment="-1",col_name="addr",colhdr="Address"},{width="40",alignment="2",col_name="what",colhdr="What"}],body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x0000000000400434",func="foo",file="1228556.c",fullname="/home/keiths/tmp/1228556.c",line="3",call-site-func="main",call-site-file="1228556.c",call-site-fullname="/home/keiths/tmp/1228556.c",call-site-line="10",thread-groups=["i1"],times="0",original-location="foo"}]}
> 
> Here you can see the new call-site-func, call-site-file, call-site-fullname,
> and call-site-line.

The non-inlined call site seems sufficient info to me, at least for
the  CLI.  I'm not sure whether "call-site" is sufficiently clear that
it's referring to that vs the immediate potential inlined-too
caller though.  I think at least the docs need such a clarification.


> --- a/gdb/breakpoint.c
> +++ b/gdb/breakpoint.c
> @@ -6153,24 +6153,73 @@ print_breakpoint_location (struct breakpoint *b,
>      uiout->field_string ("what", event_location_to_string (b->location.get ()));
>    else if (loc && loc->symtab)
>      {
> -      struct symbol *sym 
> -	= find_pc_sect_function (loc->address, loc->section);
> -      if (sym)
> +      /* If a location is associated with an inlined symbol, print out
> +	 information about its call site, too.  */
> +      if (loc->symbol != NULL && SYMBOL_INLINED (loc->symbol))
>  	{
> +	  struct inlined_symbol *isym = (struct inlined_symbol *) (loc->symbol);
> +	  struct symbol *sym
> +	    = find_pc_sect_function (loc->address, loc->section);
> +
> +	  /* ISYM contains information about the inlined function, and
> +	     LOC->SYMBOL describes the call site.  */
>  	  uiout->text ("in ");
> -	  uiout->field_string ("func", SYMBOL_PRINT_NAME (sym));
> +	  uiout->field_string ("func", SYMBOL_PRINT_NAME (loc->symbol));
>  	  uiout->text (" ");
>  	  uiout->wrap_hint (wrap_indent_at_field (uiout, "what"));
>  	  uiout->text ("at ");
> +
> +	  const char *s = symtab_to_filename_for_display (isym->decl_file);
> +          uiout->field_string ("file", s);
> +
> +	  uiout->text (":");
> +	  if (uiout->is_mi_like_p ())
> +	    {
> +	      uiout->field_string ("fullname",
> +				   symtab_to_fullname (isym->decl_file));
> +	    }

Isn't there a good deal of duplication between the above and the
else branch?


> +	  uiout->field_int ("line", isym->decl_line);
> +	  uiout->text (" ");
> +	  uiout->wrap_hint (wrap_indent_at_field (uiout, "what"));
> +	  uiout->text ("inlined in ");
> +	  if (sym != NULL)
> +	    uiout->field_string ("call-site-func", SYMBOL_PRINT_NAME (sym));
> +	  uiout->text (" ");
> +	  uiout->wrap_hint (wrap_indent_at_field (uiout, "what"));
> +	  uiout->text ("at ");
> +	  s = symtab_to_filename_for_display (symbol_symtab (loc->symbol));
> +	  uiout->field_string ("call-site-file", s);
> +
> +	  uiout->text (":");
> +	  if (uiout->is_mi_like_p ())
> +	    {
> +	      s = symtab_to_fullname (symbol_symtab (loc->symbol));
> +	      uiout->field_string ("call-site-fullname", s);
> +	    }
> +	  uiout->field_int ("call-site-line", SYMBOL_LINE (loc->symbol));
>  	}
> -      uiout->field_string ("file",
> -			   symtab_to_filename_for_display (loc->symtab));
> -      uiout->text (":");
> +      else
> +	{
> +	  struct symbol *sym
> +	    = find_pc_sect_function (loc->address, loc->section);
>  

I wonder whether the else branch could use loc->symbol now that you
have it, instead of looking it up.


> -      if (uiout->is_mi_like_p ())
> -	uiout->field_string ("fullname", symtab_to_fullname (loc->symtab));
> +	  if (sym)
> +	    {
> +	      uiout->text ("in ");
> +	      uiout->field_string ("func", SYMBOL_PRINT_NAME (sym));
> +	      uiout->text (" ");
> +	      uiout->wrap_hint (wrap_indent_at_field (uiout, "what"));
> +	      uiout->text ("at ");
> +	    }
> +	  uiout->field_string ("file",
> +			       symtab_to_filename_for_display (loc->symtab));
> +	  uiout->text (":");
> +
> +	  if (uiout->is_mi_like_p ())
> +	    uiout->field_string ("fullname", symtab_to_fullname (loc->symtab));
>        
> -      uiout->field_int ("line", loc->line_number);
> +	  uiout->field_int ("line", loc->line_number);
> +	}
>      }
>    else if (loc)
>      {
> @@ -8986,6 +9035,7 @@ add_location_to_breakpoint (struct breakpoint *b,
>    loc->gdbarch = loc_gdbarch;
>    loc->line_number = sal->line;
>    loc->symtab = sal->symtab;
> +  loc->symbol = sal->symbol;
>  
>    set_breakpoint_location_function (loc,
>  				    sal->explicit_pc || sal->explicit_line);
> diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
> index d955184..2f10c3b 100644
> --- a/gdb/breakpoint.h
> +++ b/gdb/breakpoint.h
> @@ -484,6 +484,11 @@ public:
>       to find the corresponding source file name.  */
>  
>    struct symtab *symtab = NULL;
> +
> +  /* The symbol found by the location parser, if any.  This may be used to
> +     ascertain when an event location was set at a different location than
> +     the one originally selected by parsing, e.g., inlined symbols.  */
> +  const struct symbol *symbol = NULL;
>  };
>  
>  /* The possible return values for print_bpstat, print_it_normal,
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index c167a86..58cdc1a 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -26755,6 +26755,14 @@ known.  If not known, this field is not present.
>  The line number at which this breakpoint appears, if known.
>  If not known, this field is not present.
>  
> +@item call-site-func
> +@item call-site-file
> +@item call-site-fullname
> +@item call-site-line
> +These fields describe the call site for a breakpoint set on an inlined function.
> +The fields are analogous to those fields of the same name for normal breakpoint
> +locations.

IMO, the non-inlined call site is sufficient info, at least for the 
CLI.  I'm not sure whether "call-site" is sufficiently clear that
it's referring to that vs the immediate potential inlined-too
caller though.  I think at least the docs need that clarified.

> +
>  @item at
>  If the source file is not known, this field may be provided.  If
>  provided, this holds the address of the breakpoint, possibly followed
> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
> index 0fdcd42..3b3193b 100644
> --- a/gdb/dwarf2read.c
> +++ b/gdb/dwarf2read.c
> @@ -11470,6 +11470,35 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu)
>    do_cleanups (cleanups);
>  }
>  
> +/* Get the symtab representing the KIND of file of DIE in CU or NULL
> +   if no such file exists.  KIND is any valid DWARF file attribute such as
> +   DW_AT_decl_file or DW_AT_call_file.  */
> +
> +static struct symtab *
> +dwarf2_file_symtab (unsigned int kind, struct die_info *die,
> +		    struct dwarf2_cu *cu)
> +{
> +  struct attribute *attr = dwarf2_attr (die, kind, cu);
> +
> +  if (attr != NULL)
> +    {
> +      file_name_index file_index = (file_name_index) DW_UNSND (attr);
> +      struct file_entry *fe;
> +
> +      if (cu->line_header != NULL)
> +	fe = cu->line_header->file_name_at (file_index);
> +      else
> +	fe = NULL;
> +
> +      if (fe == NULL)
> +	complaint (&symfile_complaints, _("file index out of range"));
> +      else
> +	return fe->symtab;
> +    }
> +
> +  return NULL;
> +}
> +
>  static void
>  read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
>  {
> @@ -11486,6 +11515,8 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
>    int inlined_func = (die->tag == DW_TAG_inlined_subroutine);
>    VEC (symbolp) *template_args = NULL;
>    struct template_symbol *templ_func = NULL;
> +  struct inlined_symbol *isym = NULL;
> +  struct symbol *symbol_storage = NULL;
>  
>    if (inlined_func)
>      {
> @@ -11540,13 +11571,28 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
>  	{
>  	  templ_func = allocate_template_symbol (objfile);
>  	  templ_func->base.is_cplus_template_function = 1;
> +	  symbol_storage = (struct symbol *) templ_func;
>  	  break;
>  	}
>      }
>  
> +  /* If we have an inlined symbol, we must also allocate a different
> +     symbol.  */

How does this work when you have an (inlined) template function?
Like e.g.,:

template<typename T>
static T inline_func (T val)
{
  return val * 2;
}

int
not_inline_func (int val)
{
  return inline_func (val * 2);
}


 <1><31>: Abbrev Number: 2 (DW_TAG_subprogram)
    <32>   DW_AT_name        : (indirect string, offset: 0x929): inline_func<int>
    <36>   DW_AT_decl_file   : 1
    <37>   DW_AT_decl_line   : 2
    <38>   DW_AT_type        : <0x54>
    <3c>   DW_AT_inline      : 1        (inlined)
    <3d>   DW_AT_sibling     : <0x54>


> +  if (inlined_func)
> +    {
> +      isym = allocate_inlined_symbol (objfile);
> +      isym->decl_file = dwarf2_file_symtab (DW_AT_decl_file, die, cu);
> +
> +      attr = dwarf2_attr (die, DW_AT_decl_line, cu);
> +      if (attr != NULL)
> +	isym->decl_line = DW_UNSND (attr);
> +
> +      symbol_storage = (struct symbol *) isym;
> +    }
> +
>    newobj = push_context (0, lowpc);
>    newobj->name = new_symbol_full (die, read_type_die (die, cu), cu,
> -			       (struct symbol *) templ_func);
> +				  symbol_storage);
>  
>    /* If there is a location expression for DW_AT_frame_base, record
>       it.  */
> @@ -18948,25 +18994,11 @@ new_symbol_full (struct die_info *die, struct type *type, struct dwarf2_cu *cu,
>  	  SYMBOL_LINE (sym) = DW_UNSND (attr);
>  	}
>  


>  /* See symtab.h.  */
>  
>  struct objfile *
> diff --git a/gdb/symtab.h b/gdb/symtab.h
> index 341deca..a70ed54 100644
> --- a/gdb/symtab.h
> +++ b/gdb/symtab.h
> @@ -910,6 +910,21 @@ struct template_symbol
>    struct symbol **template_arguments;
>  };
>  
> +/* A superclass of struct symbol used to represent an inlined symbol.
> +   A symbol is really of this type if SYMBOL_INLINED is true.  */
> +
> +struct inlined_symbol
> +{
> +  /* The base class.  */
> +  struct symbol base;

Couldn't this be real C++ inheritance?

  struct inlined_symbol : symbol 
  {
     ...
  };


I ran out of time ... more later.

Thanks,
Pedro Alves
Keith Seitz Oct. 20, 2017, 6:46 p.m. UTC | #4
[My reply seems not to have made it to sourceware, so I'm resending.]

On 07/18/2017 12:05 PM, Pedro Alves wrote:
> On 07/11/2017 03:36 AM, Keith Seitz wrote:
>>
>> Here you can see the new call-site-func, call-site-file, call-site-fullname,
>> and call-site-line.
> 
> The non-inlined call site seems sufficient info to me, at least for
> the  CLI.  I'm not sure whether "call-site" is sufficiently clear that
> it's referring to that vs the immediate potential inlined-too
> caller though.  I think at least the docs need such a clarification.

There seems to be consensus on the (lack of) utility of this call-site info, so in the next revision (to follow), I've chosen to remove it all.  I think that pretty much resolves (by deletion!) most of your concerns/comments. I've also removed the inlined_symbol proposal since that is also no longer necessary. 

>> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
>> index 0fdcd42..3b3193b 100644
>> --- a/gdb/dwarf2read.c
>> +++ b/gdb/dwarf2read.c
>> @@ -11540,13 +11571,28 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
>>  	{
>>  	  templ_func = allocate_template_symbol (objfile);
>>  	  templ_func->base.is_cplus_template_function = 1;
>> +	  symbol_storage = (struct symbol *) templ_func;
>>  	  break;
>>  	}
>>      }
>>  
>> +  /* If we have an inlined symbol, we must also allocate a different
>> +     symbol.  */
> 
> How does this work when you have an (inlined) template function?
> Like e.g.,:
> 
> template<typename T>
> static T inline_func (T val)
> {
>   return val * 2;
> }
> 
> int
> not_inline_func (int val)
> {
>   return inline_func (val * 2);
> }
> 

I realize this was asked in relation to the proposed (and now deleted) inline_symbol, but to be clear, the above example works:

(gdb) b inline_func<int>
Breakpoint 1 at 0x4004c0: file inline-t.cc, line 9.
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000004004c0 in inline_func<int> 
                                                   at inline-t.cc:9

Keith
Pedro Alves Oct. 27, 2017, 12:49 p.m. UTC | #5
On 10/20/2017 07:46 PM, Keith Seitz wrote:
> [My reply seems not to have made it to sourceware, so I'm resending.]
> 
> On 07/18/2017 12:05 PM, Pedro Alves wrote:
>> On 07/11/2017 03:36 AM, Keith Seitz wrote:
>>>
>>> Here you can see the new call-site-func, call-site-file, call-site-fullname,
>>> and call-site-line.
>>
>> The non-inlined call site seems sufficient info to me, at least for
>> the  CLI.  I'm not sure whether "call-site" is sufficiently clear that
>> it's referring to that vs the immediate potential inlined-too
>> caller though.  I think at least the docs need such a clarification.
> 
> There seems to be consensus on the (lack of) utility of this call-site info, so in the next revision (to follow), I've chosen to remove it all.  I think that pretty much resolves (by deletion!) most of your concerns/comments. I've also removed the inlined_symbol proposal since that is also no longer necessary. 

I think my observations in
<https://sourceware.org/ml/gdb-patches/2017-03/msg00382.html>
still make sense, and I don't know about consensus, but OK, let's
move on.  I'll take a look at the new version.

> 
>>> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
>>> index 0fdcd42..3b3193b 100644
>>> --- a/gdb/dwarf2read.c
>>> +++ b/gdb/dwarf2read.c
>>> @@ -11540,13 +11571,28 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
>>>  	{
>>>  	  templ_func = allocate_template_symbol (objfile);
>>>  	  templ_func->base.is_cplus_template_function = 1;
>>> +	  symbol_storage = (struct symbol *) templ_func;
>>>  	  break;
>>>  	}
>>>      }
>>>  
>>> +  /* If we have an inlined symbol, we must also allocate a different
>>> +     symbol.  */
>>
>> How does this work when you have an (inlined) template function?
>> Like e.g.,:
>>
>> template<typename T>
>> static T inline_func (T val)
>> {
>>   return val * 2;
>> }
>>
>> int
>> not_inline_func (int val)
>> {
>>   return inline_func (val * 2);
>> }
>>
> 
> I realize this was asked in relation to the proposed (and now deleted) inline_symbol, but to be clear, the above example works:
> 
> (gdb) b inline_func<int>
> Breakpoint 1 at 0x4004c0: file inline-t.cc, line 9.
> (gdb) i b
> Num     Type           Disp Enb Address            What
> 1       breakpoint     keep y   0x00000000004004c0 in inline_func<int> 
>                                                    at inline-t.cc:9

OK, it seems like a DIE can't be both DW_TAG_inlined_subroutine
and a template (kind of makes sense), so you couldn't ever end up
calling both allocate_template_symbol and allocate_inlined_symbol
for the same symbol (which is what I was worrying about, because
the code didn't consider/guard against that).

Thanks,
Pedro Alves
diff mbox

Patch

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 053ccef..c283ec0 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -6153,24 +6153,73 @@  print_breakpoint_location (struct breakpoint *b,
     uiout->field_string ("what", event_location_to_string (b->location.get ()));
   else if (loc && loc->symtab)
     {
-      struct symbol *sym 
-	= find_pc_sect_function (loc->address, loc->section);
-      if (sym)
+      /* If a location is associated with an inlined symbol, print out
+	 information about its call site, too.  */
+      if (loc->symbol != NULL && SYMBOL_INLINED (loc->symbol))
 	{
+	  struct inlined_symbol *isym = (struct inlined_symbol *) (loc->symbol);
+	  struct symbol *sym
+	    = find_pc_sect_function (loc->address, loc->section);
+
+	  /* ISYM contains information about the inlined function, and
+	     LOC->SYMBOL describes the call site.  */
 	  uiout->text ("in ");
-	  uiout->field_string ("func", SYMBOL_PRINT_NAME (sym));
+	  uiout->field_string ("func", SYMBOL_PRINT_NAME (loc->symbol));
 	  uiout->text (" ");
 	  uiout->wrap_hint (wrap_indent_at_field (uiout, "what"));
 	  uiout->text ("at ");
+
+	  const char *s = symtab_to_filename_for_display (isym->decl_file);
+          uiout->field_string ("file", s);
+
+	  uiout->text (":");
+	  if (uiout->is_mi_like_p ())
+	    {
+	      uiout->field_string ("fullname",
+				   symtab_to_fullname (isym->decl_file));
+	    }
+	  uiout->field_int ("line", isym->decl_line);
+	  uiout->text (" ");
+	  uiout->wrap_hint (wrap_indent_at_field (uiout, "what"));
+	  uiout->text ("inlined in ");
+	  if (sym != NULL)
+	    uiout->field_string ("call-site-func", SYMBOL_PRINT_NAME (sym));
+	  uiout->text (" ");
+	  uiout->wrap_hint (wrap_indent_at_field (uiout, "what"));
+	  uiout->text ("at ");
+	  s = symtab_to_filename_for_display (symbol_symtab (loc->symbol));
+	  uiout->field_string ("call-site-file", s);
+
+	  uiout->text (":");
+	  if (uiout->is_mi_like_p ())
+	    {
+	      s = symtab_to_fullname (symbol_symtab (loc->symbol));
+	      uiout->field_string ("call-site-fullname", s);
+	    }
+	  uiout->field_int ("call-site-line", SYMBOL_LINE (loc->symbol));
 	}
-      uiout->field_string ("file",
-			   symtab_to_filename_for_display (loc->symtab));
-      uiout->text (":");
+      else
+	{
+	  struct symbol *sym
+	    = find_pc_sect_function (loc->address, loc->section);
 
-      if (uiout->is_mi_like_p ())
-	uiout->field_string ("fullname", symtab_to_fullname (loc->symtab));
+	  if (sym)
+	    {
+	      uiout->text ("in ");
+	      uiout->field_string ("func", SYMBOL_PRINT_NAME (sym));
+	      uiout->text (" ");
+	      uiout->wrap_hint (wrap_indent_at_field (uiout, "what"));
+	      uiout->text ("at ");
+	    }
+	  uiout->field_string ("file",
+			       symtab_to_filename_for_display (loc->symtab));
+	  uiout->text (":");
+
+	  if (uiout->is_mi_like_p ())
+	    uiout->field_string ("fullname", symtab_to_fullname (loc->symtab));
       
-      uiout->field_int ("line", loc->line_number);
+	  uiout->field_int ("line", loc->line_number);
+	}
     }
   else if (loc)
     {
@@ -8986,6 +9035,7 @@  add_location_to_breakpoint (struct breakpoint *b,
   loc->gdbarch = loc_gdbarch;
   loc->line_number = sal->line;
   loc->symtab = sal->symtab;
+  loc->symbol = sal->symbol;
 
   set_breakpoint_location_function (loc,
 				    sal->explicit_pc || sal->explicit_line);
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index d955184..2f10c3b 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -484,6 +484,11 @@  public:
      to find the corresponding source file name.  */
 
   struct symtab *symtab = NULL;
+
+  /* The symbol found by the location parser, if any.  This may be used to
+     ascertain when an event location was set at a different location than
+     the one originally selected by parsing, e.g., inlined symbols.  */
+  const struct symbol *symbol = NULL;
 };
 
 /* The possible return values for print_bpstat, print_it_normal,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index c167a86..58cdc1a 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -26755,6 +26755,14 @@  known.  If not known, this field is not present.
 The line number at which this breakpoint appears, if known.
 If not known, this field is not present.
 
+@item call-site-func
+@item call-site-file
+@item call-site-fullname
+@item call-site-line
+These fields describe the call site for a breakpoint set on an inlined function.
+The fields are analogous to those fields of the same name for normal breakpoint
+locations.
+
 @item at
 If the source file is not known, this field may be provided.  If
 provided, this holds the address of the breakpoint, possibly followed
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 0fdcd42..3b3193b 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -11470,6 +11470,35 @@  inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu)
   do_cleanups (cleanups);
 }
 
+/* Get the symtab representing the KIND of file of DIE in CU or NULL
+   if no such file exists.  KIND is any valid DWARF file attribute such as
+   DW_AT_decl_file or DW_AT_call_file.  */
+
+static struct symtab *
+dwarf2_file_symtab (unsigned int kind, struct die_info *die,
+		    struct dwarf2_cu *cu)
+{
+  struct attribute *attr = dwarf2_attr (die, kind, cu);
+
+  if (attr != NULL)
+    {
+      file_name_index file_index = (file_name_index) DW_UNSND (attr);
+      struct file_entry *fe;
+
+      if (cu->line_header != NULL)
+	fe = cu->line_header->file_name_at (file_index);
+      else
+	fe = NULL;
+
+      if (fe == NULL)
+	complaint (&symfile_complaints, _("file index out of range"));
+      else
+	return fe->symtab;
+    }
+
+  return NULL;
+}
+
 static void
 read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
 {
@@ -11486,6 +11515,8 @@  read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
   int inlined_func = (die->tag == DW_TAG_inlined_subroutine);
   VEC (symbolp) *template_args = NULL;
   struct template_symbol *templ_func = NULL;
+  struct inlined_symbol *isym = NULL;
+  struct symbol *symbol_storage = NULL;
 
   if (inlined_func)
     {
@@ -11540,13 +11571,28 @@  read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
 	{
 	  templ_func = allocate_template_symbol (objfile);
 	  templ_func->base.is_cplus_template_function = 1;
+	  symbol_storage = (struct symbol *) templ_func;
 	  break;
 	}
     }
 
+  /* If we have an inlined symbol, we must also allocate a different
+     symbol.  */
+  if (inlined_func)
+    {
+      isym = allocate_inlined_symbol (objfile);
+      isym->decl_file = dwarf2_file_symtab (DW_AT_decl_file, die, cu);
+
+      attr = dwarf2_attr (die, DW_AT_decl_line, cu);
+      if (attr != NULL)
+	isym->decl_line = DW_UNSND (attr);
+
+      symbol_storage = (struct symbol *) isym;
+    }
+
   newobj = push_context (0, lowpc);
   newobj->name = new_symbol_full (die, read_type_die (die, cu), cu,
-			       (struct symbol *) templ_func);
+				  symbol_storage);
 
   /* If there is a location expression for DW_AT_frame_base, record
      it.  */
@@ -18948,25 +18994,11 @@  new_symbol_full (struct die_info *die, struct type *type, struct dwarf2_cu *cu,
 	  SYMBOL_LINE (sym) = DW_UNSND (attr);
 	}
 
-      attr = dwarf2_attr (die,
-			  inlined_func ? DW_AT_call_file : DW_AT_decl_file,
-			  cu);
-      if (attr)
-	{
-	  file_name_index file_index = (file_name_index) DW_UNSND (attr);
-	  struct file_entry *fe;
-
-	  if (cu->line_header != NULL)
-	    fe = cu->line_header->file_name_at (file_index);
-	  else
-	    fe = NULL;
-
-	  if (fe == NULL)
-	    complaint (&symfile_complaints,
-		       _("file index out of range"));
-	  else
-	    symbol_set_symtab (sym, fe->symtab);
-	}
+      struct symtab *symtab
+	= dwarf2_file_symtab (inlined_func ? DW_AT_call_file : DW_AT_decl_file,
+			      die, cu);
+      if (symtab != NULL)
+	symbol_set_symtab (sym, symtab);
 
       switch (die->tag)
 	{
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 497d520..55bfc93 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3472,6 +3472,7 @@  find_function_start_sal (struct symbol *sym, int funfirstline)
   fixup_symbol_section (sym, NULL);
   section = SYMBOL_OBJ_SECTION (symbol_objfile (sym), sym);
   sal = find_pc_sect_line (BLOCK_START (SYMBOL_BLOCK_VALUE (sym)), section, 0);
+  sal.symbol = sym;
 
   if (funfirstline && sal.symtab != NULL
       && (COMPUNIT_LOCATIONS_VALID (SYMTAB_COMPUNIT (sal.symtab))
@@ -5998,6 +5999,19 @@  allocate_template_symbol (struct objfile *objfile)
   return result;
 }
 
+/* See description in symtab.h.  */
+
+struct inlined_symbol *
+allocate_inlined_symbol (struct objfile *objfile)
+{
+  struct inlined_symbol *result;
+
+  result = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct inlined_symbol);
+  initialize_objfile_symbol_1 (&result->base);
+
+  return result;
+}
+
 /* See symtab.h.  */
 
 struct objfile *
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 341deca..a70ed54 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -910,6 +910,21 @@  struct template_symbol
   struct symbol **template_arguments;
 };
 
+/* A superclass of struct symbol used to represent an inlined symbol.
+   A symbol is really of this type if SYMBOL_INLINED is true.  */
+
+struct inlined_symbol
+{
+  /* The base class.  */
+  struct symbol base;
+
+  /* The inlined symbol's symtab.  */
+  struct symtab *decl_file;
+
+  /* Line number of the inlined symbol.  */
+  int decl_line;
+};
+
 
 /* Each item represents a line-->pc (or the reverse) mapping.  This is
    somewhat more wasteful of space than one might wish, but since only
@@ -1436,6 +1451,9 @@  struct symtab_and_line
   /* If PROBE is not NULL, then this is the objfile in which the probe
      originated.  */
   struct objfile *objfile;
+
+  /* The symbol for which this SaL was created.  */
+  struct symbol *symbol;
 };
 
 extern void init_sal (struct symtab_and_line *sal);
@@ -1676,4 +1694,6 @@  void initialize_objfile_symbol (struct symbol *);
 
 struct template_symbol *allocate_template_symbol (struct objfile *);
 
+extern struct inlined_symbol *allocate_inlined_symbol (struct objfile *);
+
 #endif /* !defined(SYMTAB_H) */
diff --git a/gdb/testsuite/gdb.opt/inline-break.exp b/gdb/testsuite/gdb.opt/inline-break.exp
index 7be3a34..91766d4 100644
--- a/gdb/testsuite/gdb.opt/inline-break.exp
+++ b/gdb/testsuite/gdb.opt/inline-break.exp
@@ -24,6 +24,76 @@  if { [prepare_for_testing "failed to prepare" $testfile $srcfile \
     return -1
 }
 
+# Return a string that may be used to match the output of "info break NUM".
+#
+# Optional arguments:
+#
+# source    - the name of the source file
+# func      - the name of the function
+# disp      - the event disposition
+# enabled   - enable state
+# locs      - number of locations
+# line      - source line number (ignored without -source)
+# inlined   - add inlined list, argument is a list of regexps to match
+#             *per location* (e.g., [list "" "foo at $source:$line"] will yield
+#             no inline for first location, and "inlined in foo at ..." for
+#             the second location)
+
+proc break_info_1 {num args} {
+    global decimal
+
+    # Column delimiter
+    set c {[\t ]+}
+
+    # Row delimiter
+    set end {[\r\n \t]+}
+
+    # Table header
+    set header "[join [list Num Type Disp Enb Address What] ${c}]"
+
+    # Get/configure any optional parameters.
+    parse_args [list {source ""} {func ".*"} {disp "keep"} \
+		    {enabled "y"} {locs 1} [list line $decimal] \
+		    {inlined {}} {type "breakpoint"}]
+
+    if {$source != ""} {
+	set source "/$source:$line"
+    }
+
+    # Result starts with the standard header.
+    set result "$header${end}"
+
+    # Set up for multi-location breakpoint marker.
+    if {$locs == 1} {
+	set multi ".*"
+    } else {
+	set multi "<MULTIPLE>${end}"
+    }
+    append result "[join [list $num $type $disp $enabled $multi] $c]"
+
+    # Add location info.
+    for {set i 1} {$i <= $locs} {incr i} {
+	if {$locs > 1} {
+	    append result "[join [list $num.$i $enabled] $c].*"
+	}
+
+	#  Add function/source file info.
+	append result "in $func at .*$source${end}"
+
+	# Add inline, if requested.
+	if {$i <= [llength $inlined]} {
+	    set inlined_regexp [lindex $inlined [expr $i - 1]]
+	} else {
+	    set inlined_regexp ""
+	}
+	if {$inlined_regexp != ""} {
+	    append result ".*inlined in $inlined_regexp${end}"
+	}
+    }
+
+    return $result
+}
+
 #
 # func1 is a static inlined function that is called once.
 # The result should be a single-location breakpoint.
@@ -111,3 +181,34 @@  gdb_test "print func1" \
 #
 gdb_test "print func2" \
     "\\\$.* = {int \\(int\\)} .* <func2>"
+
+# Test that "info break" reports the location of the breakpoints "inside"
+# the inlined functions
+
+set results(1) [break_info_1 1 -source $srcfile -func "func1" \
+		    -inlined [list "main at .*"]]
+set results(2) [break_info_1 2 -locs 2 -source $srcfile -func "func2" \
+		    -inlined [list "" "main at .*"]]
+set results(3) [break_info_1 3 -source $srcfile -func "func3b" \
+		    -inlined [list "main at .*"]]
+set results(4) [break_info_1 4 -locs 2 -source $srcfile -func "func4b" \
+		    -inlined [list "func4a at .*" "main at .*"]]
+set results(5) [break_info_1 5 -locs 2 -source $srcfile -func "func5b" \
+		    -inlined [list "" ".*"]]
+set results(6) [break_info_1 6 -locs 3 -source $srcfile -func "func6b" \
+		    -inlined [list "" ".*"]]
+set results(7) [break_info_1 7 -locs 2 -source $srcfile -func "func7b" \
+		    -inlined [list ".*" ".*"]]
+set results(8) [break_info_1 8 -locs 3 -source $srcfile -func "func8b"\
+		    -inlined [list "" ".*" ".*"]]
+
+for {set i 1} {$i <= [llength [array names results]]} {incr i} {
+    send_log "Expecting: $results($i)\n"
+    gdb_test "info break $i" $results($i)
+}
+
+# A quick check that MI is outputting new call-site parameters.
+gdb_test "interpreter-exec mi -break-info 1" \
+    ".*,call-site-func=\"main\",call-site-file=\".*\",call-site-fullname=\".*\",call-site-line=\".*\".*"
+
+unset -nocomplain results