This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 1/2] Report call site for inlined functions


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(-)

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
-- 
2.1.0


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]