public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-02-05 11:37 [PATCH 0/2] Line table is_stmt support Andrew Burgess
@ 2020-02-05 11:37 ` Andrew Burgess
  2020-02-05 17:55   ` Bernd Edlinger
                     ` (2 more replies)
  2020-02-05 11:37 ` [PATCH 1/2] gdb/testsuite: Add is-stmt support to the DWARF compiler Andrew Burgess
  1 sibling, 3 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-02-05 11:37 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Andrew Burgess

This commit brings support for the DWARF line table is_stmt field to
GDB.  The is_stmt field is used by the compiler when a single source
line is split into multiple assembler instructions, especially if the
assembler instructions are interleaved with instruction from other
source lines.

The compiler will set the is_stmt flag false from some instructions
from the source lines, these instructions are not a good place to
insert a breakpoint in order to stop at the source line.
Instructions which are marked with the is_stmt flag true are a good
place to insert a breakpoint for that source line.

Currently GDB ignores all instructions for which is_stmt is false.
This is fine in a lot of cases, however, there are some cases where
this means the debug experience is not as good as it could be.

Consider stopping at a random instruction, currently this instruction
will be attributed to the last line table entry before this point for
which is_stmt was true - as these are the only line table entries that
GDB tracks.  This can easily be incorrect in code with even a low
level of optimisation.

With is_stmt tracking in place, when stopping at a random instruction
we now attribute the instruction back to the real source line, even
when is_stmt is false for that instruction in the line table.

When inserting breakpoints we still select line table entries for
which is_stmt is true, so the breakpoint placing behaviour should not
change.

When stepping though code (at the line level, not the instruction
level) we will still stop at instruction where is_stmt is true, I
think this is more likely to be the desired behaviour.

Instruction stepping is, of course, unchanged, stepping one
instruction at a time, but we should now report more accurate line
table information with each instruction step.

The original motivation for this work was a patch posted by Bernd
here:
  https://sourceware.org/ml/gdb-patches/2019-11/msg00792.html

As part of that thread it was suggested that many issues would be
resolved if GDB supported line table views, this isn't something I've
attempted in this patch, though reading the spec, it seems like this
would be a useful feature to support in GDB in the future.  The spec
is here:
  http://dwarfstd.org/ShowIssue.php?issue=170427.1

And Bernd gives a brief description of the benefits here:
  https://sourceware.org/ml/gdb-patches/2020-01/msg00147.html

With that all said, I think that there is benefit to having proper
is_stmt support regardless of whether we have views support, so I
think we should consider getting this (or something like this) in
first, and then building view support on top of this.

The gdb.cp/next-inline.exp test is based off a test proposed by Bernd
Edlinger in message:
  https://sourceware.org/ml/gdb-patches/2019-12/msg00842.html

gdb/ChangeLog:

	* buildsym-legacy.c (record_line): Pass extra parameter to
	record_line.
	* buildsym.c (buildsym_compunit::record_line): Take an extra
	parameter, reduce duplication in the line table, and record the
	is_stmt flag in the line table.
	* buildsym.h (buildsym_compunit::record_line): Add extra
	parameter.
	* disasm.c (do_mixed_source_and_assembly_deprecated): Ignore
	non-statement lines.
	* dwarf2read.c (dwarf_record_line_1): Add extra parameter, pass
	this to the symtab builder.
	(dwarf_finish_line): Pass extra parameter to dwarf_record_line_1.
	(lnp_state_machine::record_line): Pass a suitable is_stmt flag
	through to dwarf_record_line_1.
	* infrun.c (process_event_stop_test): When stepping, don't stop at
	a non-statement instruction, and only refresh the step info when
	we land in the middle of a line's range.
	* jit.c (jit_symtab_line_mapping_add_impl): Initialise is_stmt
	field.
	* record-btrace.c (btrace_find_line_range): Only record lines
	marked as is-statement.
	* stack.c (frame_show_address): Show the frame address if we are
	in a non-statement sal.
	* symmisc.c (dump_symtab_1): Print the is_stmt flag.
	(maintenance_print_one_line_table): Print a header for the is_stmt
	column, and include is_stmt information in the output.
	* symtab.c (find_pc_sect_line): Find lines marked as statements in
	preference to non-statements.
	(find_pcs_for_symtab_line): Prefer is-statement entries.
	(find_line_common): Likewise.
	* symtab.h (struct linetable_entry): Add is_stmt field.
	(struct symtab_and_line): Likewise.
	* xcoffread.c (arrange_linetable): Initialise is_stmt field when
	arranging the line table.

gdb/testsuite/ChangeLog:

	* gdb.cp/next-inline.cc: New file.
	* gdb.cp/next-inline.exp: New file.
	* gdb.cp/next-inline.h: New file.
	* gdb.dwarf2/dw2-is-stmt.c: New file.
	* gdb.dwarf2/dw2-is-stmt.exp: New file.
	* gdb.dwarf2/dw2-is-stmt-2.c: New file.
	* gdb.dwarf2/dw2-is-stmt-2.exp: New file.
	* gdb.dwarf2/dw2-ranges-base.exp: Update line table pattern.

Change-Id: Id81de4c7d7e83558abe050f37a5c5927c42967e3
---
 gdb/ChangeLog                                |  37 ++++
 gdb/buildsym-legacy.c                        |   4 +-
 gdb/buildsym.c                               |  14 +-
 gdb/buildsym.h                               |   3 +-
 gdb/disasm.c                                 |   6 +
 gdb/dwarf2read.c                             |  13 +-
 gdb/infrun.c                                 |  42 +++--
 gdb/jit.c                                    |   1 +
 gdb/record-btrace.c                          |  11 +-
 gdb/stack.c                                  |   3 +-
 gdb/symmisc.c                                |  10 +-
 gdb/symtab.c                                 |  25 ++-
 gdb/symtab.h                                 |  10 +
 gdb/testsuite/ChangeLog                      |  11 ++
 gdb/testsuite/gdb.cp/next-inline.cc          |  65 +++++++
 gdb/testsuite/gdb.cp/next-inline.exp         |  70 +++++++
 gdb/testsuite/gdb.cp/next-inline.h           |  38 ++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c     |  99 ++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp   | 265 ++++++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c       |  61 ++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp     | 267 +++++++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp |   6 +-
 gdb/xcoffread.c                              |   4 +
 23 files changed, 1036 insertions(+), 29 deletions(-)
 create mode 100644 gdb/testsuite/gdb.cp/next-inline.cc
 create mode 100644 gdb/testsuite/gdb.cp/next-inline.exp
 create mode 100644 gdb/testsuite/gdb.cp/next-inline.h
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp

diff --git a/gdb/buildsym-legacy.c b/gdb/buildsym-legacy.c
index bf19d2d90a7..d9c27ebc956 100644
--- a/gdb/buildsym-legacy.c
+++ b/gdb/buildsym-legacy.c
@@ -252,7 +252,9 @@ void
 record_line (struct subfile *subfile, int line, CORE_ADDR pc)
 {
   gdb_assert (buildsym_compunit != nullptr);
-  buildsym_compunit->record_line (subfile, line, pc);
+  /* Assume every line entry is a statement start, that is a good place to
+     put a breakpoint for that line number.  */
+  buildsym_compunit->record_line (subfile, line, pc, true);
 }
 
 /* Start a new symtab for a new source file in OBJFILE.  Called, for example,
diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 4965b552b32..7fd256fecce 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -666,7 +666,7 @@ buildsym_compunit::pop_subfile ()
 
 void
 buildsym_compunit::record_line (struct subfile *subfile, int line,
-				CORE_ADDR pc)
+				CORE_ADDR pc, bool is_stmt)
 {
   struct linetable_entry *e;
 
@@ -681,6 +681,17 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
       m_have_line_numbers = true;
     }
 
+  /* If we have a duplicate for the previous entry then ignore the new
+     entry, except, if the new entry is setting the is_stmt flag, then
+     ensure the previous entry respects the new setting.  */
+  e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
+  if (e->line == line && e->pc == pc)
+    {
+      if (is_stmt && !e->is_stmt)
+	e->is_stmt = 1;
+      return;
+    }
+
   if (subfile->line_vector->nitems + 1 >= subfile->line_vector_length)
     {
       subfile->line_vector_length *= 2;
@@ -716,6 +727,7 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
 
   e = subfile->line_vector->item + subfile->line_vector->nitems++;
   e->line = line;
+  e->is_stmt = is_stmt ? 1 : 0;
   e->pc = pc;
 }
 
diff --git a/gdb/buildsym.h b/gdb/buildsym.h
index c8e1bd0d5a7..c768a4c2dae 100644
--- a/gdb/buildsym.h
+++ b/gdb/buildsym.h
@@ -187,7 +187,8 @@ struct buildsym_compunit
 
   const char *pop_subfile ();
 
-  void record_line (struct subfile *subfile, int line, CORE_ADDR pc);
+  void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
+		    bool is_stmt);
 
   struct compunit_symtab *get_compunit_symtab ()
   {
diff --git a/gdb/disasm.c b/gdb/disasm.c
index e45c8400689..143ba2f59b9 100644
--- a/gdb/disasm.c
+++ b/gdb/disasm.c
@@ -376,6 +376,12 @@ do_mixed_source_and_assembly_deprecated
       if (le[i].line == le[i + 1].line && le[i].pc == le[i + 1].pc)
 	continue;		/* Ignore duplicates.  */
 
+      /* Ignore non-statement line table entries.  This means we print the
+	 source line at the place where GDB would insert a breakpoint for
+	 that line, which seems more intuitive.  */
+      if (le[i].is_stmt == 0)
+	continue;
+
       /* Skip any end-of-function markers.  */
       if (le[i].line == 0)
 	continue;
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index dafe01d94a0..cbee4abcb74 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -21222,7 +21222,7 @@ dwarf_record_line_p (struct dwarf2_cu *cu,
 
 static void
 dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
-		     unsigned int line, CORE_ADDR address,
+		     unsigned int line, CORE_ADDR address, bool is_stmt,
 		     struct dwarf2_cu *cu)
 {
   CORE_ADDR addr = gdbarch_addr_bits_remove (gdbarch, address);
@@ -21236,7 +21236,7 @@ dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
     }
 
   if (cu != nullptr)
-    cu->get_builder ()->record_line (subfile, line, addr);
+    cu->get_builder ()->record_line (subfile, line, addr, is_stmt);
 }
 
 /* Subroutine of dwarf_decode_lines_1 to simplify it.
@@ -21259,7 +21259,7 @@ dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
 			  paddress (gdbarch, address));
     }
 
-  dwarf_record_line_1 (gdbarch, subfile, 0, address, cu);
+  dwarf_record_line_1 (gdbarch, subfile, 0, address, true, cu);
 }
 
 void
@@ -21286,8 +21286,7 @@ lnp_state_machine::record_line (bool end_sequence)
   else if (m_op_index == 0 || end_sequence)
     {
       fe->included_p = 1;
-      if (m_record_lines_p
-	  && (producer_is_codewarrior (m_cu) || m_is_stmt || end_sequence))
+      if (m_record_lines_p)
 	{
 	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
 	      || end_sequence)
@@ -21298,6 +21297,8 @@ lnp_state_machine::record_line (bool end_sequence)
 
 	  if (!end_sequence)
 	    {
+	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
+
 	      if (dwarf_record_line_p (m_cu, m_line, m_last_line,
 				       m_line_has_non_zero_discriminator,
 				       m_last_subfile))
@@ -21305,7 +21306,7 @@ lnp_state_machine::record_line (bool end_sequence)
 		  buildsym_compunit *builder = m_cu->get_builder ();
 		  dwarf_record_line_1 (m_gdbarch,
 				       builder->get_current_subfile (),
-				       m_line, m_address,
+				       m_line, m_address, is_stmt,
 				       m_currently_recording_lines ? m_cu : nullptr);
 		}
 	      m_last_subfile = m_cu->get_builder ()->get_current_subfile ();
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 3e846f8e680..3919de81f90 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -7146,19 +7146,31 @@ process_event_stop_test (struct execution_control_state *ecs)
       return;
     }
 
+  bool refresh_step_info = true;
   if ((ecs->event_thread->suspend.stop_pc == stop_pc_sal.pc)
       && (ecs->event_thread->current_line != stop_pc_sal.line
  	  || ecs->event_thread->current_symtab != stop_pc_sal.symtab))
     {
-      /* We are at the start of a different line.  So stop.  Note that
-         we don't stop if we step into the middle of a different line.
-         That is said to make things like for (;;) statements work
-         better.  */
-      if (debug_infrun)
-	 fprintf_unfiltered (gdb_stdlog,
-			     "infrun: stepped to a different line\n");
-      end_stepping_range (ecs);
-      return;
+      if (stop_pc_sal.is_stmt)
+	{
+	  /* We are at the start of a different line.  So stop.  Note that
+	     we don't stop if we step into the middle of a different line.
+	     That is said to make things like for (;;) statements work
+	     better.  */
+	  if (debug_infrun)
+	    fprintf_unfiltered (gdb_stdlog,
+				"infrun: stepped to a different line\n");
+	  end_stepping_range (ecs);
+	  return;
+	}
+      else
+	{
+	  refresh_step_info = false;
+	  if (debug_infrun)
+	    fprintf_unfiltered (gdb_stdlog,
+				"infrun: stepped to a different line, but "
+				"it's not the start of a statement\n");
+	}
     }
 
   /* We aren't done stepping.
@@ -7166,12 +7178,20 @@ process_event_stop_test (struct execution_control_state *ecs)
      Optimize by setting the stepping range to the line.
      (We might not be in the original line, but if we entered a
      new line in mid-statement, we continue stepping.  This makes
-     things like for(;;) statements work better.)  */
+     things like for(;;) statements work better.)
+
+     If we entered a SAL that indicates a non-statement line table entry,
+     then we update the stepping range, but we don't update the step info,
+     which includes things like the line number we are stepping away from.
+     This means we will stop when we find a line table entry that is marked
+     as is-statement, even if it matches the non-statement one we just
+     stepped into.   */
 
   ecs->event_thread->control.step_range_start = stop_pc_sal.pc;
   ecs->event_thread->control.step_range_end = stop_pc_sal.end;
   ecs->event_thread->control.may_range_step = 1;
-  set_step_info (frame, stop_pc_sal);
+  if (refresh_step_info)
+    set_step_info (frame, stop_pc_sal);
 
   if (debug_infrun)
      fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
diff --git a/gdb/jit.c b/gdb/jit.c
index eeaab70bfe0..0d81951e4fd 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -578,6 +578,7 @@ jit_symtab_line_mapping_add_impl (struct gdb_symbol_callbacks *cb,
     {
       stab->linetable->item[i].pc = (CORE_ADDR) map[i].pc;
       stab->linetable->item[i].line = map[i].line;
+      stab->linetable->item[i].is_stmt = 1;
     }
 }
 
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index ef23a0b7af7..d3da8527c5c 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -718,7 +718,16 @@ btrace_find_line_range (CORE_ADDR pc)
   range = btrace_mk_line_range (symtab, 0, 0);
   for (i = 0; i < nlines - 1; i++)
     {
-      if ((lines[i].pc == pc) && (lines[i].line != 0))
+      /* The test of is_stmt here was added when the is_stmt field was
+	 introduced to the 'struct linetable_entry' structure.  This
+	 ensured that this loop maintained the same behaviour as before we
+	 introduced is_stmt.  That said, it might be that we would be
+	 better off not checking is_stmt here, this would lead to us
+	 possibly adding more line numbers to the range.  At the time this
+	 change was made I was unsure how to test this so chose to go with
+	 maintaining the existing experience.  */
+      if ((lines[i].pc == pc) && (lines[i].line != 0)
+	  && (lines[i].is_stmt == 1))
 	range = btrace_line_range_add (range, lines[i].line);
     }
 
diff --git a/gdb/stack.c b/gdb/stack.c
index af30405f29e..6ecb878c476 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -330,7 +330,7 @@ frame_show_address (struct frame_info *frame,
       return false;
     }
 
-  return get_frame_pc (frame) != sal.pc;
+  return get_frame_pc (frame) != sal.pc || !sal.is_stmt;
 }
 
 /* See frame.h.  */
@@ -1036,6 +1036,7 @@ print_frame_info (const frame_print_options &fp_opts,
   int location_print;
   struct ui_out *uiout = current_uiout;
 
+
   if (!current_uiout->is_mi_like_p ()
       && fp_opts.print_frame_info != print_frame_info_auto)
     {
diff --git a/gdb/symmisc.c b/gdb/symmisc.c
index a6a7e728c4a..3011bdecf72 100644
--- a/gdb/symmisc.c
+++ b/gdb/symmisc.c
@@ -305,6 +305,8 @@ dump_symtab_1 (struct symtab *symtab, struct ui_file *outfile)
 	{
 	  fprintf_filtered (outfile, " line %d at ", l->item[i].line);
 	  fputs_filtered (paddress (gdbarch, l->item[i].pc), outfile);
+	  if (l->item[i].is_stmt)
+	    fprintf_filtered (outfile, "\t(stmt)");
 	  fprintf_filtered (outfile, "\n");
 	}
     }
@@ -991,8 +993,8 @@ maintenance_print_one_line_table (struct symtab *symtab, void *data)
 
       /* Leave space for 6 digits of index and line number.  After that the
 	 tables will just not format as well.  */
-      printf_filtered (_("%-6s %6s %s\n"),
-		       _("INDEX"), _("LINE"), _("ADDRESS"));
+      printf_filtered (_("%-6s %6s %s %s\n"),
+		       _("INDEX"), _("LINE"), _("ADDRESS"), _("IS-STMT"));
 
       for (i = 0; i < linetable->nitems; ++i)
 	{
@@ -1004,7 +1006,9 @@ maintenance_print_one_line_table (struct symtab *symtab, void *data)
 	    printf_filtered ("%6d ", item->line);
 	  else
 	    printf_filtered ("%6s ", _("END"));
-	  printf_filtered ("%s\n", core_addr_to_string (item->pc));
+	  printf_filtered ("%s%s\n",
+			   core_addr_to_string (item->pc),
+			   (item->is_stmt ? " Y" : ""));
 	}
     }
 
diff --git a/gdb/symtab.c b/gdb/symtab.c
index f456f4d852d..d6a7271f636 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3244,6 +3244,23 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
 	  best = prev;
 	  best_symtab = iter_s;
 
+	  /* If during the binary search we land on a non-statement entry,
+	     scan backward through entries at the same address to see if
+	     there is an entry marked as is-statement.  In theory this
+	     duplication should have been removed from the line table
+	     during construction, this is just a double check.  If the line
+	     table has had the duplication removed then this should be
+	     pretty cheap.  */
+	  if (!best->is_stmt)
+	    {
+	      struct linetable_entry *tmp = best;
+	      while (tmp > first && (tmp - 1)->pc == tmp->pc
+		     && (tmp - 1)->line != 0 && !tmp->is_stmt)
+		--tmp;
+	      if (tmp->is_stmt)
+		best = tmp;
+	    }
+
 	  /* Discard BEST_END if it's before the PC of the current BEST.  */
 	  if (best_end <= best->pc)
 	    best_end = 0;
@@ -3274,6 +3291,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
     }
   else
     {
+      val.is_stmt = best->is_stmt;
       val.symtab = best_symtab;
       val.line = best->line;
       val.pc = best->pc;
@@ -3442,7 +3460,8 @@ find_pcs_for_symtab_line (struct symtab *symtab, int line,
 	{
 	  struct linetable_entry *item = &SYMTAB_LINETABLE (symtab)->item[idx];
 
-	  if (*best_item == NULL || item->line < (*best_item)->line)
+	  if (*best_item == NULL
+	      || (item->line < (*best_item)->line && item->is_stmt))
 	    *best_item = item;
 
 	  break;
@@ -3553,6 +3572,10 @@ find_line_common (struct linetable *l, int lineno,
     {
       struct linetable_entry *item = &(l->item[i]);
 
+      /* Ignore non-statements.  */
+      if (!item->is_stmt)
+	continue;
+
       if (item->line == lineno)
 	{
 	  /* Return the first (lowest address) entry which matches.  */
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 5fa067b5f48..771b5ec5bf7 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1277,7 +1277,13 @@ struct rust_vtable_symbol : public symbol
 
 struct linetable_entry
 {
+  /* The line number for this entry.  */
   int line;
+
+  /* True if this PC is a good location to place a breakpoint for LINE.  */
+  unsigned is_stmt : 1;
+
+  /* The address for this entry.  */
   CORE_ADDR pc;
 };
 
@@ -1853,6 +1859,10 @@ struct symtab_and_line
   bool explicit_pc = false;
   bool explicit_line = false;
 
+  /* If the line number information is valid, then this indicates if this
+     line table entry had the is-stmt flag set or not.  */
+  bool is_stmt = false;
+
   /* The probe associated with this symtab_and_line.  */
   probe *prob = NULL;
   /* If PROBE is not NULL, then this is the objfile in which the probe
diff --git a/gdb/testsuite/gdb.cp/next-inline.cc b/gdb/testsuite/gdb.cp/next-inline.cc
new file mode 100644
index 00000000000..24f5c6916b2
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/next-inline.cc
@@ -0,0 +1,65 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019-2020 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/>.  */
+
+#ifdef USE_NEXT_INLINE_H
+
+#include "next-inline.h"
+
+#else	/* USE_NEXT_INLINE_H */
+
+/* The code below must remain identical to the code in next-inline.h.  */
+
+#include <stdlib.h>
+
+struct tree
+{
+  volatile int x;
+  volatile int z;
+};
+
+#define TREE_TYPE(NODE) (*tree_check (NODE, 0))
+
+inline tree *
+tree_check (tree *t, int i)
+{
+  if (t->x != i)
+    abort();
+  tree *x = t;
+  return x;
+}
+
+#endif	/* USE_NEXT_INLINE_H */
+
+int __attribute__((noinline, noclone))
+get_alias_set (tree *t)
+{
+  if (t != NULL
+      && TREE_TYPE (t).z != 1
+      && TREE_TYPE (t).z != 2
+      && TREE_TYPE (t).z != 3)
+    return 0;
+  return 1;
+}
+
+tree xx;
+
+int
+main()
+{
+  get_alias_set (&xx);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.cp/next-inline.exp b/gdb/testsuite/gdb.cp/next-inline.exp
new file mode 100644
index 00000000000..0b2b22d8894
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/next-inline.exp
@@ -0,0 +1,70 @@
+# Copyright 2019-2020 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/>.
+
+standard_testfile .cc
+
+# Compile the test source with USE_NEXT_INLINE_H defined (when
+# use_header is true), or not defined.
+proc do_test { use_header } {
+    global srcfile testfile
+
+    set options {c++ debug nowarnings optimize=-O2}
+    if { $use_header } {
+	lappend options additional_flags=-DUSE_NEXT_INLINE_H
+	set executable "$testfile-with-header"
+    } else {
+	set executable "$testfile-no-header"
+    }
+
+    if { [prepare_for_testing "failed to prepare" $executable \
+	      $srcfile $options] } {
+	return -1
+    }
+
+    if ![runto_main] {
+	fail "can't run to main"
+	return
+    }
+
+    gdb_test "bt" "\\s*\\#0\\s+main.*" "in main"
+    gdb_test "step" ".*" "step into get_alias_set"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 1"
+    # It's possible that this first failure (when not using a header
+    # file) is GCC's fault, though the remaining failures would best
+    # be fixed by adding location views support (though it could be
+    # that some easier heuristic could be figured out).  Still, it is
+    # not certain that the first failure wouldn't also be fixed by
+    # having location view support, so for now it is tagged as such.
+    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
+    gdb_test "next" ".*TREE_TYPE.*" "next step 1"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 2"
+    gdb_test "next" ".*TREE_TYPE.*" "next step 2"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 3"
+    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
+    gdb_test "next" ".*TREE_TYPE.*" "next step 3"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 4"
+    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
+    gdb_test "next" "return 0.*" "next step 4"
+    gdb_test "bt" \
+	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
+	"not in inline 5"
+}
+
+do_test 0
+do_test 1
diff --git a/gdb/testsuite/gdb.cp/next-inline.h b/gdb/testsuite/gdb.cp/next-inline.h
new file mode 100644
index 00000000000..f6a3991ae7d
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/next-inline.h
@@ -0,0 +1,38 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019-2020 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/>.  */
+
+/* The code below must remain identical to the block of code in
+   next-inline.cc.  */
+
+#include <stdlib.h>
+
+struct tree
+{
+  volatile int x;
+  volatile int z;
+};
+
+#define TREE_TYPE(NODE) (*tree_check (NODE, 0))
+
+inline tree *
+tree_check (tree *t, int i)
+{
+  if (t->x != i)
+    abort();
+  tree *x = t;
+  return x;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c
new file mode 100644
index 00000000000..be3fa458cf0
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c
@@ -0,0 +1,99 @@
+/* Copyright 2020 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/>.  */
+
+/* This tests GDB's handling of the DWARF is-stmt field in the line table.
+
+   This field is used when many addresses all represent the same source
+   line.  The address(es) at which it is suitable to place a breakpoint for
+   a line are marked with is-stmt true, while address(es) that are not good
+   places to place a breakpoint are marked as is-stmt false.
+
+   In order to build a reproducible test and exercise GDB's is-stmt
+   support, we will be generating our own DWARF.  The test will contain a
+   series of C source lines, ensuring that we get a series of assembler
+   instructions.  Each C source line will be given an assembler label,
+   which we use to generate a fake line table.
+
+   In this fake line table each assembler block is claimed to represent a
+   single C source line, however, we will toggle the is-stmt flag.  We can
+   then debug this with GDB and test the handling of is-stmt.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N)						\
+  do							\
+    {							\
+      asm ("line_label_" #N ": .globl line_label_" #N); \
+      var = (N);					\
+      bar = ((N) + 1);					\
+    }							\
+  while (0)
+
+volatile int var;
+volatile int bar;
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+
+  /* main start */
+
+  /* Line 1.  */
+  /* Line 2.  */
+  /* Line 3.  */
+  /* Line 4.  */
+  /* Line 5.  */
+
+  LL (0);
+  LL (1);
+  LL (2);
+  LL (3);
+  LL (4);
+  LL (5);
+  LL (6);
+  LL (7);
+  LL (8);
+  LL (9);
+  LL (10);
+  LL (11);
+  LL (12);
+  LL (13);
+  LL (14);
+  LL (15);
+  LL (16);
+  return 0;				/* main end */
+}
+
+#if 0
+
+PROLOGUE*
+1: L1
+2: L1*
+3: L2
+4: L1
+L3
+L4
+L4*
+L2*
+L2
+L3
+L5
+L5*
+L3
+L4
+L5
+RETURN
+
+#endif
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
new file mode 100644
index 00000000000..860b13140c6
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
@@ -0,0 +1,265 @@
+# Copyright 2020 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/>.
+
+# This test shows the importance of not corrupting the order of line
+# table information.  When multiple lines are given for the same
+# address the compiler usually lists these in the order in which we
+# would expect to encounter them.  When stepping through nested inline
+# frames the last line given for an address is assumed by GDB to be
+# the most inner frame, and this is what GDB displays.
+#
+# If we corrupt the order of the line table entries then GDB will
+# display the wrong line as being the inner most frame.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-is-stmt-2.c dw2-is-stmt-2.S
+
+# Extract the start, length, and end for function called NAME and
+# create suitable variables in the callers scope.
+proc get_func_info { name } {
+    global srcdir subdir srcfile
+
+    upvar 1 "${name}_start" func_start
+    upvar 1 "${name}_len" func_len
+    upvar 1 "${name}_end" func_end
+
+    lassign [function_range ${name} [list ${srcdir}/${subdir}/$srcfile]] \
+	func_start func_len
+    set func_end "$func_start + $func_len"
+}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile
+    declare_labels lines_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {language @DW_LANG_C}
+	    {name dw2-is-stmt.c}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {}
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile" 1
+
+	program {
+	    {DW_LNE_set_address main}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "main prologue"] - 1]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_0}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "main start"] \
+		      - [gdb_get_line_number "main prologue"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 1"] \
+		      - [gdb_get_line_number "main start"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 2"] \
+		      - [gdb_get_line_number "Line 1"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 1"] \
+		      - [gdb_get_line_number "Line 2"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 3"] \
+		      - [gdb_get_line_number "Line 1"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 4"] \
+		      - [gdb_get_line_number "Line 3"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_8}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 2"] \
+		      - [gdb_get_line_number "Line 4"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_9}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_10}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 3"] \
+		      - [gdb_get_line_number "Line 2"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_11}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 5"] \
+		      - [gdb_get_line_number "Line 3"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_12}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_13}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 3"] \
+		      - [gdb_get_line_number "Line 5"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_14}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 4"] \
+		      - [gdb_get_line_number "Line 3"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_15}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 5"] \
+		      - [gdb_get_line_number "Line 4"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_16}
+	    {DW_LNS_advance_line \
+ 		 [expr [gdb_get_line_number "main end"] \
+		      - [gdb_get_line_number "Line 5"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address ${main_end}}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Check stepping through the out of order lines gives the experience
+# we expect; lines in the correct order, and stop at the correct
+# labels.Q
+set locs { { "Line 1." "line_label_2" } \
+	       { "Line 4." "line_label_7" } \
+	       { "Line 2." "line_label_8" } \
+	       { "Line 5." "line_label_12" } \
+	       { "Line 3." "line_label_13" } }
+foreach entry $locs {
+    set pattern [lindex $entry 0]
+    set label  [lindex $entry 1]
+
+    set linum [gdb_get_line_number "$pattern"]
+    gdb_test "step" "\r\n$linum\[ \t\]+/\\* $pattern  \\*/" \
+	"step to $pattern"
+
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+	       "read \$pc at $pattern"]
+    set val [get_hexadecimal_valueof "&${label}" "INVALID"]
+    gdb_assert { $pc == $val } \
+	"check pc at $pattern"
+}
+
+# Now restart the test, and this time, single instruction step
+# through.  This is checking that the is-stmt marked lines are
+# displayed differently (without addresses) to addresses that are
+# mid-way through a line, or not marked as is-stmt.
+clean_restart $binfile
+runto_main
+
+foreach entry $locs {
+    set pattern [lindex $entry 0]
+    set label  [lindex $entry 1]
+
+    with_test_prefix "stepi to $label" {
+	# If can take many instruction steps to get to the next label.
+	set i 0
+	set pc [get_hexadecimal_valueof "\$pc" "NO-PC" ]
+	set val [get_hexadecimal_valueof "&${label}" "INVALID"]
+	while { $pc < $val } {
+	    incr i
+	    set line_changed -1
+	    gdb_test_multiple "stepi" "stepi $i" {
+		-re "\r\n$hex\[ \t\]+$decimal\[^\r\n\]+\r\n$gdb_prompt" {
+		    set line_changed 0
+		}
+		-re "\r\n$decimal\[ \t\]+/\\* $pattern  \\*/\r\n$gdb_prompt" {
+		    set line_changed 1
+		}
+	    }
+	    gdb_assert { $line_changed != -1 } \
+		"ensure we saw a valid pattern, $i"
+	    set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+			"get pc inside stepi loop, $i"]
+	    if { $pc == $val } {
+		gdb_assert { $line_changed } \
+		    "line must change at $label"
+	    } else {
+		gdb_assert { !$line_changed } "same line $i"
+	    }
+	}
+    }
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c
new file mode 100644
index 00000000000..7e70995d80f
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c
@@ -0,0 +1,61 @@
+/* Copyright 2020 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/>.  */
+
+/* This tests GDB's handling of the DWARF is-stmt field in the line table.
+
+   This field is used when many addresses all represent the same source
+   line.  The address(es) at which it is suitable to place a breakpoint for
+   a line are marked with is-stmt true, while address(es) that are not good
+   places to place a breakpoint are marked as is-stmt false.
+
+   In order to build a reproducible test and exercise GDB's is-stmt
+   support, we will be generating our own DWARF.  The test will contain a
+   series of C source lines, ensuring that we get a series of assembler
+   instructions.  Each C source line will be given an assembler label,
+   which we use to generate a fake line table.
+
+   In this fake line table each assembler block is claimed to represent a
+   single C source line, however, we will toggle the is-stmt flag.  We can
+   then debug this with GDB and test the handling of is-stmt.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+volatile int var;
+volatile int bar;
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+  LL (1);
+  var = 99;				/* main, set var to 99 */
+  bar = 99;
+
+  LL (2);
+  var = 0;				/* main, set var to 0 */
+  bar = 0;
+
+  LL (3);
+  var = 1;				/* main, set var to 1 */
+  bar = 1;
+
+  LL (4);
+  var = 2;				/* main, set var to 2 */
+  bar = 2;
+
+  LL (5);
+  return 0;				/* main end */
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp
new file mode 100644
index 00000000000..6e5f3e6bf07
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp
@@ -0,0 +1,267 @@
+# Copyright 2020 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/>.
+
+# This test shows the importance of not corrupting the order of line
+# table information.  When multiple lines are given for the same
+# address the compiler usually lists these in the order in which we
+# would expect to encounter them.  When stepping through nested inline
+# frames the last line given for an address is assumed by GDB to be
+# the most inner frame, and this is what GDB displays.
+#
+# If we corrupt the order of the line table entries then GDB will
+# display the wrong line as being the inner most frame.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-is-stmt.c dw2-is-stmt.S
+
+# Extract the start, length, and end for function called NAME and
+# create suitable variables in the callers scope.
+proc get_func_info { name } {
+    global srcdir subdir srcfile
+
+    upvar 1 "${name}_start" func_start
+    upvar 1 "${name}_len" func_len
+    upvar 1 "${name}_end" func_end
+
+    lassign [function_range ${name} [list ${srcdir}/${subdir}/$srcfile]] \
+	func_start func_len
+    set func_end "$func_start + $func_len"
+}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile
+    declare_labels lines_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {language @DW_LANG_C}
+	    {name dw2-is-stmt.c}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {}
+	}
+    }
+
+    lines {version 2 default_is_stmt 0} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile" 1
+
+	program {
+	    {DW_LNE_set_address main}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "main prologue"] - 1]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "main, set var to 99"] \
+		      - [gdb_get_line_number "main prologue"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line \
+ 		 [expr [gdb_get_line_number "main, set var to 0"] \
+		      - [gdb_get_line_number "main, set var to 99"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line \
+ 		 [expr [gdb_get_line_number "main end"] \
+		      - [gdb_get_line_number "main, set var to 0"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address ${main_end}}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# First, break by address at a location we know is marked as not a
+# statement.  GDB should still correctly report the file and line
+# number.
+gdb_breakpoint "*line_label_2"
+gdb_continue_to_breakpoint "*line_label_2"
+
+# Now step by instruction.  We should skip over the is-stmt location
+# for this line, and land on the next source line.
+gdb_test "step" "/\\* main end \\*/" \
+    "step to end from line_label_2"
+
+# Restart the test.  This time, stop at a location we know is marked
+# as a statement.
+clean_restart ${binfile}
+runto_main
+
+gdb_breakpoint "*line_label_3"
+gdb_continue_to_breakpoint "*line_label_3"
+
+# Now step by instruction.  As you would expect we should leave this
+# line and stop at the next source line.
+gdb_test "step" "/\\* main end \\*/" \
+    "step to end from line_label_3"
+
+# Restart the test, this time, step through line by line, ensure we
+# only stop at the places where is-stmt is true.
+clean_restart ${binfile}
+runto_main
+
+# Get the values of the labels where we expect to stop.
+set ll1 [get_hexadecimal_valueof "&line_label_1" "INVALID"]
+set ll2 [get_hexadecimal_valueof "&line_label_2" "INVALID"]
+set ll3 [get_hexadecimal_valueof "&line_label_3" "INVALID"]
+set ll5 [get_hexadecimal_valueof "&line_label_5" "INVALID"]
+
+# The first stop should be at line_label_1
+with_test_prefix "check we're at line_label_1" {
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC"]
+    gdb_assert { $ll1 == $pc } "check initial \$pc is line_label_1"
+}
+
+# Now step, this should take us to line_label_3 which is the next
+# location marked as is-stmt.
+with_test_prefix "step to line_label_3" {
+    gdb_test "step" "/\\* main, set var to 0 \\*/"
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC"]
+    gdb_assert { $ll3 == $pc } "check initial \$pc is line_label_3"
+}
+
+# A final step should take us to line_label_5.
+with_test_prefix "step to line_label_5" {
+    gdb_test "step" "/\\* main end \\*/"
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC"]
+    gdb_assert { $ll5 == $pc } "check initial \$pc"
+}
+
+# Now restart the test, and place a breakpoint by line number.  GDB
+# should select the location that is marked as is-stmt.
+clean_restart ${binfile}
+runto_main
+set linum [gdb_get_line_number "main, set var to 0"]
+gdb_breakpoint "$srcfile:$linum"
+gdb_continue_to_breakpoint "Breakpoint on line, set var to 0"
+set pc [get_hexadecimal_valueof "\$pc" "NO-PC"]
+gdb_assert { $ll3 == $pc } "check initial \$pc"
+
+# Restart the test again, this time we will test stepping by
+# instruction.
+clean_restart ${binfile}
+runto_main
+
+# We will be at line_label_1 at this point - we already tested this
+# above.  Now single instruction step forward until we get to
+# line_label_2.  Every instruction before line_label_2 should be
+# attributed to the 'var = 99' line.  For most targets there will only
+# be a single instruction between line_label_1 and line_label_2, but
+# we allow for up to 25 (just a random number).
+
+set $i 0
+set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+	   "get pc before stepi loop at line_label_1"]
+while { $pc < $ll2 } {
+    incr i
+    set line_changed -1
+    gdb_test_multiple "stepi" "stepi until line_label_2, $i" {
+	-re "main, set var to 99.*$gdb_prompt" {
+	    set line_changed 0
+	}
+	-re "main, set var to 0.*$gdb_prompt " {
+	    set line_changed 1
+	}
+    }
+    gdb_assert { $line_changed != -1 } \
+	"ensure we saw a valid line pattern, $i"
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+		"get pc inside stepi loop from line_label_1, $i"]
+    if { $ll2 == $pc } {
+	gdb_assert { $line_changed } \
+	    "line must change at line_label_2"
+    } else {
+	gdb_assert { !$line_changed } \
+	    "line should not change until line_label_2, $i"
+    }
+}
+
+# Now single instruction step forward until GDB reports a new source
+# line, at which point we should be at line_label_5.
+
+set $i 0
+set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+	   "get pc before stepi loop at line_label_2"]
+while { $pc < $ll5 } {
+    incr i
+    set line_changed -1
+    gdb_test_multiple "stepi" "stepi until line_label_5, $i" {
+	-re "main, set var to 0.*$gdb_prompt" {
+	    set line_changed 0
+	}
+	-re "main end.*$gdb_prompt " {
+	    set line_changed 1
+	}
+    }
+    gdb_assert { $line_changed != -1 } \
+	"ensure we saw a valid line pattern, $i"
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+		"get pc inside stepi loop from line_label_2, $i"]
+    if { $ll5 == $pc } {
+	gdb_assert { $line_changed } \
+	    "line must change at line_label_5"
+    } else {
+	gdb_assert { !$line_changed } \
+	    "line should not change until line_label_5, $i"
+    }
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
index 33177336f58..7f7301502ab 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
@@ -146,10 +146,10 @@ gdb_test "info line frame3" \
 set end_seq_count 0
 gdb_test_multiple "maint info line-table" \
     "count END markers in line table" {
-	-re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\r\n" {
+	-re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\(\[ \t\]+Y\)?\r\n" {
 	    exp_continue
 	}
-	-re "^$decimal\[ \t\]+END\[ \t\]+$hex\r\n" {
+	-re "^$decimal\[ \t\]+END\[ \t\]+$hex\(\[ \t\]+Y\)?\r\n" {
 	    incr end_seq_count
 	    exp_continue
 	}
@@ -159,7 +159,7 @@ gdb_test_multiple "maint info line-table" \
 	-re ".*linetable: \\(\\(struct linetable \\*\\) 0x0\\):\r\nNo line table.\r\n" {
 	    exp_continue
 	}
-	-re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+ADDRESS\r\n" {
+	-re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+ADDRESS\[ \t\]+IS-STMT\r\n" {
 	    exp_continue
 	}
     }
diff --git a/gdb/xcoffread.c b/gdb/xcoffread.c
index b7da3f944c7..735f8b08825 100644
--- a/gdb/xcoffread.c
+++ b/gdb/xcoffread.c
@@ -432,6 +432,9 @@ arrange_linetable (struct linetable *oldLineTb)
 
   for (function_count = 0, ii = 0; ii < oldLineTb->nitems; ++ii)
     {
+      if (oldLineTb->item[ii].is_stmt == 0)
+	continue;
+
       if (oldLineTb->item[ii].line == 0)
 	{			/* Function entry found.  */
 	  if (function_count >= fentry_size)
@@ -442,6 +445,7 @@ arrange_linetable (struct linetable *oldLineTb)
 			  fentry_size * sizeof (struct linetable_entry));
 	    }
 	  fentry[function_count].line = ii;
+	  fentry[function_count].is_stmt = 1;
 	  fentry[function_count].pc = oldLineTb->item[ii].pc;
 	  ++function_count;
 
-- 
2.14.5

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH 0/2] Line table is_stmt support
@ 2020-02-05 11:37 Andrew Burgess
  2020-02-05 11:37 ` [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
  2020-02-05 11:37 ` [PATCH 1/2] gdb/testsuite: Add is-stmt support to the DWARF compiler Andrew Burgess
  0 siblings, 2 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-02-05 11:37 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Andrew Burgess

This series adds support for the DWARF line table is_stmt property.
Patch #2 includes a fuller description of the background to this
series (patch #1 is a minor setup patch), but this was previously
discussed on the list as part of a different patch series, but I
wanted to start a new thread so this got some visibility.

All feedback welcome.

thanks,
Andrew

--

Andrew Burgess (2):
  gdb/testsuite: Add is-stmt support to the DWARF compiler
  gdb: Add support for tracking the DWARF line table is-stmt field

 gdb/ChangeLog                                |  37 ++++
 gdb/buildsym-legacy.c                        |   4 +-
 gdb/buildsym.c                               |  14 +-
 gdb/buildsym.h                               |   3 +-
 gdb/disasm.c                                 |   6 +
 gdb/dwarf2read.c                             |  13 +-
 gdb/infrun.c                                 |  42 +++--
 gdb/jit.c                                    |   1 +
 gdb/record-btrace.c                          |  11 +-
 gdb/stack.c                                  |   3 +-
 gdb/symmisc.c                                |  10 +-
 gdb/symtab.c                                 |  25 ++-
 gdb/symtab.h                                 |  10 +
 gdb/testsuite/ChangeLog                      |  16 ++
 gdb/testsuite/gdb.cp/next-inline.cc          |  65 +++++++
 gdb/testsuite/gdb.cp/next-inline.exp         |  70 +++++++
 gdb/testsuite/gdb.cp/next-inline.h           |  38 ++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c     |  99 ++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp   | 265 ++++++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c       |  61 ++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp     | 267 +++++++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp |   6 +-
 gdb/testsuite/lib/dwarf.exp                  |   8 +-
 gdb/xcoffread.c                              |   4 +
 24 files changed, 1048 insertions(+), 30 deletions(-)
 create mode 100644 gdb/testsuite/gdb.cp/next-inline.cc
 create mode 100644 gdb/testsuite/gdb.cp/next-inline.exp
 create mode 100644 gdb/testsuite/gdb.cp/next-inline.h
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp

-- 
2.14.5

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH 1/2] gdb/testsuite: Add is-stmt support to the DWARF compiler
  2020-02-05 11:37 [PATCH 0/2] Line table is_stmt support Andrew Burgess
  2020-02-05 11:37 ` [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
@ 2020-02-05 11:37 ` Andrew Burgess
  1 sibling, 0 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-02-05 11:37 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Andrew Burgess

This commit adds the ability to set and toggle the DWARF line table
is-stmt flag.

A DWARF line table can now be created with the attribute
'default_is_stmt' like this:

  lines {version 2 default_is_stmt 0} label {
    ...
  }

If 'default_is_stmt' is not specified then the current default is 1,
which matches the existing behaviour.

Inside the DWARF line table program you can now make use of
{DW_LNS_negate_stmt} to toggle the is-stmt flag, for example this
meaningless program:

  lines {version 2 default_is_stmt 0} label {
    include_dir "some_directory"
    file_name "some_filename" 1

    program {
      {DW_LNS_negate_stmt}
      {DW_LNE_end_sequence}
    }
  }

This new functionality will be used in a later commit.

gdb/testsuite/ChangeLog:

	* lib/dwarf.exp (Dwarf::lines) Add support for modifying the
	is-stmt flag in the line table.

Change-Id: Ia3f61d523826382dd2333f65b9aae368ad29c4a5
---
 gdb/testsuite/ChangeLog     | 5 +++++
 gdb/testsuite/lib/dwarf.exp | 8 +++++++-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index 6c6ffbe7c2f..417b22d2345 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -1311,12 +1311,14 @@ namespace eval Dwarf {
 	set _unit_addr_size default
 	set _line_saw_program 0
 	set _line_saw_file 0
+	set _default_is_stmt 1
 
 	foreach { name value } $options {
 	    switch -exact -- $name {
 		is_64 { set is_64 $value }
 		version { set _unit_version $value }
 		addr_size { set _unit_addr_size $value }
+		default_is_stmt { set _default_is_stmt $value }
 		default { error "unknown option $name" }
 	    }
 	}
@@ -1363,7 +1365,7 @@ namespace eval Dwarf {
 	define_label $header_len_label
 
 	_op .byte 1 "minimum_instruction_length"
-	_op .byte 1 "default_is_stmt"
+	_op .byte $_default_is_stmt "default_is_stmt"
 	_op .byte 1 "line_base"
 	_op .byte 1 "line_range"
 	_op .byte 10 "opcode_base"
@@ -1438,6 +1440,10 @@ namespace eval Dwarf {
 		_op .byte 1
 	    }
 
+	    proc DW_LNS_negate_stmt {} {
+		_op .byte 6
+	    }
+
 	    proc DW_LNS_advance_pc {offset} {
 		_op .byte 2
 		_op .uleb128 ${offset}
-- 
2.14.5

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-02-05 11:37 ` [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
@ 2020-02-05 17:55   ` Bernd Edlinger
  2020-02-10 18:30     ` Bernd Edlinger
  2020-02-11 13:57     ` Andrew Burgess
  2020-02-06  9:01   ` Luis Machado
  2020-02-09 21:07   ` [PATCH] Fix range end handling of inlined subroutines Bernd Edlinger
  2 siblings, 2 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-02-05 17:55 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 2/5/20 12:37 PM, Andrew Burgess wrote:
> This commit brings support for the DWARF line table is_stmt field to
> GDB.  The is_stmt field is used by the compiler when a single source
> line is split into multiple assembler instructions, especially if the
> assembler instructions are interleaved with instruction from other
> source lines.
> 
> The compiler will set the is_stmt flag false from some instructions
> from the source lines, these instructions are not a good place to
> insert a breakpoint in order to stop at the source line.
> Instructions which are marked with the is_stmt flag true are a good
> place to insert a breakpoint for that source line.
> 
> Currently GDB ignores all instructions for which is_stmt is false.
> This is fine in a lot of cases, however, there are some cases where
> this means the debug experience is not as good as it could be.
> 
> Consider stopping at a random instruction, currently this instruction
> will be attributed to the last line table entry before this point for
> which is_stmt was true - as these are the only line table entries that
> GDB tracks.  This can easily be incorrect in code with even a low
> level of optimisation.
> 
> With is_stmt tracking in place, when stopping at a random instruction
> we now attribute the instruction back to the real source line, even
> when is_stmt is false for that instruction in the line table.
> 
> When inserting breakpoints we still select line table entries for
> which is_stmt is true, so the breakpoint placing behaviour should not
> change.
> 
> When stepping though code (at the line level, not the instruction
> level) we will still stop at instruction where is_stmt is true, I
> think this is more likely to be the desired behaviour.
> 
> Instruction stepping is, of course, unchanged, stepping one
> instruction at a time, but we should now report more accurate line
> table information with each instruction step.
> 
> The original motivation for this work was a patch posted by Bernd
> here:
>   https://sourceware.org/ml/gdb-patches/2019-11/msg00792.html
> 
> As part of that thread it was suggested that many issues would be
> resolved if GDB supported line table views, this isn't something I've
> attempted in this patch, though reading the spec, it seems like this
> would be a useful feature to support in GDB in the future.  The spec
> is here:
>   http://dwarfstd.org/ShowIssue.php?issue=170427.1
> 
> And Bernd gives a brief description of the benefits here:
>   https://sourceware.org/ml/gdb-patches/2020-01/msg00147.html
> 
> With that all said, I think that there is benefit to having proper
> is_stmt support regardless of whether we have views support, so I
> think we should consider getting this (or something like this) in
> first, and then building view support on top of this.
> 
> The gdb.cp/next-inline.exp test is based off a test proposed by Bernd
> Edlinger in message:
>   https://sourceware.org/ml/gdb-patches/2019-12/msg00842.html
> 

Thanks Andrew, I played a bit with debugging the gcc internals,
and I think the debug-experience is good, the problem with step
over are completely gone, also in the original context.

Just a few comments on the patch follow below.

> gdb/ChangeLog:
> 
> 	* buildsym-legacy.c (record_line): Pass extra parameter to
> 	record_line.
> 	* buildsym.c (buildsym_compunit::record_line): Take an extra
> 	parameter, reduce duplication in the line table, and record the
> 	is_stmt flag in the line table.
> 	* buildsym.h (buildsym_compunit::record_line): Add extra
> 	parameter.
> 	* disasm.c (do_mixed_source_and_assembly_deprecated): Ignore
> 	non-statement lines.
> 	* dwarf2read.c (dwarf_record_line_1): Add extra parameter, pass
> 	this to the symtab builder.
> 	(dwarf_finish_line): Pass extra parameter to dwarf_record_line_1.
> 	(lnp_state_machine::record_line): Pass a suitable is_stmt flag
> 	through to dwarf_record_line_1.
> 	* infrun.c (process_event_stop_test): When stepping, don't stop at
> 	a non-statement instruction, and only refresh the step info when
> 	we land in the middle of a line's range.

did you mean, when we don't land in the middle of a line ?

> --- a/gdb/stack.c
> +++ b/gdb/stack.c
> @@ -330,7 +330,7 @@ frame_show_address (struct frame_info *frame,
>        return false;
>      }
>  
> -  return get_frame_pc (frame) != sal.pc;
> +  return get_frame_pc (frame) != sal.pc || !sal.is_stmt;
>  }
>  
>  /* See frame.h.  */
> @@ -1036,6 +1036,7 @@ print_frame_info (const frame_print_options &fp_opts,
>    int location_print;
>    struct ui_out *uiout = current_uiout;
>  
> +
>    if (!current_uiout->is_mi_like_p ()
>        && fp_opts.print_frame_info != print_frame_info_auto)
>      {

Is this white space change intentional?




Thanks
Bernd.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-02-05 11:37 ` [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
  2020-02-05 17:55   ` Bernd Edlinger
@ 2020-02-06  9:01   ` Luis Machado
  2020-02-11 15:39     ` Andrew Burgess
  2020-02-09 21:07   ` [PATCH] Fix range end handling of inlined subroutines Bernd Edlinger
  2 siblings, 1 reply; 79+ messages in thread
From: Luis Machado @ 2020-02-06  9:01 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Bernd Edlinger

Hi,

I still need to check the patch itself, but i had a question about one 
particular paragraph...

On 2/5/20 8:37 AM, Andrew Burgess wrote:
> This commit brings support for the DWARF line table is_stmt field to
> GDB.  The is_stmt field is used by the compiler when a single source
> line is split into multiple assembler instructions, especially if the
> assembler instructions are interleaved with instruction from other
> source lines.
> 
> The compiler will set the is_stmt flag false from some instructions
> from the source lines, these instructions are not a good place to
> insert a breakpoint in order to stop at the source line.
> Instructions which are marked with the is_stmt flag true are a good
> place to insert a breakpoint for that source line.
> 
> Currently GDB ignores all instructions for which is_stmt is false.
> This is fine in a lot of cases, however, there are some cases where
> this means the debug experience is not as good as it could be.
> 
> Consider stopping at a random instruction, currently this instruction
> will be attributed to the last line table entry before this point for
> which is_stmt was true - as these are the only line table entries that
> GDB tracks.  This can easily be incorrect in code with even a low
> level of optimisation.
> 
> With is_stmt tracking in place, when stopping at a random instruction
> we now attribute the instruction back to the real source line, even
> when is_stmt is false for that instruction in the line table.
> 
> When inserting breakpoints we still select line table entries for
> which is_stmt is true, so the breakpoint placing behaviour should not
> change.
> 
> When stepping though code (at the line level, not the instruction
> level) we will still stop at instruction where is_stmt is true, I
> think this is more likely to be the desired behaviour.

Considering a block of continuous instructions that map to the same 
source line, will line-stepping stop at each one of these instructions 
if is_stmt is true? As opposed to stepping over the the whole block 
until we see a line change?

I'm wondering if this would help this bug 
(https://sourceware.org/bugzilla/show_bug.cgi?id=21221) in any way.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH] Fix range end handling of inlined subroutines
  2020-02-05 11:37 ` [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
  2020-02-05 17:55   ` Bernd Edlinger
  2020-02-06  9:01   ` Luis Machado
@ 2020-02-09 21:07   ` Bernd Edlinger
  2020-02-10 21:48     ` Andrew Burgess
  2020-02-22  6:39     ` [PATCHv2] " Bernd Edlinger
  2 siblings, 2 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-02-09 21:07 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1071 bytes --]

Hi,

this is based on Andrew's patch here:

https://sourceware.org/ml/gdb-patches/2020-02/msg00092.html

This and adds a heuristic to fix the case where caller
and callee reside in the same subfile, it uses
the recorded is-stmt bits and locates end of
range infos, including the previously ignored empty
range, and adjusting is-stmt info at this
same pc, when the last item is not-is-stmt, the
previous line table entries are dubious and
we reset the is-stmt bit there.
This fixes the new test case in Andrew's patch.

It understood, that this is just a heuristic
approach, since we do not have exactly the data,
which is needed to determine at which of the identical
PCs in the line table the subroutine actually ends.
So, this tries to be as conservative as possible,
just changing what is absolutely necessary.

This patch itself is preliminary, since the is-stmt patch
needs to be rebased after the refactoring of
dwarf2read.c from yesterday, so I will have to rebase
this patch as well, but decided to wait for Andrew.


Thanks
Bernd.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-range-end-handling-of-inlined-subroutines.patch --]
[-- Type: text/x-patch; name="0001-Fix-range-end-handling-of-inlined-subroutines.patch", Size: 6866 bytes --]

From d15f3346feb1ed5cbbc14e708f3f6b58d88bc0fe Mon Sep 17 00:00:00 2001
From: Bernd Edlinger <bernd.edlinger@hotmail.de>
Date: Sun, 9 Feb 2020 21:13:17 +0100
Subject: [PATCH] Fix range end handling of inlined subroutines

Since the is_stmt is now handled, it became
possible to locate dubious is_stmt line entries
at the end of an inlined function, in the same
subfile.

If any is-stmt line is followed by a non-is-stmt,
at the same PC as the inlined subroutine ends,
clear the is-stmt there (except the line = 0
end marker, of course).

This is about the best we can do at the moment,
unless location view information are added to the
block ranges debug info structure, and location
views are implemented in gdb in general.

gdb:
2020-02-09  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	* buildsym.c (buildsym_compunit::record_inline_range_end):
	New function.
	* buildsym.h (buildsym_compunit::record_inline_range_end): Declare.
	* dwarf2read.c (dwarf2_rnglists_process,
	dwarf2_ranges_process): Don't ignore empty ranges here.
	(dwarf2_ranges_read): Ignore empty ranges here.
	(dwarf2_record_block_ranges): Pass end of range PC to
	record_inline_range_end for inline functions.

gdb/testsuite:
2020-02-09  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	* gdb.cp/next-inline.exp: Adjust test.
---
 gdb/buildsym.c                       | 23 +++++++++++++++++++++++
 gdb/buildsym.h                       |  2 ++
 gdb/dwarf2read.c                     | 22 +++++++++++++---------
 gdb/testsuite/gdb.cp/next-inline.exp |  9 ---------
 4 files changed, 38 insertions(+), 18 deletions(-)

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 7fd256f..381a7c8 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -732,6 +732,29 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
 }
 
 \f
+/* Record a PC where a inlined subroutine ends.  */
+
+void
+buildsym_compunit::record_inline_range_end (CORE_ADDR end)
+{
+  struct subfile *subfile;
+
+  for (subfile = m_subfiles; subfile != NULL; subfile = subfile->next)
+    {
+      if (subfile->line_vector != NULL)
+	{
+	  struct linetable_entry *items = subfile->line_vector->item;
+	  int i;
+
+	  for (i = subfile->line_vector->nitems - 1; i > 0; i--)
+	    if (items[i].pc == end && items[i - 1].pc == end
+		&& !items[i].is_stmt && items[i - 1].line != 0)
+	      items[i - 1].is_stmt = 0;
+	}
+    }
+}
+
+\f
 /* Subroutine of end_symtab to simplify it.  Look for a subfile that
    matches the main source file's basename.  If there is only one, and
    if the main source file doesn't have any symbol or line number
diff --git a/gdb/buildsym.h b/gdb/buildsym.h
index c768a4c..3cf0f8b 100644
--- a/gdb/buildsym.h
+++ b/gdb/buildsym.h
@@ -190,6 +190,8 @@ struct buildsym_compunit
   void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
 		    bool is_stmt);
 
+  void record_inline_range_end (CORE_ADDR end);
+
   struct compunit_symtab *get_compunit_symtab ()
   {
     return m_compunit_symtab;
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index cbee4ab..6919bf1 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -14407,10 +14407,6 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  return false;
 	}
 
-      /* Empty range entries have no effect.  */
-      if (range_beginning == range_end)
-	continue;
-
       range_beginning += base;
       range_end += base;
 
@@ -14521,10 +14517,6 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
 	  return 0;
 	}
 
-      /* Empty range entries have no effect.  */
-      if (range_beginning == range_end)
-	continue;
-
       range_beginning += base;
       range_end += base;
 
@@ -14564,6 +14556,10 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
   retval = dwarf2_ranges_process (offset, cu,
     [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
     {
+      /* Empty range entries have no effect.  */
+      if (range_beginning == range_end)
+	return;
+
       if (ranges_pst != NULL)
 	{
 	  CORE_ADDR lowpc;
@@ -14801,6 +14797,7 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
   struct gdbarch *gdbarch = get_objfile_arch (objfile);
   struct attribute *attr;
   struct attribute *attr_high;
+  bool inlined_subroutine = (die->tag == DW_TAG_inlined_subroutine);
 
   attr_high = dwarf2_attr (die, DW_AT_high_pc, cu);
   if (attr_high)
@@ -14816,7 +14813,10 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
 
 	  low = gdbarch_adjust_dwarf2_addr (gdbarch, low + baseaddr);
 	  high = gdbarch_adjust_dwarf2_addr (gdbarch, high + baseaddr);
-	  cu->get_builder ()->record_block_range (block, low, high - 1);
+	  if (inlined_subroutine)
+	    cu->get_builder ()->record_inline_range_end (high);
+	  if (low < high)
+	    cu->get_builder ()->record_block_range (block, low, high - 1);
         }
     }
 
@@ -14841,6 +14841,10 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
 	  end += baseaddr;
 	  start = gdbarch_adjust_dwarf2_addr (gdbarch, start);
 	  end = gdbarch_adjust_dwarf2_addr (gdbarch, end);
+	  if (inlined_subroutine)
+	    cu->get_builder ()->record_inline_range_end (end);
+	  if (start == end)
+	    return;
 	  cu->get_builder ()->record_block_range (block, start, end - 1);
 	  blockvec.emplace_back (start, end);
 	});
diff --git a/gdb/testsuite/gdb.cp/next-inline.exp b/gdb/testsuite/gdb.cp/next-inline.exp
index 0b2b22d..11d9e2e 100644
--- a/gdb/testsuite/gdb.cp/next-inline.exp
+++ b/gdb/testsuite/gdb.cp/next-inline.exp
@@ -42,24 +42,15 @@ proc do_test { use_header } {
     gdb_test "step" ".*" "step into get_alias_set"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 1"
-    # It's possible that this first failure (when not using a header
-    # file) is GCC's fault, though the remaining failures would best
-    # be fixed by adding location views support (though it could be
-    # that some easier heuristic could be figured out).  Still, it is
-    # not certain that the first failure wouldn't also be fixed by
-    # having location view support, so for now it is tagged as such.
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" ".*TREE_TYPE.*" "next step 1"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 2"
     gdb_test "next" ".*TREE_TYPE.*" "next step 2"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 3"
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" ".*TREE_TYPE.*" "next step 3"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 4"
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" "return 0.*" "next step 4"
     gdb_test "bt" \
 	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
-- 
1.9.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-02-05 17:55   ` Bernd Edlinger
@ 2020-02-10 18:30     ` Bernd Edlinger
  2020-02-11 13:57     ` Andrew Burgess
  1 sibling, 0 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-02-10 18:30 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

Hi,

I just did a rebase of your patch, (and was surprised that it worked),
but got an interesting message from git:

First, rewinding head to replay your work on top of it...
Applying: - is-stmt patch
Using index info to reconstruct a base tree...
A	gdb/dwarf2read.c
<stdin>:874: space before tab in indent.
 		 [expr [gdb_get_line_number "main end"] \
<stdin>:1128: space before tab in indent.
 		 [expr [gdb_get_line_number "main, set var to 0"] \
<stdin>:1143: space before tab in indent.
 		 [expr [gdb_get_line_number "main end"] \
warning: 3 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging gdb/dwarf2/read.c


and indeed there are SPACE followed by TAB in
gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp

not sure if that is only because I copied these lines
out of the E-mail and messed it up by doing so, or if the whitespace
is wrong in the original patch as well.


Thanks
Bernd.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH] Fix range end handling of inlined subroutines
  2020-02-09 21:07   ` [PATCH] Fix range end handling of inlined subroutines Bernd Edlinger
@ 2020-02-10 21:48     ` Andrew Burgess
  2020-02-22  6:39     ` [PATCHv2] " Bernd Edlinger
  1 sibling, 0 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-02-10 21:48 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: gdb-patches

* Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-02-09 21:07:32 +0000]:

> This patch itself is preliminary, since the is-stmt patch
> needs to be rebased after the refactoring of
> dwarf2read.c from yesterday, so I will have to rebase
> this patch as well, but decided to wait for Andrew.

I've been busy with other projects for the last few days, but plan to
look at this series tomorrow.

Thanks,
Andrew


> 
> 
> Thanks
> Bernd.

> From d15f3346feb1ed5cbbc14e708f3f6b58d88bc0fe Mon Sep 17 00:00:00 2001
> From: Bernd Edlinger <bernd.edlinger@hotmail.de>
> Date: Sun, 9 Feb 2020 21:13:17 +0100
> Subject: [PATCH] Fix range end handling of inlined subroutines
> 
> Since the is_stmt is now handled, it became
> possible to locate dubious is_stmt line entries
> at the end of an inlined function, in the same
> subfile.
> 
> If any is-stmt line is followed by a non-is-stmt,
> at the same PC as the inlined subroutine ends,
> clear the is-stmt there (except the line = 0
> end marker, of course).
> 
> This is about the best we can do at the moment,
> unless location view information are added to the
> block ranges debug info structure, and location
> views are implemented in gdb in general.
> 
> gdb:
> 2020-02-09  Bernd Edlinger  <bernd.edlinger@hotmail.de>
> 
> 	* buildsym.c (buildsym_compunit::record_inline_range_end):
> 	New function.
> 	* buildsym.h (buildsym_compunit::record_inline_range_end): Declare.
> 	* dwarf2read.c (dwarf2_rnglists_process,
> 	dwarf2_ranges_process): Don't ignore empty ranges here.
> 	(dwarf2_ranges_read): Ignore empty ranges here.
> 	(dwarf2_record_block_ranges): Pass end of range PC to
> 	record_inline_range_end for inline functions.
> 
> gdb/testsuite:
> 2020-02-09  Bernd Edlinger  <bernd.edlinger@hotmail.de>
> 
> 	* gdb.cp/next-inline.exp: Adjust test.
> ---
>  gdb/buildsym.c                       | 23 +++++++++++++++++++++++
>  gdb/buildsym.h                       |  2 ++
>  gdb/dwarf2read.c                     | 22 +++++++++++++---------
>  gdb/testsuite/gdb.cp/next-inline.exp |  9 ---------
>  4 files changed, 38 insertions(+), 18 deletions(-)
> 
> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> index 7fd256f..381a7c8 100644
> --- a/gdb/buildsym.c
> +++ b/gdb/buildsym.c
> @@ -732,6 +732,29 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
>  }
>  
>  \f
> +/* Record a PC where a inlined subroutine ends.  */
> +
> +void
> +buildsym_compunit::record_inline_range_end (CORE_ADDR end)
> +{
> +  struct subfile *subfile;
> +
> +  for (subfile = m_subfiles; subfile != NULL; subfile = subfile->next)
> +    {
> +      if (subfile->line_vector != NULL)
> +	{
> +	  struct linetable_entry *items = subfile->line_vector->item;
> +	  int i;
> +
> +	  for (i = subfile->line_vector->nitems - 1; i > 0; i--)
> +	    if (items[i].pc == end && items[i - 1].pc == end
> +		&& !items[i].is_stmt && items[i - 1].line != 0)
> +	      items[i - 1].is_stmt = 0;
> +	}
> +    }
> +}
> +
> +\f
>  /* Subroutine of end_symtab to simplify it.  Look for a subfile that
>     matches the main source file's basename.  If there is only one, and
>     if the main source file doesn't have any symbol or line number
> diff --git a/gdb/buildsym.h b/gdb/buildsym.h
> index c768a4c..3cf0f8b 100644
> --- a/gdb/buildsym.h
> +++ b/gdb/buildsym.h
> @@ -190,6 +190,8 @@ struct buildsym_compunit
>    void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
>  		    bool is_stmt);
>  
> +  void record_inline_range_end (CORE_ADDR end);
> +
>    struct compunit_symtab *get_compunit_symtab ()
>    {
>      return m_compunit_symtab;
> diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
> index cbee4ab..6919bf1 100644
> --- a/gdb/dwarf2read.c
> +++ b/gdb/dwarf2read.c
> @@ -14407,10 +14407,6 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
>  	  return false;
>  	}
>  
> -      /* Empty range entries have no effect.  */
> -      if (range_beginning == range_end)
> -	continue;
> -
>        range_beginning += base;
>        range_end += base;
>  
> @@ -14521,10 +14517,6 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
>  	  return 0;
>  	}
>  
> -      /* Empty range entries have no effect.  */
> -      if (range_beginning == range_end)
> -	continue;
> -
>        range_beginning += base;
>        range_end += base;
>  
> @@ -14564,6 +14556,10 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
>    retval = dwarf2_ranges_process (offset, cu,
>      [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
>      {
> +      /* Empty range entries have no effect.  */
> +      if (range_beginning == range_end)
> +	return;
> +
>        if (ranges_pst != NULL)
>  	{
>  	  CORE_ADDR lowpc;
> @@ -14801,6 +14797,7 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
>    struct gdbarch *gdbarch = get_objfile_arch (objfile);
>    struct attribute *attr;
>    struct attribute *attr_high;
> +  bool inlined_subroutine = (die->tag == DW_TAG_inlined_subroutine);
>  
>    attr_high = dwarf2_attr (die, DW_AT_high_pc, cu);
>    if (attr_high)
> @@ -14816,7 +14813,10 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
>  
>  	  low = gdbarch_adjust_dwarf2_addr (gdbarch, low + baseaddr);
>  	  high = gdbarch_adjust_dwarf2_addr (gdbarch, high + baseaddr);
> -	  cu->get_builder ()->record_block_range (block, low, high - 1);
> +	  if (inlined_subroutine)
> +	    cu->get_builder ()->record_inline_range_end (high);
> +	  if (low < high)
> +	    cu->get_builder ()->record_block_range (block, low, high - 1);
>          }
>      }
>  
> @@ -14841,6 +14841,10 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
>  	  end += baseaddr;
>  	  start = gdbarch_adjust_dwarf2_addr (gdbarch, start);
>  	  end = gdbarch_adjust_dwarf2_addr (gdbarch, end);
> +	  if (inlined_subroutine)
> +	    cu->get_builder ()->record_inline_range_end (end);
> +	  if (start == end)
> +	    return;
>  	  cu->get_builder ()->record_block_range (block, start, end - 1);
>  	  blockvec.emplace_back (start, end);
>  	});
> diff --git a/gdb/testsuite/gdb.cp/next-inline.exp b/gdb/testsuite/gdb.cp/next-inline.exp
> index 0b2b22d..11d9e2e 100644
> --- a/gdb/testsuite/gdb.cp/next-inline.exp
> +++ b/gdb/testsuite/gdb.cp/next-inline.exp
> @@ -42,24 +42,15 @@ proc do_test { use_header } {
>      gdb_test "step" ".*" "step into get_alias_set"
>      gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
>  	"not in inline 1"
> -    # It's possible that this first failure (when not using a header
> -    # file) is GCC's fault, though the remaining failures would best
> -    # be fixed by adding location views support (though it could be
> -    # that some easier heuristic could be figured out).  Still, it is
> -    # not certain that the first failure wouldn't also be fixed by
> -    # having location view support, so for now it is tagged as such.
> -    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
>      gdb_test "next" ".*TREE_TYPE.*" "next step 1"
>      gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
>  	"not in inline 2"
>      gdb_test "next" ".*TREE_TYPE.*" "next step 2"
>      gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
>  	"not in inline 3"
> -    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
>      gdb_test "next" ".*TREE_TYPE.*" "next step 3"
>      gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
>  	"not in inline 4"
> -    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
>      gdb_test "next" "return 0.*" "next step 4"
>      gdb_test "bt" \
>  	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-02-05 17:55   ` Bernd Edlinger
  2020-02-10 18:30     ` Bernd Edlinger
@ 2020-02-11 13:57     ` Andrew Burgess
  2020-02-14 20:05       ` Bernd Edlinger
  1 sibling, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-02-11 13:57 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: gdb-patches

* Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-02-05 17:55:37 +0000]:

> On 2/5/20 12:37 PM, Andrew Burgess wrote:
> > This commit brings support for the DWARF line table is_stmt field to
> > GDB.  The is_stmt field is used by the compiler when a single source
> > line is split into multiple assembler instructions, especially if the
> > assembler instructions are interleaved with instruction from other
> > source lines.
> > 
> > The compiler will set the is_stmt flag false from some instructions
> > from the source lines, these instructions are not a good place to
> > insert a breakpoint in order to stop at the source line.
> > Instructions which are marked with the is_stmt flag true are a good
> > place to insert a breakpoint for that source line.
> > 
> > Currently GDB ignores all instructions for which is_stmt is false.
> > This is fine in a lot of cases, however, there are some cases where
> > this means the debug experience is not as good as it could be.
> > 
> > Consider stopping at a random instruction, currently this instruction
> > will be attributed to the last line table entry before this point for
> > which is_stmt was true - as these are the only line table entries that
> > GDB tracks.  This can easily be incorrect in code with even a low
> > level of optimisation.
> > 
> > With is_stmt tracking in place, when stopping at a random instruction
> > we now attribute the instruction back to the real source line, even
> > when is_stmt is false for that instruction in the line table.
> > 
> > When inserting breakpoints we still select line table entries for
> > which is_stmt is true, so the breakpoint placing behaviour should not
> > change.
> > 
> > When stepping though code (at the line level, not the instruction
> > level) we will still stop at instruction where is_stmt is true, I
> > think this is more likely to be the desired behaviour.
> > 
> > Instruction stepping is, of course, unchanged, stepping one
> > instruction at a time, but we should now report more accurate line
> > table information with each instruction step.
> > 
> > The original motivation for this work was a patch posted by Bernd
> > here:
> >   https://sourceware.org/ml/gdb-patches/2019-11/msg00792.html
> > 
> > As part of that thread it was suggested that many issues would be
> > resolved if GDB supported line table views, this isn't something I've
> > attempted in this patch, though reading the spec, it seems like this
> > would be a useful feature to support in GDB in the future.  The spec
> > is here:
> >   http://dwarfstd.org/ShowIssue.php?issue=170427.1
> > 
> > And Bernd gives a brief description of the benefits here:
> >   https://sourceware.org/ml/gdb-patches/2020-01/msg00147.html
> > 
> > With that all said, I think that there is benefit to having proper
> > is_stmt support regardless of whether we have views support, so I
> > think we should consider getting this (or something like this) in
> > first, and then building view support on top of this.
> > 
> > The gdb.cp/next-inline.exp test is based off a test proposed by Bernd
> > Edlinger in message:
> >   https://sourceware.org/ml/gdb-patches/2019-12/msg00842.html
> > 
> 
> Thanks Andrew, I played a bit with debugging the gcc internals,
> and I think the debug-experience is good, the problem with step
> over are completely gone, also in the original context.
> 
> Just a few comments on the patch follow below.
> 
> > gdb/ChangeLog:
> > 
> > 	* buildsym-legacy.c (record_line): Pass extra parameter to
> > 	record_line.
> > 	* buildsym.c (buildsym_compunit::record_line): Take an extra
> > 	parameter, reduce duplication in the line table, and record the
> > 	is_stmt flag in the line table.
> > 	* buildsym.h (buildsym_compunit::record_line): Add extra
> > 	parameter.
> > 	* disasm.c (do_mixed_source_and_assembly_deprecated): Ignore
> > 	non-statement lines.
> > 	* dwarf2read.c (dwarf_record_line_1): Add extra parameter, pass
> > 	this to the symtab builder.
> > 	(dwarf_finish_line): Pass extra parameter to dwarf_record_line_1.
> > 	(lnp_state_machine::record_line): Pass a suitable is_stmt flag
> > 	through to dwarf_record_line_1.
> > 	* infrun.c (process_event_stop_test): When stepping, don't stop at
> > 	a non-statement instruction, and only refresh the step info when
> > 	we land in the middle of a line's range.
> 
> did you mean, when we don't land in the middle of a line ?

No, in this case I did write the correct thing.

So GDB had/has some code designed to "improve" the handling of looping
constructs.  I think the idea is to handle cases like this:

  0x100      LN=5
  0x104 <-\
  0x108   |
  0x10c   |  LN=6
  0x110   |
  0x114 --/

So when line 6 branches back to the middle of line 5, we don't stop
(because we are in the middle of line 5), but we do want to stop at
the start of line 6, so we "reset" the starting point back to line 5.

This is done by calling set_step_info in process_event_stop_test, in
infrun.c.

What we actually did though was whenever we were at an address that
didn't exactly match a SAL (in the example above, marked LN=5, LN=6)
we called set_step_info.  This worked fine, at address 0x104 we reset
back to LN=5, same at address 0x108.

However, in the new world things can look different, for example, like
this:

  0x100      LN=5,STMT=1
  0x104
  0x108      LN=6,STMT=0
  0x10c      LN=6,STMT=1
  0x110
  0x114

In this world, when we move forward to 0x100 we don't have a matching
SAL, so we get the SAL for address 0x100.  We can then safely call
set_step_info like we did before, that's fine.  But when we step to
0x108 we do now have a matching SAL, but we don't want to stop yet as
this address is 'STMT=0'.  However, if we call set_step_info then GDB
is going to think we are stepping away from LN=6, and so will refuse
to stop at address 0x10c.

The proposal in this commit is that if we land at an address which
doesn't specifically match a SAL, 0x104, 0x110, 0x114 in the above
example, then we do call set_step_info, but otherwise we don't.

There are going to be situations where the behaviour of GDB changes
after this patch, but I don't think we can avoid that.  What we had
previously was just a heuristic.  I think enabling this new
information in GDB will allow us to do better overall, so I think we
should make this change and fix any issues as they show up.

> 
> > --- a/gdb/stack.c
> > +++ b/gdb/stack.c
> > @@ -330,7 +330,7 @@ frame_show_address (struct frame_info *frame,
> >        return false;
> >      }
> >  
> > -  return get_frame_pc (frame) != sal.pc;
> > +  return get_frame_pc (frame) != sal.pc || !sal.is_stmt;
> >  }
> >  
> >  /* See frame.h.  */
> > @@ -1036,6 +1036,7 @@ print_frame_info (const frame_print_options &fp_opts,
> >    int location_print;
> >    struct ui_out *uiout = current_uiout;
> >  
> > +
> >    if (!current_uiout->is_mi_like_p ()
> >        && fp_opts.print_frame_info != print_frame_info_auto)
> >      {
> 
> Is this white space change intentional?

Ooops.  Fixed.

I also fixed the space before tab issue you pointed out in another
mail.

I see you have a patch that depends on this one, but I'd like to leave
this on the mailing list for a little longer before I merge it to give
others a chance to comment.

I'll probably merge this over the weekend if there's no other
feedback.

Thanks,
Andrew

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-02-06  9:01   ` Luis Machado
@ 2020-02-11 15:39     ` Andrew Burgess
  0 siblings, 0 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-02-11 15:39 UTC (permalink / raw)
  To: Luis Machado; +Cc: gdb-patches, Bernd Edlinger

* Luis Machado <luis.machado@linaro.org> [2020-02-06 06:00:29 -0300]:

> Hi,
> 
> I still need to check the patch itself, but i had a question about one
> particular paragraph...
> 
> On 2/5/20 8:37 AM, Andrew Burgess wrote:
> > This commit brings support for the DWARF line table is_stmt field to
> > GDB.  The is_stmt field is used by the compiler when a single source
> > line is split into multiple assembler instructions, especially if the
> > assembler instructions are interleaved with instruction from other
> > source lines.
> > 
> > The compiler will set the is_stmt flag false from some instructions
> > from the source lines, these instructions are not a good place to
> > insert a breakpoint in order to stop at the source line.
> > Instructions which are marked with the is_stmt flag true are a good
> > place to insert a breakpoint for that source line.
> > 
> > Currently GDB ignores all instructions for which is_stmt is false.
> > This is fine in a lot of cases, however, there are some cases where
> > this means the debug experience is not as good as it could be.
> > 
> > Consider stopping at a random instruction, currently this instruction
> > will be attributed to the last line table entry before this point for
> > which is_stmt was true - as these are the only line table entries that
> > GDB tracks.  This can easily be incorrect in code with even a low
> > level of optimisation.
> > 
> > With is_stmt tracking in place, when stopping at a random instruction
> > we now attribute the instruction back to the real source line, even
> > when is_stmt is false for that instruction in the line table.
> > 
> > When inserting breakpoints we still select line table entries for
> > which is_stmt is true, so the breakpoint placing behaviour should not
> > change.
> > 
> > When stepping though code (at the line level, not the instruction
> > level) we will still stop at instruction where is_stmt is true, I
> > think this is more likely to be the desired behaviour.
> 
> Considering a block of continuous instructions that map to the same source
> line, will line-stepping stop at each one of these instructions if is_stmt
> is true? As opposed to stepping over the the whole block until we see a line
> change?

No, a continuous block will be skipped as it is at the moment, even if
is_stmt is true for all entries.

>
> I'm wondering if this would help this bug
> (https://sourceware.org/bugzilla/show_bug.cgi?id=21221) in any way.

I took a look at this bug and even had a few ideas on how we might
improve GDB.  Below is one patch that I put together, though this only
fixes one of the example cases from that bug.  The problem this patch
runs into is that it regresses a few GDB tests
(gdb.base/gdb-sigterm.exp and
gdb.thread/step-bg-decr-pc-switch-thread.exp) in at least one of these
tests GDB makes the following assumption:  Single stepping on a one
line loop (e.g. 'for (;;);') will block forever.  The question then
is:  Does single step move us to the next source line to be executed
that is not the current line, or does single step move us to the next
source line to be executed, even if it is the same as the current
line?

I'm not sure what the answer is to this question...

Thanks,
Andrew

---

commit 35a72ddacf86c7e7f899c869558d1af3cfadb549
Author: Andrew Burgess <andrew.burgess@embecosm.com>
Date:   Tue Feb 11 15:08:10 2020 +0000

    WIP: Try to better handle small loops
    
    This handles this case:
    
      int main(void)
      {
        int var = 0;
    
        for (;;)
          {
            var++;
          }
    
        return 0;
      }
    
    Compiled as 'gcc -g -o test.x test.c'.
    
    Change-Id: I6f499181eca5fb51b6edb050cbce0bda15665d38

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 3919de81f90..03878025959 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -6674,11 +6674,14 @@ process_event_stop_test (struct execution_control_state *ecs)
 
       /* When stepping backward, stop at beginning of line range
 	 (unless it's the function entry point, in which case
-	 keep going back to the call point).  */
+	 keep going back to the call point).  When stepping forward we
+	 stop at the beginning of the range as this indicates we must be
+	 looping.  */
       CORE_ADDR stop_pc = ecs->event_thread->suspend.stop_pc;
       if (stop_pc == ecs->event_thread->control.step_range_start
-	  && stop_pc != ecs->stop_func_start
-	  && execution_direction == EXEC_REVERSE)
+	  && (execution_direction != EXEC_REVERSE
+	      || (stop_pc != ecs->stop_func_start
+		  && execution_direction == EXEC_REVERSE)))
 	end_stepping_range (ecs);
       else
 	keep_going (ecs);
@@ -7173,6 +7176,16 @@ process_event_stop_test (struct execution_control_state *ecs)
 	}
     }
 
+  if (ecs->event_thread->suspend.stop_pc
+      == ecs->event_thread->control.step_range_start)
+    {
+      /* Very small loops might only contain a single line information
+	 entry.  If this is the case then spot when we return to the start
+	 of the line range and stop again.  */
+      end_stepping_range (ecs);
+      return;
+    }
+
   /* We aren't done stepping.
 
      Optimize by setting the stepping range to the line.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-02-11 13:57     ` Andrew Burgess
@ 2020-02-14 20:05       ` Bernd Edlinger
  2020-03-05 18:01         ` Bernd Edlinger
  0 siblings, 1 reply; 79+ messages in thread
From: Bernd Edlinger @ 2020-02-14 20:05 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

On 2/11/20 2:57 PM, Andrew Burgess wrote:
> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-02-05 17:55:37 +0000]:
> 
>> On 2/5/20 12:37 PM, Andrew Burgess wrote:
>>
>> did you mean, when we don't land in the middle of a line ?
> 
> No, in this case I did write the correct thing.
> 
> So GDB had/has some code designed to "improve" the handling of looping
> constructs.  I think the idea is to handle cases like this:
> 
>   0x100      LN=5
>   0x104 <-\
>   0x108   |
>   0x10c   |  LN=6
>   0x110   |
>   0x114 --/
> 
> So when line 6 branches back to the middle of line 5, we don't stop
> (because we are in the middle of line 5), but we do want to stop at
> the start of line 6, so we "reset" the starting point back to line 5.
> 
> This is done by calling set_step_info in process_event_stop_test, in
> infrun.c.
> 
> What we actually did though was whenever we were at an address that
> didn't exactly match a SAL (in the example above, marked LN=5, LN=6)
> we called set_step_info.  This worked fine, at address 0x104 we reset
> back to LN=5, same at address 0x108.
> 
> However, in the new world things can look different, for example, like
> this:
> 
>   0x100      LN=5,STMT=1
>   0x104
>   0x108      LN=6,STMT=0
>   0x10c      LN=6,STMT=1
>   0x110
>   0x114
> 
> In this world, when we move forward to 0x100 we don't have a matching
> SAL, so we get the SAL for address 0x100.  We can then safely call
> set_step_info like we did before, that's fine.  But when we step to
> 0x108 we do now have a matching SAL, but we don't want to stop yet as
> this address is 'STMT=0'.  However, if we call set_step_info then GDB
> is going to think we are stepping away from LN=6, and so will refuse
> to stop at address 0x10c.
> 
> The proposal in this commit is that if we land at an address which
> doesn't specifically match a SAL, 0x104, 0x110, 0x114 in the above
> example, then we do call set_step_info, but otherwise we don't.
> 
> There are going to be situations where the behaviour of GDB changes
> after this patch, but I don't think we can avoid that.  What we had
> previously was just a heuristic.  I think enabling this new
> information in GDB will allow us to do better overall, so I think we
> should make this change and fix any issues as they show up.
> 

I agree with that, thanks for this explanation.

However, I think I found a small problem in this patch.
You can see it with the next-inline test case.
When you use just step the execution does not stop
between the inline functions, because not calling 

current behaviour is this:

Breakpoint 1, main () at next-inline.cc:63
63	  get_alias_set (&xx);
(gdb) s
get_alias_set (t=0x404040 <xx>) at next-inline.cc:50
50	  if (t != NULL
(gdb) s
51	      && TREE_TYPE (t).z != 1
(gdb) s
0x0000000000401169 in tree_check (i=0, t=0x404040 <xx>) at next-inline.h:34
34	  if (t->x != i)
(gdb) s
0x000000000040117d in tree_check (i=0, t=0x404040 <xx>) at next-inline.h:34
34	  if (t->x != i)
(gdb) s
0x000000000040118f in tree_check (i=0, t=0x404040 <xx>) at next-inline.h:34
34	  if (t->x != i)
(gdb) s
main () at next-inline.cc:64
64	  return 0;
(gdb) 

I debugged a bit because I was not sure why that happens, and it looks
like set_step_info does also set the current inline frame, but you
only want to suppress the line number not the currently executing
inline function.
With this small patch the step works as expected.

commit 193d81765d88b11e68111a8856a84cdf084d0b22
Author: Bernd Edlinger <bernd.edlinger@hotmail.de>
Date:   Fri Feb 14 20:41:51 2020 +0100

    Fix next-inline test case with step
    
    Should stop between inline function invocations.

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 3919de8..89e6654 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -7190,6 +7190,8 @@ process_event_stop_test (struct execution_control_state *e
   ecs->event_thread->control.step_range_start = stop_pc_sal.pc;
   ecs->event_thread->control.step_range_end = stop_pc_sal.end;
   ecs->event_thread->control.may_range_step = 1;
+  ecs->event_thread->control.step_frame_id = get_frame_id (frame);
+  ecs->event_thread->control.step_stack_frame_id = get_stack_frame_id (frame);
   if (refresh_step_info)
     set_step_info (frame, stop_pc_sal);
 

I'm not sure if that is the cleanest solution, but it works.
With that adjustment, the stepping out of the inline and back into
makes the step call stop, which is the correct behavior:

Breakpoint 1, main () at next-inline.cc:63
63	  get_alias_set (&xx);
(gdb) s
get_alias_set (t=0x404040 <xx>) at next-inline.cc:50
50	  if (t != NULL
(gdb) s
51	      && TREE_TYPE (t).z != 1
(gdb) s
0x0000000000401169 in tree_check (i=0, t=0x404040 <xx>) at next-inline.h:34
34	  if (t->x != i)
(gdb) s
52	      && TREE_TYPE (t).z != 2
(gdb) s
tree_check (i=0, t=0x404040 <xx>) at next-inline.h:34
34	  if (t->x != i)
(gdb) s
53	      && TREE_TYPE (t).z != 3)
(gdb) s
tree_check (i=0, t=0x404040 <xx>) at next-inline.h:34
34	  if (t->x != i)
(gdb) s
main () at next-inline.cc:64
64	  return 0;
(gdb) 

If you like you can integrate that into your patch.
You will probably want to add that to the test case.

>>
>>> --- a/gdb/stack.c
>>> +++ b/gdb/stack.c
>>> @@ -330,7 +330,7 @@ frame_show_address (struct frame_info *frame,
>>>        return false;
>>>      }
>>>  
>>> -  return get_frame_pc (frame) != sal.pc;
>>> +  return get_frame_pc (frame) != sal.pc || !sal.is_stmt;
>>>  }
>>>  
>>>  /* See frame.h.  */
>>> @@ -1036,6 +1036,7 @@ print_frame_info (const frame_print_options &fp_opts,
>>>    int location_print;
>>>    struct ui_out *uiout = current_uiout;
>>>  
>>> +
>>>    if (!current_uiout->is_mi_like_p ()
>>>        && fp_opts.print_frame_info != print_frame_info_auto)
>>>      {
>>
>> Is this white space change intentional?
> 
> Ooops.  Fixed.
> 
> I also fixed the space before tab issue you pointed out in another
> mail.
> 
> I see you have a patch that depends on this one, but I'd like to leave
> this on the mailing list for a little longer before I merge it to give
> others a chance to comment.
> 

Thanks, regarding my other patch...

I used gprof to get performance numbers, because I was not sure if that
simple approach adds too much execution time, and it turns out that it
spent 30% of the complete execution time in the record_inline_range_end
function, when I was debugging GCC's cc1 executable.
So I decided that this needs to be optimized, I will send a new
version that does the line table patching with a binary search,
after the weekend.

Thanks
Bernd.


> I'll probably merge this over the weekend if there's no other
> feedback.
> 
> Thanks,
> Andrew
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCHv2] Fix range end handling of inlined subroutines
  2020-02-09 21:07   ` [PATCH] Fix range end handling of inlined subroutines Bernd Edlinger
  2020-02-10 21:48     ` Andrew Burgess
@ 2020-02-22  6:39     ` Bernd Edlinger
  2020-03-08 14:57       ` [PATCHv3] " Bernd Edlinger
  1 sibling, 1 reply; 79+ messages in thread
From: Bernd Edlinger @ 2020-02-22  6:39 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1519 bytes --]

On 2/9/20 10:07 PM, Bernd Edlinger wrote:
> Hi,
> 
> this is based on Andrew's patch here:
> 
> https://sourceware.org/ml/gdb-patches/2020-02/msg00092.html
> 
> This and adds a heuristic to fix the case where caller
> and callee reside in the same subfile, it uses
> the recorded is-stmt bits and locates end of
> range infos, including the previously ignored empty
> range, and adjusting is-stmt info at this
> same pc, when the last item is not-is-stmt, the
> previous line table entries are dubious and
> we reset the is-stmt bit there.
> This fixes the new test case in Andrew's patch.
> 
> It understood, that this is just a heuristic
> approach, since we do not have exactly the data,
> which is needed to determine at which of the identical
> PCs in the line table the subroutine actually ends.
> So, this tries to be as conservative as possible,
> just changing what is absolutely necessary.
> 
> This patch itself is preliminary, since the is-stmt patch
> needs to be rebased after the refactoring of
> dwarf2read.c from yesterday, so I will have to rebase
> this patch as well, but decided to wait for Andrew.
> 
> 

So, this is an update to my previous patch above:
https://sourceware.org/ml/gdb-patches/2020-02/msg00262.html

It improves performance on big data, by using binary
search to locate the bogus line table entries.
Otherwise it behaves identical to the previous version,
and is still waiting for Andrew's patch before it can
be applied.


Thanks
Bernd.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-range-end-handling-of-inlined-subroutines.patch --]
[-- Type: text/x-patch; name="0001-Fix-range-end-handling-of-inlined-subroutines.patch", Size: 10417 bytes --]

From 758b584e1447fce4648a3be3b9af930af20a6fb6 Mon Sep 17 00:00:00 2001
From: Bernd Edlinger <bernd.edlinger@hotmail.de>
Date: Sun, 9 Feb 2020 21:13:17 +0100
Subject: [PATCHv2] Fix range end handling of inlined subroutines

Since the is_stmt is now handled, it becomes
possible to locate dubious is_stmt line entries
at the end of an inlined function, even if the
called inline function is in the same subfile.

When there is a sequence of line entries at the
same address where an inline range ends, and the
last item has is_stmt = 0, we force all previous
items to have is_stmt = 0 as well.

If the last line at that address has is_stmt = 1,
there is no need to change anything, since a step
over will always stop at that last line from the
same address, which is fine, since it is outside
the subroutine.

This is about the best we can do at the moment,
unless location view information are added to the
block ranges debug info structure, and location
views are implemented in gdb in general.

gdb:
2020-02-22  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	* buildsym.c (buildsym_compunit::record_inline_range_end,
	patch_inline_end_pos): New helper functions.
	(buildsym_compunit::end_symtab_with_blockvector): Patch line table.
	(buildsym_compunit::~buildsym_compunit): Cleanup m_inline_end_vector.
	* buildsym.h (buildsym_compunit::record_inline_range_end): Declare.
	(buildsym_compunit::m_inline_end_vector,
	buildsym_compunit::m_inline_end_vector_length,
	buildsym_compunit::m_inline_end_vector_nitems): New data items.
	* dwarf2/read.c (dwarf2_rnglists_process,
	dwarf2_ranges_process): Don't ignore empty ranges here.
	(dwarf2_ranges_read): Ignore empty ranges here.
	(dwarf2_record_block_ranges): Pass end of range PC to
	record_inline_range_end for inline functions.

gdb/testsuite:
2020-02-22  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	* gdb.cp/next-inline.exp: Adjust test.
---
 gdb/buildsym.c                       | 84 ++++++++++++++++++++++++++++++++++++
 gdb/buildsym.h                       | 11 +++++
 gdb/dwarf2/read.c                    | 22 ++++++----
 gdb/testsuite/gdb.cp/next-inline.exp |  9 ----
 4 files changed, 108 insertions(+), 18 deletions(-)

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 7fd256f..c93944a 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -113,6 +113,8 @@ buildsym_compunit::~buildsym_compunit ()
       next1 = next->next;
       xfree ((void *) next);
     }
+
+  xfree (m_inline_end_vector);
 }
 
 struct macro_table *
@@ -732,6 +734,84 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
 }
 
 \f
+/* Record a PC where a inlined subroutine ends.  */
+
+void
+buildsym_compunit::record_inline_range_end (CORE_ADDR end)
+{
+  if (m_inline_end_vector == nullptr)
+    {
+      m_inline_end_vector_length = INITIAL_LINE_VECTOR_LENGTH;
+      m_inline_end_vector = (CORE_ADDR *)
+	xmalloc (sizeof (CORE_ADDR) * m_inline_end_vector_length);
+      m_inline_end_vector_nitems = 0;
+    }
+  else if (m_inline_end_vector_nitems == m_inline_end_vector_length)
+    {
+      m_inline_end_vector_length *= 2;
+      m_inline_end_vector = (CORE_ADDR *)
+	xrealloc ((char *) m_inline_end_vector,
+		  sizeof (CORE_ADDR) * m_inline_end_vector_length);
+    }
+
+  m_inline_end_vector[m_inline_end_vector_nitems++] = end;
+}
+
+\f
+/* Patch the is_stmt bits at the given inline end address.
+   The line table has to be already sorted.  */
+
+static void
+patch_inline_end_pos (struct linetable *table, CORE_ADDR end)
+{
+  int a = 2, b = table->nitems - 1;
+  struct linetable_entry *items = table->item;
+
+  /* We need at least two items with pc = end in the table.
+     The lowest usable items are at pos 0 and 1, the highest
+     usable items are at pos b - 2 and b - 1.  */
+  if (a > b || end < items[1].pc || end > items[b - 2].pc)
+    return;
+
+  /* Look for the first item with pc > end in the range [a,b].
+     The previous element has pc = end or there is no match.
+     We set a = 2, since we need at least two consecutive elements
+     with pc = end to do anything useful.
+     We set b = nitems - 1, since we are not interested in the last
+     element which should be an end of sequence marker with line = 0
+     and is_stmt = 1.  */
+  while (a < b)
+    {
+      int c = (a + b) / 2;
+
+      if (end < items[c].pc)
+	b = c;
+      else
+	a = c + 1;
+    }
+
+  a--;
+  if (items[a].pc != end || items[a].is_stmt)
+    return;
+
+  /* When there is a sequence of line entries at the same address
+     where an inline range ends, and the last item has is_stmt = 0,
+     we force all previous items to have is_stmt = 0 as well.
+     Setting breakpoints at those addresses is currently not
+     supported, since it is unclear if the previous addresses are
+     part of the subroutine or the calling program.  */
+  while (a-- > 0)
+    {
+      /* We stop at the first line entry with a different address,
+         or when we see an end of sequence marker.  */
+      if (items[a].pc != end || items[a].line == 0)
+	break;
+
+      items[a].is_stmt = 0;
+    }
+}
+
+\f
 /* Subroutine of end_symtab to simplify it.  Look for a subfile that
    matches the main source file's basename.  If there is only one, and
    if the main source file doesn't have any symbol or line number
@@ -965,6 +1045,10 @@ buildsym_compunit::end_symtab_with_blockvector (struct block *static_block,
 			      subfile->line_vector->item
 			      + subfile->line_vector->nitems,
 			      lte_is_less_than);
+
+	   for (int i = 0; i < m_inline_end_vector_nitems; i++)
+	     patch_inline_end_pos (subfile->line_vector,
+				   m_inline_end_vector[i]);
 	}
 
       /* Allocate a symbol table if necessary.  */
diff --git a/gdb/buildsym.h b/gdb/buildsym.h
index c768a4c..2845789 100644
--- a/gdb/buildsym.h
+++ b/gdb/buildsym.h
@@ -190,6 +190,8 @@ struct buildsym_compunit
   void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
 		    bool is_stmt);
 
+  void record_inline_range_end (CORE_ADDR end);
+
   struct compunit_symtab *get_compunit_symtab ()
   {
     return m_compunit_symtab;
@@ -397,6 +399,15 @@ private:
 
   /* Pending symbols that are local to the lexical context.  */
   struct pending *m_local_symbols = nullptr;
+
+  /* Pending inline end range addresses.  */
+  CORE_ADDR * m_inline_end_vector = nullptr;
+
+  /* Number of allocated inline end range addresses.  */
+  int m_inline_end_vector_length = 0;
+
+  /* Number of pending inline end range addresses.  */
+  int m_inline_end_vector_nitems = 0;
 };
 
 \f
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 2fa346c..1c4cd8b 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -13571,10 +13571,6 @@ dwarf2_rnglists_process (unsigned offset, struct dwarf2_cu *cu,
 	  return false;
 	}
 
-      /* Empty range entries have no effect.  */
-      if (range_beginning == range_end)
-	continue;
-
       range_beginning += base;
       range_end += base;
 
@@ -13685,10 +13681,6 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu,
 	  return 0;
 	}
 
-      /* Empty range entries have no effect.  */
-      if (range_beginning == range_end)
-	continue;
-
       range_beginning += base;
       range_end += base;
 
@@ -13728,6 +13720,10 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
   retval = dwarf2_ranges_process (offset, cu,
     [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
     {
+      /* Empty range entries have no effect.  */
+      if (range_beginning == range_end)
+	return;
+
       if (ranges_pst != NULL)
 	{
 	  CORE_ADDR lowpc;
@@ -13965,6 +13961,7 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
   struct gdbarch *gdbarch = get_objfile_arch (objfile);
   struct attribute *attr;
   struct attribute *attr_high;
+  bool inlined_subroutine = (die->tag == DW_TAG_inlined_subroutine);
 
   attr_high = dwarf2_attr (die, DW_AT_high_pc, cu);
   if (attr_high)
@@ -13980,7 +13977,10 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
 
 	  low = gdbarch_adjust_dwarf2_addr (gdbarch, low + baseaddr);
 	  high = gdbarch_adjust_dwarf2_addr (gdbarch, high + baseaddr);
-	  cu->get_builder ()->record_block_range (block, low, high - 1);
+	  if (inlined_subroutine)
+	    cu->get_builder ()->record_inline_range_end (high);
+	  if (low < high)
+	    cu->get_builder ()->record_block_range (block, low, high - 1);
         }
     }
 
@@ -14005,6 +14005,10 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block,
 	  end += baseaddr;
 	  start = gdbarch_adjust_dwarf2_addr (gdbarch, start);
 	  end = gdbarch_adjust_dwarf2_addr (gdbarch, end);
+	  if (inlined_subroutine)
+	    cu->get_builder ()->record_inline_range_end (end);
+	  if (start == end)
+	    return;
 	  cu->get_builder ()->record_block_range (block, start, end - 1);
 	  blockvec.emplace_back (start, end);
 	});
diff --git a/gdb/testsuite/gdb.cp/next-inline.exp b/gdb/testsuite/gdb.cp/next-inline.exp
index 0b2b22d..11d9e2e 100644
--- a/gdb/testsuite/gdb.cp/next-inline.exp
+++ b/gdb/testsuite/gdb.cp/next-inline.exp
@@ -42,24 +42,15 @@ proc do_test { use_header } {
     gdb_test "step" ".*" "step into get_alias_set"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 1"
-    # It's possible that this first failure (when not using a header
-    # file) is GCC's fault, though the remaining failures would best
-    # be fixed by adding location views support (though it could be
-    # that some easier heuristic could be figured out).  Still, it is
-    # not certain that the first failure wouldn't also be fixed by
-    # having location view support, so for now it is tagged as such.
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" ".*TREE_TYPE.*" "next step 1"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 2"
     gdb_test "next" ".*TREE_TYPE.*" "next step 2"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 3"
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" ".*TREE_TYPE.*" "next step 3"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 4"
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" "return 0.*" "next step 4"
     gdb_test "bt" \
 	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
-- 
1.9.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-02-14 20:05       ` Bernd Edlinger
@ 2020-03-05 18:01         ` Bernd Edlinger
  2020-03-08 12:50           ` [PATCHv2 0/2] Line table is_stmt support Andrew Burgess
                             ` (3 more replies)
  0 siblings, 4 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-03-05 18:01 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 4139 bytes --]

On 2/14/20 9:05 PM, Bernd Edlinger wrote:
> On 2/11/20 2:57 PM, Andrew Burgess wrote:
>> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-02-05 17:55:37 +0000]:
>>
>>> On 2/5/20 12:37 PM, Andrew Burgess wrote:
>>>
>>> did you mean, when we don't land in the middle of a line ?
>>
>> No, in this case I did write the correct thing.
>>
>> So GDB had/has some code designed to "improve" the handling of looping
>> constructs.  I think the idea is to handle cases like this:
>>
>>   0x100      LN=5
>>   0x104 <-\
>>   0x108   |
>>   0x10c   |  LN=6
>>   0x110   |
>>   0x114 --/
>>
>> So when line 6 branches back to the middle of line 5, we don't stop
>> (because we are in the middle of line 5), but we do want to stop at
>> the start of line 6, so we "reset" the starting point back to line 5.
>>
>> This is done by calling set_step_info in process_event_stop_test, in
>> infrun.c.
>>
>> What we actually did though was whenever we were at an address that
>> didn't exactly match a SAL (in the example above, marked LN=5, LN=6)
>> we called set_step_info.  This worked fine, at address 0x104 we reset
>> back to LN=5, same at address 0x108.
>>
>> However, in the new world things can look different, for example, like
>> this:
>>
>>   0x100      LN=5,STMT=1
>>   0x104
>>   0x108      LN=6,STMT=0
>>   0x10c      LN=6,STMT=1
>>   0x110
>>   0x114
>>
>> In this world, when we move forward to 0x100 we don't have a matching
>> SAL, so we get the SAL for address 0x100.  We can then safely call
>> set_step_info like we did before, that's fine.  But when we step to
>> 0x108 we do now have a matching SAL, but we don't want to stop yet as
>> this address is 'STMT=0'.  However, if we call set_step_info then GDB
>> is going to think we are stepping away from LN=6, and so will refuse
>> to stop at address 0x10c.
>>
>> The proposal in this commit is that if we land at an address which
>> doesn't specifically match a SAL, 0x104, 0x110, 0x114 in the above
>> example, then we do call set_step_info, but otherwise we don't.
>>
>> There are going to be situations where the behaviour of GDB changes
>> after this patch, but I don't think we can avoid that.  What we had
>> previously was just a heuristic.  I think enabling this new
>> information in GDB will allow us to do better overall, so I think we
>> should make this change and fix any issues as they show up.
>>
> 
> I agree with that, thanks for this explanation.
> 
> However, I think I found a small problem in this patch.
> You can see it with the next-inline test case.
> When you use just step the execution does not stop
> between the inline functions, because not calling 
> 
> current behaviour is this:
> 
> Breakpoint 1, main () at next-inline.cc:63
> 63	  get_alias_set (&xx);
> (gdb) s
> get_alias_set (t=0x404040 <xx>) at next-inline.cc:50
> 50	  if (t != NULL
> (gdb) s
> 51	      && TREE_TYPE (t).z != 1
> (gdb) s
> 0x0000000000401169 in tree_check (i=0, t=0x404040 <xx>) at next-inline.h:34
> 34	  if (t->x != i)
> (gdb) s
> 0x000000000040117d in tree_check (i=0, t=0x404040 <xx>) at next-inline.h:34
> 34	  if (t->x != i)
> (gdb) s
> 0x000000000040118f in tree_check (i=0, t=0x404040 <xx>) at next-inline.h:34
> 34	  if (t->x != i)
> (gdb) s
> main () at next-inline.cc:64
> 64	  return 0;
> (gdb) 
> 
> I debugged a bit because I was not sure why that happens, and it looks
> like set_step_info does also set the current inline frame, but you
> only want to suppress the line number not the currently executing
> inline function.
> With this small patch the step works as expected.
> 

Hmm, I think this got stuck, so I just worked a follow-up patch out for you.

I also noticed that the test case cannot work with gcc before gcc-8,
since the is_stmt feature is not implemented earlier, at least with
gcc-4.8 and gcc-5.4 the test case fails.

I thought it would be good to detect that by adding the -gstatement-frontiers
option, so that the gcc driver complains when it is not able to generate
sufficient debug info for that test.

Thoughts?


Bernd.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-next-inline-test-case-with-step.patch --]
[-- Type: text/x-patch; name="0001-Fix-next-inline-test-case-with-step.patch", Size: 4079 bytes --]

From 3f39d068e3386ba204a598fa3d2946d357b1d7b2 Mon Sep 17 00:00:00 2001
From: Bernd Edlinger <bernd.edlinger@hotmail.de>
Date: Fri, 14 Feb 2020 20:41:51 +0100
Subject: [PATCH] Fix next-inline test case with step

Should stop between inline function invocations.

Add -gstatement-frontiers compile option to avoid
running test with gcc version that don't support
this feature, which would fail the test unnecessarily.
Instead make the compile step fail which is less noisy.

Add a prefix use_header / no_header to all test cases.

gdb:
2020-03-05  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	* infrun.c (process_event_stop_test): Set step_frame_id.

gdb/testsuite:
2020-03-05  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	* gdb.cp/next-inline.exp: Extend test case.
---
 gdb/infrun.c                         |  2 ++
 gdb/testsuite/gdb.cp/next-inline.exp | 45 +++++++++++++++++++++++++++++++++++-
 2 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 6c35805..e8221ba 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -7219,6 +7219,8 @@ process_event_stop_test (struct execution_control_state *ecs)
   ecs->event_thread->control.step_range_start = stop_pc_sal.pc;
   ecs->event_thread->control.step_range_end = stop_pc_sal.end;
   ecs->event_thread->control.may_range_step = 1;
+  ecs->event_thread->control.step_frame_id = get_frame_id (frame);
+  ecs->event_thread->control.step_stack_frame_id = get_stack_frame_id (frame);
   if (refresh_step_info)
     set_step_info (frame, stop_pc_sal);
 
diff --git a/gdb/testsuite/gdb.cp/next-inline.exp b/gdb/testsuite/gdb.cp/next-inline.exp
index 0b2b22d..ec04694 100644
--- a/gdb/testsuite/gdb.cp/next-inline.exp
+++ b/gdb/testsuite/gdb.cp/next-inline.exp
@@ -20,12 +20,16 @@ standard_testfile .cc
 proc do_test { use_header } {
     global srcfile testfile
 
-    set options {c++ debug nowarnings optimize=-O2}
+    set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
     if { $use_header } {
 	lappend options additional_flags=-DUSE_NEXT_INLINE_H
 	set executable "$testfile-with-header"
+	set hdrfile "next-inline.h"
+	set prefix "use_header"
     } else {
 	set executable "$testfile-no-header"
+	set hdrfile "$srcfile"
+	set prefix "no_header"
     }
 
     if { [prepare_for_testing "failed to prepare" $executable \
@@ -33,6 +37,8 @@ proc do_test { use_header } {
 	return -1
     }
 
+    with_test_prefix $prefix {
+
     if ![runto_main] {
 	fail "can't run to main"
 	return
@@ -64,6 +70,43 @@ proc do_test { use_header } {
     gdb_test "bt" \
 	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
 	"not in inline 5"
+
+    if {!$use_header} {
+        return #until that is fixed
+    }
+
+    if ![runto_main] {
+	fail "can't run to main pass 2"
+	return
+    }
+
+    gdb_test "bt" "\\s*\\#0\\s+main.*" "in main pass 2"
+    gdb_test "step" ".*" "step into get_alias_set pass 2"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"in get_alias_set pass 2"
+    gdb_test "step" ".*TREE_TYPE.*" "step 1"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 1 pass 2"
+    gdb_test "step" ".*if \\(t->x != i\\).*" "step 2"
+    gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
+	"in inline 1 pass 2"
+    gdb_test "step" ".*TREE_TYPE.*" "step 3"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 2 pass 2"
+    gdb_test "step" ".*if \\(t->x != i\\).*" "step 4"
+    gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
+	"in inline 2 pass 2"
+    gdb_test "step" ".*TREE_TYPE.*" "step 5"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 3 pass 2"
+    gdb_test "step" ".*if \\(t->x != i\\).*" "step 6"
+    gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
+	"in inline 3 pass 2"
+    gdb_test "step" "return 0.*" "step 7"
+    gdb_test "bt" \
+	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
+	"not in inline 4 pass 2"
+    }
 }
 
 do_test 0
-- 
1.9.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCHv2 0/2] Line table is_stmt support
  2020-03-05 18:01         ` Bernd Edlinger
@ 2020-03-08 12:50           ` Andrew Burgess
  2020-03-08 14:39             ` Bernd Edlinger
                               ` (3 more replies)
  2020-03-08 12:50           ` [PATCHv2 1/2] gdb/testsuite: Add is-stmt support to the DWARF compiler Andrew Burgess
                             ` (2 subsequent siblings)
  3 siblings, 4 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-03-08 12:50 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Andrew Burgess

Patch #1 is unchanged.

Patch #2 includes additional changes in infrun.c based on Bernd's
suggested fix, as well as his additional tests.

Bernd,

If you are happy with this version of the patch that I'll merge this
in the next few days.

Thanks,
Andrew

--

Andrew Burgess (2):
  gdb/testsuite: Add is-stmt support to the DWARF compiler
  gdb: Add support for tracking the DWARF line table is-stmt field

 gdb/ChangeLog                                 |  38 ++++
 gdb/buildsym-legacy.c                         |   4 +-
 gdb/buildsym.c                                |  14 +-
 gdb/buildsym.h                                |   3 +-
 gdb/disasm.c                                  |   6 +
 gdb/dwarf2/read.c                             |  13 +-
 gdb/infrun.c                                  |  51 +++--
 gdb/jit.c                                     |   1 +
 gdb/record-btrace.c                           |  11 +-
 gdb/stack.c                                   |   2 +-
 gdb/symmisc.c                                 |  10 +-
 gdb/symtab.c                                  |  25 ++-
 gdb/symtab.h                                  |  10 +
 gdb/testsuite/ChangeLog                       |  16 ++
 gdb/testsuite/gdb.cp/step-and-next-inline.cc  |  66 +++++++
 gdb/testsuite/gdb.cp/step-and-next-inline.exp | 119 ++++++++++++
 gdb/testsuite/gdb.cp/step-and-next-inline.h   |  38 ++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c      |  99 ++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp    | 265 +++++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c        |  61 ++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp      | 267 ++++++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp  |   6 +-
 gdb/testsuite/lib/dwarf.exp                   |   8 +-
 gdb/xcoffread.c                               |   4 +
 24 files changed, 1107 insertions(+), 30 deletions(-)
 create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.cc
 create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.exp
 create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.h
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp

-- 
2.14.5


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCHv2 1/2] gdb/testsuite: Add is-stmt support to the DWARF compiler
  2020-03-05 18:01         ` Bernd Edlinger
  2020-03-08 12:50           ` [PATCHv2 0/2] Line table is_stmt support Andrew Burgess
@ 2020-03-08 12:50           ` Andrew Burgess
  2020-03-08 12:50           ` [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
  2020-06-01 13:26           ` [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Pedro Alves
  3 siblings, 0 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-03-08 12:50 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Andrew Burgess

This commit adds the ability to set and toggle the DWARF line table
is-stmt flag.

A DWARF line table can now be created with the attribute
'default_is_stmt' like this:

  lines {version 2 default_is_stmt 0} label {
    ...
  }

If 'default_is_stmt' is not specified then the current default is 1,
which matches the existing behaviour.

Inside the DWARF line table program you can now make use of
{DW_LNS_negate_stmt} to toggle the is-stmt flag, for example this
meaningless program:

  lines {version 2 default_is_stmt 0} label {
    include_dir "some_directory"
    file_name "some_filename" 1

    program {
      {DW_LNS_negate_stmt}
      {DW_LNE_end_sequence}
    }
  }

This new functionality will be used in a later commit.

gdb/testsuite/ChangeLog:

	* lib/dwarf.exp (Dwarf::lines) Add support for modifying the
	is-stmt flag in the line table.

Change-Id: Ia3f61d523826382dd2333f65b9aae368ad29c4a5
---
 gdb/testsuite/ChangeLog     | 5 +++++
 gdb/testsuite/lib/dwarf.exp | 8 +++++++-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index 6c6ffbe7c2f..417b22d2345 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -1311,12 +1311,14 @@ namespace eval Dwarf {
 	set _unit_addr_size default
 	set _line_saw_program 0
 	set _line_saw_file 0
+	set _default_is_stmt 1
 
 	foreach { name value } $options {
 	    switch -exact -- $name {
 		is_64 { set is_64 $value }
 		version { set _unit_version $value }
 		addr_size { set _unit_addr_size $value }
+		default_is_stmt { set _default_is_stmt $value }
 		default { error "unknown option $name" }
 	    }
 	}
@@ -1363,7 +1365,7 @@ namespace eval Dwarf {
 	define_label $header_len_label
 
 	_op .byte 1 "minimum_instruction_length"
-	_op .byte 1 "default_is_stmt"
+	_op .byte $_default_is_stmt "default_is_stmt"
 	_op .byte 1 "line_base"
 	_op .byte 1 "line_range"
 	_op .byte 10 "opcode_base"
@@ -1438,6 +1440,10 @@ namespace eval Dwarf {
 		_op .byte 1
 	    }
 
+	    proc DW_LNS_negate_stmt {} {
+		_op .byte 6
+	    }
+
 	    proc DW_LNS_advance_pc {offset} {
 		_op .byte 2
 		_op .uleb128 ${offset}
-- 
2.14.5


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-03-05 18:01         ` Bernd Edlinger
  2020-03-08 12:50           ` [PATCHv2 0/2] Line table is_stmt support Andrew Burgess
  2020-03-08 12:50           ` [PATCHv2 1/2] gdb/testsuite: Add is-stmt support to the DWARF compiler Andrew Burgess
@ 2020-03-08 12:50           ` Andrew Burgess
  2020-03-16 20:57             ` Tom Tromey
                               ` (4 more replies)
  2020-06-01 13:26           ` [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Pedro Alves
  3 siblings, 5 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-03-08 12:50 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Andrew Burgess

This commit brings support for the DWARF line table is_stmt field to
GDB.  The is_stmt field is used by the compiler when a single source
line is split into multiple assembler instructions, especially if the
assembler instructions are interleaved with instruction from other
source lines.

The compiler will set the is_stmt flag false from some instructions
from the source lines, these instructions are not a good place to
insert a breakpoint in order to stop at the source line.
Instructions which are marked with the is_stmt flag true are a good
place to insert a breakpoint for that source line.

Currently GDB ignores all instructions for which is_stmt is false.
This is fine in a lot of cases, however, there are some cases where
this means the debug experience is not as good as it could be.

Consider stopping at a random instruction, currently this instruction
will be attributed to the last line table entry before this point for
which is_stmt was true - as these are the only line table entries that
GDB tracks.  This can easily be incorrect in code with even a low
level of optimisation.

With is_stmt tracking in place, when stopping at a random instruction
we now attribute the instruction back to the real source line, even
when is_stmt is false for that instruction in the line table.

When inserting breakpoints we still select line table entries for
which is_stmt is true, so the breakpoint placing behaviour should not
change.

When stepping though code (at the line level, not the instruction
level) we will still stop at instruction where is_stmt is true, I
think this is more likely to be the desired behaviour.

Instruction stepping is, of course, unchanged, stepping one
instruction at a time, but we should now report more accurate line
table information with each instruction step.

The original motivation for this work was a patch posted by Bernd
here:
  https://sourceware.org/ml/gdb-patches/2019-11/msg00792.html

As part of that thread it was suggested that many issues would be
resolved if GDB supported line table views, this isn't something I've
attempted in this patch, though reading the spec, it seems like this
would be a useful feature to support in GDB in the future.  The spec
is here:
  http://dwarfstd.org/ShowIssue.php?issue=170427.1

And Bernd gives a brief description of the benefits here:
  https://sourceware.org/ml/gdb-patches/2020-01/msg00147.html

With that all said, I think that there is benefit to having proper
is_stmt support regardless of whether we have views support, so I
think we should consider getting this in first, and then building view
support on top of this.

The gdb.cp/step-and-next-inline.exp test is based off a test proposed
by Bernd Edlinger in this message:
  https://sourceware.org/ml/gdb-patches/2019-12/msg00842.html

gdb/ChangeLog:

	* buildsym-legacy.c (record_line): Pass extra parameter to
	record_line.
	* buildsym.c (buildsym_compunit::record_line): Take an extra
	parameter, reduce duplication in the line table, and record the
	is_stmt flag in the line table.
	* buildsym.h (buildsym_compunit::record_line): Add extra
	parameter.
	* disasm.c (do_mixed_source_and_assembly_deprecated): Ignore
	non-statement lines.
	* dwarf2/read.c (dwarf_record_line_1): Add extra parameter, pass
	this to the symtab builder.
	(dwarf_finish_line): Pass extra parameter to dwarf_record_line_1.
	(lnp_state_machine::record_line): Pass a suitable is_stmt flag
	through to dwarf_record_line_1.
	* infrun.c (process_event_stop_test): When stepping, don't stop at
	a non-statement instruction, and only refresh the step info when
	we land in the middle of a line's range.  Also add an extra
	comment.
	* jit.c (jit_symtab_line_mapping_add_impl): Initialise is_stmt
	field.
	* record-btrace.c (btrace_find_line_range): Only record lines
	marked as is-statement.
	* stack.c (frame_show_address): Show the frame address if we are
	in a non-statement sal.
	* symmisc.c (dump_symtab_1): Print the is_stmt flag.
	(maintenance_print_one_line_table): Print a header for the is_stmt
	column, and include is_stmt information in the output.
	* symtab.c (find_pc_sect_line): Find lines marked as statements in
	preference to non-statements.
	(find_pcs_for_symtab_line): Prefer is-statement entries.
	(find_line_common): Likewise.
	* symtab.h (struct linetable_entry): Add is_stmt field.
	(struct symtab_and_line): Likewise.
	* xcoffread.c (arrange_linetable): Initialise is_stmt field when
	arranging the line table.

gdb/testsuite/ChangeLog:

	* gdb.cp/step-and-next-inline.cc: New file.
	* gdb.cp/step-and-next-inline.exp: New file.
	* gdb.cp/step-and-next-inline.h: New file.
	* gdb.dwarf2/dw2-is-stmt.c: New file.
	* gdb.dwarf2/dw2-is-stmt.exp: New file.
	* gdb.dwarf2/dw2-is-stmt-2.c: New file.
	* gdb.dwarf2/dw2-is-stmt-2.exp: New file.
	* gdb.dwarf2/dw2-ranges-base.exp: Update line table pattern.
---
 gdb/ChangeLog                                 |  38 ++++
 gdb/buildsym-legacy.c                         |   4 +-
 gdb/buildsym.c                                |  14 +-
 gdb/buildsym.h                                |   3 +-
 gdb/disasm.c                                  |   6 +
 gdb/dwarf2/read.c                             |  13 +-
 gdb/infrun.c                                  |  51 +++--
 gdb/jit.c                                     |   1 +
 gdb/record-btrace.c                           |  11 +-
 gdb/stack.c                                   |   2 +-
 gdb/symmisc.c                                 |  10 +-
 gdb/symtab.c                                  |  25 ++-
 gdb/symtab.h                                  |  10 +
 gdb/testsuite/ChangeLog                       |  11 ++
 gdb/testsuite/gdb.cp/step-and-next-inline.cc  |  66 +++++++
 gdb/testsuite/gdb.cp/step-and-next-inline.exp | 119 ++++++++++++
 gdb/testsuite/gdb.cp/step-and-next-inline.h   |  38 ++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c      |  99 ++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp    | 265 +++++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c        |  61 ++++++
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp      | 267 ++++++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp  |   6 +-
 gdb/xcoffread.c                               |   4 +
 23 files changed, 1095 insertions(+), 29 deletions(-)
 create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.cc
 create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.exp
 create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.h
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp

diff --git a/gdb/buildsym-legacy.c b/gdb/buildsym-legacy.c
index bf19d2d90a7..d9c27ebc956 100644
--- a/gdb/buildsym-legacy.c
+++ b/gdb/buildsym-legacy.c
@@ -252,7 +252,9 @@ void
 record_line (struct subfile *subfile, int line, CORE_ADDR pc)
 {
   gdb_assert (buildsym_compunit != nullptr);
-  buildsym_compunit->record_line (subfile, line, pc);
+  /* Assume every line entry is a statement start, that is a good place to
+     put a breakpoint for that line number.  */
+  buildsym_compunit->record_line (subfile, line, pc, true);
 }
 
 /* Start a new symtab for a new source file in OBJFILE.  Called, for example,
diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 84cb44277a4..24aeba8e252 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -666,7 +666,7 @@ buildsym_compunit::pop_subfile ()
 
 void
 buildsym_compunit::record_line (struct subfile *subfile, int line,
-				CORE_ADDR pc)
+				CORE_ADDR pc, bool is_stmt)
 {
   struct linetable_entry *e;
 
@@ -681,6 +681,17 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
       m_have_line_numbers = true;
     }
 
+  /* If we have a duplicate for the previous entry then ignore the new
+     entry, except, if the new entry is setting the is_stmt flag, then
+     ensure the previous entry respects the new setting.  */
+  e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
+  if (e->line == line && e->pc == pc)
+    {
+      if (is_stmt && !e->is_stmt)
+	e->is_stmt = 1;
+      return;
+    }
+
   if (subfile->line_vector->nitems + 1 >= subfile->line_vector_length)
     {
       subfile->line_vector_length *= 2;
@@ -716,6 +727,7 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
 
   e = subfile->line_vector->item + subfile->line_vector->nitems++;
   e->line = line;
+  e->is_stmt = is_stmt ? 1 : 0;
   e->pc = pc;
 }
 
diff --git a/gdb/buildsym.h b/gdb/buildsym.h
index c8e1bd0d5a7..c768a4c2dae 100644
--- a/gdb/buildsym.h
+++ b/gdb/buildsym.h
@@ -187,7 +187,8 @@ struct buildsym_compunit
 
   const char *pop_subfile ();
 
-  void record_line (struct subfile *subfile, int line, CORE_ADDR pc);
+  void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
+		    bool is_stmt);
 
   struct compunit_symtab *get_compunit_symtab ()
   {
diff --git a/gdb/disasm.c b/gdb/disasm.c
index e45c8400689..143ba2f59b9 100644
--- a/gdb/disasm.c
+++ b/gdb/disasm.c
@@ -376,6 +376,12 @@ do_mixed_source_and_assembly_deprecated
       if (le[i].line == le[i + 1].line && le[i].pc == le[i + 1].pc)
 	continue;		/* Ignore duplicates.  */
 
+      /* Ignore non-statement line table entries.  This means we print the
+	 source line at the place where GDB would insert a breakpoint for
+	 that line, which seems more intuitive.  */
+      if (le[i].is_stmt == 0)
+	continue;
+
       /* Skip any end-of-function markers.  */
       if (le[i].line == 0)
 	continue;
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 1d4397dfabc..1706b96cc04 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -19939,7 +19939,7 @@ dwarf_record_line_p (struct dwarf2_cu *cu,
 
 static void
 dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
-		     unsigned int line, CORE_ADDR address,
+		     unsigned int line, CORE_ADDR address, bool is_stmt,
 		     struct dwarf2_cu *cu)
 {
   CORE_ADDR addr = gdbarch_addr_bits_remove (gdbarch, address);
@@ -19953,7 +19953,7 @@ dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
     }
 
   if (cu != nullptr)
-    cu->get_builder ()->record_line (subfile, line, addr);
+    cu->get_builder ()->record_line (subfile, line, addr, is_stmt);
 }
 
 /* Subroutine of dwarf_decode_lines_1 to simplify it.
@@ -19976,7 +19976,7 @@ dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
 			  paddress (gdbarch, address));
     }
 
-  dwarf_record_line_1 (gdbarch, subfile, 0, address, cu);
+  dwarf_record_line_1 (gdbarch, subfile, 0, address, true, cu);
 }
 
 void
@@ -20003,8 +20003,7 @@ lnp_state_machine::record_line (bool end_sequence)
   else if (m_op_index == 0 || end_sequence)
     {
       fe->included_p = 1;
-      if (m_record_lines_p
-	  && (producer_is_codewarrior (m_cu) || m_is_stmt || end_sequence))
+      if (m_record_lines_p)
 	{
 	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
 	      || end_sequence)
@@ -20015,6 +20014,8 @@ lnp_state_machine::record_line (bool end_sequence)
 
 	  if (!end_sequence)
 	    {
+	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
+
 	      if (dwarf_record_line_p (m_cu, m_line, m_last_line,
 				       m_line_has_non_zero_discriminator,
 				       m_last_subfile))
@@ -20022,7 +20023,7 @@ lnp_state_machine::record_line (bool end_sequence)
 		  buildsym_compunit *builder = m_cu->get_builder ();
 		  dwarf_record_line_1 (m_gdbarch,
 				       builder->get_current_subfile (),
-				       m_line, m_address,
+				       m_line, m_address, is_stmt,
 				       m_currently_recording_lines ? m_cu : nullptr);
 		}
 	      m_last_subfile = m_cu->get_builder ()->get_current_subfile ();
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 2a319295d36..d672d1a1609 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -7045,6 +7045,10 @@ process_event_stop_test (struct execution_control_state *ecs)
 	}
     }
 
+  /* This always returns the sal for the inner-most frame when we are in a
+     stack of inlined frames, even if GDB actually believes that it is in a
+     more outer frame.  This is checked for below by calls to
+     inline_skipped_frames.  */
   stop_pc_sal = find_pc_line (ecs->event_thread->suspend.stop_pc, 0);
 
   /* NOTE: tausq/2004-05-24: This if block used to be done before all
@@ -7179,19 +7183,36 @@ process_event_stop_test (struct execution_control_state *ecs)
       return;
     }
 
+  bool refresh_step_info = true;
   if ((ecs->event_thread->suspend.stop_pc == stop_pc_sal.pc)
       && (ecs->event_thread->current_line != stop_pc_sal.line
  	  || ecs->event_thread->current_symtab != stop_pc_sal.symtab))
     {
-      /* We are at the start of a different line.  So stop.  Note that
-         we don't stop if we step into the middle of a different line.
-         That is said to make things like for (;;) statements work
-         better.  */
-      if (debug_infrun)
-	 fprintf_unfiltered (gdb_stdlog,
-			     "infrun: stepped to a different line\n");
-      end_stepping_range (ecs);
-      return;
+      if (stop_pc_sal.is_stmt)
+	{
+	  /* We are at the start of a different line.  So stop.  Note that
+	     we don't stop if we step into the middle of a different line.
+	     That is said to make things like for (;;) statements work
+	     better.  */
+	  if (debug_infrun)
+	    fprintf_unfiltered (gdb_stdlog,
+				"infrun: stepped to a different line\n");
+	  end_stepping_range (ecs);
+	  return;
+	}
+      else if (frame_id_eq (get_frame_id (get_current_frame ()),
+			    ecs->event_thread->control.step_frame_id))
+	{
+	  /* We are at the start of a different line, however, this line is
+	     not marked as a statement, and we have not changed frame.  We
+	     ignore this line table entry, and continue stepping forward,
+	     looking for a better place to stop.  */
+	  refresh_step_info = false;
+	  if (debug_infrun)
+	    fprintf_unfiltered (gdb_stdlog,
+				"infrun: stepped to a different line, but "
+				"it's not the start of a statement\n");
+	}
     }
 
   /* We aren't done stepping.
@@ -7199,12 +7220,20 @@ process_event_stop_test (struct execution_control_state *ecs)
      Optimize by setting the stepping range to the line.
      (We might not be in the original line, but if we entered a
      new line in mid-statement, we continue stepping.  This makes
-     things like for(;;) statements work better.)  */
+     things like for(;;) statements work better.)
+
+     If we entered a SAL that indicates a non-statement line table entry,
+     then we update the stepping range, but we don't update the step info,
+     which includes things like the line number we are stepping away from.
+     This means we will stop when we find a line table entry that is marked
+     as is-statement, even if it matches the non-statement one we just
+     stepped into.   */
 
   ecs->event_thread->control.step_range_start = stop_pc_sal.pc;
   ecs->event_thread->control.step_range_end = stop_pc_sal.end;
   ecs->event_thread->control.may_range_step = 1;
-  set_step_info (ecs->event_thread, frame, stop_pc_sal);
+  if (refresh_step_info)
+    set_step_info (ecs->event_thread, frame, stop_pc_sal);
 
   if (debug_infrun)
      fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
diff --git a/gdb/jit.c b/gdb/jit.c
index 35b7167270d..8eb98f4a38a 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -579,6 +579,7 @@ jit_symtab_line_mapping_add_impl (struct gdb_symbol_callbacks *cb,
     {
       stab->linetable->item[i].pc = (CORE_ADDR) map[i].pc;
       stab->linetable->item[i].line = map[i].line;
+      stab->linetable->item[i].is_stmt = 1;
     }
 }
 
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index ef23a0b7af7..d3da8527c5c 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -718,7 +718,16 @@ btrace_find_line_range (CORE_ADDR pc)
   range = btrace_mk_line_range (symtab, 0, 0);
   for (i = 0; i < nlines - 1; i++)
     {
-      if ((lines[i].pc == pc) && (lines[i].line != 0))
+      /* The test of is_stmt here was added when the is_stmt field was
+	 introduced to the 'struct linetable_entry' structure.  This
+	 ensured that this loop maintained the same behaviour as before we
+	 introduced is_stmt.  That said, it might be that we would be
+	 better off not checking is_stmt here, this would lead to us
+	 possibly adding more line numbers to the range.  At the time this
+	 change was made I was unsure how to test this so chose to go with
+	 maintaining the existing experience.  */
+      if ((lines[i].pc == pc) && (lines[i].line != 0)
+	  && (lines[i].is_stmt == 1))
 	range = btrace_line_range_add (range, lines[i].line);
     }
 
diff --git a/gdb/stack.c b/gdb/stack.c
index 266d771e35f..024ead0b611 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -330,7 +330,7 @@ frame_show_address (struct frame_info *frame,
       return false;
     }
 
-  return get_frame_pc (frame) != sal.pc;
+  return get_frame_pc (frame) != sal.pc || !sal.is_stmt;
 }
 
 /* See frame.h.  */
diff --git a/gdb/symmisc.c b/gdb/symmisc.c
index 1d7c3816670..4bf1f08849f 100644
--- a/gdb/symmisc.c
+++ b/gdb/symmisc.c
@@ -301,6 +301,8 @@ dump_symtab_1 (struct symtab *symtab, struct ui_file *outfile)
 	{
 	  fprintf_filtered (outfile, " line %d at ", l->item[i].line);
 	  fputs_filtered (paddress (gdbarch, l->item[i].pc), outfile);
+	  if (l->item[i].is_stmt)
+	    fprintf_filtered (outfile, "\t(stmt)");
 	  fprintf_filtered (outfile, "\n");
 	}
     }
@@ -987,8 +989,8 @@ maintenance_print_one_line_table (struct symtab *symtab, void *data)
 
       /* Leave space for 6 digits of index and line number.  After that the
 	 tables will just not format as well.  */
-      printf_filtered (_("%-6s %6s %s\n"),
-		       _("INDEX"), _("LINE"), _("ADDRESS"));
+      printf_filtered (_("%-6s %6s %s %s\n"),
+		       _("INDEX"), _("LINE"), _("ADDRESS"), _("IS-STMT"));
 
       for (i = 0; i < linetable->nitems; ++i)
 	{
@@ -1000,7 +1002,9 @@ maintenance_print_one_line_table (struct symtab *symtab, void *data)
 	    printf_filtered ("%6d ", item->line);
 	  else
 	    printf_filtered ("%6s ", _("END"));
-	  printf_filtered ("%s\n", core_addr_to_string (item->pc));
+	  printf_filtered ("%s%s\n",
+			   core_addr_to_string (item->pc),
+			   (item->is_stmt ? " Y" : ""));
 	}
     }
 
diff --git a/gdb/symtab.c b/gdb/symtab.c
index a80b80db5af..44b711397d5 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3236,6 +3236,23 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
 	  best = prev;
 	  best_symtab = iter_s;
 
+	  /* If during the binary search we land on a non-statement entry,
+	     scan backward through entries at the same address to see if
+	     there is an entry marked as is-statement.  In theory this
+	     duplication should have been removed from the line table
+	     during construction, this is just a double check.  If the line
+	     table has had the duplication removed then this should be
+	     pretty cheap.  */
+	  if (!best->is_stmt)
+	    {
+	      struct linetable_entry *tmp = best;
+	      while (tmp > first && (tmp - 1)->pc == tmp->pc
+		     && (tmp - 1)->line != 0 && !tmp->is_stmt)
+		--tmp;
+	      if (tmp->is_stmt)
+		best = tmp;
+	    }
+
 	  /* Discard BEST_END if it's before the PC of the current BEST.  */
 	  if (best_end <= best->pc)
 	    best_end = 0;
@@ -3266,6 +3283,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
     }
   else
     {
+      val.is_stmt = best->is_stmt;
       val.symtab = best_symtab;
       val.line = best->line;
       val.pc = best->pc;
@@ -3434,7 +3452,8 @@ find_pcs_for_symtab_line (struct symtab *symtab, int line,
 	{
 	  struct linetable_entry *item = &SYMTAB_LINETABLE (symtab)->item[idx];
 
-	  if (*best_item == NULL || item->line < (*best_item)->line)
+	  if (*best_item == NULL
+	      || (item->line < (*best_item)->line && item->is_stmt))
 	    *best_item = item;
 
 	  break;
@@ -3545,6 +3564,10 @@ find_line_common (struct linetable *l, int lineno,
     {
       struct linetable_entry *item = &(l->item[i]);
 
+      /* Ignore non-statements.  */
+      if (!item->is_stmt)
+	continue;
+
       if (item->line == lineno)
 	{
 	  /* Return the first (lowest address) entry which matches.  */
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 5fa067b5f48..771b5ec5bf7 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1277,7 +1277,13 @@ struct rust_vtable_symbol : public symbol
 
 struct linetable_entry
 {
+  /* The line number for this entry.  */
   int line;
+
+  /* True if this PC is a good location to place a breakpoint for LINE.  */
+  unsigned is_stmt : 1;
+
+  /* The address for this entry.  */
   CORE_ADDR pc;
 };
 
@@ -1853,6 +1859,10 @@ struct symtab_and_line
   bool explicit_pc = false;
   bool explicit_line = false;
 
+  /* If the line number information is valid, then this indicates if this
+     line table entry had the is-stmt flag set or not.  */
+  bool is_stmt = false;
+
   /* The probe associated with this symtab_and_line.  */
   probe *prob = NULL;
   /* If PROBE is not NULL, then this is the objfile in which the probe
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.cc b/gdb/testsuite/gdb.cp/step-and-next-inline.cc
new file mode 100644
index 00000000000..d923cc53413
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.cc
@@ -0,0 +1,66 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019-2020 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/>.  */
+
+#ifdef USE_NEXT_INLINE_H
+
+#include "step-and-next-inline.h"
+
+#else	/* USE_NEXT_INLINE_H */
+
+/* The code below must remain identical to the code in
+   step-and-next-inline.h.  */
+
+#include <stdlib.h>
+
+struct tree
+{
+  volatile int x;
+  volatile int z;
+};
+
+#define TREE_TYPE(NODE) (*tree_check (NODE, 0))
+
+inline tree *
+tree_check (tree *t, int i)
+{
+  if (t->x != i)
+    abort();
+  tree *x = t;
+  return x;
+}
+
+#endif	/* USE_NEXT_INLINE_H */
+
+int __attribute__((noinline, noclone))
+get_alias_set (tree *t)
+{
+  if (t != NULL
+      && TREE_TYPE (t).z != 1
+      && TREE_TYPE (t).z != 2
+      && TREE_TYPE (t).z != 3)
+    return 0;
+  return 1;
+}
+
+tree xx;
+
+int
+main()
+{
+  get_alias_set (&xx);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
new file mode 100644
index 00000000000..acec48ba81d
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -0,0 +1,119 @@
+# Copyright 2019-2020 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/>.
+
+standard_testfile .cc
+
+# Compile the test source with USE_NEXT_INLINE_H defined (when
+# use_header is true), or not defined.
+proc do_test { use_header } {
+    global srcfile testfile
+
+    set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
+    if { $use_header } {
+	lappend options additional_flags=-DUSE_NEXT_INLINE_H
+	set executable "$testfile-with-header"
+	set hdrfile "step-and-next-inline.h"
+	set prefix "use_header"
+    } else {
+	set executable "$testfile-no-header"
+	set hdrfile "$srcfile"
+	set prefix "no_header"
+    }
+
+    if { [prepare_for_testing "failed to prepare" $executable \
+	      $srcfile $options] } {
+	return -1
+    }
+
+    with_test_prefix $prefix {
+
+    if ![runto_main] {
+	fail "can't run to main"
+	return
+    }
+
+    gdb_test "bt" "\\s*\\#0\\s+main.*" "in main"
+    gdb_test "step" ".*" "step into get_alias_set"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 1"
+    # It's possible that this first failure (when not using a header
+    # file) is GCC's fault, though the remaining failures would best
+    # be fixed by adding location views support (though it could be
+    # that some easier heuristic could be figured out).  Still, it is
+    # not certain that the first failure wouldn't also be fixed by
+    # having location view support, so for now it is tagged as such.
+    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
+    gdb_test "next" ".*TREE_TYPE.*" "next step 1"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 2"
+    gdb_test "next" ".*TREE_TYPE.*" "next step 2"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 3"
+    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
+    gdb_test "next" ".*TREE_TYPE.*" "next step 3"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 4"
+    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
+    gdb_test "next" "return 0.*" "next step 4"
+    gdb_test "bt" \
+	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
+	"not in inline 5"
+
+    if {!$use_header} {
+	# With the debug from GCC 10.x (and earlier) GDB is currently
+	# unable to successfully complete the following tests when we
+	# are not using a header file.
+	kfail symtab/25507 "stepping tests"
+	return
+    }
+
+    clean_restart ${executable}
+
+    if ![runto_main] {
+	fail "can't run to main pass 2"
+	return
+    }
+
+    gdb_test "bt" "\\s*\\#0\\s+main.*" "in main pass 2"
+    gdb_test "step" ".*" "step into get_alias_set pass 2"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"in get_alias_set pass 2"
+    gdb_test "step" ".*TREE_TYPE.*" "step 1"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 1 pass 2"
+    gdb_test "step" ".*if \\(t->x != i\\).*" "step 2"
+    gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
+	"in inline 1 pass 2"
+    gdb_test "step" ".*TREE_TYPE.*" "step 3"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 2 pass 2"
+    gdb_test "step" ".*if \\(t->x != i\\).*" "step 4"
+    gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
+	"in inline 2 pass 2"
+    gdb_test "step" ".*TREE_TYPE.*" "step 5"
+    gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
+	"not in inline 3 pass 2"
+    gdb_test "step" ".*if \\(t->x != i\\).*" "step 6"
+    gdb_test "bt" "\\s*\\#0\\s+\[^\r\]*tree_check\[^\r\]*${hdrfile}:.*" \
+	"in inline 3 pass 2"
+    gdb_test "step" "return 0.*" "step 7"
+    gdb_test "bt" \
+	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
+	"not in inline 4 pass 2"
+    }
+}
+
+do_test 0
+do_test 1
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.h b/gdb/testsuite/gdb.cp/step-and-next-inline.h
new file mode 100644
index 00000000000..6c6d90a37ff
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.h
@@ -0,0 +1,38 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019-2020 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/>.  */
+
+/* The code below must remain identical to the block of code in
+   step-and-next-inline.cc.  */
+
+#include <stdlib.h>
+
+struct tree
+{
+  volatile int x;
+  volatile int z;
+};
+
+#define TREE_TYPE(NODE) (*tree_check (NODE, 0))
+
+inline tree *
+tree_check (tree *t, int i)
+{
+  if (t->x != i)
+    abort();
+  tree *x = t;
+  return x;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c
new file mode 100644
index 00000000000..be3fa458cf0
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c
@@ -0,0 +1,99 @@
+/* Copyright 2020 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/>.  */
+
+/* This tests GDB's handling of the DWARF is-stmt field in the line table.
+
+   This field is used when many addresses all represent the same source
+   line.  The address(es) at which it is suitable to place a breakpoint for
+   a line are marked with is-stmt true, while address(es) that are not good
+   places to place a breakpoint are marked as is-stmt false.
+
+   In order to build a reproducible test and exercise GDB's is-stmt
+   support, we will be generating our own DWARF.  The test will contain a
+   series of C source lines, ensuring that we get a series of assembler
+   instructions.  Each C source line will be given an assembler label,
+   which we use to generate a fake line table.
+
+   In this fake line table each assembler block is claimed to represent a
+   single C source line, however, we will toggle the is-stmt flag.  We can
+   then debug this with GDB and test the handling of is-stmt.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N)						\
+  do							\
+    {							\
+      asm ("line_label_" #N ": .globl line_label_" #N); \
+      var = (N);					\
+      bar = ((N) + 1);					\
+    }							\
+  while (0)
+
+volatile int var;
+volatile int bar;
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+
+  /* main start */
+
+  /* Line 1.  */
+  /* Line 2.  */
+  /* Line 3.  */
+  /* Line 4.  */
+  /* Line 5.  */
+
+  LL (0);
+  LL (1);
+  LL (2);
+  LL (3);
+  LL (4);
+  LL (5);
+  LL (6);
+  LL (7);
+  LL (8);
+  LL (9);
+  LL (10);
+  LL (11);
+  LL (12);
+  LL (13);
+  LL (14);
+  LL (15);
+  LL (16);
+  return 0;				/* main end */
+}
+
+#if 0
+
+PROLOGUE*
+1: L1
+2: L1*
+3: L2
+4: L1
+L3
+L4
+L4*
+L2*
+L2
+L3
+L5
+L5*
+L3
+L4
+L5
+RETURN
+
+#endif
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
new file mode 100644
index 00000000000..436c4d01024
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
@@ -0,0 +1,265 @@
+# Copyright 2020 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/>.
+
+# This test shows the importance of not corrupting the order of line
+# table information.  When multiple lines are given for the same
+# address the compiler usually lists these in the order in which we
+# would expect to encounter them.  When stepping through nested inline
+# frames the last line given for an address is assumed by GDB to be
+# the most inner frame, and this is what GDB displays.
+#
+# If we corrupt the order of the line table entries then GDB will
+# display the wrong line as being the inner most frame.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-is-stmt-2.c dw2-is-stmt-2.S
+
+# Extract the start, length, and end for function called NAME and
+# create suitable variables in the callers scope.
+proc get_func_info { name } {
+    global srcdir subdir srcfile
+
+    upvar 1 "${name}_start" func_start
+    upvar 1 "${name}_len" func_len
+    upvar 1 "${name}_end" func_end
+
+    lassign [function_range ${name} [list ${srcdir}/${subdir}/$srcfile]] \
+	func_start func_len
+    set func_end "$func_start + $func_len"
+}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile
+    declare_labels lines_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {language @DW_LANG_C}
+	    {name dw2-is-stmt.c}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {}
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile" 1
+
+	program {
+	    {DW_LNE_set_address main}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "main prologue"] - 1]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_0}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "main start"] \
+		      - [gdb_get_line_number "main prologue"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 1"] \
+		      - [gdb_get_line_number "main start"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 2"] \
+		      - [gdb_get_line_number "Line 1"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 1"] \
+		      - [gdb_get_line_number "Line 2"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 3"] \
+		      - [gdb_get_line_number "Line 1"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 4"] \
+		      - [gdb_get_line_number "Line 3"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_8}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 2"] \
+		      - [gdb_get_line_number "Line 4"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_9}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_10}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 3"] \
+		      - [gdb_get_line_number "Line 2"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_11}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 5"] \
+		      - [gdb_get_line_number "Line 3"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_12}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_13}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 3"] \
+		      - [gdb_get_line_number "Line 5"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_14}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 4"] \
+		      - [gdb_get_line_number "Line 3"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_15}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "Line 5"] \
+		      - [gdb_get_line_number "Line 4"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_16}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "main end"] \
+		      - [gdb_get_line_number "Line 5"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address ${main_end}}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Check stepping through the out of order lines gives the experience
+# we expect; lines in the correct order, and stop at the correct
+# labels.Q
+set locs { { "Line 1." "line_label_2" } \
+	       { "Line 4." "line_label_7" } \
+	       { "Line 2." "line_label_8" } \
+	       { "Line 5." "line_label_12" } \
+	       { "Line 3." "line_label_13" } }
+foreach entry $locs {
+    set pattern [lindex $entry 0]
+    set label  [lindex $entry 1]
+
+    set linum [gdb_get_line_number "$pattern"]
+    gdb_test "step" "\r\n$linum\[ \t\]+/\\* $pattern  \\*/" \
+	"step to $pattern"
+
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+	       "read \$pc at $pattern"]
+    set val [get_hexadecimal_valueof "&${label}" "INVALID"]
+    gdb_assert { $pc == $val } \
+	"check pc at $pattern"
+}
+
+# Now restart the test, and this time, single instruction step
+# through.  This is checking that the is-stmt marked lines are
+# displayed differently (without addresses) to addresses that are
+# mid-way through a line, or not marked as is-stmt.
+clean_restart $binfile
+runto_main
+
+foreach entry $locs {
+    set pattern [lindex $entry 0]
+    set label  [lindex $entry 1]
+
+    with_test_prefix "stepi to $label" {
+	# If can take many instruction steps to get to the next label.
+	set i 0
+	set pc [get_hexadecimal_valueof "\$pc" "NO-PC" ]
+	set val [get_hexadecimal_valueof "&${label}" "INVALID"]
+	while { $pc < $val } {
+	    incr i
+	    set line_changed -1
+	    gdb_test_multiple "stepi" "stepi $i" {
+		-re "\r\n$hex\[ \t\]+$decimal\[^\r\n\]+\r\n$gdb_prompt" {
+		    set line_changed 0
+		}
+		-re "\r\n$decimal\[ \t\]+/\\* $pattern  \\*/\r\n$gdb_prompt" {
+		    set line_changed 1
+		}
+	    }
+	    gdb_assert { $line_changed != -1 } \
+		"ensure we saw a valid pattern, $i"
+	    set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+			"get pc inside stepi loop, $i"]
+	    if { $pc == $val } {
+		gdb_assert { $line_changed } \
+		    "line must change at $label"
+	    } else {
+		gdb_assert { !$line_changed } "same line $i"
+	    }
+	}
+    }
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c
new file mode 100644
index 00000000000..7e70995d80f
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c
@@ -0,0 +1,61 @@
+/* Copyright 2020 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/>.  */
+
+/* This tests GDB's handling of the DWARF is-stmt field in the line table.
+
+   This field is used when many addresses all represent the same source
+   line.  The address(es) at which it is suitable to place a breakpoint for
+   a line are marked with is-stmt true, while address(es) that are not good
+   places to place a breakpoint are marked as is-stmt false.
+
+   In order to build a reproducible test and exercise GDB's is-stmt
+   support, we will be generating our own DWARF.  The test will contain a
+   series of C source lines, ensuring that we get a series of assembler
+   instructions.  Each C source line will be given an assembler label,
+   which we use to generate a fake line table.
+
+   In this fake line table each assembler block is claimed to represent a
+   single C source line, however, we will toggle the is-stmt flag.  We can
+   then debug this with GDB and test the handling of is-stmt.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+volatile int var;
+volatile int bar;
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+  LL (1);
+  var = 99;				/* main, set var to 99 */
+  bar = 99;
+
+  LL (2);
+  var = 0;				/* main, set var to 0 */
+  bar = 0;
+
+  LL (3);
+  var = 1;				/* main, set var to 1 */
+  bar = 1;
+
+  LL (4);
+  var = 2;				/* main, set var to 2 */
+  bar = 2;
+
+  LL (5);
+  return 0;				/* main end */
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp
new file mode 100644
index 00000000000..1bcf5b0c698
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp
@@ -0,0 +1,267 @@
+# Copyright 2020 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/>.
+
+# This test shows the importance of not corrupting the order of line
+# table information.  When multiple lines are given for the same
+# address the compiler usually lists these in the order in which we
+# would expect to encounter them.  When stepping through nested inline
+# frames the last line given for an address is assumed by GDB to be
+# the most inner frame, and this is what GDB displays.
+#
+# If we corrupt the order of the line table entries then GDB will
+# display the wrong line as being the inner most frame.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-is-stmt.c dw2-is-stmt.S
+
+# Extract the start, length, and end for function called NAME and
+# create suitable variables in the callers scope.
+proc get_func_info { name } {
+    global srcdir subdir srcfile
+
+    upvar 1 "${name}_start" func_start
+    upvar 1 "${name}_len" func_len
+    upvar 1 "${name}_end" func_end
+
+    lassign [function_range ${name} [list ${srcdir}/${subdir}/$srcfile]] \
+	func_start func_len
+    set func_end "$func_start + $func_len"
+}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile
+    declare_labels lines_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {language @DW_LANG_C}
+	    {name dw2-is-stmt.c}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {}
+	}
+    }
+
+    lines {version 2 default_is_stmt 0} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile" 1
+
+	program {
+	    {DW_LNE_set_address main}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "main prologue"] - 1]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "main, set var to 99"] \
+		      - [gdb_get_line_number "main prologue"]]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "main, set var to 0"] \
+		      - [gdb_get_line_number "main, set var to 99"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line \
+		 [expr [gdb_get_line_number "main end"] \
+		      - [gdb_get_line_number "main, set var to 0"]]}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address ${main_end}}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# First, break by address at a location we know is marked as not a
+# statement.  GDB should still correctly report the file and line
+# number.
+gdb_breakpoint "*line_label_2"
+gdb_continue_to_breakpoint "*line_label_2"
+
+# Now step by instruction.  We should skip over the is-stmt location
+# for this line, and land on the next source line.
+gdb_test "step" "/\\* main end \\*/" \
+    "step to end from line_label_2"
+
+# Restart the test.  This time, stop at a location we know is marked
+# as a statement.
+clean_restart ${binfile}
+runto_main
+
+gdb_breakpoint "*line_label_3"
+gdb_continue_to_breakpoint "*line_label_3"
+
+# Now step by instruction.  As you would expect we should leave this
+# line and stop at the next source line.
+gdb_test "step" "/\\* main end \\*/" \
+    "step to end from line_label_3"
+
+# Restart the test, this time, step through line by line, ensure we
+# only stop at the places where is-stmt is true.
+clean_restart ${binfile}
+runto_main
+
+# Get the values of the labels where we expect to stop.
+set ll1 [get_hexadecimal_valueof "&line_label_1" "INVALID"]
+set ll2 [get_hexadecimal_valueof "&line_label_2" "INVALID"]
+set ll3 [get_hexadecimal_valueof "&line_label_3" "INVALID"]
+set ll5 [get_hexadecimal_valueof "&line_label_5" "INVALID"]
+
+# The first stop should be at line_label_1
+with_test_prefix "check we're at line_label_1" {
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC"]
+    gdb_assert { $ll1 == $pc } "check initial \$pc is line_label_1"
+}
+
+# Now step, this should take us to line_label_3 which is the next
+# location marked as is-stmt.
+with_test_prefix "step to line_label_3" {
+    gdb_test "step" "/\\* main, set var to 0 \\*/"
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC"]
+    gdb_assert { $ll3 == $pc } "check initial \$pc is line_label_3"
+}
+
+# A final step should take us to line_label_5.
+with_test_prefix "step to line_label_5" {
+    gdb_test "step" "/\\* main end \\*/"
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC"]
+    gdb_assert { $ll5 == $pc } "check initial \$pc"
+}
+
+# Now restart the test, and place a breakpoint by line number.  GDB
+# should select the location that is marked as is-stmt.
+clean_restart ${binfile}
+runto_main
+set linum [gdb_get_line_number "main, set var to 0"]
+gdb_breakpoint "$srcfile:$linum"
+gdb_continue_to_breakpoint "Breakpoint on line, set var to 0"
+set pc [get_hexadecimal_valueof "\$pc" "NO-PC"]
+gdb_assert { $ll3 == $pc } "check initial \$pc"
+
+# Restart the test again, this time we will test stepping by
+# instruction.
+clean_restart ${binfile}
+runto_main
+
+# We will be at line_label_1 at this point - we already tested this
+# above.  Now single instruction step forward until we get to
+# line_label_2.  Every instruction before line_label_2 should be
+# attributed to the 'var = 99' line.  For most targets there will only
+# be a single instruction between line_label_1 and line_label_2, but
+# we allow for up to 25 (just a random number).
+
+set $i 0
+set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+	   "get pc before stepi loop at line_label_1"]
+while { $pc < $ll2 } {
+    incr i
+    set line_changed -1
+    gdb_test_multiple "stepi" "stepi until line_label_2, $i" {
+	-re "main, set var to 99.*$gdb_prompt" {
+	    set line_changed 0
+	}
+	-re "main, set var to 0.*$gdb_prompt " {
+	    set line_changed 1
+	}
+    }
+    gdb_assert { $line_changed != -1 } \
+	"ensure we saw a valid line pattern, $i"
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+		"get pc inside stepi loop from line_label_1, $i"]
+    if { $ll2 == $pc } {
+	gdb_assert { $line_changed } \
+	    "line must change at line_label_2"
+    } else {
+	gdb_assert { !$line_changed } \
+	    "line should not change until line_label_2, $i"
+    }
+}
+
+# Now single instruction step forward until GDB reports a new source
+# line, at which point we should be at line_label_5.
+
+set $i 0
+set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+	   "get pc before stepi loop at line_label_2"]
+while { $pc < $ll5 } {
+    incr i
+    set line_changed -1
+    gdb_test_multiple "stepi" "stepi until line_label_5, $i" {
+	-re "main, set var to 0.*$gdb_prompt" {
+	    set line_changed 0
+	}
+	-re "main end.*$gdb_prompt " {
+	    set line_changed 1
+	}
+    }
+    gdb_assert { $line_changed != -1 } \
+	"ensure we saw a valid line pattern, $i"
+    set pc [get_hexadecimal_valueof "\$pc" "NO-PC" \
+		"get pc inside stepi loop from line_label_2, $i"]
+    if { $ll5 == $pc } {
+	gdb_assert { $line_changed } \
+	    "line must change at line_label_5"
+    } else {
+	gdb_assert { !$line_changed } \
+	    "line should not change until line_label_5, $i"
+    }
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
index 33177336f58..7f7301502ab 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
@@ -146,10 +146,10 @@ gdb_test "info line frame3" \
 set end_seq_count 0
 gdb_test_multiple "maint info line-table" \
     "count END markers in line table" {
-	-re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\r\n" {
+	-re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\(\[ \t\]+Y\)?\r\n" {
 	    exp_continue
 	}
-	-re "^$decimal\[ \t\]+END\[ \t\]+$hex\r\n" {
+	-re "^$decimal\[ \t\]+END\[ \t\]+$hex\(\[ \t\]+Y\)?\r\n" {
 	    incr end_seq_count
 	    exp_continue
 	}
@@ -159,7 +159,7 @@ gdb_test_multiple "maint info line-table" \
 	-re ".*linetable: \\(\\(struct linetable \\*\\) 0x0\\):\r\nNo line table.\r\n" {
 	    exp_continue
 	}
-	-re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+ADDRESS\r\n" {
+	-re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+ADDRESS\[ \t\]+IS-STMT\r\n" {
 	    exp_continue
 	}
     }
diff --git a/gdb/xcoffread.c b/gdb/xcoffread.c
index b7da3f944c7..735f8b08825 100644
--- a/gdb/xcoffread.c
+++ b/gdb/xcoffread.c
@@ -432,6 +432,9 @@ arrange_linetable (struct linetable *oldLineTb)
 
   for (function_count = 0, ii = 0; ii < oldLineTb->nitems; ++ii)
     {
+      if (oldLineTb->item[ii].is_stmt == 0)
+	continue;
+
       if (oldLineTb->item[ii].line == 0)
 	{			/* Function entry found.  */
 	  if (function_count >= fentry_size)
@@ -442,6 +445,7 @@ arrange_linetable (struct linetable *oldLineTb)
 			  fentry_size * sizeof (struct linetable_entry));
 	    }
 	  fentry[function_count].line = ii;
+	  fentry[function_count].is_stmt = 1;
 	  fentry[function_count].pc = oldLineTb->item[ii].pc;
 	  ++function_count;
 
-- 
2.14.5


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 0/2] Line table is_stmt support
  2020-03-08 12:50           ` [PATCHv2 0/2] Line table is_stmt support Andrew Burgess
@ 2020-03-08 14:39             ` Bernd Edlinger
  2020-03-10 23:01               ` Andrew Burgess
  2020-04-03 22:21             ` [PATCH 0/2] More regression fixing from is-stmt patches Andrew Burgess
                               ` (2 subsequent siblings)
  3 siblings, 1 reply; 79+ messages in thread
From: Bernd Edlinger @ 2020-03-08 14:39 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 3/8/20 1:50 PM, Andrew Burgess wrote:
> Patch #1 is unchanged.
> 
> Patch #2 includes additional changes in infrun.c based on Bernd's
> suggested fix, as well as his additional tests.
> 
> Bernd,
> 
> If you are happy with this version of the patch that I'll merge this
> in the next few days.
> 

Sure, a quick smoke test shows this is still on the right track.

I will post a re-based version of my follow-up patch in a moment.


Thanks
Bernd.

> Thanks,
> Andrew
> 
> --
> 
> Andrew Burgess (2):
>   gdb/testsuite: Add is-stmt support to the DWARF compiler
>   gdb: Add support for tracking the DWARF line table is-stmt field
> 
>  gdb/ChangeLog                                 |  38 ++++
>  gdb/buildsym-legacy.c                         |   4 +-
>  gdb/buildsym.c                                |  14 +-
>  gdb/buildsym.h                                |   3 +-
>  gdb/disasm.c                                  |   6 +
>  gdb/dwarf2/read.c                             |  13 +-
>  gdb/infrun.c                                  |  51 +++--
>  gdb/jit.c                                     |   1 +
>  gdb/record-btrace.c                           |  11 +-
>  gdb/stack.c                                   |   2 +-
>  gdb/symmisc.c                                 |  10 +-
>  gdb/symtab.c                                  |  25 ++-
>  gdb/symtab.h                                  |  10 +
>  gdb/testsuite/ChangeLog                       |  16 ++
>  gdb/testsuite/gdb.cp/step-and-next-inline.cc  |  66 +++++++
>  gdb/testsuite/gdb.cp/step-and-next-inline.exp | 119 ++++++++++++
>  gdb/testsuite/gdb.cp/step-and-next-inline.h   |  38 ++++
>  gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c      |  99 ++++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp    | 265 +++++++++++++++++++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c        |  61 ++++++
>  gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp      | 267 ++++++++++++++++++++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp  |   6 +-
>  gdb/testsuite/lib/dwarf.exp                   |   8 +-
>  gdb/xcoffread.c                               |   4 +
>  24 files changed, 1107 insertions(+), 30 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.cc
>  create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.exp
>  create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.h
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCHv3] Fix range end handling of inlined subroutines
  2020-02-22  6:39     ` [PATCHv2] " Bernd Edlinger
@ 2020-03-08 14:57       ` Bernd Edlinger
  2020-03-11 22:02         ` Andrew Burgess
  2020-03-13 12:47         ` [PATCHv4] " Bernd Edlinger
  0 siblings, 2 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-03-08 14:57 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 2/22/20 7:38 AM, Bernd Edlinger wrote:
> On 2/9/20 10:07 PM, Bernd Edlinger wrote:
>> Hi,
>>
>> this is based on Andrew's patch here:
>>
>> https://sourceware.org/ml/gdb-patches/2020-02/msg00092.html
>>
>> This and adds a heuristic to fix the case where caller
>> and callee reside in the same subfile, it uses
>> the recorded is-stmt bits and locates end of
>> range infos, including the previously ignored empty
>> range, and adjusting is-stmt info at this
>> same pc, when the last item is not-is-stmt, the
>> previous line table entries are dubious and
>> we reset the is-stmt bit there.
>> This fixes the new test case in Andrew's patch.
>>
>> It understood, that this is just a heuristic
>> approach, since we do not have exactly the data,
>> which is needed to determine at which of the identical
>> PCs in the line table the subroutine actually ends.
>> So, this tries to be as conservative as possible,
>> just changing what is absolutely necessary.
>>
>> This patch itself is preliminary, since the is-stmt patch
>> needs to be rebased after the refactoring of
>> dwarf2read.c from yesterday, so I will have to rebase
>> this patch as well, but decided to wait for Andrew.
>>
>>
> 
> So, this is an update to my previous patch above:
> https://sourceware.org/ml/gdb-patches/2020-02/msg00262.html
> 
> It improves performance on big data, by using binary
> search to locate the bogus line table entries.
> Otherwise it behaves identical to the previous version,
> and is still waiting for Andrew's patch before it can
> be applied.
> 
> 

Rebased to match Andrew's updated patch of today.


Thanks
Bernd.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 0/2] Line table is_stmt support
  2020-03-08 14:39             ` Bernd Edlinger
@ 2020-03-10 23:01               ` Andrew Burgess
  2020-03-11  6:50                 ` Simon Marchi
  0 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-03-10 23:01 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: gdb-patches

* Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-03-08 14:39:44 +0000]:

> On 3/8/20 1:50 PM, Andrew Burgess wrote:
> > Patch #1 is unchanged.
> > 
> > Patch #2 includes additional changes in infrun.c based on Bernd's
> > suggested fix, as well as his additional tests.
> > 
> > Bernd,
> > 
> > If you are happy with this version of the patch that I'll merge this
> > in the next few days.
> > 
> 
> Sure, a quick smoke test shows this is still on the right track.
> 
> I will post a re-based version of my follow-up patch in a moment.

I have now pushed this series to master.  I will review your follow up
patch in more detail tomorrow.

Thanks,
Andrew



> 
> 
> Thanks
> Bernd.
> 
> > Thanks,
> > Andrew
> > 
> > --
> > 
> > Andrew Burgess (2):
> >   gdb/testsuite: Add is-stmt support to the DWARF compiler
> >   gdb: Add support for tracking the DWARF line table is-stmt field
> > 
> >  gdb/ChangeLog                                 |  38 ++++
> >  gdb/buildsym-legacy.c                         |   4 +-
> >  gdb/buildsym.c                                |  14 +-
> >  gdb/buildsym.h                                |   3 +-
> >  gdb/disasm.c                                  |   6 +
> >  gdb/dwarf2/read.c                             |  13 +-
> >  gdb/infrun.c                                  |  51 +++--
> >  gdb/jit.c                                     |   1 +
> >  gdb/record-btrace.c                           |  11 +-
> >  gdb/stack.c                                   |   2 +-
> >  gdb/symmisc.c                                 |  10 +-
> >  gdb/symtab.c                                  |  25 ++-
> >  gdb/symtab.h                                  |  10 +
> >  gdb/testsuite/ChangeLog                       |  16 ++
> >  gdb/testsuite/gdb.cp/step-and-next-inline.cc  |  66 +++++++
> >  gdb/testsuite/gdb.cp/step-and-next-inline.exp | 119 ++++++++++++
> >  gdb/testsuite/gdb.cp/step-and-next-inline.h   |  38 ++++
> >  gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c      |  99 ++++++++++
> >  gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp    | 265 +++++++++++++++++++++++++
> >  gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c        |  61 ++++++
> >  gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp      | 267 ++++++++++++++++++++++++++
> >  gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp  |   6 +-
> >  gdb/testsuite/lib/dwarf.exp                   |   8 +-
> >  gdb/xcoffread.c                               |   4 +
> >  24 files changed, 1107 insertions(+), 30 deletions(-)
> >  create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.cc
> >  create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.exp
> >  create mode 100644 gdb/testsuite/gdb.cp/step-and-next-inline.h
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.c
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.c
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp
> > 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 0/2] Line table is_stmt support
  2020-03-10 23:01               ` Andrew Burgess
@ 2020-03-11  6:50                 ` Simon Marchi
  2020-03-11 11:28                   ` Andrew Burgess
  0 siblings, 1 reply; 79+ messages in thread
From: Simon Marchi @ 2020-03-11  6:50 UTC (permalink / raw)
  To: Andrew Burgess, Bernd Edlinger; +Cc: gdb-patches

On 2020-03-10 7:01 p.m., Andrew Burgess wrote:
> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-03-08 14:39:44 +0000]:
> 
>> On 3/8/20 1:50 PM, Andrew Burgess wrote:
>>> Patch #1 is unchanged.
>>>
>>> Patch #2 includes additional changes in infrun.c based on Bernd's
>>> suggested fix, as well as his additional tests.
>>>
>>> Bernd,
>>>
>>> If you are happy with this version of the patch that I'll merge this
>>> in the next few days.
>>>
>>
>> Sure, a quick smoke test shows this is still on the right track.
>>
>> I will post a re-based version of my follow-up patch in a moment.
> 
> I have now pushed this series to master.  I will review your follow up
> patch in more detail tomorrow.
> 
> Thanks,
> Andrew

Hi Andrew,

It appears that this series (patch 2/2) causes an ASan failure, see below.
Compiling a C file with an empty main, with debug info, and loading it in GDB is
sufficient to trigger it.

Reading symbols from a.out...
=================================================================
==15419==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6280000340f8 at pc 0x000000687c5a bp 0x7fffd0281e30 sp 0x7fffd0281e20
READ of size 4 at 0x6280000340f8 thread T0
    #0 0x687c59 in buildsym_compunit::record_line(subfile*, int, unsigned long, bool) /home/smarchi/src/binutils-gdb/gdb/buildsym.c:688
    #1 0x96a451 in dwarf_record_line_1 /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:19956
    #2 0x96acce in lnp_state_machine::record_line(bool) /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:20024
    #3 0x969d69 in lnp_state_machine::handle_special_opcode(unsigned char) /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:19851
    #4 0x96b731 in dwarf_decode_lines_1 /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:20135
    #5 0x96c237 in dwarf_decode_lines /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:20328
    #6 0x92ea4a in handle_DW_AT_stmt_list /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:10748
    #7 0x92f133 in read_file_scope /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:10796
    #8 0x92a543 in process_die /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:9815
    #9 0x92915f in process_full_comp_unit /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:9580
    #10 0x920c82 in process_queue /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:8867
    #11 0x8f74e7 in dw2_do_instantiate_symtab /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:2374
    #12 0x8f7795 in dw2_instantiate_symtab /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:2395
    #13 0x8fed42 in dw2_lookup_symbol /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:3539
    #14 0x1251110 in lookup_symbol_via_quick_fns /home/smarchi/src/binutils-gdb/gdb/symtab.c:2401
    #15 0x1251ac1 in lookup_symbol_in_objfile /home/smarchi/src/binutils-gdb/gdb/symtab.c:2550
    #16 0x1251da0 in lookup_symbol_global_or_static_iterator_cb /home/smarchi/src/binutils-gdb/gdb/symtab.c:2597
    #17 0x118135c in svr4_iterate_over_objfiles_in_search_order /home/smarchi/src/binutils-gdb/gdb/solib-svr4.c:3258
    #18 0xa90518 in gdbarch_iterate_over_objfiles_in_search_order(gdbarch*, int (*)(objfile*, void*), void*, objfile*) /home/smarchi/src/binutils-gdb/gdb/gdbarch.c:4859
    #19 0x1252174 in lookup_global_or_static_symbol /home/smarchi/src/binutils-gdb/gdb/symtab.c:2642
    #20 0x1252469 in lookup_global_symbol(char const*, block const*, domain_enum_tag) /home/smarchi/src/binutils-gdb/gdb/symtab.c:2684
    #21 0x1251631 in basic_lookup_symbol_nonlocal(language_defn const*, char const*, block const*, domain_enum_tag) /home/smarchi/src/binutils-gdb/gdb/symtab.c:2471
    #22 0x124f374 in lookup_symbol_aux /home/smarchi/src/binutils-gdb/gdb/symtab.c:2112
    #23 0x124df4e in lookup_symbol_in_language(char const*, block const*, domain_enum_tag, language, field_of_this_result*) /home/smarchi/src/binutils-gdb/gdb/symtab.c:1908
    #24 0x12164a5 in set_initial_language() /home/smarchi/src/binutils-gdb/gdb/symfile.c:1695
    #25 0x1213d40 in symbol_file_add_main_1 /home/smarchi/src/binutils-gdb/gdb/symfile.c:1226
    #26 0x1213ac9 in symbol_file_add_main(char const*, enum_flags<symfile_add_flag>) /home/smarchi/src/binutils-gdb/gdb/symfile.c:1208
    #27 0xce438c in symbol_file_add_main_adapter /home/smarchi/src/binutils-gdb/gdb/main.c:424
    #28 0xce422c in catch_command_errors /home/smarchi/src/binutils-gdb/gdb/main.c:401
    #29 0xce68e5 in captured_main_1 /home/smarchi/src/binutils-gdb/gdb/main.c:1068
    #30 0xce7567 in captured_main /home/smarchi/src/binutils-gdb/gdb/main.c:1188
    #31 0xce75fc in gdb_main(captured_main_args*) /home/smarchi/src/binutils-gdb/gdb/main.c:1213
    #32 0x412a3d in main /home/smarchi/src/binutils-gdb/gdb/gdb.c:32
    #33 0x7fa0a1d8782f in __libc_start_main ../csu/libc-start.c:291
    #34 0x412838 in _start (/home/smarchi/build/binutils-gdb/gdb/gdb+0x412838)

0x6280000340f8 is located 8 bytes to the left of 16024-byte region [0x628000034100,0x628000037f98)
allocated by thread T0 here:
    #0 0x7fa0a4537f88 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10bf88)
    #1 0x4c9419 in xmalloc /home/smarchi/src/binutils-gdb/gdb/alloc.c:60
    #2 0x687b03 in buildsym_compunit::record_line(subfile*, int, unsigned long, bool) /home/smarchi/src/binutils-gdb/gdb/buildsym.c:678
    #3 0x96a451 in dwarf_record_line_1 /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:19956
    #4 0x96acce in lnp_state_machine::record_line(bool) /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:20024
    #5 0x969d69 in lnp_state_machine::handle_special_opcode(unsigned char) /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:19851
    #6 0x96b731 in dwarf_decode_lines_1 /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:20135
    #7 0x96c237 in dwarf_decode_lines /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:20328
    #8 0x92ea4a in handle_DW_AT_stmt_list /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:10748
    #9 0x92f133 in read_file_scope /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:10796
    #10 0x92a543 in process_die /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:9815
    #11 0x92915f in process_full_comp_unit /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:9580
    #12 0x920c82 in process_queue /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:8867
    #13 0x8f74e7 in dw2_do_instantiate_symtab /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:2374
    #14 0x8f7795 in dw2_instantiate_symtab /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:2395
    #15 0x8fed42 in dw2_lookup_symbol /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:3539
    #16 0x1251110 in lookup_symbol_via_quick_fns /home/smarchi/src/binutils-gdb/gdb/symtab.c:2401
    #17 0x1251ac1 in lookup_symbol_in_objfile /home/smarchi/src/binutils-gdb/gdb/symtab.c:2550
    #18 0x1251da0 in lookup_symbol_global_or_static_iterator_cb /home/smarchi/src/binutils-gdb/gdb/symtab.c:2597
    #19 0x118135c in svr4_iterate_over_objfiles_in_search_order /home/smarchi/src/binutils-gdb/gdb/solib-svr4.c:3258
    #20 0xa90518 in gdbarch_iterate_over_objfiles_in_search_order(gdbarch*, int (*)(objfile*, void*), void*, objfile*) /home/smarchi/src/binutils-gdb/gdb/gdbarch.c:4859
    #21 0x1252174 in lookup_global_or_static_symbol /home/smarchi/src/binutils-gdb/gdb/symtab.c:2642
    #22 0x1252469 in lookup_global_symbol(char const*, block const*, domain_enum_tag) /home/smarchi/src/binutils-gdb/gdb/symtab.c:2684
    #23 0x1251631 in basic_lookup_symbol_nonlocal(language_defn const*, char const*, block const*, domain_enum_tag) /home/smarchi/src/binutils-gdb/gdb/symtab.c:2471
    #24 0x124f374 in lookup_symbol_aux /home/smarchi/src/binutils-gdb/gdb/symtab.c:2112
    #25 0x124df4e in lookup_symbol_in_language(char const*, block const*, domain_enum_tag, language, field_of_this_result*) /home/smarchi/src/binutils-gdb/gdb/symtab.c:1908
    #26 0x12164a5 in set_initial_language() /home/smarchi/src/binutils-gdb/gdb/symfile.c:1695
    #27 0x1213d40 in symbol_file_add_main_1 /home/smarchi/src/binutils-gdb/gdb/symfile.c:1226
    #28 0x1213ac9 in symbol_file_add_main(char const*, enum_flags<symfile_add_flag>) /home/smarchi/src/binutils-gdb/gdb/symfile.c:1208
    #29 0xce438c in symbol_file_add_main_adapter /home/smarchi/src/binutils-gdb/gdb/main.c:424

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/smarchi/src/binutils-gdb/gdb/buildsym.c:688 in buildsym_compunit::record_line(subfile*, int, unsigned long, bool)
Shadow bytes around the buggy address:
  0x0c507fffe7c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c507fffe7d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c507fffe7e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c507fffe7f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c507fffe800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c507fffe810: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa[fa]
  0x0c507fffe820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c507fffe830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c507fffe840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c507fffe850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c507fffe860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==15419==ABORTING

Simon

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 0/2] Line table is_stmt support
  2020-03-11  6:50                 ` Simon Marchi
@ 2020-03-11 11:28                   ` Andrew Burgess
  2020-03-11 13:27                     ` Simon Marchi
  0 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-03-11 11:28 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Bernd Edlinger, gdb-patches

* Simon Marchi <simark@simark.ca> [2020-03-11 02:50:07 -0400]:

> On 2020-03-10 7:01 p.m., Andrew Burgess wrote:
> > * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-03-08 14:39:44 +0000]:
> > 
> >> On 3/8/20 1:50 PM, Andrew Burgess wrote:
> >>> Patch #1 is unchanged.
> >>>
> >>> Patch #2 includes additional changes in infrun.c based on Bernd's
> >>> suggested fix, as well as his additional tests.
> >>>
> >>> Bernd,
> >>>
> >>> If you are happy with this version of the patch that I'll merge this
> >>> in the next few days.
> >>>
> >>
> >> Sure, a quick smoke test shows this is still on the right track.
> >>
> >> I will post a re-based version of my follow-up patch in a moment.
> > 
> > I have now pushed this series to master.  I will review your follow up
> > patch in more detail tomorrow.
> > 
> > Thanks,
> > Andrew
> 
> Hi Andrew,
> 
> It appears that this series (patch 2/2) causes an ASan failure, see below.
> Compiling a C file with an empty main, with debug info, and loading it in GDB is
> sufficient to trigger it.

Apologies.

I pushed the patch below to fix this issue.

Thanks,
Andrew

---

From dcc050c86c3e5160497da7aab480adae9ba284aa Mon Sep 17 00:00:00 2001
From: Andrew Burgess <andrew.burgess@embecosm.com>
Date: Wed, 11 Mar 2020 11:17:39 +0000
Subject: [PATCH] gdb: Fix out of bounds array access in
 buildsym_compunit::record_line

This commit:

  commit 8c95582da858ac981f689a6f599acacb8c5c490f
  Date:   Mon Dec 30 21:04:51 2019 +0000

      gdb: Add support for tracking the DWARF line table is-stmt field

Introduced an invalid memory access, by reading outside the bounds of
an array.

This would cause this valgrind error:

  ==7633== Invalid read of size 4
  ==7633==    at 0x4D002C: buildsym_compunit::record_line(subfile*, int, unsigned long, bool) (buildsym.c:688)
  ==7633==    by 0x5F60A5: dwarf_record_line_1(gdbarch*, subfile*, unsigned int, unsigned long, bool, dwarf2_cu*) (read.c:19956)
  ==7633==    by 0x5F63B0: lnp_state_machine::record_line(bool) (read.c:20024)
  ==7633==    by 0x5F5DD5: lnp_state_machine::handle_special_opcode(unsigned char) (read.c:19851)
  ==7633==    by 0x5F6706: dwarf_decode_lines_1(line_header*, dwarf2_cu*, int, unsigned long) (read.c:20135)
  ==7633==    by 0x5F6C57: dwarf_decode_lines(line_header*, char const*, dwarf2_cu*, dwarf2_psymtab*, unsigned long, int) (read.c:20328)
  ==7633==    by 0x5DF5F1: handle_DW_AT_stmt_list(die_info*, dwarf2_cu*, char const*, unsigned long) (read.c:10748)
  ==7633==    by 0x5DF823: read_file_scope(die_info*, dwarf2_cu*) (read.c:10796)
  ==7633==    by 0x5DDA63: process_die(die_info*, dwarf2_cu*) (read.c:9815)
  ==7633==    by 0x5DD44A: process_full_comp_unit(dwarf2_per_cu_data*, language) (read.c:9580)
  ==7633==    by 0x5DAB58: process_queue(dwarf2_per_objfile*) (read.c:8867)
  ==7633==    by 0x5CB30E: dw2_do_instantiate_symtab(dwarf2_per_cu_data*, bool) (read.c:2374)
  ==7633==  Address 0xa467f48 is 8 bytes before a block of size 16,024 alloc'd
  ==7633==    at 0x4C2CDCB: malloc (vg_replace_malloc.c:299)
  ==7633==    by 0x451FC4: xmalloc (alloc.c:60)
  ==7633==    by 0x4CFFDF: buildsym_compunit::record_line(subfile*, int, unsigned long, bool) (buildsym.c:678)
  ==7633==    by 0x5F60A5: dwarf_record_line_1(gdbarch*, subfile*, unsigned int, unsigned long, bool, dwarf2_cu*) (read.c:19956)
  ==7633==    by 0x5F63B0: lnp_state_machine::record_line(bool) (read.c:20024)
  ==7633==    by 0x5F5DD5: lnp_state_machine::handle_special_opcode(unsigned char) (read.c:19851)
  ==7633==    by 0x5F6706: dwarf_decode_lines_1(line_header*, dwarf2_cu*, int, unsigned long) (read.c:20135)
  ==7633==    by 0x5F6C57: dwarf_decode_lines(line_header*, char const*, dwarf2_cu*, dwarf2_psymtab*, unsigned long, int) (read.c:20328)
  ==7633==    by 0x5DF5F1: handle_DW_AT_stmt_list(die_info*, dwarf2_cu*, char const*, unsigned long) (read.c:10748)
  ==7633==    by 0x5DF823: read_file_scope(die_info*, dwarf2_cu*) (read.c:10796)
  ==7633==    by 0x5DDA63: process_die(die_info*, dwarf2_cu*) (read.c:9815)
  ==7633==    by 0x5DD44A: process_full_comp_unit(dwarf2_per_cu_data*, language) (read.c:9580)

gdb/ChangeLog:

	* buildsyms.c (buildsym_compunit::record_line): Avoid accessing
	previous item in the list, when the list has no items.
---
 gdb/ChangeLog  |  5 +++++
 gdb/buildsym.c | 19 +++++++++++--------
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 24aeba8e252..7155db34b08 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -681,15 +681,18 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
       m_have_line_numbers = true;
     }
 
-  /* If we have a duplicate for the previous entry then ignore the new
-     entry, except, if the new entry is setting the is_stmt flag, then
-     ensure the previous entry respects the new setting.  */
-  e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
-  if (e->line == line && e->pc == pc)
+  if (subfile->line_vector->nitems > 0)
     {
-      if (is_stmt && !e->is_stmt)
-	e->is_stmt = 1;
-      return;
+      /* If we have a duplicate for the previous entry then ignore the new
+	 entry, except, if the new entry is setting the is_stmt flag, then
+	 ensure the previous entry respects the new setting.  */
+      e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
+      if (e->line == line && e->pc == pc)
+	{
+	  if (is_stmt && !e->is_stmt)
+	    e->is_stmt = 1;
+	  return;
+	}
     }
 
   if (subfile->line_vector->nitems + 1 >= subfile->line_vector_length)
-- 
2.14.5



^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 0/2] Line table is_stmt support
  2020-03-11 11:28                   ` Andrew Burgess
@ 2020-03-11 13:27                     ` Simon Marchi
  0 siblings, 0 replies; 79+ messages in thread
From: Simon Marchi @ 2020-03-11 13:27 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Bernd Edlinger, gdb-patches

On 2020-03-11 7:28 a.m., Andrew Burgess wrote:
> * Simon Marchi <simark@simark.ca> [2020-03-11 02:50:07 -0400]:
> 
>> On 2020-03-10 7:01 p.m., Andrew Burgess wrote:
>>> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-03-08 14:39:44 +0000]:
>>>
>>>> On 3/8/20 1:50 PM, Andrew Burgess wrote:
>>>>> Patch #1 is unchanged.
>>>>>
>>>>> Patch #2 includes additional changes in infrun.c based on Bernd's
>>>>> suggested fix, as well as his additional tests.
>>>>>
>>>>> Bernd,
>>>>>
>>>>> If you are happy with this version of the patch that I'll merge this
>>>>> in the next few days.
>>>>>
>>>>
>>>> Sure, a quick smoke test shows this is still on the right track.
>>>>
>>>> I will post a re-based version of my follow-up patch in a moment.
>>>
>>> I have now pushed this series to master.  I will review your follow up
>>> patch in more detail tomorrow.
>>>
>>> Thanks,
>>> Andrew
>>
>> Hi Andrew,
>>
>> It appears that this series (patch 2/2) causes an ASan failure, see below.
>> Compiling a C file with an empty main, with debug info, and loading it in GDB is
>> sufficient to trigger it.
> 
> Apologies.
> 
> I pushed the patch below to fix this issue.
> 
> Thanks,
> Andrew

Thanks!

Simon

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv3] Fix range end handling of inlined subroutines
  2020-03-08 14:57       ` [PATCHv3] " Bernd Edlinger
@ 2020-03-11 22:02         ` Andrew Burgess
  2020-03-12 18:21           ` Bernd Edlinger
  2020-03-13 12:47         ` [PATCHv4] " Bernd Edlinger
  1 sibling, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-03-11 22:02 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: gdb-patches

* Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-03-08 14:57:35 +0000]:

> On 2/22/20 7:38 AM, Bernd Edlinger wrote:
> > On 2/9/20 10:07 PM, Bernd Edlinger wrote:
> >> Hi,
> >>
> >> this is based on Andrew's patch here:
> >>
> >> https://sourceware.org/ml/gdb-patches/2020-02/msg00092.html
> >>
> >> This and adds a heuristic to fix the case where caller
> >> and callee reside in the same subfile, it uses
> >> the recorded is-stmt bits and locates end of
> >> range infos, including the previously ignored empty
> >> range, and adjusting is-stmt info at this
> >> same pc, when the last item is not-is-stmt, the
> >> previous line table entries are dubious and
> >> we reset the is-stmt bit there.
> >> This fixes the new test case in Andrew's patch.
> >>
> >> It understood, that this is just a heuristic
> >> approach, since we do not have exactly the data,
> >> which is needed to determine at which of the identical
> >> PCs in the line table the subroutine actually ends.
> >> So, this tries to be as conservative as possible,
> >> just changing what is absolutely necessary.
> >>
> >> This patch itself is preliminary, since the is-stmt patch
> >> needs to be rebased after the refactoring of
> >> dwarf2read.c from yesterday, so I will have to rebase
> >> this patch as well, but decided to wait for Andrew.
> >>
> >>
> > 
> > So, this is an update to my previous patch above:
> > https://sourceware.org/ml/gdb-patches/2020-02/msg00262.html
> > 
> > It improves performance on big data, by using binary
> > search to locate the bogus line table entries.
> > Otherwise it behaves identical to the previous version,
> > and is still waiting for Andrew's patch before it can
> > be applied.
> > 
> > 
> 
> Rebased to match Andrew's updated patch of today.


Bernd,

Thanks for working on this.  This looks like a great improvement, but
I have a could of questions / comment inline below.

Thanks,
Andrew


> 
> 
> Thanks
> Bernd.

> From 010f1774ec69d5cab48db9b7a5fb9de3d5860f97 Mon Sep 17 00:00:00 2001
> From: Bernd Edlinger <bernd.edlinger@hotmail.de>
> Date: Sun, 9 Feb 2020 21:13:17 +0100
> Subject: [PATCH] Fix range end handling of inlined subroutines
> 
> Since the is_stmt is now handled, it becomes
> possible to locate dubious is_stmt line entries
> at the end of an inlined function, even if the
> called inline function is in the same subfile.
> 
> When there is a sequence of line entries at the
> same address where an inline range ends, and the
> last item has is_stmt = 0, we force all previous
> items to have is_stmt = 0 as well.

This describes _what_ we're doing...

> 
> If the last line at that address has is_stmt = 1,
> there is no need to change anything, since a step
> over will always stop at that last line from the
> same address, which is fine, since it is outside
> the subroutine.

This describes what we're _not_ doing...

... it just feels like this description would benefit for a bit more
_why_.  How does setting all the is_stmt fields to 0 help us?  What
cases step/next should we expect to see improvement in?

I'm also worried that setting is_stmt to false will mean we loose the
ability to set breakpoints on some lines using file:line syntax.  I
wonder if we should consider coming up with a solution that both
preserves the ability to set breakpoints, while also giving the
benefits for step/next.

> 
> This is about the best we can do at the moment,
> unless location view information are added to the
> block ranges debug info structure, and location
> views are implemented in gdb in general.
> 
> gdb:
> 2020-03-08  Bernd Edlinger  <bernd.edlinger@hotmail.de>
> 
> 	* buildsym.c (buildsym_compunit::record_inline_range_end,
> 	patch_inline_end_pos): New helper functions.
> 	(buildsym_compunit::end_symtab_with_blockvector): Patch line table.
> 	(buildsym_compunit::~buildsym_compunit): Cleanup m_inline_end_vector.
> 	* buildsym.h (buildsym_compunit::record_inline_range_end): Declare.
> 	(buildsym_compunit::m_inline_end_vector,
> 	buildsym_compunit::m_inline_end_vector_length,
> 	buildsym_compunit::m_inline_end_vector_nitems): New data items.
> 	* dwarf2/read.c (dwarf2_rnglists_process,
> 	dwarf2_ranges_process): Don't ignore empty ranges here.
> 	(dwarf2_ranges_read): Ignore empty ranges here.
> 	(dwarf2_record_block_ranges): Pass end of range PC to
> 	record_inline_range_end for inline functions.
> 
> gdb/testsuite:
> 2020-03-08  Bernd Edlinger  <bernd.edlinger@hotmail.de>
> 
> 	* gdb.cp/step-and-next-inline.exp: Adjust test.
> ---
>  gdb/buildsym.c                                | 84 +++++++++++++++++++++++++++
>  gdb/buildsym.h                                | 11 ++++
>  gdb/dwarf2/read.c                             | 22 ++++---
>  gdb/testsuite/gdb.cp/step-and-next-inline.exp | 17 ------
>  4 files changed, 108 insertions(+), 26 deletions(-)
> 
> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> index 24aeba8..f02b7ce 100644
> --- a/gdb/buildsym.c
> +++ b/gdb/buildsym.c
> @@ -113,6 +113,8 @@ struct pending_block
>        next1 = next->next;
>        xfree ((void *) next);
>      }
> +
> +  xfree (m_inline_end_vector);
>  }
>  
>  struct macro_table *
> @@ -732,6 +734,84 @@ struct blockvector *
>  }
>  
>  \f
> +/* Record a PC where a inlined subroutine ends.  */
> +
> +void
> +buildsym_compunit::record_inline_range_end (CORE_ADDR end)
> +{
> +  if (m_inline_end_vector == nullptr)
> +    {
> +      m_inline_end_vector_length = INITIAL_LINE_VECTOR_LENGTH;
> +      m_inline_end_vector = (CORE_ADDR *)
> +	xmalloc (sizeof (CORE_ADDR) * m_inline_end_vector_length);
> +      m_inline_end_vector_nitems = 0;
> +    }
> +  else if (m_inline_end_vector_nitems == m_inline_end_vector_length)
> +    {
> +      m_inline_end_vector_length *= 2;
> +      m_inline_end_vector = (CORE_ADDR *)
> +	xrealloc ((char *) m_inline_end_vector,
> +		  sizeof (CORE_ADDR) * m_inline_end_vector_length);
> +    }
> +
> +  m_inline_end_vector[m_inline_end_vector_nitems++] = end;
> +}
> +
> +\f
> +/* Patch the is_stmt bits at the given inline end address.
> +   The line table has to be already sorted.  */
> +
> +static void
> +patch_inline_end_pos (struct linetable *table, CORE_ADDR end)
> +{
> +  int a = 2, b = table->nitems - 1;
> +  struct linetable_entry *items = table->item;
> +
> +  /* We need at least two items with pc = end in the table.
> +     The lowest usable items are at pos 0 and 1, the highest
> +     usable items are at pos b - 2 and b - 1.  */
> +  if (a > b || end < items[1].pc || end > items[b - 2].pc)
> +    return;
> +
> +  /* Look for the first item with pc > end in the range [a,b].
> +     The previous element has pc = end or there is no match.
> +     We set a = 2, since we need at least two consecutive elements
> +     with pc = end to do anything useful.
> +     We set b = nitems - 1, since we are not interested in the last
> +     element which should be an end of sequence marker with line = 0
> +     and is_stmt = 1.  */
> +  while (a < b)
> +    {
> +      int c = (a + b) / 2;
> +
> +      if (end < items[c].pc)
> +	b = c;
> +      else
> +	a = c + 1;
> +    }
> +
> +  a--;
> +  if (items[a].pc != end || items[a].is_stmt)
> +    return;
> +
> +  /* When there is a sequence of line entries at the same address
> +     where an inline range ends, and the last item has is_stmt = 0,
> +     we force all previous items to have is_stmt = 0 as well.
> +     Setting breakpoints at those addresses is currently not
> +     supported, since it is unclear if the previous addresses are
> +     part of the subroutine or the calling program.  */
> +  while (a-- > 0)

I know this is a personal preference, but I really dislike having
these inc/dec adjustments within expressions like this.  I feel it's
too easy to overlook, and I always end up having to think twice just
to make sure I've parsed the intention correctly.  I'd like to make a
plea to have the adjustment moved into the body of the loop please.

> +    {
> +      /* We stop at the first line entry with a different address,
> +         or when we see an end of sequence marker.  */

I think there's a whitespace error at the start of this line.

> +      if (items[a].pc != end || items[a].line == 0)
> +	break;
> +
> +      items[a].is_stmt = 0;
> +    }
> +}
> +
> +\f
>  /* Subroutine of end_symtab to simplify it.  Look for a subfile that
>     matches the main source file's basename.  If there is only one, and
>     if the main source file doesn't have any symbol or line number
> @@ -965,6 +1045,10 @@ struct compunit_symtab *
>  			      subfile->line_vector->item
>  			      + subfile->line_vector->nitems,
>  			      lte_is_less_than);
> +
> +	   for (int i = 0; i < m_inline_end_vector_nitems; i++)
> +	     patch_inline_end_pos (subfile->line_vector,
> +				   m_inline_end_vector[i]);
>  	}
>  
>        /* Allocate a symbol table if necessary.  */
> diff --git a/gdb/buildsym.h b/gdb/buildsym.h
> index c768a4c..2845789 100644
> --- a/gdb/buildsym.h
> +++ b/gdb/buildsym.h
> @@ -190,6 +190,8 @@ struct buildsym_compunit
>    void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
>  		    bool is_stmt);
>  
> +  void record_inline_range_end (CORE_ADDR end);
> +
>    struct compunit_symtab *get_compunit_symtab ()
>    {
>      return m_compunit_symtab;
> @@ -397,6 +399,15 @@ struct buildsym_compunit
>  
>    /* Pending symbols that are local to the lexical context.  */
>    struct pending *m_local_symbols = nullptr;
> +
> +  /* Pending inline end range addresses.  */
> +  CORE_ADDR * m_inline_end_vector = nullptr;
> +
> +  /* Number of allocated inline end range addresses.  */
> +  int m_inline_end_vector_length = 0;
> +
> +  /* Number of pending inline end range addresses.  */
> +  int m_inline_end_vector_nitems = 0;
>  };

I think we should probably just use a std::vector<CORE_ADDR> for
m_inline_end_vector - unless I'm missing a good reason.  I think this
would simplify some of the code in buildsym.c.

>  
>  \f
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index 1706b96..6be008c 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -13609,10 +13609,6 @@ class process_die_scope
>  	  return false;
>  	}
>  
> -      /* Empty range entries have no effect.  */
> -      if (range_beginning == range_end)
> -	continue;
> -
>        range_beginning += base;
>        range_end += base;
>  
> @@ -13723,10 +13719,6 @@ class process_die_scope
>  	  return 0;
>  	}
>  
> -      /* Empty range entries have no effect.  */
> -      if (range_beginning == range_end)
> -	continue;
> -
>        range_beginning += base;
>        range_end += base;
>  
> @@ -13766,6 +13758,10 @@ class process_die_scope
>    retval = dwarf2_ranges_process (offset, cu,
>      [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
>      {
> +      /* Empty range entries have no effect.  */
> +      if (range_beginning == range_end)
> +	return;
> +
>        if (ranges_pst != NULL)
>  	{
>  	  CORE_ADDR lowpc;
> @@ -14003,6 +13999,7 @@ class process_die_scope
>    struct gdbarch *gdbarch = get_objfile_arch (objfile);
>    struct attribute *attr;
>    struct attribute *attr_high;
> +  bool inlined_subroutine = (die->tag == DW_TAG_inlined_subroutine);
>  
>    attr_high = dwarf2_attr (die, DW_AT_high_pc, cu);
>    if (attr_high)
> @@ -14018,7 +14015,10 @@ class process_die_scope
>  
>  	  low = gdbarch_adjust_dwarf2_addr (gdbarch, low + baseaddr);
>  	  high = gdbarch_adjust_dwarf2_addr (gdbarch, high + baseaddr);
> -	  cu->get_builder ()->record_block_range (block, low, high - 1);
> +	  if (inlined_subroutine)
> +	    cu->get_builder ()->record_inline_range_end (high);
> +	  if (low < high)
> +	    cu->get_builder ()->record_block_range (block, low, high - 1);
>          }
>      }
>  
> @@ -14043,6 +14043,10 @@ class process_die_scope
>  	  end += baseaddr;
>  	  start = gdbarch_adjust_dwarf2_addr (gdbarch, start);
>  	  end = gdbarch_adjust_dwarf2_addr (gdbarch, end);
> +	  if (inlined_subroutine)
> +	    cu->get_builder ()->record_inline_range_end (end);
> +	  if (start == end)
> +	    return;
>  	  cu->get_builder ()->record_block_range (block, start, end - 1);
>  	  blockvec.emplace_back (start, end);
>  	});
> diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> index acec48b..84c0901 100644
> --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> @@ -48,37 +48,20 @@ proc do_test { use_header } {
>      gdb_test "step" ".*" "step into get_alias_set"
>      gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
>  	"not in inline 1"
> -    # It's possible that this first failure (when not using a header
> -    # file) is GCC's fault, though the remaining failures would best
> -    # be fixed by adding location views support (though it could be
> -    # that some easier heuristic could be figured out).  Still, it is
> -    # not certain that the first failure wouldn't also be fixed by
> -    # having location view support, so for now it is tagged as such.
> -    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
>      gdb_test "next" ".*TREE_TYPE.*" "next step 1"
>      gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
>  	"not in inline 2"
>      gdb_test "next" ".*TREE_TYPE.*" "next step 2"
>      gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
>  	"not in inline 3"
> -    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
>      gdb_test "next" ".*TREE_TYPE.*" "next step 3"
>      gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
>  	"not in inline 4"
> -    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
>      gdb_test "next" "return 0.*" "next step 4"
>      gdb_test "bt" \
>  	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
>  	"not in inline 5"
>  
> -    if {!$use_header} {
> -	# With the debug from GCC 10.x (and earlier) GDB is currently
> -	# unable to successfully complete the following tests when we
> -	# are not using a header file.
> -	kfail symtab/25507 "stepping tests"
> -	return
> -    }
> -
>      clean_restart ${executable}
>  
>      if ![runto_main] {
> -- 
> 1.9.1
> 


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv3] Fix range end handling of inlined subroutines
  2020-03-11 22:02         ` Andrew Burgess
@ 2020-03-12 18:21           ` Bernd Edlinger
  2020-03-12 18:27             ` Christian Biesinger
  0 siblings, 1 reply; 79+ messages in thread
From: Bernd Edlinger @ 2020-03-12 18:21 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

On 3/11/20 11:02 PM, Andrew Burgess wrote:
> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-03-08 14:57:35 +0000]:
> 
>> On 2/22/20 7:38 AM, Bernd Edlinger wrote:
>>> On 2/9/20 10:07 PM, Bernd Edlinger wrote:
>>>> Hi,
>>>>
>>>> this is based on Andrew's patch here:
>>>>
>>>> https://sourceware.org/ml/gdb-patches/2020-02/msg00092.html
>>>>
>>>> This and adds a heuristic to fix the case where caller
>>>> and callee reside in the same subfile, it uses
>>>> the recorded is-stmt bits and locates end of
>>>> range infos, including the previously ignored empty
>>>> range, and adjusting is-stmt info at this
>>>> same pc, when the last item is not-is-stmt, the
>>>> previous line table entries are dubious and
>>>> we reset the is-stmt bit there.
>>>> This fixes the new test case in Andrew's patch.
>>>>
>>>> It understood, that this is just a heuristic
>>>> approach, since we do not have exactly the data,
>>>> which is needed to determine at which of the identical
>>>> PCs in the line table the subroutine actually ends.
>>>> So, this tries to be as conservative as possible,
>>>> just changing what is absolutely necessary.
>>>>
>>>> This patch itself is preliminary, since the is-stmt patch
>>>> needs to be rebased after the refactoring of
>>>> dwarf2read.c from yesterday, so I will have to rebase
>>>> this patch as well, but decided to wait for Andrew.
>>>>
>>>>
>>>
>>> So, this is an update to my previous patch above:
>>> https://sourceware.org/ml/gdb-patches/2020-02/msg00262.html
>>>
>>> It improves performance on big data, by using binary
>>> search to locate the bogus line table entries.
>>> Otherwise it behaves identical to the previous version,
>>> and is still waiting for Andrew's patch before it can
>>> be applied.
>>>
>>>
>>
>> Rebased to match Andrew's updated patch of today.
> 
> 
> Bernd,
> 
> Thanks for working on this.  This looks like a great improvement, but
> I have a could of questions / comment inline below.
> 
> Thanks,
> Andrew
> 
> 
>>
>>
>> Thanks
>> Bernd.
> 
>> From 010f1774ec69d5cab48db9b7a5fb9de3d5860f97 Mon Sep 17 00:00:00 2001
>> From: Bernd Edlinger <bernd.edlinger@hotmail.de>
>> Date: Sun, 9 Feb 2020 21:13:17 +0100
>> Subject: [PATCH] Fix range end handling of inlined subroutines
>>
>> Since the is_stmt is now handled, it becomes
>> possible to locate dubious is_stmt line entries
>> at the end of an inlined function, even if the
>> called inline function is in the same subfile.
>>
>> When there is a sequence of line entries at the
>> same address where an inline range ends, and the
>> last item has is_stmt = 0, we force all previous
>> items to have is_stmt = 0 as well.
> 
> This describes _what_ we're doing...
> 
>>
>> If the last line at that address has is_stmt = 1,
>> there is no need to change anything, since a step
>> over will always stop at that last line from the
>> same address, which is fine, since it is outside
>> the subroutine.
> 
> This describes what we're _not_ doing...
> 
> ... it just feels like this description would benefit for a bit more
> _why_.  How does setting all the is_stmt fields to 0 help us?  What
> cases step/next should we expect to see improvement in?
> 
> I'm also worried that setting is_stmt to false will mean we loose the
> ability to set breakpoints on some lines using file:line syntax.  I
> wonder if we should consider coming up with a solution that both
> preserves the ability to set breakpoints, while also giving the
> benefits for step/next.
> 

Maybe as a followup.  The breakpoints on theses lines are *very*
problematic, as the call stack is almost always wrong.  From the block
info they appear to be part of the caller, but from the line table they
are located in the subroutine, when you are at this breakpoint, an
ordinary user can't make sense of the call stack, as one call frame is
missing.

The is-stmt patch does the same, removing line infos that could
in theory be used as breakpoints. See buildsym_compunit::record_line:

  /* Normally, we treat lines as unsorted.  But the end of sequence
     marker is special.  We sort line markers at the same PC by line
     number, so end of sequence markers (which have line == 0) appear
     first.  This is right if the marker ends the previous function,
     and there is no padding before the next function.  But it is
     wrong if the previous line was empty and we are now marking a
     switch to a different subfile.  We must leave the end of sequence
     marker at the end of this group of lines, not sort the empty line
     to after the marker.  The easiest way to accomplish this is to
     delete any empty lines from our table, if they are followed by
     end of sequence markers.  All we lose is the ability to set
     breakpoints at some lines which contain no instructions
     anyway.  */
  if (line == 0 && subfile->line_vector->nitems > 0)
    {
      e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
      while (subfile->line_vector->nitems > 0 && e->pc == pc)
        {
          e--;
          subfile->line_vector->nitems--;
        }
    }


This is invoked if a is-stmt line is followed by
a non-stmt line of another CU, at the same PC.
This is previously the non-is-stmt lines were all ignored,
but now the non-is-stmt makes an end marker in the current
CU and a non-is-stmt line in the next CU.
This deletion is exactly what makes the other patch work.

But I think it is not necessary to remove those lines altogether,
instead we can just make them non-is-stmt lines, which would
better match to what this patch does.

While I write these lines, I see an UB in the above code,
since e-- can decrement the pointer before the beginning of the
line_vector, when nitems becomes 0, the value is not used, but
the pointer value e indeterminate if I see that right.

I will send a patch for that, when I find time for it.

>>
>> This is about the best we can do at the moment,
>> unless location view information are added to the
>> block ranges debug info structure, and location
>> views are implemented in gdb in general.
>>
>> gdb:
>> 2020-03-08  Bernd Edlinger  <bernd.edlinger@hotmail.de>
>>
>> 	* buildsym.c (buildsym_compunit::record_inline_range_end,
>> 	patch_inline_end_pos): New helper functions.
>> 	(buildsym_compunit::end_symtab_with_blockvector): Patch line table.
>> 	(buildsym_compunit::~buildsym_compunit): Cleanup m_inline_end_vector.
>> 	* buildsym.h (buildsym_compunit::record_inline_range_end): Declare.
>> 	(buildsym_compunit::m_inline_end_vector,
>> 	buildsym_compunit::m_inline_end_vector_length,
>> 	buildsym_compunit::m_inline_end_vector_nitems): New data items.
>> 	* dwarf2/read.c (dwarf2_rnglists_process,
>> 	dwarf2_ranges_process): Don't ignore empty ranges here.
>> 	(dwarf2_ranges_read): Ignore empty ranges here.
>> 	(dwarf2_record_block_ranges): Pass end of range PC to
>> 	record_inline_range_end for inline functions.
>>
>> gdb/testsuite:
>> 2020-03-08  Bernd Edlinger  <bernd.edlinger@hotmail.de>
>>
>> 	* gdb.cp/step-and-next-inline.exp: Adjust test.
>> ---
>>  gdb/buildsym.c                                | 84 +++++++++++++++++++++++++++
>>  gdb/buildsym.h                                | 11 ++++
>>  gdb/dwarf2/read.c                             | 22 ++++---
>>  gdb/testsuite/gdb.cp/step-and-next-inline.exp | 17 ------
>>  4 files changed, 108 insertions(+), 26 deletions(-)
>>
>> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
>> index 24aeba8..f02b7ce 100644
>> --- a/gdb/buildsym.c
>> +++ b/gdb/buildsym.c
>> @@ -113,6 +113,8 @@ struct pending_block
>>        next1 = next->next;
>>        xfree ((void *) next);
>>      }
>> +
>> +  xfree (m_inline_end_vector);
>>  }
>>  
>>  struct macro_table *
>>>> this patch as well, but decided to wait for Andrew.
>> @@ -732,6 +734,84 @@ struct blockvector *
>>  }
>>  
>>  \f
>> +/* Record a PC where a inlined subroutine ends.  */
>> +
>> +void
>> +buildsym_compunit::record_inline_range_end (CORE_ADDR end)
>> +{
>> +  if (m_inline_end_vector == nullptr)
>> +    {
>> +      m_inline_end_vector_length = INITIAL_LINE_VECTOR_LENGTH;
>> +      m_inline_end_vector = (CORE_ADDR *)
>> +	xmalloc (sizeof (CORE_ADDR) * m_inline_end_vector_length);
>> +      m_inline_end_vector_nitems = 0;
>> +    }
>> +  else if (m_inline_end_vector_nitems == m_inline_end_vector_length)
>> +    {
>> +      m_inline_end_vector_length *= 2;
>> +      m_inline_end_vector = (CORE_ADDR *)
>> +	xrealloc ((char *) m_inline_end_vector,
>> +		  sizeof (CORE_ADDR) * m_inline_end_vector_length);
>> +    }
>> +
>> +  m_inline_end_vector[m_inline_end_vector_nitems++] = end;
>> +}
>> +
>> +\f
>> +/* Patch the is_stmt bits at the given inline end address.
>> +   The line table has to be already sorted.  */
>> +
>> +static void
>> +patch_inline_end_pos (struct linetable *table, CORE_ADDR end)
>> +{
>> +  int a = 2, b = table->nitems - 1;
>> +  struct linetable_entry *items = table->item;
>> +
>> +  /* We need at least two items with pc = end in the table.
>> +     The lowest usable items are at pos 0 and 1, the highest
>> +     usable items are at pos b - 2 and b - 1.  */
>> +  if (a > b || end < items[1].pc || end > items[b - 2].pc)
>> +    return;
>> +
>> +  /* Look for the first item with pc > end in the range [a,b].
>> +     The previous element has pc = end or there is no match.
>> +     We set a = 2, since we need at least two consecutive elements
>> +     with pc = end to do anything useful.
>> +     We set b = nitems - 1, since we are not interested in the last
>> +     element which should be an end of sequence marker with line = 0
>> +     and is_stmt = 1.  */
>> +  while (a < b)
>> +    {
>> +      int c = (a + b) / 2;
>> +
>> +      if (end < items[c].pc)
>> +	b = c;
>> +      else
>> +	a = c + 1;
>> +    }
>> +
>> +  a--;
>> +  if (items[a].pc != end || items[a].is_stmt)
>> +    return;
>> +
>> +  /* When there is a sequence of line entries at the same address
>> +     where an inline range ends, and the last item has is_stmt = 0,
>> +     we force all previous items to have is_stmt = 0 as well.
>> +     Setting breakpoints at those addresses is currently not
>> +     supported, since it is unclear if the previous addresses are
>> +     part of the subroutine or the calling program.  */
>> +  while (a-- > 0)
> 
> I know this is a personal preference, but I really dislike having
> these inc/dec adjustments within expressions like this.  I feel it's
> too easy to overlook, and I always end up having to think twice just
> to make sure I've parsed the intention correctly.  I'd like to make a
> plea to have the adjustment moved into the body of the loop please.
> 

No problem, I can do that better, the first iteration does
not need to check for a > 0, so I will make that a
do { a--; .... } while (a > 0), so that will be even more
efficient this way.

>> +    {
>> +      /* We stop at the first line entry with a different address,
>> +         or when we see an end of sequence marker.  */
> 
> I think there's a whitespace error at the start of this line.

Yes, indeed.

> 
>> +      if (items[a].pc != end || items[a].line == 0)
>> +	break;
>> +
>> +      items[a].is_stmt = 0;
>> +    }
>> +}
>> +
>> +\f
>>  /* Subroutine of end_symtab to simplify it.  Look for a subfile that
>>     matches the main source file's basename.  If there is only one, and
>>     if the main source file doesn't have any symbol or line number
>> @@ -965,6 +1045,10 @@ struct compunit_symtab *
>>  			      subfile->line_vector->item
>>  			      + subfile->line_vector->nitems,
>>  			      lte_is_less_than);
>> +
>> +	   for (int i = 0; i < m_inline_end_vector_nitems; i++)
>> +	     patch_inline_end_pos (subfile->line_vector,
>> +				   m_inline_end_vector[i]);
>>  	}
>>  
>>        /* Allocate a symbol table if necessary.  */
>> diff --git a/gdb/buildsym.h b/gdb/buildsym.h
>> index c768a4c..2845789 100644
>> --- a/gdb/buildsym.h
>> +++ b/gdb/buildsym.h
>> @@ -190,6 +190,8 @@ struct buildsym_compunit
>>    void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
>>  		    bool is_stmt);
>>  
>> +  void record_inline_range_end (CORE_ADDR end);
>> +
>>    struct compunit_symtab *get_compunit_symtab ()
>>    {
>>      return m_compunit_symtab;
>> @@ -397,6 +399,15 @@ struct buildsym_compunit
>>  
>>    /* Pending symbols that are local to the lexical context.  */
>>    struct pending *m_local_symbols = nullptr;
>> +
>> +  /* Pending inline end range addresses.  */
>> +  CORE_ADDR * m_inline_end_vector = nullptr;
>> +
>> +  /* Number of allocated inline end range addresses.  */
>> +  int m_inline_end_vector_length = 0;
>> +
>> +  /* Number of pending inline end range addresses.  */
>> +  int m_inline_end_vector_nitems = 0;
>>  };
> 
> I think we should probably just use a std::vector<CORE_ADDR> for
> m_inline_end_vector - unless I'm missing a good reason.  I think this
> would simplify some of the code in buildsym.c.
> 

I am worried that the vector is not as efficient as it is necessary here.
I know for instance that a straight forward

m_inline_end_vector.push_back(pc);

has often an O(n^2) complexity alone for this adding new elements,
(and it can throw, which makes error handling a mess).

But the performance of this table need to be O(n * log(n))
since n is often around 1.000.000 (when I debug large
apps like gcc).

A previous version of this patch used O(n^2) and was exactly doubling
the execution time for loading of the debug info (for debugging gcc's cc1).
Although that appears to be already done incrementally.


Bernd.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv3] Fix range end handling of inlined subroutines
  2020-03-12 18:21           ` Bernd Edlinger
@ 2020-03-12 18:27             ` Christian Biesinger
  2020-03-13  8:03               ` Bernd Edlinger
  0 siblings, 1 reply; 79+ messages in thread
From: Christian Biesinger @ 2020-03-12 18:27 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: Andrew Burgess, gdb-patches

On Thu, Mar 12, 2020 at 1:21 PM Bernd Edlinger
<bernd.edlinger@hotmail.de> wrote:
>
> On 3/11/20 11:02 PM, Andrew Burgess wrote:
> > * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-03-08 14:57:35 +0000]:
> >
> >> On 2/22/20 7:38 AM, Bernd Edlinger wrote:
> >>> On 2/9/20 10:07 PM, Bernd Edlinger wrote:
> >>>> Hi,
> >>>>
> >>>> this is based on Andrew's patch here:
> >>>>
> >>>> https://sourceware.org/ml/gdb-patches/2020-02/msg00092.html
> >>>>
> >>>> This and adds a heuristic to fix the case where caller
> >>>> and callee reside in the same subfile, it uses
> >>>> the recorded is-stmt bits and locates end of
> >>>> range infos, including the previously ignored empty
> >>>> range, and adjusting is-stmt info at this
> >>>> same pc, when the last item is not-is-stmt, the
> >>>> previous line table entries are dubious and
> >>>> we reset the is-stmt bit there.
> >>>> This fixes the new test case in Andrew's patch.
> >>>>
> >>>> It understood, that this is just a heuristic
> >>>> approach, since we do not have exactly the data,
> >>>> which is needed to determine at which of the identical
> >>>> PCs in the line table the subroutine actually ends.
> >>>> So, this tries to be as conservative as possible,
> >>>> just changing what is absolutely necessary.
> >>>>
> >>>> This patch itself is preliminary, since the is-stmt patch
> >>>> needs to be rebased after the refactoring of
> >>>> dwarf2read.c from yesterday, so I will have to rebase
> >>>> this patch as well, but decided to wait for Andrew.
> >>>>
> >>>>
> >>>
> >>> So, this is an update to my previous patch above:
> >>> https://sourceware.org/ml/gdb-patches/2020-02/msg00262.html
> >>>
> >>> It improves performance on big data, by using binary
> >>> search to locate the bogus line table entries.
> >>> Otherwise it behaves identical to the previous version,
> >>> and is still waiting for Andrew's patch before it can
> >>> be applied.
> >>>
> >>>
> >>
> >> Rebased to match Andrew's updated patch of today.
> >
> >
> > Bernd,
> >
> > Thanks for working on this.  This looks like a great improvement, but
> > I have a could of questions / comment inline below.
> >
> > Thanks,
> > Andrew
> >
> >
> >>
> >>
> >> Thanks
> >> Bernd.
> >
> >> From 010f1774ec69d5cab48db9b7a5fb9de3d5860f97 Mon Sep 17 00:00:00 2001
> >> From: Bernd Edlinger <bernd.edlinger@hotmail.de>
> >> Date: Sun, 9 Feb 2020 21:13:17 +0100
> >> Subject: [PATCH] Fix range end handling of inlined subroutines
> >>
> >> Since the is_stmt is now handled, it becomes
> >> possible to locate dubious is_stmt line entries
> >> at the end of an inlined function, even if the
> >> called inline function is in the same subfile.
> >>
> >> When there is a sequence of line entries at the
> >> same address where an inline range ends, and the
> >> last item has is_stmt = 0, we force all previous
> >> items to have is_stmt = 0 as well.
> >
> > This describes _what_ we're doing...
> >
> >>
> >> If the last line at that address has is_stmt = 1,
> >> there is no need to change anything, since a step
> >> over will always stop at that last line from the
> >> same address, which is fine, since it is outside
> >> the subroutine.
> >
> > This describes what we're _not_ doing...
> >
> > ... it just feels like this description would benefit for a bit more
> > _why_.  How does setting all the is_stmt fields to 0 help us?  What
> > cases step/next should we expect to see improvement in?
> >
> > I'm also worried that setting is_stmt to false will mean we loose the
> > ability to set breakpoints on some lines using file:line syntax.  I
> > wonder if we should consider coming up with a solution that both
> > preserves the ability to set breakpoints, while also giving the
> > benefits for step/next.
> >
>
> Maybe as a followup.  The breakpoints on theses lines are *very*
> problematic, as the call stack is almost always wrong.  From the block
> info they appear to be part of the caller, but from the line table they
> are located in the subroutine, when you are at this breakpoint, an
> ordinary user can't make sense of the call stack, as one call frame is
> missing.
>
> The is-stmt patch does the same, removing line infos that could
> in theory be used as breakpoints. See buildsym_compunit::record_line:
>
>   /* Normally, we treat lines as unsorted.  But the end of sequence
>      marker is special.  We sort line markers at the same PC by line
>      number, so end of sequence markers (which have line == 0) appear
>      first.  This is right if the marker ends the previous function,
>      and there is no padding before the next function.  But it is
>      wrong if the previous line was empty and we are now marking a
>      switch to a different subfile.  We must leave the end of sequence
>      marker at the end of this group of lines, not sort the empty line
>      to after the marker.  The easiest way to accomplish this is to
>      delete any empty lines from our table, if they are followed by
>      end of sequence markers.  All we lose is the ability to set
>      breakpoints at some lines which contain no instructions
>      anyway.  */
>   if (line == 0 && subfile->line_vector->nitems > 0)
>     {
>       e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
>       while (subfile->line_vector->nitems > 0 && e->pc == pc)
>         {
>           e--;
>           subfile->line_vector->nitems--;
>         }
>     }
>
>
> This is invoked if a is-stmt line is followed by
> a non-stmt line of another CU, at the same PC.
> This is previously the non-is-stmt lines were all ignored,
> but now the non-is-stmt makes an end marker in the current
> CU and a non-is-stmt line in the next CU.
> This deletion is exactly what makes the other patch work.
>
> But I think it is not necessary to remove those lines altogether,
> instead we can just make them non-is-stmt lines, which would
> better match to what this patch does.
>
> While I write these lines, I see an UB in the above code,
> since e-- can decrement the pointer before the beginning of the
> line_vector, when nitems becomes 0, the value is not used, but
> the pointer value e indeterminate if I see that right.
>
> I will send a patch for that, when I find time for it.
>
> >>
> >> This is about the best we can do at the moment,
> >> unless location view information are added to the
> >> block ranges debug info structure, and location
> >> views are implemented in gdb in general.
> >>
> >> gdb:
> >> 2020-03-08  Bernd Edlinger  <bernd.edlinger@hotmail.de>
> >>
> >>      * buildsym.c (buildsym_compunit::record_inline_range_end,
> >>      patch_inline_end_pos): New helper functions.
> >>      (buildsym_compunit::end_symtab_with_blockvector): Patch line table.
> >>      (buildsym_compunit::~buildsym_compunit): Cleanup m_inline_end_vector.
> >>      * buildsym.h (buildsym_compunit::record_inline_range_end): Declare.
> >>      (buildsym_compunit::m_inline_end_vector,
> >>      buildsym_compunit::m_inline_end_vector_length,
> >>      buildsym_compunit::m_inline_end_vector_nitems): New data items.
> >>      * dwarf2/read.c (dwarf2_rnglists_process,
> >>      dwarf2_ranges_process): Don't ignore empty ranges here.
> >>      (dwarf2_ranges_read): Ignore empty ranges here.
> >>      (dwarf2_record_block_ranges): Pass end of range PC to
> >>      record_inline_range_end for inline functions.
> >>
> >> gdb/testsuite:
> >> 2020-03-08  Bernd Edlinger  <bernd.edlinger@hotmail.de>
> >>
> >>      * gdb.cp/step-and-next-inline.exp: Adjust test.
> >> ---
> >>  gdb/buildsym.c                                | 84 +++++++++++++++++++++++++++
> >>  gdb/buildsym.h                                | 11 ++++
> >>  gdb/dwarf2/read.c                             | 22 ++++---
> >>  gdb/testsuite/gdb.cp/step-and-next-inline.exp | 17 ------
> >>  4 files changed, 108 insertions(+), 26 deletions(-)
> >>
> >> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> >> index 24aeba8..f02b7ce 100644
> >> --- a/gdb/buildsym.c
> >> +++ b/gdb/buildsym.c
> >> @@ -113,6 +113,8 @@ struct pending_block
> >>        next1 = next->next;
> >>        xfree ((void *) next);
> >>      }
> >> +
> >> +  xfree (m_inline_end_vector);
> >>  }
> >>
> >>  struct macro_table *
> >>>> this patch as well, but decided to wait for Andrew.
> >> @@ -732,6 +734,84 @@ struct blockvector *
> >>  }
> >>
> >>
> >> +/* Record a PC where a inlined subroutine ends.  */
> >> +
> >> +void
> >> +buildsym_compunit::record_inline_range_end (CORE_ADDR end)
> >> +{
> >> +  if (m_inline_end_vector == nullptr)
> >> +    {
> >> +      m_inline_end_vector_length = INITIAL_LINE_VECTOR_LENGTH;
> >> +      m_inline_end_vector = (CORE_ADDR *)
> >> +    xmalloc (sizeof (CORE_ADDR) * m_inline_end_vector_length);
> >> +      m_inline_end_vector_nitems = 0;
> >> +    }
> >> +  else if (m_inline_end_vector_nitems == m_inline_end_vector_length)
> >> +    {
> >> +      m_inline_end_vector_length *= 2;
> >> +      m_inline_end_vector = (CORE_ADDR *)
> >> +    xrealloc ((char *) m_inline_end_vector,
> >> +              sizeof (CORE_ADDR) * m_inline_end_vector_length);
> >> +    }
> >> +
> >> +  m_inline_end_vector[m_inline_end_vector_nitems++] = end;
> >> +}
> >> +
> >> +
> >> +/* Patch the is_stmt bits at the given inline end address.
> >> +   The line table has to be already sorted.  */
> >> +
> >> +static void
> >> +patch_inline_end_pos (struct linetable *table, CORE_ADDR end)
> >> +{
> >> +  int a = 2, b = table->nitems - 1;
> >> +  struct linetable_entry *items = table->item;
> >> +
> >> +  /* We need at least two items with pc = end in the table.
> >> +     The lowest usable items are at pos 0 and 1, the highest
> >> +     usable items are at pos b - 2 and b - 1.  */
> >> +  if (a > b || end < items[1].pc || end > items[b - 2].pc)
> >> +    return;
> >> +
> >> +  /* Look for the first item with pc > end in the range [a,b].
> >> +     The previous element has pc = end or there is no match.
> >> +     We set a = 2, since we need at least two consecutive elements
> >> +     with pc = end to do anything useful.
> >> +     We set b = nitems - 1, since we are not interested in the last
> >> +     element which should be an end of sequence marker with line = 0
> >> +     and is_stmt = 1.  */
> >> +  while (a < b)
> >> +    {
> >> +      int c = (a + b) / 2;
> >> +
> >> +      if (end < items[c].pc)
> >> +    b = c;
> >> +      else
> >> +    a = c + 1;
> >> +    }
> >> +
> >> +  a--;
> >> +  if (items[a].pc != end || items[a].is_stmt)
> >> +    return;
> >> +
> >> +  /* When there is a sequence of line entries at the same address
> >> +     where an inline range ends, and the last item has is_stmt = 0,
> >> +     we force all previous items to have is_stmt = 0 as well.
> >> +     Setting breakpoints at those addresses is currently not
> >> +     supported, since it is unclear if the previous addresses are
> >> +     part of the subroutine or the calling program.  */
> >> +  while (a-- > 0)
> >
> > I know this is a personal preference, but I really dislike having
> > these inc/dec adjustments within expressions like this.  I feel it's
> > too easy to overlook, and I always end up having to think twice just
> > to make sure I've parsed the intention correctly.  I'd like to make a
> > plea to have the adjustment moved into the body of the loop please.
> >
>
> No problem, I can do that better, the first iteration does
> not need to check for a > 0, so I will make that a
> do { a--; .... } while (a > 0), so that will be even more
> efficient this way.
>
> >> +    {
> >> +      /* We stop at the first line entry with a different address,
> >> +         or when we see an end of sequence marker.  */
> >
> > I think there's a whitespace error at the start of this line.
>
> Yes, indeed.
>
> >
> >> +      if (items[a].pc != end || items[a].line == 0)
> >> +    break;
> >> +
> >> +      items[a].is_stmt = 0;
> >> +    }
> >> +}
> >> +
> >> +
> >>  /* Subroutine of end_symtab to simplify it.  Look for a subfile that
> >>     matches the main source file's basename.  If there is only one, and
> >>     if the main source file doesn't have any symbol or line number
> >> @@ -965,6 +1045,10 @@ struct compunit_symtab *
> >>                            subfile->line_vector->item
> >>                            + subfile->line_vector->nitems,
> >>                            lte_is_less_than);
> >> +
> >> +       for (int i = 0; i < m_inline_end_vector_nitems; i++)
> >> +         patch_inline_end_pos (subfile->line_vector,
> >> +                               m_inline_end_vector[i]);
> >>      }
> >>
> >>        /* Allocate a symbol table if necessary.  */
> >> diff --git a/gdb/buildsym.h b/gdb/buildsym.h
> >> index c768a4c..2845789 100644
> >> --- a/gdb/buildsym.h
> >> +++ b/gdb/buildsym.h
> >> @@ -190,6 +190,8 @@ struct buildsym_compunit
> >>    void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
> >>                  bool is_stmt);
> >>
> >> +  void record_inline_range_end (CORE_ADDR end);
> >> +
> >>    struct compunit_symtab *get_compunit_symtab ()
> >>    {
> >>      return m_compunit_symtab;
> >> @@ -397,6 +399,15 @@ struct buildsym_compunit
> >>
> >>    /* Pending symbols that are local to the lexical context.  */
> >>    struct pending *m_local_symbols = nullptr;
> >> +
> >> +  /* Pending inline end range addresses.  */
> >> +  CORE_ADDR * m_inline_end_vector = nullptr;
> >> +
> >> +  /* Number of allocated inline end range addresses.  */
> >> +  int m_inline_end_vector_length = 0;
> >> +
> >> +  /* Number of pending inline end range addresses.  */
> >> +  int m_inline_end_vector_nitems = 0;
> >>  };
> >
> > I think we should probably just use a std::vector<CORE_ADDR> for
> > m_inline_end_vector - unless I'm missing a good reason.  I think this
> > would simplify some of the code in buildsym.c.
> >
>
> I am worried that the vector is not as efficient as it is necessary here.
> I know for instance that a straight forward
>
> m_inline_end_vector.push_back(pc);
>
> has often an O(n^2) complexity alone for this adding new elements,
> (and it can throw, which makes error handling a mess).

push_back is not O(n^2). See
https://en.cppreference.com/w/cpp/container/vector/push_back:
"Complexity
Amortized constant"

That's because it doubles the capacity whenever it has to resize.

Christian

> But the performance of this table need to be O(n * log(n))
> since n is often around 1.000.000 (when I debug large
> apps like gcc).
>
> A previous version of this patch used O(n^2) and was exactly doubling
> the execution time for loading of the debug info (for debugging gcc's cc1).
> Although that appears to be already done incrementally.
>
>
> Bernd.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv3] Fix range end handling of inlined subroutines
  2020-03-12 18:27             ` Christian Biesinger
@ 2020-03-13  8:03               ` Bernd Edlinger
  2020-03-17 22:27                 ` Andrew Burgess
  0 siblings, 1 reply; 79+ messages in thread
From: Bernd Edlinger @ 2020-03-13  8:03 UTC (permalink / raw)
  To: Christian Biesinger; +Cc: Andrew Burgess, gdb-patches

On 3/12/20 7:27 PM, Christian Biesinger wrote:
> On Thu, Mar 12, 2020 at 1:21 PM Bernd Edlinger
> <bernd.edlinger@hotmail.de> wrote:
>>
>> I am worried that the vector is not as efficient as it is necessary here.
>> I know for instance that a straight forward
>>
>> m_inline_end_vector.push_back(pc);
>>
>> has often an O(n^2) complexity alone for this adding new elements,
>> (and it can throw, which makes error handling a mess).
> 
> push_back is not O(n^2). See
> https://en.cppreference.com/w/cpp/container/vector/push_back:
> "Complexity
> Amortized constant"
> 
> That's because it doubles the capacity whenever it has to resize.
> 
> Christian
> 

While that may be true for g++, this is not the case for all compilers.

If you don't know which compiler will be used, (gcc, clang, diab, msvc to
name a few) you cannot rely on that.

I just repeated what I already learned the hard way, and tried the following

#include <vector>

int main()
{
  int i, m = 1000000;
  stc::vector<int> v;
  for (i = 0; i < m; i++)
     v.push_back(i);
  return 0;
}

Compiled it with visual studio 2010, and see it take
a mutex 1 million times, allocate each time only one
element more, does not use realloc so must move in every
case.

All in all, for a performance critical part in the software
like this, I would not recommend to use a component where
it depends so much on the compiler and the implementation
details of the stl library.

A similar surprise happens with stl::list's size(), which
may be O(1) or O(n) is even documented that way.

So that is my personal experience, in that field.
And bad experiences last longer in my memory than good ones :(


Bernd.

>> But the performance of this table need to be O(n * log(n))
>> since n is often around 1.000.000 (when I debug large
>> apps like gcc).
>>
>> A previous version of this patch used O(n^2) and was exactly doubling
>> the execution time for loading of the debug info (for debugging gcc's cc1).
>> Although that appears to be already done incrementally.
>>
>>
>> Bernd.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCHv4] Fix range end handling of inlined subroutines
  2020-03-08 14:57       ` [PATCHv3] " Bernd Edlinger
  2020-03-11 22:02         ` Andrew Burgess
@ 2020-03-13 12:47         ` Bernd Edlinger
  1 sibling, 0 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-03-13 12:47 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 245 bytes --]

This is the updated version of my patch, with additional
information "why" this is done in the changelog,
and addressing all review comments so far.

I tested again, also together with my other patch,
all test results look good.


Thanks
Bernd.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-range-end-handling-of-inlined-subroutines.patch --]
[-- Type: text/x-patch; name="0001-Fix-range-end-handling-of-inlined-subroutines.patch", Size: 10992 bytes --]

From faec33a8d7fea3a990505eb9d65e6c93c0afd0eb Mon Sep 17 00:00:00 2001
From: Bernd Edlinger <bernd.edlinger@hotmail.de>
Date: Sun, 9 Feb 2020 21:13:17 +0100
Subject: [PATCH] Fix range end handling of inlined subroutines

Since the is_stmt is now handled, it becomes
possible to locate dubious is_stmt line entries
at the end of an inlined function, even if the
called inline function is in the same subfile.

When there is a sequence of line entries at the
same address where an inline range ends, and the
last item has is_stmt = 0, we force all previous
items to have is_stmt = 0 as well.

If the last line at that address has is_stmt = 1,
there is no need to change anything, since a step
over will always stop at that last line from the
same address, which is fine, since it is outside
the subroutine.

With this change we loose the ability to set
breakpoints on some lines using file:line syntax,
but the data is not completely lost, as the
line table is still holding those lines, just
with the is_stmt flag reset.

This is necessary as breakpoints on these lines
are problematic, because the call stack is often
wrong.  From the block info they appear to be
located in the caller, but they are actually meant
to be part of the subroutine, therefore usually one
frame is missing from the callstack when the
execution stops there.

This is about the best we can do at the moment,
unless location view information are added to the
block ranges debug info structure, and location
views are implemented in gdb in general.

gdb:
2020-03-13  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	* buildsym.c (buildsym_compunit::record_inline_range_end,
	patch_inline_end_pos): New helper functions.
	(buildsym_compunit::end_symtab_with_blockvector): Patch line table.
	(buildsym_compunit::~buildsym_compunit): Cleanup m_inline_end_vector.
	* buildsym.h (buildsym_compunit::record_inline_range_end): Declare.
	(buildsym_compunit::m_inline_end_vector,
	buildsym_compunit::m_inline_end_vector_length,
	buildsym_compunit::m_inline_end_vector_nitems): New data items.
	* dwarf2/read.c (dwarf2_rnglists_process,
	dwarf2_ranges_process): Don't ignore empty ranges here.
	(dwarf2_ranges_read): Ignore empty ranges here.
	(dwarf2_record_block_ranges): Pass end of range PC to
	record_inline_range_end for inline functions.

gdb/testsuite:
2020-03-13  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	* gdb.cp/step-and-next-inline.exp: Adjust test.
---
 gdb/buildsym.c                                | 86 +++++++++++++++++++++++++++
 gdb/buildsym.h                                | 11 ++++
 gdb/dwarf2/read.c                             | 22 ++++---
 gdb/testsuite/gdb.cp/step-and-next-inline.exp | 17 ------
 4 files changed, 110 insertions(+), 26 deletions(-)

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 960a36c..46f985a 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -113,6 +113,8 @@ struct pending_block
       next1 = next->next;
       xfree ((void *) next);
     }
+
+  xfree (m_inline_end_vector);
 }
 
 struct macro_table *
@@ -729,6 +731,86 @@ struct blockvector *
 }
 
 \f
+/* Record a PC where a inlined subroutine ends.  */
+
+void
+buildsym_compunit::record_inline_range_end (CORE_ADDR end)
+{
+  if (m_inline_end_vector == nullptr)
+    {
+      m_inline_end_vector_length = INITIAL_LINE_VECTOR_LENGTH;
+      m_inline_end_vector = (CORE_ADDR *)
+	xmalloc (sizeof (CORE_ADDR) * m_inline_end_vector_length);
+      m_inline_end_vector_nitems = 0;
+    }
+  else if (m_inline_end_vector_nitems == m_inline_end_vector_length)
+    {
+      m_inline_end_vector_length *= 2;
+      m_inline_end_vector = (CORE_ADDR *)
+	xrealloc ((char *) m_inline_end_vector,
+		  sizeof (CORE_ADDR) * m_inline_end_vector_length);
+    }
+
+  m_inline_end_vector[m_inline_end_vector_nitems++] = end;
+}
+
+\f
+/* Patch the is_stmt bits at the given inline end address.
+   The line table has to be already sorted.  */
+
+static void
+patch_inline_end_pos (struct linetable *table, CORE_ADDR end)
+{
+  int a = 2, b = table->nitems - 1;
+  struct linetable_entry *items = table->item;
+
+  /* We need at least two items with pc = end in the table.
+     The lowest usable items are at pos 0 and 1, the highest
+     usable items are at pos b - 2 and b - 1.  */
+  if (a > b || end < items[1].pc || end > items[b - 2].pc)
+    return;
+
+  /* Look for the first item with pc > end in the range [a,b].
+     The previous element has pc = end or there is no match.
+     We set a = 2, since we need at least two consecutive elements
+     with pc = end to do anything useful.
+     We set b = nitems - 1, since we are not interested in the last
+     element which should be an end of sequence marker with line = 0
+     and is_stmt = 1.  */
+  while (a < b)
+    {
+      int c = (a + b) / 2;
+
+      if (end < items[c].pc)
+	b = c;
+      else
+	a = c + 1;
+    }
+
+  a--;
+  if (items[a].pc != end || items[a].is_stmt)
+    return;
+
+  /* When there is a sequence of line entries at the same address
+     where an inline range ends, and the last item has is_stmt = 0,
+     we force all previous items to have is_stmt = 0 as well.
+     Setting breakpoints at those addresses is currently not
+     supported, since it is unclear if the previous addresses are
+     part of the subroutine or the calling program.  */
+  do
+    {
+      /* We stop at the first line entry with a different address,
+	 or when we see an end of sequence marker.  */
+      a--;
+      if (items[a].pc != end || items[a].line == 0)
+	break;
+
+      items[a].is_stmt = 0;
+    }
+  while (a > 0);
+}
+
+\f
 /* Subroutine of end_symtab to simplify it.  Look for a subfile that
    matches the main source file's basename.  If there is only one, and
    if the main source file doesn't have any symbol or line number
@@ -962,6 +1044,10 @@ struct compunit_symtab *
 			      subfile->line_vector->item
 			      + subfile->line_vector->nitems,
 			      lte_is_less_than);
+
+	   for (int i = 0; i < m_inline_end_vector_nitems; i++)
+	     patch_inline_end_pos (subfile->line_vector,
+				   m_inline_end_vector[i]);
 	}
 
       /* Allocate a symbol table if necessary.  */
diff --git a/gdb/buildsym.h b/gdb/buildsym.h
index c768a4c..2845789 100644
--- a/gdb/buildsym.h
+++ b/gdb/buildsym.h
@@ -190,6 +190,8 @@ struct buildsym_compunit
   void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
 		    bool is_stmt);
 
+  void record_inline_range_end (CORE_ADDR end);
+
   struct compunit_symtab *get_compunit_symtab ()
   {
     return m_compunit_symtab;
@@ -397,6 +399,15 @@ struct buildsym_compunit
 
   /* Pending symbols that are local to the lexical context.  */
   struct pending *m_local_symbols = nullptr;
+
+  /* Pending inline end range addresses.  */
+  CORE_ADDR * m_inline_end_vector = nullptr;
+
+  /* Number of allocated inline end range addresses.  */
+  int m_inline_end_vector_length = 0;
+
+  /* Number of pending inline end range addresses.  */
+  int m_inline_end_vector_nitems = 0;
 };
 
 \f
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 1706b96..6be008c 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -13609,10 +13609,6 @@ class process_die_scope
 	  return false;
 	}
 
-      /* Empty range entries have no effect.  */
-      if (range_beginning == range_end)
-	continue;
-
       range_beginning += base;
       range_end += base;
 
@@ -13723,10 +13719,6 @@ class process_die_scope
 	  return 0;
 	}
 
-      /* Empty range entries have no effect.  */
-      if (range_beginning == range_end)
-	continue;
-
       range_beginning += base;
       range_end += base;
 
@@ -13766,6 +13758,10 @@ class process_die_scope
   retval = dwarf2_ranges_process (offset, cu,
     [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
     {
+      /* Empty range entries have no effect.  */
+      if (range_beginning == range_end)
+	return;
+
       if (ranges_pst != NULL)
 	{
 	  CORE_ADDR lowpc;
@@ -14003,6 +13999,7 @@ class process_die_scope
   struct gdbarch *gdbarch = get_objfile_arch (objfile);
   struct attribute *attr;
   struct attribute *attr_high;
+  bool inlined_subroutine = (die->tag == DW_TAG_inlined_subroutine);
 
   attr_high = dwarf2_attr (die, DW_AT_high_pc, cu);
   if (attr_high)
@@ -14018,7 +14015,10 @@ class process_die_scope
 
 	  low = gdbarch_adjust_dwarf2_addr (gdbarch, low + baseaddr);
 	  high = gdbarch_adjust_dwarf2_addr (gdbarch, high + baseaddr);
-	  cu->get_builder ()->record_block_range (block, low, high - 1);
+	  if (inlined_subroutine)
+	    cu->get_builder ()->record_inline_range_end (high);
+	  if (low < high)
+	    cu->get_builder ()->record_block_range (block, low, high - 1);
         }
     }
 
@@ -14043,6 +14043,10 @@ class process_die_scope
 	  end += baseaddr;
 	  start = gdbarch_adjust_dwarf2_addr (gdbarch, start);
 	  end = gdbarch_adjust_dwarf2_addr (gdbarch, end);
+	  if (inlined_subroutine)
+	    cu->get_builder ()->record_inline_range_end (end);
+	  if (start == end)
+	    return;
 	  cu->get_builder ()->record_block_range (block, start, end - 1);
 	  blockvec.emplace_back (start, end);
 	});
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
index acec48b..84c0901 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -48,37 +48,20 @@ proc do_test { use_header } {
     gdb_test "step" ".*" "step into get_alias_set"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 1"
-    # It's possible that this first failure (when not using a header
-    # file) is GCC's fault, though the remaining failures would best
-    # be fixed by adding location views support (though it could be
-    # that some easier heuristic could be figured out).  Still, it is
-    # not certain that the first failure wouldn't also be fixed by
-    # having location view support, so for now it is tagged as such.
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" ".*TREE_TYPE.*" "next step 1"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 2"
     gdb_test "next" ".*TREE_TYPE.*" "next step 2"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 3"
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" ".*TREE_TYPE.*" "next step 3"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 4"
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" "return 0.*" "next step 4"
     gdb_test "bt" \
 	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
 	"not in inline 5"
 
-    if {!$use_header} {
-	# With the debug from GCC 10.x (and earlier) GDB is currently
-	# unable to successfully complete the following tests when we
-	# are not using a header file.
-	kfail symtab/25507 "stepping tests"
-	return
-    }
-
     clean_restart ${executable}
 
     if ![runto_main] {
-- 
1.9.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-03-08 12:50           ` [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
@ 2020-03-16 20:57             ` Tom Tromey
  2020-03-16 22:37               ` Bernd Edlinger
  2020-03-17 12:47               ` Tom Tromey
  2020-03-23 17:30             ` [PATCH 0/3] Keep duplicate line table entries Andrew Burgess
                               ` (3 subsequent siblings)
  4 siblings, 2 replies; 79+ messages in thread
From: Tom Tromey @ 2020-03-16 20:57 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, Bernd Edlinger

Hi.

This particular hunk causes a regression on an internal test case for a
RISC-V target:

Andrew> +  /* If we have a duplicate for the previous entry then ignore the new
Andrew> +     entry, except, if the new entry is setting the is_stmt flag, then
Andrew> +     ensure the previous entry respects the new setting.  */
Andrew> +  e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
Andrew> +  if (e->line == line && e->pc == pc)
Andrew> +    {
Andrew> +      if (is_stmt && !e->is_stmt)
Andrew> +	e->is_stmt = 1;
Andrew> +      return;
Andrew> +    }

Now, I'm not 100% certain that this is a "true" regression.  The test
case in question already has a large number of XFAILs for various
targets, because in practice gdb reports different stop locations for
different compilers.

It's just 2 short files so I can share it here:

    /* r.c */
    #include "r.h"

    int
    main ()
    {
      callee ();
    }

    /* r.h */
    int counter = 42;

    inline void
    callee () {
      counter = 0;  /* break here */
    }

When I look at this with readelf I see:

    $ readelf --debug-dump=decodedline ./r 
    Contents of the .debug_line section:

    r.c:
    File name                            Line number    Starting address    View    Stmt
    r.c                                            6          0x80000042               x
    r.c                                            7          0x80000042       1       x

    r.h:
    r.h                                            6          0x80000042       2       x
    r.h                                            6          0x80000042       3

    r.c:
    r.c                                            8          0x8000004a       4
    r.c                                            8          0x8000004e       5

I can send an executable if you want.

If I run gdb on the resulting program and "break r.h:6", before the
is-statement patch it reports r.h:6, but afterward it says:

    (gdb) b r.h:6
    Breakpoint 1 at 0x8000004a: file r.c, line 8.

Backing out the hunk in question up above is enough to fix the problem.

What is happening is that gdb is no longer recognizing the special
prologue rule in skip_prologue_sal:

      /* For languages other than assembly, treat two consecutive line
	 entries at the same address as a zero-instruction prologue.
	 The GNU assembler emits separate line notes for each instruction
	 in a multi-instruction macro, but compilers generally will not
	 do this.  */

TBH this has always seemed like a big hack, at least in the DWARF era.
But, I suppose it's a hack we continue to need, at least until GCC emits
proper prologue end notes.

Just removing the "return" is enough to make it work for me.  I didn't
try a real test run, though.

I'd appreciate any thoughts you have on this.

thanks,
Tom

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-03-16 20:57             ` Tom Tromey
@ 2020-03-16 22:37               ` Bernd Edlinger
  2020-03-17 12:47               ` Tom Tromey
  1 sibling, 0 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-03-16 22:37 UTC (permalink / raw)
  To: Tom Tromey, Andrew Burgess; +Cc: gdb-patches



On 3/16/20 9:57 PM, Tom Tromey wrote:
> Hi.
> 
> This particular hunk causes a regression on an internal test case for a
> RISC-V target:
> 
> Andrew> +  /* If we have a duplicate for the previous entry then ignore the new
> Andrew> +     entry, except, if the new entry is setting the is_stmt flag, then
> Andrew> +     ensure the previous entry respects the new setting.  */
> Andrew> +  e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
> Andrew> +  if (e->line == line && e->pc == pc)
> Andrew> +    {
> Andrew> +      if (is_stmt && !e->is_stmt)
> Andrew> +	e->is_stmt = 1;
> Andrew> +      return;
> Andrew> +    }
> 

I don't think this whole if statement is really needed.
I looks more sane this way, but everything seems to work also without.


> Now, I'm not 100% certain that this is a "true" regression.  The test
> case in question already has a large number of XFAILs for various
> targets, because in practice gdb reports different stop locations for
> different compilers.
> 
> It's just 2 short files so I can share it here:
> 
>     /* r.c */
>     #include "r.h"
> 
>     int
>     main ()
>     {
>       callee ();
>     }
> 
>     /* r.h */
>     int counter = 42;
> 
>     inline void
>     callee () {
>       counter = 0;  /* break here */
>     }
> 
> When I look at this with readelf I see:
> 
>     $ readelf --debug-dump=decodedline ./r 
>     Contents of the .debug_line section:
> 
>     r.c:
>     File name                            Line number    Starting address    View    Stmt
>     r.c                                            6          0x80000042               x
>     r.c                                            7          0x80000042       1       x
> 
>     r.h:
>     r.h                                            6          0x80000042       2       x
>     r.h                                            6          0x80000042       3
> 
>     r.c:
>     r.c                                            8          0x8000004a       4
>     r.c                                            8          0x8000004e       5
> 
> I can send an executable if you want.
> 
> If I run gdb on the resulting program and "break r.h:6", before the
> is-statement patch it reports r.h:6, but afterward it says:
> 
>     (gdb) b r.h:6
>     Breakpoint 1 at 0x8000004a: file r.c, line 8.
> 
> Backing out the hunk in question up above is enough to fix the problem.
> 
> What is happening is that gdb is no longer recognizing the special
> prologue rule in skip_prologue_sal:
> 
>       /* For languages other than assembly, treat two consecutive line
> 	 entries at the same address as a zero-instruction prologue.
> 	 The GNU assembler emits separate line notes for each instruction
> 	 in a multi-instruction macro, but compilers generally will not
> 	 do this.  */
> 
> TBH this has always seemed like a big hack, at least in the DWARF era.
> But, I suppose it's a hack we continue to need, at least until GCC emits
> proper prologue end notes.
> 
> Just removing the "return" is enough to make it work for me.  I didn't
> try a real test run, though.
> 
> I'd appreciate any thoughts you have on this.
> 
> thanks,
> Tom
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-03-16 20:57             ` Tom Tromey
  2020-03-16 22:37               ` Bernd Edlinger
@ 2020-03-17 12:47               ` Tom Tromey
  2020-03-17 18:23                 ` Tom Tromey
  1 sibling, 1 reply; 79+ messages in thread
From: Tom Tromey @ 2020-03-17 12:47 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Andrew Burgess, gdb-patches, Bernd Edlinger

Tom>     (gdb) b r.h:6
Tom>     Breakpoint 1 at 0x8000004a: file r.c, line 8.

Tom> Backing out the hunk in question up above is enough to fix the problem.

Actually, when I back out the hunk, I get:

    Breakpoint 1 at 0x80000042: file r.h, line 6.

However, in the past it appears this mentioned the function name.

Tom

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-03-17 12:47               ` Tom Tromey
@ 2020-03-17 18:23                 ` Tom Tromey
  2020-03-17 18:51                   ` Bernd Edlinger
  2020-03-17 18:56                   ` Andrew Burgess
  0 siblings, 2 replies; 79+ messages in thread
From: Tom Tromey @ 2020-03-17 18:23 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Andrew Burgess, gdb-patches, Bernd Edlinger

Tom> Actually, when I back out the hunk, I get:
Tom>     Breakpoint 1 at 0x80000042: file r.h, line 6.
Tom> However, in the past it appears this mentioned the function name.

I think I'm mistaken about this, so IMO we can just drop this part and
focus on whether we should remove that buildsym.c hunk.

thanks,
Tom

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-03-17 18:23                 ` Tom Tromey
@ 2020-03-17 18:51                   ` Bernd Edlinger
  2020-03-17 18:56                   ` Andrew Burgess
  1 sibling, 0 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-03-17 18:51 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Andrew Burgess, gdb-patches

On 3/17/20 7:23 PM, Tom Tromey wrote:
> Tom> Actually, when I back out the hunk, I get:
> Tom>     Breakpoint 1 at 0x80000042: file r.h, line 6.
> Tom> However, in the past it appears this mentioned the function name.
> 
> I think I'm mistaken about this, so IMO we can just drop this part and
> focus on whether we should remove that buildsym.c hunk.
> 

Agreed, I'd just remove that for now

--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -683,20 +683,6 @@ struct blockvector *
       m_have_line_numbers = true;
     }
 
-  if (subfile->line_vector->nitems > 0)
-    {
-      /* If we have a duplicate for the previous entry then ignore the new
-        entry, except, if the new entry is setting the is_stmt flag, then
-        ensure the previous entry respects the new setting.  */
-      e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
-      if (e->line == line && e->pc == pc)
-       {
-         if (is_stmt && !e->is_stmt)
-           e->is_stmt = 1;
-         return;
-       }
-    }
-

Andrew?

> thanks,
> Tom
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-03-17 18:23                 ` Tom Tromey
  2020-03-17 18:51                   ` Bernd Edlinger
@ 2020-03-17 18:56                   ` Andrew Burgess
  2020-03-17 20:18                     ` Tom Tromey
  1 sibling, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-03-17 18:56 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, Bernd Edlinger

* Tom Tromey <tom@tromey.com> [2020-03-17 12:23:49 -0600]:

> Tom> Actually, when I back out the hunk, I get:
> Tom>     Breakpoint 1 at 0x80000042: file r.h, line 6.
> Tom> However, in the past it appears this mentioned the function name.
> 
> I think I'm mistaken about this, so IMO we can just drop this part and
> focus on whether we should remove that buildsym.c hunk.

The hunk in question is only an optimisation because we now see lots
of duplicate entries toggling between is-stmt true and is-stmt false.
I don't believe that keeping the duplicates around would cause any
problems.

I would be quite happy if you wanted to remove the hunk.  We might
also consider something like this totally untested patch:

## START ##

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 7155db34b08..b0d0bc53eb5 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -683,16 +683,12 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
 
   if (subfile->line_vector->nitems > 0)
     {
-      /* If we have a duplicate for the previous entry then ignore the new
-        entry, except, if the new entry is setting the is_stmt flag, then
-        ensure the previous entry respects the new setting.  */
+      /* If the previous entry is for the same line, at the same pc, and
+        is marked as a statement, while we are trying to add a
+        non-statement, then lets just drop this new non-statement.  */
       e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
-      if (e->line == line && e->pc == pc)
-       {
-         if (is_stmt && !e->is_stmt)
-           e->is_stmt = 1;
-         return;
-       }
+      if (e->line == line && e->pc == pc && e->is_stmt && !is_stmt)
+       return
     }
 
   if (subfile->line_vector->nitems + 1 >= subfile->line_vector_length)

## END ##

This will remove fewer duplicates, but should keep around the ones we
care about I hope.

I'll test that patch at this end, but do let me know if it solves the
regression you're seeing.

Thanks,
Andrew

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-03-17 18:56                   ` Andrew Burgess
@ 2020-03-17 20:18                     ` Tom Tromey
  2020-03-17 22:21                       ` Andrew Burgess
  0 siblings, 1 reply; 79+ messages in thread
From: Tom Tromey @ 2020-03-17 20:18 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Tom Tromey, Bernd Edlinger, gdb-patches

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

Andrew> +      /* If the previous entry is for the same line, at the same pc, and
Andrew> +        is marked as a statement, while we are trying to add a
Andrew> +        non-statement, then lets just drop this new non-statement.  */

I didn't try it yet, but I suspect it won't work, because the readelf
output up-thread showed that the 2nd r.h line was not a statement:

    File name                            Line number    Starting address    View    Stmt
[...]
    r.h                                            6          0x80000042       2       x
    r.h                                            6          0x80000042       3

Tom

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-03-17 20:18                     ` Tom Tromey
@ 2020-03-17 22:21                       ` Andrew Burgess
  0 siblings, 0 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-03-17 22:21 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Bernd Edlinger, gdb-patches

* Tom Tromey <tom@tromey.com> [2020-03-17 14:18:43 -0600]:

> >>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
> 
> Andrew> +      /* If the previous entry is for the same line, at the same pc, and
> Andrew> +        is marked as a statement, while we are trying to add a
> Andrew> +        non-statement, then lets just drop this new non-statement.  */
> 
> I didn't try it yet, but I suspect it won't work, because the readelf
> output up-thread showed that the 2nd r.h line was not a statement:
> 
>     File name                            Line number    Starting address    View    Stmt
> [...]
>     r.h                                            6          0x80000042       2       x
>     r.h                                            6          0x80000042       3

OK, but prior to the is-stmt patches GDB didn't track any
non-statement line table entries, so my thinking was that previously
we were spotting two sequential entries that were marked as
statements.  This led me to think that deleting duplicate
non-statement entries would be fine.

With current HEAD of GDB the line table looks like this:

  (gdb) maintenance info line-table 
  objfile: /home/andrew/tmp/r.x ((struct objfile *) 0x2ac3b90)
  compunit_symtab: ((struct compunit_symtab *) 0x2aec530)
  symtab: /home/andrew/tmp/r.c ((struct symtab *) 0x2aec5b0)
  linetable: ((struct linetable *) 0x2b861d0):
  INDEX    LINE ADDRESS IS-STMT
  0         END 0x0000000000010150 Y
  1           8 0x0000000000010154
  2         END 0x0000000000010158 Y
  objfile: /home/andrew/tmp/r.x ((struct objfile *) 0x2ac3b90)
  compunit_symtab: ((struct compunit_symtab *) 0x2aec530)
  symtab: /home/andrew/tmp/r.h ((struct symtab *) 0x2aec5e0)
  linetable: ((struct linetable *) 0x2b86190):
  INDEX    LINE ADDRESS IS-STMT
  0           6 0x0000000000010150 Y
  1         END 0x0000000000010154 Y

If we completely remove the hunk that you identified then the line
table becomes:

  (gdb) maintenance info line-table
  objfile: /home/andrew/tmp/r.x ((struct objfile *) 0x3040bd0)
  compunit_symtab: ((struct compunit_symtab *) 0x306d2e0)
  symtab: /home/andrew/tmp/r.c ((struct symtab *) 0x306d360)
  linetable: ((struct linetable *) 0x31012c0):
  INDEX    LINE ADDRESS IS-STMT
  0         END 0x0000000000010150 Y
  1           8 0x0000000000010154
  2         END 0x0000000000010158 Y
  objfile: /home/andrew/tmp/r.x ((struct objfile *) 0x3040bd0)
  compunit_symtab: ((struct compunit_symtab *) 0x306d2e0)
  symtab: /home/andrew/tmp/r.h ((struct symtab *) 0x306d390)
  linetable: ((struct linetable *) 0x3101270):
  INDEX    LINE ADDRESS IS-STMT
  0           6 0x0000000000010150 Y
  1           6 0x0000000000010150
  2         END 0x0000000000010154 Y

Interestingly, without any of the is-stmt patches the GDB's line table
looks like this:

  (gdb) maintenance info line-table
  objfile: /home/andrew/tmp/r.x ((struct objfile *) 0x15da130)
  compunit_symtab: ((struct compunit_symtab *) 0x15ca5c0)
  symtab: /home/andrew/tmp/r.c ((struct symtab *) 0x15ca640)
  linetable: ((struct linetable *) 0x1675df0):
  INDEX    LINE ADDRESS
  0           0 0x0000000000010150
  objfile: /home/andrew/tmp/r.x ((struct objfile *) 0x15da130)
  compunit_symtab: ((struct compunit_symtab *) 0x15ca5c0)
  symtab: /home/andrew/tmp/r.h ((struct symtab *) 0x15ca680)
  linetable: ((struct linetable *) 0x1675dc0):
  INDEX    LINE ADDRESS
  0           6 0x0000000000010150

So what happens is we run into this bit of code in
skip_prologue_using_sal:

      /* If there is only one sal that covers the entire function,
	 then it is probably a single line function, like
	 "foo(){}".  */
      if (prologue_sal.end >= end_pc)
	return 0;

So, it looks like we have two different parts of the is-stmt change
playing off against each other, the extra END marker is what changes
the function from a single entry line table to a multi-entry one, this
is what breaks thinks.  Then when you remove the hunk you identified
this causes the prologue skipping to spot the dual entry case and we
do the right thing again.

It feels like this is more luck than design.

I need to revisit the code that added the extra END marker and
convince myself again that this is the right thing to do I think.

Hopefully I'll have an update tomorrow.

Thanks,
Andrew

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv3] Fix range end handling of inlined subroutines
  2020-03-13  8:03               ` Bernd Edlinger
@ 2020-03-17 22:27                 ` Andrew Burgess
  2020-03-19  1:33                   ` Bernd Edlinger
  0 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-03-17 22:27 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: Christian Biesinger, gdb-patches

* Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-03-13 09:03:15 +0100]:

> On 3/12/20 7:27 PM, Christian Biesinger wrote:
> > On Thu, Mar 12, 2020 at 1:21 PM Bernd Edlinger
> > <bernd.edlinger@hotmail.de> wrote:
> >>
> >> I am worried that the vector is not as efficient as it is necessary here.
> >> I know for instance that a straight forward
> >>
> >> m_inline_end_vector.push_back(pc);
> >>
> >> has often an O(n^2) complexity alone for this adding new elements,
> >> (and it can throw, which makes error handling a mess).
> > 
> > push_back is not O(n^2). See
> > https://en.cppreference.com/w/cpp/container/vector/push_back:
> > "Complexity
> > Amortized constant"
> > 
> > That's because it doubles the capacity whenever it has to resize.
> > 
> > Christian
> > 
> 
> While that may be true for g++, this is not the case for all compilers.
> 
> If you don't know which compiler will be used, (gcc, clang, diab, msvc to
> name a few) you cannot rely on that.
> 
> I just repeated what I already learned the hard way, and tried the following
> 
> #include <vector>
> 
> int main()
> {
>   int i, m = 1000000;
>   stc::vector<int> v;
>   for (i = 0; i < m; i++)
>      v.push_back(i);
>   return 0;
> }
> 
> Compiled it with visual studio 2010, and see it take
> a mutex 1 million times, allocate each time only one
> element more, does not use realloc so must move in every
> case.
> 
> All in all, for a performance critical part in the software
> like this, I would not recommend to use a component where
> it depends so much on the compiler and the implementation
> details of the stl library.
> 
> A similar surprise happens with stl::list's size(), which
> may be O(1) or O(n) is even documented that way.
> 
> So that is my personal experience, in that field.
> And bad experiences last longer in my memory than good ones :(

Are you arguing that we shouldn't use std::vector anywhere in GDB?  Or
that this particular piece of code shouldn't use std::vector?

It was my understanding that we were moving GDB away from bespoke
container management code unless there was a very compelling reason.
I think as a minimum any new code that was written not using std::
data structures (or some other gdb specific type) would need a comment
explaining why, if nothing else to stop someone replacing the code
with std:: types a few months down the line.

Thanks,
Andrew

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv3] Fix range end handling of inlined subroutines
  2020-03-17 22:27                 ` Andrew Burgess
@ 2020-03-19  1:33                   ` Bernd Edlinger
  2020-03-21 20:31                     ` Bernd Edlinger
  0 siblings, 1 reply; 79+ messages in thread
From: Bernd Edlinger @ 2020-03-19  1:33 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Christian Biesinger, gdb-patches

On 3/17/20 11:27 PM, Andrew Burgess wrote:
> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-03-13 09:03:15 +0100]:
> 
>> On 3/12/20 7:27 PM, Christian Biesinger wrote:
>>> On Thu, Mar 12, 2020 at 1:21 PM Bernd Edlinger
>>> <bernd.edlinger@hotmail.de> wrote:
>>>>
>>>> I am worried that the vector is not as efficient as it is necessary here.
>>>> I know for instance that a straight forward
>>>>
>>>> m_inline_end_vector.push_back(pc);
>>>>
>>>> has often an O(n^2) complexity alone for this adding new elements,
>>>> (and it can throw, which makes error handling a mess).
>>>
>>> push_back is not O(n^2). See
>>> https://en.cppreference.com/w/cpp/container/vector/push_back:
>>> "Complexity
>>> Amortized constant"
>>>
>>> That's because it doubles the capacity whenever it has to resize.
>>>
>>> Christian
>>>
>>
>> While that may be true for g++, this is not the case for all compilers.
>>
>> If you don't know which compiler will be used, (gcc, clang, diab, msvc to
>> name a few) you cannot rely on that.
>>
>> I just repeated what I already learned the hard way, and tried the following
>>
>> #include <vector>
>>
>> int main()
>> {
>>   int i, m = 1000000;
>>   stc::vector<int> v;
>>   for (i = 0; i < m; i++)
>>      v.push_back(i);
>>   return 0;
>> }
>>
>> Compiled it with visual studio 2010, and see it take
>> a mutex 1 million times, allocate each time only one
>> element more, does not use realloc so must move in every
>> case.
>>
>> All in all, for a performance critical part in the software
>> like this, I would not recommend to use a component where
>> it depends so much on the compiler and the implementation
>> details of the stl library.
>>
>> A similar surprise happens with stl::list's size(), which
>> may be O(1) or O(n) is even documented that way.
>>
>> So that is my personal experience, in that field.
>> And bad experiences last longer in my memory than good ones :(
> 
> Are you arguing that we shouldn't use std::vector anywhere in GDB?  Or
> that this particular piece of code shouldn't use std::vector?
> 

No, in general std::vector is just fine.
I think just here it is especially important to be below O(n*log(n)),
and don't depend on the underlying implementation.

> It was my understanding that we were moving GDB away from bespoke
> container management code unless there was a very compelling reason.
> I think as a minimum any new code that was written not using std::
> data structures (or some other gdb specific type) would need a comment
> explaining why, if nothing else to stop someone replacing the code
> with std:: types a few months down the line.
> 

Right, good point.
I can add a comment here, so that will not happen:

index 46f985a..4f0d536 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -736,6 +736,10 @@ struct blockvector *
 void
 buildsym_compunit::record_inline_range_end (CORE_ADDR end)
 {
+  /* The performance of this function is very important,
+     it shall be O(n*log(n)) therefore we do not use std::vector
+     here since some compilers, e.g. visual studio, do not
+     guarantee that for vector::push_back.  */
   if (m_inline_end_vector == nullptr)
     {
       m_inline_end_vector_length = INITIAL_LINE_VECTOR_LENGTH;


Does that look good (also from the english)?

Thanks
Bernd.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv3] Fix range end handling of inlined subroutines
  2020-03-19  1:33                   ` Bernd Edlinger
@ 2020-03-21 20:31                     ` Bernd Edlinger
  2020-03-23 17:53                       ` Andrew Burgess
  0 siblings, 1 reply; 79+ messages in thread
From: Bernd Edlinger @ 2020-03-21 20:31 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Christian Biesinger, gdb-patches

On 3/19/20 2:33 AM, Bernd Edlinger wrote:
> On 3/17/20 11:27 PM, Andrew Burgess wrote:
>>
>> Are you arguing that we shouldn't use std::vector anywhere in GDB?  Or
>> that this particular piece of code shouldn't use std::vector?
>>
> 
> No, in general std::vector is just fine.
> I think just here it is especially important to be below O(n*log(n)),
> and don't depend on the underlying implementation.
> 
>> It was my understanding that we were moving GDB away from bespoke
>> container management code unless there was a very compelling reason.
>> I think as a minimum any new code that was written not using std::
>> data structures (or some other gdb specific type) would need a comment
>> explaining why, if nothing else to stop someone replacing the code
>> with std:: types a few months down the line.
>>
> 
> Right, good point.
> I can add a comment here, so that will not happen:
> 
> index 46f985a..4f0d536 100644
> --- a/gdb/buildsym.c
> +++ b/gdb/buildsym.c
> @@ -736,6 +736,10 @@ struct blockvector *
>  void
>  buildsym_compunit::record_inline_range_end (CORE_ADDR end)
>  {
> +  /* The performance of this function is very important,
> +     it shall be O(n*log(n)) therefore we do not use std::vector
> +     here since some compilers, e.g. visual studio, do not
> +     guarantee that for vector::push_back.  */
>    if (m_inline_end_vector == nullptr)
>      {
>        m_inline_end_vector_length = INITIAL_LINE_VECTOR_LENGTH;
> 
> 
> Does that look good (also from the english)?
> 

Hi Andrew,

how are you doing?

are you ok?


Bernd.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH 0/3] Keep duplicate line table entries
  2020-03-08 12:50           ` [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
  2020-03-16 20:57             ` Tom Tromey
@ 2020-03-23 17:30             ` Andrew Burgess
  2020-03-23 17:30             ` [PATCH 1/3] gdb/testsuite: Add compiler options parameter to function_range helper Andrew Burgess
                               ` (2 subsequent siblings)
  4 siblings, 0 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-03-23 17:30 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Tom Tromey, Andrew Burgess

Sorry for it taking so long, but I finally got back around to this
patch.

The fix in patch #3 of this series is the one suggested by both Bernd
and Tom, that is, keep the duplicate entries in the line table.

I've also got a couple of tests that should cover this issue.

Thanks,
Andrew


---

Andrew Burgess (3):
  gdb/testsuite: Add compiler options parameter to function_range helper
  gdb/testsuite: Add support for DW_LNS_set_file to DWARF compiler
  gdb: Don't remove duplicate entries from the line table

 gdb/ChangeLog                                      |   7 +
 gdb/buildsym.c                                     |  14 --
 gdb/testsuite/ChangeLog                            |  20 +++
 .../gdb.dwarf2/dw2-inline-small-func-lbls.c        |  37 +++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.c   |  22 +++
 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp | 158 +++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.h   |  21 +++
 gdb/testsuite/gdb.opt/inline-small-func.c          |  22 +++
 gdb/testsuite/gdb.opt/inline-small-func.exp        |  60 ++++++++
 gdb/testsuite/gdb.opt/inline-small-func.h          |  21 +++
 gdb/testsuite/lib/dwarf.exp                        |   9 +-
 11 files changed, 375 insertions(+), 16 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func-lbls.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.h
 create mode 100644 gdb/testsuite/gdb.opt/inline-small-func.c
 create mode 100644 gdb/testsuite/gdb.opt/inline-small-func.exp
 create mode 100644 gdb/testsuite/gdb.opt/inline-small-func.h

-- 
2.14.5


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH 1/3] gdb/testsuite: Add compiler options parameter to function_range helper
  2020-03-08 12:50           ` [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
  2020-03-16 20:57             ` Tom Tromey
  2020-03-23 17:30             ` [PATCH 0/3] Keep duplicate line table entries Andrew Burgess
@ 2020-03-23 17:30             ` Andrew Burgess
  2020-04-01 18:31               ` Tom Tromey
  2020-03-23 17:30             ` [PATCH 2/3] gdb/testsuite: Add support for DW_LNS_set_file to DWARF compiler Andrew Burgess
  2020-03-23 17:30             ` [PATCH 3/3] gdb: Don't remove duplicate entries from the line table Andrew Burgess
  4 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-03-23 17:30 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Tom Tromey, Andrew Burgess

When using the Dejagnu DWARF compiler tests will often use the
function_range helper function to extract the extents of a function.
If the plan is to compiler the file with non-default compiler flags
then we must pass those same compiler flags through to the
function_range helper function.

This will be used in a later commit, there should be no change in the
testsuite behaviour after this commit.

gdb/testsuite/ChangeLog:

	* lib/dwarf.exp (function_range): Allow compiler options to be
	specified.
---
 gdb/testsuite/ChangeLog     | 5 +++++
 gdb/testsuite/lib/dwarf.exp | 4 ++--
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index 417b22d2345..4371b56d4de 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -114,12 +114,12 @@ proc build_executable_from_fission_assembler { testname executable sources optio
 # static void func (void) {}
 #
 
-proc function_range { func src } {
+proc function_range { func src {options {debug}} } {
     global decimal gdb_prompt
 
     set exe [standard_temp_file func_addr[pid].x]
 
-    gdb_compile $src $exe executable {debug}
+    gdb_compile $src $exe executable $options
 
     gdb_exit
     gdb_start
-- 
2.14.5


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH 2/3] gdb/testsuite: Add support for DW_LNS_set_file to DWARF compiler
  2020-03-08 12:50           ` [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
                               ` (2 preceding siblings ...)
  2020-03-23 17:30             ` [PATCH 1/3] gdb/testsuite: Add compiler options parameter to function_range helper Andrew Burgess
@ 2020-03-23 17:30             ` Andrew Burgess
  2020-04-01 18:32               ` Tom Tromey
  2020-03-23 17:30             ` [PATCH 3/3] gdb: Don't remove duplicate entries from the line table Andrew Burgess
  4 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-03-23 17:30 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Tom Tromey, Andrew Burgess

Extend the Dejagnu DWARF compiler to support DW_LNS_set_file opcode.
This will be used in a later commit.  There should be no change in the
testsuite after this commit.

gdb/testsuite/ChangeLog:

	* lib/dwarf.exp (Dwarf::lines::program::DW_LNS_set_file): New
	function.
---
 gdb/testsuite/ChangeLog     | 5 +++++
 gdb/testsuite/lib/dwarf.exp | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index 4371b56d4de..93bde76de35 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -1454,6 +1454,11 @@ namespace eval Dwarf {
 		_op .sleb128 ${offset}
 	    }
 
+	    proc DW_LNS_set_file {num} {
+		_op .byte 4
+		_op .sleb128 ${num}
+	    }
+
 	    foreach statement $statements {
 		uplevel 1 $statement
 	    }
-- 
2.14.5


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH 3/3] gdb: Don't remove duplicate entries from the line table
  2020-03-08 12:50           ` [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
                               ` (3 preceding siblings ...)
  2020-03-23 17:30             ` [PATCH 2/3] gdb/testsuite: Add support for DW_LNS_set_file to DWARF compiler Andrew Burgess
@ 2020-03-23 17:30             ` Andrew Burgess
  2020-04-01 18:34               ` Tom Tromey
  4 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-03-23 17:30 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Tom Tromey, Andrew Burgess

In this commit:

  commit 8c95582da858ac981f689a6f599acacb8c5c490f
  Date:   Mon Dec 30 21:04:51 2019 +0000

      gdb: Add support for tracking the DWARF line table is-stmt field

A change was made in buildsym_compunit::record_line to remove
duplicate line table entries in some cases.  This was an invalid
change, as these duplicate line table entries are used in _some_ cases
as part of prologue detection (see skip_prologue_using_sal).

It might be possible to identify those line table entries that are
required by skip_prologue_using_sal and only keep those duplicates
around, however, I have not done this here.  The original duplicate
removal was done because (a) it was easy to implement, and (b) it
seemed obviously harmless.

As (b) is now known to be false, and implementation would be more
complex, and so (a) is also false.  As such, it seems better to keep
all duplicates until an actual reason presents itself for why we
should remove any.

The original regression was spotted on RISC-V, which makes use of
skip_prologue_using_sal as part of riscv_skip_prologue.  Originally I
created the test gdb.dwarf2/dw2-inline-small-func.exp, however, this
test will not compile on RISC-V as this target doesn't support
.uleb128 or .sleb128 assembler directives containing complex
expressions.  As a result I added the gdb.opt/inline-small-func.exp
test, which exposes the bug on RISC-V, but obviously depends on the
compiler to produce specific DWARF information in order to expose the
bug.  Still this test does ensure we always get the desired result,
even if the DWARF changes.

Originally the gdb.dwarf2/dw2-inline-small-func.exp test passed on
x86-64 even with the duplicate line table entries incorrectly
removed.  The reason for this is that when a compilation unit doesn't
have a 'producer' string then skip_prologue_using_sal is not used,
instead the prologue is always skipped using analysis of the assembler
code.

However, for Clang on x86-64 skip_prologue_using_sal is used, so I
modified the gdb.dwarf2/dw2-inline-small-func.exp test to include a
'producer' string that names the Clang compiler.  With this done the
test would fail on x86-64.

One thing to note is that the gdb.opt/inline-small-func.exp test might
fail on some targets.  For example, if we compare sparc to risc-v by
looking at sparc32_skip_prologue we see that this function doesn't use
skip_prologue_using_sal, but instead uses find_pc_partial_function
directly.  I don't know the full history behind why the code is like
it is, but it feels like sparc32_skip_prologue is an attempt to
duplicate some of the functionality of skip_prologue_using_sal, but
without all of the special cases.  If this is true then the new test
could easily fail on this target, this would suggest that sparc should
consider switching to use skip_prologue_using_sal like risc-v does.

gdb/ChangeLog:

	* buildsym.c (buildsym_compunit::record_line): Remove
	deduplication code.

gdb/testsuite/ChangeLog:

	* gdb.dwarf2/dw2-inline-small-func-lbls.c: New file.
	* gdb.dwarf2/dw2-inline-small-func.c: New file.
	* gdb.dwarf2/dw2-inline-small-func.exp: New file.
	* gdb.dwarf2/dw2-inline-small-func.h: New file.
	* gdb.opt/inline-small-func.c: New file.
	* gdb.opt/inline-small-func.exp: New file.
	* gdb.opt/inline-small-func.h: New file.
---
 gdb/ChangeLog                                      |   7 +
 gdb/buildsym.c                                     |  14 --
 gdb/testsuite/ChangeLog                            |  10 ++
 .../gdb.dwarf2/dw2-inline-small-func-lbls.c        |  37 +++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.c   |  22 +++
 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp | 158 +++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.h   |  21 +++
 gdb/testsuite/gdb.opt/inline-small-func.c          |  22 +++
 gdb/testsuite/gdb.opt/inline-small-func.exp        |  60 ++++++++
 gdb/testsuite/gdb.opt/inline-small-func.h          |  21 +++
 10 files changed, 358 insertions(+), 14 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func-lbls.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.h
 create mode 100644 gdb/testsuite/gdb.opt/inline-small-func.c
 create mode 100644 gdb/testsuite/gdb.opt/inline-small-func.exp
 create mode 100644 gdb/testsuite/gdb.opt/inline-small-func.h

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 7155db34b08..aea594ebca4 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -681,20 +681,6 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
       m_have_line_numbers = true;
     }
 
-  if (subfile->line_vector->nitems > 0)
-    {
-      /* If we have a duplicate for the previous entry then ignore the new
-	 entry, except, if the new entry is setting the is_stmt flag, then
-	 ensure the previous entry respects the new setting.  */
-      e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
-      if (e->line == line && e->pc == pc)
-	{
-	  if (is_stmt && !e->is_stmt)
-	    e->is_stmt = 1;
-	  return;
-	}
-    }
-
   if (subfile->line_vector->nitems + 1 >= subfile->line_vector_length)
     {
       subfile->line_vector_length *= 2;
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func-lbls.c
new file mode 100644
index 00000000000..5772131569c
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func-lbls.c
@@ -0,0 +1,37 @@
+/* Copyright 2020 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/>.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+volatile int var;
+volatile int bar;
+
+/* Generate some code to take up some space.  */
+#define FILLER do { \
+    var = 99;	    \
+} while (0)
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+  LL (1);
+  FILLER;
+  LL (2);
+  FILLER;
+  LL (3);
+  return 0;				/* main end */
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.c
new file mode 100644
index 00000000000..e2095e36625
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.c
@@ -0,0 +1,22 @@
+/* Copyright 2020 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/>.  */
+
+#include "dw2-inline-small-func.h"
+
+int
+main ()
+{		/* caller: before call.  */
+  callee ();	/* caller: the call.  */
+}		/* caller: after call.  */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
new file mode 100644
index 00000000000..777db062b37
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
@@ -0,0 +1,158 @@
+# Copyright 2020 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/>.
+
+# Check for an issue in GDB where buildsym_compunit::record_line was
+# removing duplicate line table entries, but skip_prologue_using_sal
+# depends on these duplicates to spot the end of the prologue.
+#
+# When the de-duplication was added this regression was not spotted as
+# it requires a particular combination of a (very) small function
+# being inlined into an also very small outer function.
+#
+# This test recreates the exact combination of line table entries that
+# were seen in the original test using the Dejagnu DWARF compiler.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-small-func-lbls.c dw2-inline-small-func.S \
+    dw2-inline-small-func.c dw2-inline-small-func.h
+
+# Extract the start, length, and end for function called NAME and
+# create suitable variables in the callers scope.
+proc get_func_info { name } {
+    global srcdir subdir srcfile
+
+    upvar 1 "${name}_start" func_start
+    upvar 1 "${name}_len" func_len
+    upvar 1 "${name}_end" func_end
+
+    lassign [function_range ${name} [list ${srcdir}/${subdir}/$srcfile] {debug optimize=-O1}] \
+	func_start func_len
+    set func_end "$func_start + $func_len"
+}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main
+
+    cu {} {
+	# It is important that the producer here be 'clang' as, at the
+	# time of writing this, GCC for x86-64 doesn't make use of
+	# skip_prologue_using_sal, while clang does.
+	compile_unit {
+	    {producer "clang xxxx" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_2 addr}
+		    {call_file 1 data1}
+		    {call_line 21 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	set f1_l1 [gdb_get_line_number "caller: before call" $srcfile3]
+	set f1_l2 [gdb_get_line_number "caller: the call" $srcfile3]
+	set f1_l3 [gdb_get_line_number "caller: after call" $srcfile3]
+
+	set f2_l1 [gdb_get_line_number "callee: body" $srcfile4]
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line [expr $f1_l1 - 1]}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line [expr ${f1_l2} - ${f1_l1}]}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNS_advance_line [expr ${f2_l1} - ${f1_l2}]}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 1}
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line [expr ${f1_l3} - ${f2_l1}]}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_copy}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+set linenum [gdb_get_line_number "callee: body" $srcfile4]
+gdb_breakpoint "${srcfile4}:${linenum}"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:${linenum}\\y.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.h
new file mode 100644
index 00000000000..69c67af6011
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.h
@@ -0,0 +1,21 @@
+/* Copyright 2020 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/>.  */
+
+int counter = 42;
+
+inline void
+callee () {
+  counter = 0;	/* callee: body.  */
+}
diff --git a/gdb/testsuite/gdb.opt/inline-small-func.c b/gdb/testsuite/gdb.opt/inline-small-func.c
new file mode 100644
index 00000000000..902674e8773
--- /dev/null
+++ b/gdb/testsuite/gdb.opt/inline-small-func.c
@@ -0,0 +1,22 @@
+/* Copyright 2020 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/>.  */
+
+#include "inline-small-func.h"
+
+int
+main ()
+{		/* caller: before call.  */
+  callee ();	/* caller: the call.  */
+}		/* caller: after call.  */
diff --git a/gdb/testsuite/gdb.opt/inline-small-func.exp b/gdb/testsuite/gdb.opt/inline-small-func.exp
new file mode 100644
index 00000000000..dfe046ad723
--- /dev/null
+++ b/gdb/testsuite/gdb.opt/inline-small-func.exp
@@ -0,0 +1,60 @@
+# Copyright 2020 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/>.
+
+# Check for an issue in GDB where buildsym_compunit::record_line was
+# removing duplicate line table entries, but skip_prologue_using_sal
+# depends on these duplicates to spot the end of the prologue.
+#
+# When the de-duplication was added this regression was not spotted as
+# it requires a particular combination of a (very) small function
+# being inlined into an also very small outer function.
+#
+# See also gdb.dwarf/dw2-inline-small-func.exp for a version of this
+# test that makes use of the Dejagnu DWARF compiler.
+#
+# This test simply compiles with optimization and checks that GDB can
+# do something suitable with the compiled binary.  Problems with this
+# test are most likely to occur when GDB asks the target specific code
+# to skip the prologue (gdbarch_skip_prologue).  Some targets make use
+# of skip_prologue_using_sal, which should be fine, however, some
+# targets make a poor attempt to duplicate parts of
+# skip_prologue_using_sal, these targets could easily fail this test.
+# This is not (necessarily) a problem with this test, but could
+# indicate a weakness with the target in question.
+
+standard_testfile inline-small-func.c inline-small-func.h
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile] {debug optimize=-O1}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+set linenum [gdb_get_line_number "callee: body" $srcfile2]
+gdb_breakpoint "${srcfile2}:${linenum}"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile2}:${linenum}\\y.*"
diff --git a/gdb/testsuite/gdb.opt/inline-small-func.h b/gdb/testsuite/gdb.opt/inline-small-func.h
new file mode 100644
index 00000000000..69c67af6011
--- /dev/null
+++ b/gdb/testsuite/gdb.opt/inline-small-func.h
@@ -0,0 +1,21 @@
+/* Copyright 2020 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/>.  */
+
+int counter = 42;
+
+inline void
+callee () {
+  counter = 0;	/* callee: body.  */
+}
-- 
2.14.5


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv3] Fix range end handling of inlined subroutines
  2020-03-21 20:31                     ` Bernd Edlinger
@ 2020-03-23 17:53                       ` Andrew Burgess
  2020-03-23 20:58                         ` Bernd Edlinger
  0 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-03-23 17:53 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: Christian Biesinger, gdb-patches

* Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-03-21 21:31:07 +0100]:

> On 3/19/20 2:33 AM, Bernd Edlinger wrote:
> > On 3/17/20 11:27 PM, Andrew Burgess wrote:
> >>
> >> Are you arguing that we shouldn't use std::vector anywhere in GDB?  Or
> >> that this particular piece of code shouldn't use std::vector?
> >>
> > 
> > No, in general std::vector is just fine.
> > I think just here it is especially important to be below O(n*log(n)),
> > and don't depend on the underlying implementation.
> > 
> >> It was my understanding that we were moving GDB away from bespoke
> >> container management code unless there was a very compelling reason.
> >> I think as a minimum any new code that was written not using std::
> >> data structures (or some other gdb specific type) would need a comment
> >> explaining why, if nothing else to stop someone replacing the code
> >> with std:: types a few months down the line.
> >>
> > 
> > Right, good point.
> > I can add a comment here, so that will not happen:
> > 
> > index 46f985a..4f0d536 100644
> > --- a/gdb/buildsym.c
> > +++ b/gdb/buildsym.c
> > @@ -736,6 +736,10 @@ struct blockvector *
> >  void
> >  buildsym_compunit::record_inline_range_end (CORE_ADDR end)
> >  {
> > +  /* The performance of this function is very important,
> > +     it shall be O(n*log(n)) therefore we do not use std::vector
> > +     here since some compilers, e.g. visual studio, do not
> > +     guarantee that for vector::push_back.  */
> >    if (m_inline_end_vector == nullptr)
> >      {
> >        m_inline_end_vector_length = INITIAL_LINE_VECTOR_LENGTH;
> > 
> > 
> > Does that look good (also from the english)?
> > 
> 
> Hi Andrew,
> 
> how are you doing?
> 
> are you ok?

Sorry, I have not been ignoring this patch.  It is top of my review
stack.  It is being held up for a number of reasons, first Tom found a
number of issues with the original patch set, I've been trying to fix
these before reviewing your work in case the fixes had an impact.

I posted some patches for the easier of the issues, but I've not
solved the harder bug yet.  I'm pretty much decided to just go ahead
and review this patch anyway.

But then, I'm still struggling with the choice not to use
std::vector.  It's still not clear if there's actually a good reason
to avoid it in this case or not, so I feel I need to review other uses
of std::vector throughout GDB and see how your use compares.  Do you
maybe have some performance figures you could share to help make the
case for using custom container type?

Then the whole converting is_stmt entries to !is_stmt is still
concerning me.  I appreciate your expanded text in the patch
description, but I need to do more testing to see the real impact of
this change.  Most of my clients are driving GDB through some kind of
GUI, which means that break points are inevitably placed using
file:line sytnax.  One of the most common complaints I get is when a
breakpoint is placed on one line, but GDB stops on another.  Generally
my users can be quite forgiving of missing stack frames when inlining
is in play.  What this means is an argument of if you did break here
you'd have a frame missing, so I'm not going to let you break there
any more isn't that compelling to me.

That isn't a rejection of the patch, it's just me explaining why it's
taking me some time to review.

Thanks,
Andrew

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv3] Fix range end handling of inlined subroutines
  2020-03-23 17:53                       ` Andrew Burgess
@ 2020-03-23 20:58                         ` Bernd Edlinger
  2020-06-01 14:28                           ` Pedro Alves
  0 siblings, 1 reply; 79+ messages in thread
From: Bernd Edlinger @ 2020-03-23 20:58 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Christian Biesinger, gdb-patches



On 3/23/20 6:53 PM, Andrew Burgess wrote:
> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-03-21 21:31:07 +0100]:
> 
>> On 3/19/20 2:33 AM, Bernd Edlinger wrote:
>>> On 3/17/20 11:27 PM, Andrew Burgess wrote:
>>>>
>>>> Are you arguing that we shouldn't use std::vector anywhere in GDB?  Or
>>>> that this particular piece of code shouldn't use std::vector?
>>>>
>>>
>>> No, in general std::vector is just fine.
>>> I think just here it is especially important to be below O(n*log(n)),
>>> and don't depend on the underlying implementation.
>>>
>>>> It was my understanding that we were moving GDB away from bespoke
>>>> container management code unless there was a very compelling reason.
>>>> I think as a minimum any new code that was written not using std::
>>>> data structures (or some other gdb specific type) would need a comment
>>>> explaining why, if nothing else to stop someone replacing the code
>>>> with std:: types a few months down the line.
>>>>
>>>
>>> Right, good point.
>>> I can add a comment here, so that will not happen:
>>>
>>> index 46f985a..4f0d536 100644
>>> --- a/gdb/buildsym.c
>>> +++ b/gdb/buildsym.c
>>> @@ -736,6 +736,10 @@ struct blockvector *
>>>  void
>>>  buildsym_compunit::record_inline_range_end (CORE_ADDR end)
>>>  {
>>> +  /* The performance of this function is very important,
>>> +     it shall be O(n*log(n)) therefore we do not use std::vector
>>> +     here since some compilers, e.g. visual studio, do not
>>> +     guarantee that for vector::push_back.  */
>>>    if (m_inline_end_vector == nullptr)
>>>      {
>>>        m_inline_end_vector_length = INITIAL_LINE_VECTOR_LENGTH;
>>>
>>>
>>> Does that look good (also from the english)?
>>>
>>
>> Hi Andrew,
>>
>> how are you doing?
>>
>> are you ok?
> 
> Sorry, I have not been ignoring this patch.  It is top of my review
> stack.  It is being held up for a number of reasons, first Tom found a
> number of issues with the original patch set, I've been trying to fix
> these before reviewing your work in case the fixes had an impact.
> 
> I posted some patches for the easier of the issues, but I've not
> solved the harder bug yet.  I'm pretty much decided to just go ahead
> and review this patch anyway.

I've probably lost track, what was the harder bug?

> 
> But then, I'm still struggling with the choice not to use
> std::vector.  It's still not clear if there's actually a good reason
> to avoid it in this case or not, so I feel I need to review other uses
> of std::vector throughout GDB and see how your use compares.  Do you
> maybe have some performance figures you could share to help make the
> case for using custom container type?
> 

Sure, probably not as impressive as I thought, but nevertheless,
the number of subroutines depends only on the debug-info, and
I am not sure if the difference can be even higher depenent on the
how the debug info looks exactly.

What I did is compare a O(nlogn) vs. O(n^2) implementation
which mimics the visual studio std::vector:

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index ae730e3..02ef2b8 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -746,19 +746,15 @@ struct blockvector *
      it shall be O(n*log(n)) therefore we do not use std::vector
      here since some compilers, e.g. visual studio, do not
      guarantee that for vector::push_back.  */
-  if (m_inline_end_vector == nullptr)
-    {
-      m_inline_end_vector_length = INITIAL_LINE_VECTOR_LENGTH;
-      m_inline_end_vector = (CORE_ADDR *)
-       xmalloc (sizeof (CORE_ADDR) * m_inline_end_vector_length);
-      m_inline_end_vector_nitems = 0;
-    }
-  else if (m_inline_end_vector_nitems == m_inline_end_vector_length)
+  CORE_ADDR *old_vec = m_inline_end_vector;
+  m_inline_end_vector_length ++;
+  m_inline_end_vector = (CORE_ADDR *)
+    xmalloc (sizeof (CORE_ADDR) * m_inline_end_vector_length);
+
+  if (m_inline_end_vector_length > 1)
     {
-      m_inline_end_vector_length *= 2;
-      m_inline_end_vector = (CORE_ADDR *)
-       xrealloc ((char *) m_inline_end_vector,
-                 sizeof (CORE_ADDR) * m_inline_end_vector_length);
+      memcpy(m_inline_end_vector, old_vec, sizeof(CORE_ADDR) * (m_inline_end_vector_length-1));
+      xfree(old_vec);
     }
 
   m_inline_end_vector[m_inline_end_vector_nitems++] = end;


make CFLAGS="-O2 -g -pg" CXXFLAGS="-O2 -g -pg" LDFLAGS="-g -pg" V=1 -j4

perf0.txt is O(nlogn)
perf1.txt is O(n^2)

             %   cumulative   self              self     total           
            time   seconds   seconds    calls   s/call   s/call  name    

perf0.txt:  0.00      4.43     0.00    33327     0.00     0.00  buildsym_compunit::record_inline_range_end(unsigned long)
perf1.txt:  0.00      5.91     0.00    33327     0.00     0.00  buildsym_compunit::record_inline_range_end(unsigned long)


so the function was called 33327 times, this O(nlogn) took 4.43 seconds
and O(n^2) took 5.91 seconds.

I think I cannot attach the perf0/1.txt because they are 2.8 MB each,
but if you like to look at them I can upload to my skydrive.


> Then the whole converting is_stmt entries to !is_stmt is still
> concerning me.  I appreciate your expanded text in the patch
> description, but I need to do more testing to see the real impact of
> this change.  Most of my clients are driving GDB through some kind of
> GUI, which means that break points are inevitably placed using
> file:line sytnax.  One of the most common complaints I get is when a
> breakpoint is placed on one line, but GDB stops on another.  Generally
> my users can be quite forgiving of missing stack frames when inlining
> is in play.  What this means is an argument of if you did break here
> you'd have a frame missing, so I'm not going to let you break there
> any more isn't that compelling to me.
> 

Note that your other patch is also deleting is-stmt locations,
because the switching between the different cu's does that with
the end-marker.

> That isn't a rejection of the patch, it's just me explaining why it's
> taking me some time to review.
> 

Absolutely, no problem, we are not in a hurry.
And thanks for all your time, and good co-operation.


Thanks
Bernd.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 1/3] gdb/testsuite: Add compiler options parameter to function_range helper
  2020-03-23 17:30             ` [PATCH 1/3] gdb/testsuite: Add compiler options parameter to function_range helper Andrew Burgess
@ 2020-04-01 18:31               ` Tom Tromey
  0 siblings, 0 replies; 79+ messages in thread
From: Tom Tromey @ 2020-04-01 18:31 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, Bernd Edlinger, Tom Tromey

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

Andrew> 	* lib/dwarf.exp (function_range): Allow compiler options to be
Andrew> 	specified.

Seems fine, thanks.

Tom

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/3] gdb/testsuite: Add support for DW_LNS_set_file to DWARF compiler
  2020-03-23 17:30             ` [PATCH 2/3] gdb/testsuite: Add support for DW_LNS_set_file to DWARF compiler Andrew Burgess
@ 2020-04-01 18:32               ` Tom Tromey
  0 siblings, 0 replies; 79+ messages in thread
From: Tom Tromey @ 2020-04-01 18:32 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, Bernd Edlinger, Tom Tromey

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

Andrew> Extend the Dejagnu DWARF compiler to support DW_LNS_set_file opcode.
Andrew> This will be used in a later commit.  There should be no change in the
Andrew> testsuite after this commit.

Andrew> gdb/testsuite/ChangeLog:

Andrew> 	* lib/dwarf.exp (Dwarf::lines::program::DW_LNS_set_file): New
Andrew> 	function.

Looks good, thanks.

Tom

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 3/3] gdb: Don't remove duplicate entries from the line table
  2020-03-23 17:30             ` [PATCH 3/3] gdb: Don't remove duplicate entries from the line table Andrew Burgess
@ 2020-04-01 18:34               ` Tom Tromey
  0 siblings, 0 replies; 79+ messages in thread
From: Tom Tromey @ 2020-04-01 18:34 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, Bernd Edlinger, Tom Tromey

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

Andrew> gdb/ChangeLog:

Andrew> 	* buildsym.c (buildsym_compunit::record_line): Remove
Andrew> 	deduplication code.

Looks good.  Thank you for doing this.

Tom

^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH 0/2] More regression fixing from is-stmt patches
  2020-03-08 12:50           ` [PATCHv2 0/2] Line table is_stmt support Andrew Burgess
  2020-03-08 14:39             ` Bernd Edlinger
@ 2020-04-03 22:21             ` Andrew Burgess
  2020-04-03 22:21             ` [PATCH 1/2] gdb/testsuite: Move helper function into lib/dwarf.exp Andrew Burgess
  2020-04-03 22:21             ` [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files Andrew Burgess
  3 siblings, 0 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-04-03 22:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Tom Tromey, Andrew Burgess

This small series fixes another regression introduced by the is-stmt
series that Tom Tromey pointed out on IRC.

Commit #1 is a small refactor, and #2 is the real work.

Thanks,
Andrew

---

Andrew Burgess (2):
  gdb/testsuite: Move helper function into lib/dwarf.exp
  gdb: Preserve is-stmt lines when switch between files

 gdb/ChangeLog                                      |  10 ++
 gdb/dwarf2/read.c                                  |  47 ++++-
 gdb/testsuite/ChangeLog                            |  21 +++
 gdb/testsuite/gdb.cp/step-and-next-inline.exp      |   7 +
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp   | 156 +++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp   | 179 +++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp   | 192 +++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c  |  46 +++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c       |  24 +++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h       |  24 +++
 .../gdb.dwarf2/dw2-inline-many-frames.exp          |  14 --
 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp |  16 +-
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp         |  14 --
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp           |  14 --
 gdb/testsuite/lib/dwarf.exp                        |  16 ++
 15 files changed, 720 insertions(+), 60 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h

-- 
2.14.5


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH 1/2] gdb/testsuite: Move helper function into lib/dwarf.exp
  2020-03-08 12:50           ` [PATCHv2 0/2] Line table is_stmt support Andrew Burgess
  2020-03-08 14:39             ` Bernd Edlinger
  2020-04-03 22:21             ` [PATCH 0/2] More regression fixing from is-stmt patches Andrew Burgess
@ 2020-04-03 22:21             ` Andrew Burgess
  2020-04-06 20:18               ` Tom Tromey
  2020-04-03 22:21             ` [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files Andrew Burgess
  3 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-04-03 22:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Tom Tromey, Andrew Burgess

Every time I write a test making use of the DWARF assembler I end up
copying in the function get_func_info.  Duplicating code is bad, so
lets put this function into lib/dwarf.exp and remove all of the
duplicates.

There should be no changes in the testsuite behaviour after this
commit.

gdb/testsuite/ChangeLog:

	* gdb.dwarf2/dw2-inline-many-frames.exp (get_func_info): Delete.
	* gdb.dwarf2/dw2-inline-small-func.exp: Pass options to
	get_func_info.
	(get_func_info): Delete.
	* gdb.dwarf2/dw2-is-stmt-2.exp (get_func_info): Delete.
	* gdb.dwarf2/dw2-is-stmt.exp (get_func_info): Delete.
	* lib/dwarf.exp (get_func_info): New function.
---
 gdb/testsuite/ChangeLog                             | 10 ++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp | 14 --------------
 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp  | 16 +---------------
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp          | 14 --------------
 gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp            | 14 --------------
 gdb/testsuite/lib/dwarf.exp                         | 16 ++++++++++++++++
 6 files changed, 27 insertions(+), 57 deletions(-)

diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
index 146af8c6ef7..0c2c6612265 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
@@ -40,20 +40,6 @@ if !$gcc_compiled {
 
 standard_testfile dw2-inline-many-frames.c dw2-inline-many-frames.S
 
-# Extract the start, length, and end for function called NAME and
-# create suitable variables in the callers scope.
-proc get_func_info { name } {
-    global srcdir subdir srcfile
-
-    upvar 1 "${name}_start" func_start
-    upvar 1 "${name}_len" func_len
-    upvar 1 "${name}_end" func_end
-
-    lassign [function_range ${name} [list ${srcdir}/${subdir}/$srcfile]] \
-	func_start func_len
-    set func_end "$func_start + $func_len"
-}
-
 set asm_file [standard_output_file $srcfile2]
 Dwarf::assemble $asm_file {
     global srcdir subdir srcfile srcfile2
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
index 777db062b37..4fcc3cfeac9 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
@@ -42,26 +42,12 @@ if !$gcc_compiled {
 standard_testfile dw2-inline-small-func-lbls.c dw2-inline-small-func.S \
     dw2-inline-small-func.c dw2-inline-small-func.h
 
-# Extract the start, length, and end for function called NAME and
-# create suitable variables in the callers scope.
-proc get_func_info { name } {
-    global srcdir subdir srcfile
-
-    upvar 1 "${name}_start" func_start
-    upvar 1 "${name}_len" func_len
-    upvar 1 "${name}_end" func_end
-
-    lassign [function_range ${name} [list ${srcdir}/${subdir}/$srcfile] {debug optimize=-O1}] \
-	func_start func_len
-    set func_end "$func_start + $func_len"
-}
-
 set asm_file [standard_output_file $srcfile2]
 Dwarf::assemble $asm_file {
     global srcdir subdir srcfile srcfile3 srcfile4
     declare_labels lines_label callee_subprog_label
 
-    get_func_info main
+    get_func_info main {debug optimize=-O1}
 
     cu {} {
 	# It is important that the producer here be 'clang' as, at the
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
index 436c4d01024..2fcad93e957 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt-2.exp
@@ -40,20 +40,6 @@ if !$gcc_compiled {
 
 standard_testfile dw2-is-stmt-2.c dw2-is-stmt-2.S
 
-# Extract the start, length, and end for function called NAME and
-# create suitable variables in the callers scope.
-proc get_func_info { name } {
-    global srcdir subdir srcfile
-
-    upvar 1 "${name}_start" func_start
-    upvar 1 "${name}_len" func_len
-    upvar 1 "${name}_end" func_end
-
-    lassign [function_range ${name} [list ${srcdir}/${subdir}/$srcfile]] \
-	func_start func_len
-    set func_end "$func_start + $func_len"
-}
-
 set asm_file [standard_output_file $srcfile2]
 Dwarf::assemble $asm_file {
     global srcdir subdir srcfile
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp
index 1bcf5b0c698..e200e91fd77 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-is-stmt.exp
@@ -40,20 +40,6 @@ if !$gcc_compiled {
 
 standard_testfile dw2-is-stmt.c dw2-is-stmt.S
 
-# Extract the start, length, and end for function called NAME and
-# create suitable variables in the callers scope.
-proc get_func_info { name } {
-    global srcdir subdir srcfile
-
-    upvar 1 "${name}_start" func_start
-    upvar 1 "${name}_len" func_len
-    upvar 1 "${name}_end" func_end
-
-    lassign [function_range ${name} [list ${srcdir}/${subdir}/$srcfile]] \
-	func_start func_len
-    set func_end "$func_start + $func_len"
-}
-
 set asm_file [standard_output_file $srcfile2]
 Dwarf::assemble $asm_file {
     global srcdir subdir srcfile
diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index 93bde76de35..a7dbe25fd45 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -164,6 +164,22 @@ proc function_range { func src {options {debug}} } {
     return [list "${func}_label - $func_label_offset" $func_length]
 }
 
+# Extract the start, length, and end for function called NAME and
+# create suitable variables in the callers scope.
+proc get_func_info { name {options {debug}} } {
+    global srcdir subdir srcfile
+
+    upvar 1 "${name}_start" func_start
+    upvar 1 "${name}_len" func_len
+    upvar 1 "${name}_end" func_end
+
+    lassign [function_range ${name} \
+		 [list ${srcdir}/${subdir}/$srcfile] \
+		 ${options}]  \
+	func_start func_len
+    set func_end "$func_start + $func_len"
+}
+
 # A DWARF assembler.
 #
 # All the variables in this namespace are private to the
-- 
2.14.5


^ permalink raw reply	[flat|nested] 79+ messages in thread

* [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-03-08 12:50           ` [PATCHv2 0/2] Line table is_stmt support Andrew Burgess
                               ` (2 preceding siblings ...)
  2020-04-03 22:21             ` [PATCH 1/2] gdb/testsuite: Move helper function into lib/dwarf.exp Andrew Burgess
@ 2020-04-03 22:21             ` Andrew Burgess
  2020-04-04 18:07               ` Bernd Edlinger
  2020-04-11  3:52               ` Bernd Edlinger
  3 siblings, 2 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-04-03 22:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Bernd Edlinger, Tom Tromey, Andrew Burgess

After the is-stmt support commit:

  commit 8c95582da858ac981f689a6f599acacb8c5c490f
  Date:   Mon Dec 30 21:04:51 2019 +0000

      gdb: Add support for tracking the DWARF line table is-stmt field

A regression was observed where a breakpoint could no longer be placed
in some cases.

Consider a line table like this:

  File 1: test.c
  File 2: test.h

  | Addr | File | Line | Stmt |
  |------|------|------|------|
  | 1    | 1    | 16   | Y    |
  | 2    | 1    | 17   | Y    |
  | 3    | 2    | 21   | Y    |
  | 4    | 2    | 22   | Y    |
  | 4    | 1    | 18   | N    |
  | 5    | 2    | 23   | N    |
  | 6    | 1    | 24   | Y    |
  | 7    | 1    | END  | Y    |
  |------|------|------|------|

Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
built two line table structures:

  File 1                 File 2
  ------                 ------

  | Addr | Line |        | Addr | Line |
  |------|------|        |------|------|
  | 1    | 16   |        | 3    | 21   |
  | 2    | 17   |        | 4    | 22   |
  | 3    | END  |        | 6    | END  |
  | 6    | 24   |        |------|------|
  | 7    | END  |
  |------|------|

After the is-stmt patch GDB now records non-stmt lines, so the
generated line table structures look like this:

  File 1                   File 2
  ------                   ------

  | Addr | Line | Stmt |  | Addr | Line | Stmt |
  |------|------|------|  |------|------|------|
  | 1    | 16   | Y    |  | 3    | 21   | Y    |
  | 2    | 17   | Y    |  | 4    | 22   | Y    |
  | 3    | END  | Y    |  | 4    | END  | Y    |
  | 4    | 18   | N    |  | 5    | 23   | N    |
  | 5    | END  | Y    |  | 6    | END  | Y    |
  | 6    | 24   | Y    |  |------|------|------|
  | 7    | END  | Y    |
  |------|------|------|

The problem is that in 'File 2', end END marker at address 4 causes
the previous line table entry to be discarded, so we actually end up
with this:

  File 2
  ------

  | Addr | Line | Stmt |
  |------|------|------|
  | 3    | 21   | Y    |
  | 4    | END  | Y    |
  | 5    | 23   | N    |
  | 6    | END  | Y    |
  |------|------|------|

When a user tries to place a breakpoint in file 2 at line 22, this is
no longer possible.

The solution I propose here is that we ignore line table entries that
would trigger a change of file if:

  1. The new line being added is at the same address as the previous
  line, and

  2. We have previously seen an is-stmt line at the current address.

The result of this is that GDB switches file, and knows that some line
entry (or entries) are going to be discarded, prefer to keep is-stmt
lines and discard non-stmt lines.

After this commit the lines tables are now:

  File 1                   File 2
  ------                   ------

  | Addr | Line | Stmt |  | Addr | Line | Stmt |
  |------|------|------|  |------|------|------|
  | 1    | 16   | Y    |  | 3    | 21   | Y    |
  | 2    | 17   | Y    |  | 4    | 22   | Y    |
  | 3    | END  | Y    |  | 5    | 23   | N    |
  | 5    | END  | Y    |  | 6    | END  | Y    |
  | 6    | 24   | Y    |  |------|------|------|
  | 7    | END  | Y    |
  |------|------|------|

We've lost the non-stmt entry for file 1, line 18, but retained the
is-stmt entry for file 2, line 22.  The user can now place a
breakpoint at that location.

One problem that came from this commit was the test
gdb.cp/step-and-next-inline.exp, which broke in several places.  After
looking at this test again I think that in some cases this test was
only ever passing by pure luck.  The debug GCC is producing for this
test is pretty broken.  I raised this GCC bug:

  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474

for this and disabled one entire half of the test.  There are still
some cases in here that do pass, and if/when GCC is fixed it would be
great to enable this test again.

gdb/ChangeLog:

	* dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
	member variable.
	<m_stmt_at_address>: New member variable.
	(lnp_state_machine::record_line): Don't record some lines, update
	tracking of is_stmt at the same address.
	(lnp_state_machine::lnp_state_machine): Initialise new member
	variables.

gdb/testsuite/ChangeLog:

	* gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
	use_header case.
	* gdb.dwarf2/dw2-inline-header-1.exp: New file.
	* gdb.dwarf2/dw2-inline-header-2.exp: New file.
	* gdb.dwarf2/dw2-inline-header-3.exp: New file.
	* gdb.dwarf2/dw2-inline-header-lbls.c: New file.
	* gdb.dwarf2/dw2-inline-header.c: New file.
	* gdb.dwarf2/dw2-inline-header.h: New file.
---
 gdb/ChangeLog                                     |  10 ++
 gdb/dwarf2/read.c                                 |  47 +++++-
 gdb/testsuite/ChangeLog                           |  11 ++
 gdb/testsuite/gdb.cp/step-and-next-inline.exp     |   7 +
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp  | 156 ++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp  | 179 ++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp  | 192 ++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c |  46 ++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c      |  24 +++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h      |  24 +++
 10 files changed, 693 insertions(+), 3 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index f94c66b4f1b..261c8455424 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -19327,6 +19327,15 @@ class lnp_state_machine
   /* The last file a line number was recorded for.  */
   struct subfile *m_last_subfile = NULL;
 
+  /* The address of the last line entry.  */
+  CORE_ADDR m_last_address;
+
+  /* Set to true when a previous line at the same address (using
+     m_last_address) had m_is_stmt true.  This is reset to false when a
+     line entry at a new address (m_address different to m_last_address) is
+     processed.  */
+  bool m_stmt_at_address = false;
+
   /* When true, record the lines we decode.  */
   bool m_currently_recording_lines = false;
 
@@ -19520,14 +19529,34 @@ lnp_state_machine::record_line (bool end_sequence)
       fe->included_p = 1;
       if (m_record_lines_p)
 	{
-	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
-	      || end_sequence)
+	  /* When we switch files we insert an end maker in the first file,
+	     switch to the second file and add a new line entry.  The
+	     problem is that the end marker inserted in the first file will
+	     discard any previous line entries at the same address.  If the
+	     line entries in the first file are marked as is-stmt, while
+	     the new line in the second file is non-stmt, then this means
+	     the end marker will discard is-stmt lines so we can have a
+	     non-stmt line.  This means that there are less addresses at
+	     which the user can insert a breakpoint.
+
+	     To improve this we track the last address in m_last_address,
+	     and whether we have seen an is-stmt at this address.  Then
+	     when switching files, if we have seen a stmt at the current
+	     address, and we are switching to create a non-stmt line, then
+	     discard the new line.  */
+	  bool file_changed
+	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
+	  bool ignore_this_line
+	    = (file_changed && !end_sequence && m_last_address == m_address
+	       && !m_is_stmt && m_stmt_at_address);
+
+	  if ((file_changed && !ignore_this_line) || end_sequence)
 	    {
 	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
 				 m_currently_recording_lines ? m_cu : nullptr);
 	    }
 
-	  if (!end_sequence)
+	  if (!end_sequence && !ignore_this_line)
 	    {
 	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
 
@@ -19546,6 +19575,15 @@ lnp_state_machine::record_line (bool end_sequence)
 	    }
 	}
     }
+
+  /* Track whether we have seen any m_is_stmt true at m_address in case we
+     have multiple line table entries all at m_address.  */
+  if (m_last_address != m_address)
+    {
+      m_stmt_at_address = false;
+      m_last_address = m_address;
+    }
+  m_stmt_at_address |= m_is_stmt;
 }
 
 lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
@@ -19565,6 +19603,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
   m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
   m_is_stmt = lh->default_is_stmt;
   m_discriminator = 0;
+
+  m_last_address = m_address;
+  m_stmt_at_addr = false;
 }
 
 void
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
index 3733fa75570..a95e21194f9 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
 proc do_test { use_header } {
     global srcfile testfile
 
+    if { $use_header } {
+	# This test will not pass due to poor debug information
+	# generated by GCC (at least upto 10.x).  See
+	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
+	return
+    }
+
     set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
     if { $use_header } {
 	lappend options additional_flags=-DUSE_NEXT_INLINE_H
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
new file mode 100644
index 00000000000..6a1e990002c
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
@@ -0,0 +1,156 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 2    | 23   | N    |
+# | 6    | 1    | 24   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
+# the line table entry for this line due to switching files for the
+# file 1, line 18 non-statement line.  After patching however, GDB now
+# discards the file 1, line 18 entry instead, and the breakpoint at
+# line 22 should succeed.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 5}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_set_file 1}
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
new file mode 100644
index 00000000000..46499919a8b
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
@@ -0,0 +1,179 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 1    | 19   | Y    |
+# | 6    | 1    | 20   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+#
+# Place the first brekpoint at file 2, line 22 and a second breakpoint
+# at file 1, line 19.  A third breakpoint is placed at file 1, line
+# 18, but as this line table entry will have been discarded[1] the
+# third breakpoint will actually be placed at the same location as the
+# second breakpoint.
+#
+# [1] The entry for file 1, line 18 is discarded because it is at the
+# same address as the previous entry, but the previous entry is-stmt,
+# while line 18 is a non-stmt.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
+    "check for breakpoint at ${srcfile4}"
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile3}:19"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
+    "check for breakpoint at ${srcfile3}"
+
+# Line table entry for line 18 will have been discarded, so this
+# brekpoint will be at the same location as line 19.
+gdb_test "break ${srcfile3}:18" \
+    "Note: breakpoint $decimal also set at pc $hex.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
new file mode 100644
index 00000000000..c683dc4bb8a
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
@@ -0,0 +1,192 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 1    | 19   | N    |
+# | 6    | 1    | 20   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+# Break at file 2, line 22, then single instruction step forward.  We
+# should pass through line 19 and then encounter line 20.
+#
+# Currently we don't expect GDB to see file 1, line 18, as this is a
+# non-stmt line in a different file at the same address as the
+# previous is-stmt line.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
+
+gdb_continue_to_breakpoint "${srcfile4}:22" \
+    ".* ${srcfile4} : 22 .*"
+
+# Now single instruction step forward.  Eventually we should hit
+# ${srcfile3}:20, but before we do we should hit the non-statement
+# line ${srcfile3}:19.
+#
+# We don't know how many instructions we'll need to step, but 100
+# should be enough for everyone (surely), and this stops us looping
+# forever if something goes wrong.
+set found_line_19 0
+set found_line_20 0
+set keep_going 1
+for { set i 0 } { $i < 100 && $keep_going } { incr i } {
+    set keep_going 0
+    gdb_test_multiple "stepi" "stepi ${i}" {
+	-re "${srcfile3} : 19 .*${gdb_prompt} " {
+	    set found_line_19 1
+	    set keep_going 1
+	}
+
+	-re "${srcfile3} : 20 .*${gdb_prompt} " {
+	    set found_line_20 1
+	}
+
+	-re "${srcfile4} : 22 .*${gdb_prompt} " {
+	    # Not left line 22 yet.
+	    set keep_going 1
+	}
+    }
+}
+
+gdb_assert { $found_line_19 && $found_line_20 } \
+    "found line 19 and 20"
+
+
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
new file mode 100644
index 00000000000..a1b7b17cbeb
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
@@ -0,0 +1,46 @@
+/* Copyright 2020 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/>.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+volatile int var;
+volatile int bar;
+
+/* Generate some code to take up some space.  */
+#define FILLER do { \
+    var = 99;	    \
+} while (0)
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+  LL (1);	// F1, Ln 16
+  FILLER;
+  LL (2);	// F1, Ln 17
+  FILLER;
+  LL (3);	// F2, Ln 21
+  FILLER;
+  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
+  FILLER;
+  LL (5);	// F1, Ln 19 !S
+  FILLER;
+  LL (6);	// F1, Ln 20
+  FILLER;
+  LL (7);
+  FILLER;
+  return 0;				/* main end */
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
new file mode 100644
index 00000000000..a8331268a09
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.c : 16 */
+/* dw2-inline-header.c : 17 */
+/* dw2-inline-header.c : 18 */
+/* dw2-inline-header.c : 19 */
+/* dw2-inline-header.c : 20 */
+/* dw2-inline-header.c : 21 */
+/* dw2-inline-header.c : 22 */
+/* dw2-inline-header.c : 23 */
+/* dw2-inline-header.c : 24 */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
new file mode 100644
index 00000000000..7233acbcd76
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.h : 16 */
+/* dw2-inline-header.h : 17 */
+/* dw2-inline-header.h : 18 */
+/* dw2-inline-header.h : 19 */
+/* dw2-inline-header.h : 20 */
+/* dw2-inline-header.h : 21 */
+/* dw2-inline-header.h : 22 */
+/* dw2-inline-header.h : 23 */
+/* dw2-inline-header.h : 24 */
-- 
2.14.5


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-03 22:21             ` [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files Andrew Burgess
@ 2020-04-04 18:07               ` Bernd Edlinger
  2020-04-04 19:59                 ` Bernd Edlinger
  2020-04-04 22:23                 ` Andrew Burgess
  2020-04-11  3:52               ` Bernd Edlinger
  1 sibling, 2 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-04-04 18:07 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Tom Tromey

Sorry, Andrew,

please hold this one.

This will definitely break all the step over inline
stuff that was already working.

Can we please go to the drawing board before we continue
from here?


Thanks
Bernd.

On 4/4/20 12:21 AM, Andrew Burgess wrote:
> After the is-stmt support commit:
> 
>   commit 8c95582da858ac981f689a6f599acacb8c5c490f
>   Date:   Mon Dec 30 21:04:51 2019 +0000
> 
>       gdb: Add support for tracking the DWARF line table is-stmt field
> 
> A regression was observed where a breakpoint could no longer be placed
> in some cases.
> 
> Consider a line table like this:
> 
>   File 1: test.c
>   File 2: test.h
> 
>   | Addr | File | Line | Stmt |
>   |------|------|------|------|
>   | 1    | 1    | 16   | Y    |
>   | 2    | 1    | 17   | Y    |
>   | 3    | 2    | 21   | Y    |
>   | 4    | 2    | 22   | Y    |
>   | 4    | 1    | 18   | N    |
>   | 5    | 2    | 23   | N    |
>   | 6    | 1    | 24   | Y    |
>   | 7    | 1    | END  | Y    |
>   |------|------|------|------|
> 
> Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
> built two line table structures:
> 
>   File 1                 File 2
>   ------                 ------
> 
>   | Addr | Line |        | Addr | Line |
>   |------|------|        |------|------|
>   | 1    | 16   |        | 3    | 21   |
>   | 2    | 17   |        | 4    | 22   |
>   | 3    | END  |        | 6    | END  |
>   | 6    | 24   |        |------|------|
>   | 7    | END  |
>   |------|------|
> 
> After the is-stmt patch GDB now records non-stmt lines, so the
> generated line table structures look like this:
> 
>   File 1                   File 2
>   ------                   ------
> 
>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>   |------|------|------|  |------|------|------|
>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>   | 3    | END  | Y    |  | 4    | END  | Y    |
>   | 4    | 18   | N    |  | 5    | 23   | N    |
>   | 5    | END  | Y    |  | 6    | END  | Y    |
>   | 6    | 24   | Y    |  |------|------|------|
>   | 7    | END  | Y    |
>   |------|------|------|
> 
> The problem is that in 'File 2', end END marker at address 4 causes
> the previous line table entry to be discarded, so we actually end up
> with this:
> 
>   File 2
>   ------
> 
>   | Addr | Line | Stmt |
>   |------|------|------|
>   | 3    | 21   | Y    |
>   | 4    | END  | Y    |
>   | 5    | 23   | N    |
>   | 6    | END  | Y    |
>   |------|------|------|
> 
> When a user tries to place a breakpoint in file 2 at line 22, this is
> no longer possible.
> 
> The solution I propose here is that we ignore line table entries that
> would trigger a change of file if:
> 
>   1. The new line being added is at the same address as the previous
>   line, and
> 
>   2. We have previously seen an is-stmt line at the current address.
> 
> The result of this is that GDB switches file, and knows that some line
> entry (or entries) are going to be discarded, prefer to keep is-stmt
> lines and discard non-stmt lines.
> 
> After this commit the lines tables are now:
> 
>   File 1                   File 2
>   ------                   ------
> 
>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>   |------|------|------|  |------|------|------|
>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>   | 3    | END  | Y    |  | 5    | 23   | N    |
>   | 5    | END  | Y    |  | 6    | END  | Y    |
>   | 6    | 24   | Y    |  |------|------|------|
>   | 7    | END  | Y    |
>   |------|------|------|
> 
> We've lost the non-stmt entry for file 1, line 18, but retained the
> is-stmt entry for file 2, line 22.  The user can now place a
> breakpoint at that location.
> 
> One problem that came from this commit was the test
> gdb.cp/step-and-next-inline.exp, which broke in several places.  After
> looking at this test again I think that in some cases this test was
> only ever passing by pure luck.  The debug GCC is producing for this
> test is pretty broken.  I raised this GCC bug:
> 
>   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> 
> for this and disabled one entire half of the test.  There are still
> some cases in here that do pass, and if/when GCC is fixed it would be
> great to enable this test again.
> 
> gdb/ChangeLog:
> 
> 	* dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
> 	member variable.
> 	<m_stmt_at_address>: New member variable.
> 	(lnp_state_machine::record_line): Don't record some lines, update
> 	tracking of is_stmt at the same address.
> 	(lnp_state_machine::lnp_state_machine): Initialise new member
> 	variables.
> 
> gdb/testsuite/ChangeLog:
> 
> 	* gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
> 	use_header case.
> 	* gdb.dwarf2/dw2-inline-header-1.exp: New file.
> 	* gdb.dwarf2/dw2-inline-header-2.exp: New file.
> 	* gdb.dwarf2/dw2-inline-header-3.exp: New file.
> 	* gdb.dwarf2/dw2-inline-header-lbls.c: New file.
> 	* gdb.dwarf2/dw2-inline-header.c: New file.
> 	* gdb.dwarf2/dw2-inline-header.h: New file.
> ---
>  gdb/ChangeLog                                     |  10 ++
>  gdb/dwarf2/read.c                                 |  47 +++++-
>  gdb/testsuite/ChangeLog                           |  11 ++
>  gdb/testsuite/gdb.cp/step-and-next-inline.exp     |   7 +
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp  | 156 ++++++++++++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp  | 179 ++++++++++++++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp  | 192 ++++++++++++++++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c |  46 ++++++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.c      |  24 +++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.h      |  24 +++
>  10 files changed, 693 insertions(+), 3 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> 
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index f94c66b4f1b..261c8455424 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -19327,6 +19327,15 @@ class lnp_state_machine
>    /* The last file a line number was recorded for.  */
>    struct subfile *m_last_subfile = NULL;
>  
> +  /* The address of the last line entry.  */
> +  CORE_ADDR m_last_address;
> +
> +  /* Set to true when a previous line at the same address (using
> +     m_last_address) had m_is_stmt true.  This is reset to false when a
> +     line entry at a new address (m_address different to m_last_address) is
> +     processed.  */
> +  bool m_stmt_at_address = false;
> +
>    /* When true, record the lines we decode.  */
>    bool m_currently_recording_lines = false;
>  
> @@ -19520,14 +19529,34 @@ lnp_state_machine::record_line (bool end_sequence)
>        fe->included_p = 1;
>        if (m_record_lines_p)
>  	{
> -	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
> -	      || end_sequence)
> +	  /* When we switch files we insert an end maker in the first file,
> +	     switch to the second file and add a new line entry.  The
> +	     problem is that the end marker inserted in the first file will
> +	     discard any previous line entries at the same address.  If the
> +	     line entries in the first file are marked as is-stmt, while
> +	     the new line in the second file is non-stmt, then this means
> +	     the end marker will discard is-stmt lines so we can have a
> +	     non-stmt line.  This means that there are less addresses at
> +	     which the user can insert a breakpoint.
> +
> +	     To improve this we track the last address in m_last_address,
> +	     and whether we have seen an is-stmt at this address.  Then
> +	     when switching files, if we have seen a stmt at the current
> +	     address, and we are switching to create a non-stmt line, then
> +	     discard the new line.  */
> +	  bool file_changed
> +	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
> +	  bool ignore_this_line
> +	    = (file_changed && !end_sequence && m_last_address == m_address
> +	       && !m_is_stmt && m_stmt_at_address);
> +
> +	  if ((file_changed && !ignore_this_line) || end_sequence)
>  	    {
>  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
>  				 m_currently_recording_lines ? m_cu : nullptr);
>  	    }
>  
> -	  if (!end_sequence)
> +	  if (!end_sequence && !ignore_this_line)
>  	    {
>  	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
>  
> @@ -19546,6 +19575,15 @@ lnp_state_machine::record_line (bool end_sequence)
>  	    }
>  	}
>      }
> +
> +  /* Track whether we have seen any m_is_stmt true at m_address in case we
> +     have multiple line table entries all at m_address.  */
> +  if (m_last_address != m_address)
> +    {
> +      m_stmt_at_address = false;
> +      m_last_address = m_address;
> +    }
> +  m_stmt_at_address |= m_is_stmt;
>  }
>  
>  lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
> @@ -19565,6 +19603,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>    m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
>    m_is_stmt = lh->default_is_stmt;
>    m_discriminator = 0;
> +
> +  m_last_address = m_address;
> +  m_stmt_at_addr = false;
>  }
>  
>  void
> diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> index 3733fa75570..a95e21194f9 100644
> --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
>  proc do_test { use_header } {
>      global srcfile testfile
>  
> +    if { $use_header } {
> +	# This test will not pass due to poor debug information
> +	# generated by GCC (at least upto 10.x).  See
> +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> +	return
> +    }
> +
>      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
>      if { $use_header } {
>  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> new file mode 100644
> index 00000000000..6a1e990002c
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> @@ -0,0 +1,156 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt |
> +# |------|------|------|------|
> +# | 1    | 1    | 16   | Y    |
> +# | 2    | 1    | 17   | Y    |
> +# | 3    | 2    | 21   | Y    |
> +# | 4    | 2    | 22   | Y    |
> +# | 4    | 1    | 18   | N    |
> +# | 5    | 2    | 23   | N    |
> +# | 6    | 1    | 24   | Y    |
> +# | 7    | 1    | END  | Y    |
> +# |------|------|------|------|
> +#
> +# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
> +# the line table entry for this line due to switching files for the
> +# file 1, line 18 non-statement line.  After patching however, GDB now
> +# discards the file 1, line 18 entry instead, and the breakpoint at
> +# line 22 should succeed.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_1 addr}
> +		    {high_pc line_label_7 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 5}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> new file mode 100644
> index 00000000000..46499919a8b
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> @@ -0,0 +1,179 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt |
> +# |------|------|------|------|
> +# | 1    | 1    | 16   | Y    |
> +# | 2    | 1    | 17   | Y    |
> +# | 3    | 2    | 21   | Y    |
> +# | 4    | 2    | 22   | Y    |
> +# | 4    | 1    | 18   | N    |
> +# | 5    | 1    | 19   | Y    |
> +# | 6    | 1    | 20   | Y    |
> +# | 7    | 1    | END  | Y    |
> +# |------|------|------|------|
> +#
> +#
> +# Place the first brekpoint at file 2, line 22 and a second breakpoint
> +# at file 1, line 19.  A third breakpoint is placed at file 1, line
> +# 18, but as this line table entry will have been discarded[1] the
> +# third breakpoint will actually be placed at the same location as the
> +# second breakpoint.
> +#
> +# [1] The entry for file 1, line 18 is discarded because it is at the
> +# same address as the previous entry, but the previous entry is-stmt,
> +# while line 18 is a non-stmt.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_1 addr}
> +		    {high_pc line_label_7 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
> +    "check for breakpoint at ${srcfile4}"
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile3}:19"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
> +    "check for breakpoint at ${srcfile3}"
> +
> +# Line table entry for line 18 will have been discarded, so this
> +# brekpoint will be at the same location as line 19.
> +gdb_test "break ${srcfile3}:18" \
> +    "Note: breakpoint $decimal also set at pc $hex.*"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> new file mode 100644
> index 00000000000..c683dc4bb8a
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> @@ -0,0 +1,192 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt |
> +# |------|------|------|------|
> +# | 1    | 1    | 16   | Y    |
> +# | 2    | 1    | 17   | Y    |
> +# | 3    | 2    | 21   | Y    |
> +# | 4    | 2    | 22   | Y    |
> +# | 4    | 1    | 18   | N    |
> +# | 5    | 1    | 19   | N    |
> +# | 6    | 1    | 20   | Y    |
> +# | 7    | 1    | END  | Y    |
> +# |------|------|------|------|
> +#
> +# Break at file 2, line 22, then single instruction step forward.  We
> +# should pass through line 19 and then encounter line 20.
> +#
> +# Currently we don't expect GDB to see file 1, line 18, as this is a
> +# non-stmt line in a different file at the same address as the
> +# previous is-stmt line.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_1 addr}
> +		    {high_pc line_label_7 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> +
> +gdb_continue_to_breakpoint "${srcfile4}:22" \
> +    ".* ${srcfile4} : 22 .*"
> +
> +# Now single instruction step forward.  Eventually we should hit
> +# ${srcfile3}:20, but before we do we should hit the non-statement
> +# line ${srcfile3}:19.
> +#
> +# We don't know how many instructions we'll need to step, but 100
> +# should be enough for everyone (surely), and this stops us looping
> +# forever if something goes wrong.
> +set found_line_19 0
> +set found_line_20 0
> +set keep_going 1
> +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
> +    set keep_going 0
> +    gdb_test_multiple "stepi" "stepi ${i}" {
> +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
> +	    set found_line_19 1
> +	    set keep_going 1
> +	}
> +
> +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
> +	    set found_line_20 1
> +	}
> +
> +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
> +	    # Not left line 22 yet.
> +	    set keep_going 1
> +	}
> +    }
> +}
> +
> +gdb_assert { $found_line_19 && $found_line_20 } \
> +    "found line 19 and 20"
> +
> +
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> new file mode 100644
> index 00000000000..a1b7b17cbeb
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> @@ -0,0 +1,46 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* Used to insert labels with which we can build a fake line table.  */
> +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
> +
> +volatile int var;
> +volatile int bar;
> +
> +/* Generate some code to take up some space.  */
> +#define FILLER do { \
> +    var = 99;	    \
> +} while (0)
> +
> +int
> +main ()
> +{					/* main prologue */
> +  asm ("main_label: .globl main_label");
> +  LL (1);	// F1, Ln 16
> +  FILLER;
> +  LL (2);	// F1, Ln 17
> +  FILLER;
> +  LL (3);	// F2, Ln 21
> +  FILLER;
> +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
> +  FILLER;
> +  LL (5);	// F1, Ln 19 !S
> +  FILLER;
> +  LL (6);	// F1, Ln 20
> +  FILLER;
> +  LL (7);
> +  FILLER;
> +  return 0;				/* main end */
> +}
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> new file mode 100644
> index 00000000000..a8331268a09
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> @@ -0,0 +1,24 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* dw2-inline-header.c : 16 */
> +/* dw2-inline-header.c : 17 */
> +/* dw2-inline-header.c : 18 */
> +/* dw2-inline-header.c : 19 */
> +/* dw2-inline-header.c : 20 */
> +/* dw2-inline-header.c : 21 */
> +/* dw2-inline-header.c : 22 */
> +/* dw2-inline-header.c : 23 */
> +/* dw2-inline-header.c : 24 */
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> new file mode 100644
> index 00000000000..7233acbcd76
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> @@ -0,0 +1,24 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* dw2-inline-header.h : 16 */
> +/* dw2-inline-header.h : 17 */
> +/* dw2-inline-header.h : 18 */
> +/* dw2-inline-header.h : 19 */
> +/* dw2-inline-header.h : 20 */
> +/* dw2-inline-header.h : 21 */
> +/* dw2-inline-header.h : 22 */
> +/* dw2-inline-header.h : 23 */
> +/* dw2-inline-header.h : 24 */
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-04 18:07               ` Bernd Edlinger
@ 2020-04-04 19:59                 ` Bernd Edlinger
  2020-04-04 22:23                 ` Andrew Burgess
  1 sibling, 0 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-04-04 19:59 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Tom Tromey

Andrew,

I think my other patch that is still in your review queue
is able to solve most if not all of the regressions,

see https://marc.info/?l=gdb-patches&m=158602983106559&w=2

I may have overlooked something, but I believe that is
at least a different fix that is focused only on the inline
functions.

What do you think?


Bernd.

On 4/4/20 8:07 PM, Bernd Edlinger wrote:
> Sorry, Andrew,
> 
> please hold this one.
> 
> This will definitely break all the step over inline
> stuff that was already working.
> 
> Can we please go to the drawing board before we continue
> from here?
> 
> 
> Thanks
> Bernd.
> 
> On 4/4/20 12:21 AM, Andrew Burgess wrote:
>> After the is-stmt support commit:
>>
>>   commit 8c95582da858ac981f689a6f599acacb8c5c490f
>>   Date:   Mon Dec 30 21:04:51 2019 +0000
>>
>>       gdb: Add support for tracking the DWARF line table is-stmt field
>>
>> A regression was observed where a breakpoint could no longer be placed
>> in some cases.
>>
>> Consider a line table like this:
>>
>>   File 1: test.c
>>   File 2: test.h
>>
>>   | Addr | File | Line | Stmt |
>>   |------|------|------|------|
>>   | 1    | 1    | 16   | Y    |
>>   | 2    | 1    | 17   | Y    |
>>   | 3    | 2    | 21   | Y    |
>>   | 4    | 2    | 22   | Y    |
>>   | 4    | 1    | 18   | N    |
>>   | 5    | 2    | 23   | N    |
>>   | 6    | 1    | 24   | Y    |
>>   | 7    | 1    | END  | Y    |
>>   |------|------|------|------|
>>
>> Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
>> built two line table structures:
>>
>>   File 1                 File 2
>>   ------                 ------
>>
>>   | Addr | Line |        | Addr | Line |
>>   |------|------|        |------|------|
>>   | 1    | 16   |        | 3    | 21   |
>>   | 2    | 17   |        | 4    | 22   |
>>   | 3    | END  |        | 6    | END  |
>>   | 6    | 24   |        |------|------|
>>   | 7    | END  |
>>   |------|------|
>>
>> After the is-stmt patch GDB now records non-stmt lines, so the
>> generated line table structures look like this:
>>
>>   File 1                   File 2
>>   ------                   ------
>>
>>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>>   |------|------|------|  |------|------|------|
>>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>>   | 3    | END  | Y    |  | 4    | END  | Y    |
>>   | 4    | 18   | N    |  | 5    | 23   | N    |
>>   | 5    | END  | Y    |  | 6    | END  | Y    |
>>   | 6    | 24   | Y    |  |------|------|------|
>>   | 7    | END  | Y    |
>>   |------|------|------|
>>
>> The problem is that in 'File 2', end END marker at address 4 causes
>> the previous line table entry to be discarded, so we actually end up
>> with this:
>>
>>   File 2
>>   ------
>>
>>   | Addr | Line | Stmt |
>>   |------|------|------|
>>   | 3    | 21   | Y    |
>>   | 4    | END  | Y    |
>>   | 5    | 23   | N    |
>>   | 6    | END  | Y    |
>>   |------|------|------|
>>
>> When a user tries to place a breakpoint in file 2 at line 22, this is
>> no longer possible.
>>
>> The solution I propose here is that we ignore line table entries that
>> would trigger a change of file if:
>>
>>   1. The new line being added is at the same address as the previous
>>   line, and
>>
>>   2. We have previously seen an is-stmt line at the current address.
>>
>> The result of this is that GDB switches file, and knows that some line
>> entry (or entries) are going to be discarded, prefer to keep is-stmt
>> lines and discard non-stmt lines.
>>
>> After this commit the lines tables are now:
>>
>>   File 1                   File 2
>>   ------                   ------
>>
>>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>>   |------|------|------|  |------|------|------|
>>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>>   | 3    | END  | Y    |  | 5    | 23   | N    |
>>   | 5    | END  | Y    |  | 6    | END  | Y    |
>>   | 6    | 24   | Y    |  |------|------|------|
>>   | 7    | END  | Y    |
>>   |------|------|------|
>>
>> We've lost the non-stmt entry for file 1, line 18, but retained the
>> is-stmt entry for file 2, line 22.  The user can now place a
>> breakpoint at that location.
>>
>> One problem that came from this commit was the test
>> gdb.cp/step-and-next-inline.exp, which broke in several places.  After
>> looking at this test again I think that in some cases this test was
>> only ever passing by pure luck.  The debug GCC is producing for this
>> test is pretty broken.  I raised this GCC bug:
>>
>>   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>>
>> for this and disabled one entire half of the test.  There are still
>> some cases in here that do pass, and if/when GCC is fixed it would be
>> great to enable this test again.
>>
>> gdb/ChangeLog:
>>
>> 	* dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
>> 	member variable.
>> 	<m_stmt_at_address>: New member variable.
>> 	(lnp_state_machine::record_line): Don't record some lines, update
>> 	tracking of is_stmt at the same address.
>> 	(lnp_state_machine::lnp_state_machine): Initialise new member
>> 	variables.
>>
>> gdb/testsuite/ChangeLog:
>>
>> 	* gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
>> 	use_header case.
>> 	* gdb.dwarf2/dw2-inline-header-1.exp: New file.
>> 	* gdb.dwarf2/dw2-inline-header-2.exp: New file.
>> 	* gdb.dwarf2/dw2-inline-header-3.exp: New file.
>> 	* gdb.dwarf2/dw2-inline-header-lbls.c: New file.
>> 	* gdb.dwarf2/dw2-inline-header.c: New file.
>> 	* gdb.dwarf2/dw2-inline-header.h: New file.
>> ---
>>  gdb/ChangeLog                                     |  10 ++
>>  gdb/dwarf2/read.c                                 |  47 +++++-
>>  gdb/testsuite/ChangeLog                           |  11 ++
>>  gdb/testsuite/gdb.cp/step-and-next-inline.exp     |   7 +
>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp  | 156 ++++++++++++++++++
>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp  | 179 ++++++++++++++++++++
>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp  | 192 ++++++++++++++++++++++
>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c |  46 ++++++
>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.c      |  24 +++
>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.h      |  24 +++
>>  10 files changed, 693 insertions(+), 3 deletions(-)
>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>>
>> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
>> index f94c66b4f1b..261c8455424 100644
>> --- a/gdb/dwarf2/read.c
>> +++ b/gdb/dwarf2/read.c
>> @@ -19327,6 +19327,15 @@ class lnp_state_machine
>>    /* The last file a line number was recorded for.  */
>>    struct subfile *m_last_subfile = NULL;
>>  
>> +  /* The address of the last line entry.  */
>> +  CORE_ADDR m_last_address;
>> +
>> +  /* Set to true when a previous line at the same address (using
>> +     m_last_address) had m_is_stmt true.  This is reset to false when a
>> +     line entry at a new address (m_address different to m_last_address) is
>> +     processed.  */
>> +  bool m_stmt_at_address = false;
>> +
>>    /* When true, record the lines we decode.  */
>>    bool m_currently_recording_lines = false;
>>  
>> @@ -19520,14 +19529,34 @@ lnp_state_machine::record_line (bool end_sequence)
>>        fe->included_p = 1;
>>        if (m_record_lines_p)
>>  	{
>> -	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
>> -	      || end_sequence)
>> +	  /* When we switch files we insert an end maker in the first file,
>> +	     switch to the second file and add a new line entry.  The
>> +	     problem is that the end marker inserted in the first file will
>> +	     discard any previous line entries at the same address.  If the
>> +	     line entries in the first file are marked as is-stmt, while
>> +	     the new line in the second file is non-stmt, then this means
>> +	     the end marker will discard is-stmt lines so we can have a
>> +	     non-stmt line.  This means that there are less addresses at
>> +	     which the user can insert a breakpoint.
>> +
>> +	     To improve this we track the last address in m_last_address,
>> +	     and whether we have seen an is-stmt at this address.  Then
>> +	     when switching files, if we have seen a stmt at the current
>> +	     address, and we are switching to create a non-stmt line, then
>> +	     discard the new line.  */
>> +	  bool file_changed
>> +	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
>> +	  bool ignore_this_line
>> +	    = (file_changed && !end_sequence && m_last_address == m_address
>> +	       && !m_is_stmt && m_stmt_at_address);
>> +
>> +	  if ((file_changed && !ignore_this_line) || end_sequence)
>>  	    {
>>  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
>>  				 m_currently_recording_lines ? m_cu : nullptr);
>>  	    }
>>  
>> -	  if (!end_sequence)
>> +	  if (!end_sequence && !ignore_this_line)
>>  	    {
>>  	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
>>  
>> @@ -19546,6 +19575,15 @@ lnp_state_machine::record_line (bool end_sequence)
>>  	    }
>>  	}
>>      }
>> +
>> +  /* Track whether we have seen any m_is_stmt true at m_address in case we
>> +     have multiple line table entries all at m_address.  */
>> +  if (m_last_address != m_address)
>> +    {
>> +      m_stmt_at_address = false;
>> +      m_last_address = m_address;
>> +    }
>> +  m_stmt_at_address |= m_is_stmt;
>>  }
>>  
>>  lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>> @@ -19565,6 +19603,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>>    m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
>>    m_is_stmt = lh->default_is_stmt;
>>    m_discriminator = 0;
>> +
>> +  m_last_address = m_address;
>> +  m_stmt_at_addr = false;
>>  }
>>  
>>  void
>> diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>> index 3733fa75570..a95e21194f9 100644
>> --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>> +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>> @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
>>  proc do_test { use_header } {
>>      global srcfile testfile
>>  
>> +    if { $use_header } {
>> +	# This test will not pass due to poor debug information
>> +	# generated by GCC (at least upto 10.x).  See
>> +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>> +	return
>> +    }
>> +
>>      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
>>      if { $use_header } {
>>  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>> new file mode 100644
>> index 00000000000..6a1e990002c
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>> @@ -0,0 +1,156 @@
>> +# Copyright 2020 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/>.
>> +
>> +# Setup a line table where:
>> +#
>> +# | Addr | File | Line | Stmt |
>> +# |------|------|------|------|
>> +# | 1    | 1    | 16   | Y    |
>> +# | 2    | 1    | 17   | Y    |
>> +# | 3    | 2    | 21   | Y    |
>> +# | 4    | 2    | 22   | Y    |
>> +# | 4    | 1    | 18   | N    |
>> +# | 5    | 2    | 23   | N    |
>> +# | 6    | 1    | 24   | Y    |
>> +# | 7    | 1    | END  | Y    |
>> +# |------|------|------|------|
>> +#
>> +# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
>> +# the line table entry for this line due to switching files for the
>> +# file 1, line 18 non-statement line.  After patching however, GDB now
>> +# discards the file 1, line 18 entry instead, and the breakpoint at
>> +# line 22 should succeed.
>> +
>> +load_lib dwarf.exp
>> +
>> +# This test can only be run on targets which support DWARF-2 and use gas.
>> +if {![dwarf2_support]} {
>> +    return 0
>> +}
>> +
>> +# The .c files use __attribute__.
>> +if [get_compiler_info] {
>> +    return -1
>> +}
>> +if !$gcc_compiled {
>> +    return 0
>> +}
>> +
>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>> +    dw2-inline-header.c dw2-inline-header.h
>> +
>> +set asm_file [standard_output_file $srcfile2]
>> +Dwarf::assemble $asm_file {
>> +    global srcdir subdir srcfile srcfile3 srcfile4
>> +    declare_labels lines_label callee_subprog_label
>> +
>> +    get_func_info main
>> +
>> +    cu {} {
>> +	compile_unit {
>> +	    {producer "gcc" }
>> +	    {language @DW_LANG_C}
>> +	    {name ${srcfile3}}
>> +	    {low_pc 0 addr}
>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>> +	} {
>> +	    callee_subprog_label: subprogram {
>> +		{external 1 flag}
>> +		{name callee}
>> +		{inline 3 data1}
>> +	    }
>> +	    subprogram {
>> +		{external 1 flag}
>> +		{name main}
>> +		{low_pc $main_start addr}
>> +		{high_pc "$main_start + $main_len" addr}
>> +	    } {
>> +		inlined_subroutine {
>> +		    {abstract_origin %$callee_subprog_label}
>> +		    {low_pc line_label_1 addr}
>> +		    {high_pc line_label_7 addr}
>> +		    {call_file 1 data1}
>> +		    {call_line 18 data1}
>> +		}
>> +	    }
>> +	}
>> +    }
>> +
>> +    lines {version 2 default_is_stmt 1} lines_label {
>> +	include_dir "${srcdir}/${subdir}"
>> +	file_name "$srcfile3" 1
>> +	file_name "$srcfile4" 1
>> +
>> +	program {
>> +	    {DW_LNE_set_address line_label_1}
>> +	    {DW_LNS_advance_line 15}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_2}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_set_file 2}
>> +	    {DW_LNE_set_address line_label_3}
>> +	    {DW_LNS_advance_line 4}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_4}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_advance_line -4}
>> +	    {DW_LNS_set_file 1}
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_set_file 2}
>> +	    {DW_LNE_set_address line_label_5}
>> +	    {DW_LNS_advance_line 5}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_set_file 1}
>> +	    {DW_LNE_set_address line_label_6}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_7}
>> +	    {DW_LNE_end_sequence}
>> +	}
>> +    }
>> +}
>> +
>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>> +    return -1
>> +}
>> +
>> +if ![runto_main] {
>> +    return -1
>> +}
>> +
>> +# Delete all breakpoints so that the output of "info breakpoints"
>> +# below will only contain a single breakpoint.
>> +delete_breakpoints
>> +
>> +# Place a breakpoint within the function in the header file.
>> +gdb_breakpoint "${srcfile4}:22"
>> +
>> +# Check that the breakpoint was placed where we expected.  It should
>> +# appear at the requested line.  When the bug in GDB was present the
>> +# breakpoint would be placed on one of the following lines instead.
>> +gdb_test "info breakpoints" \
>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>> new file mode 100644
>> index 00000000000..46499919a8b
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>> @@ -0,0 +1,179 @@
>> +# Copyright 2020 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/>.
>> +
>> +# Setup a line table where:
>> +#
>> +# | Addr | File | Line | Stmt |
>> +# |------|------|------|------|
>> +# | 1    | 1    | 16   | Y    |
>> +# | 2    | 1    | 17   | Y    |
>> +# | 3    | 2    | 21   | Y    |
>> +# | 4    | 2    | 22   | Y    |
>> +# | 4    | 1    | 18   | N    |
>> +# | 5    | 1    | 19   | Y    |
>> +# | 6    | 1    | 20   | Y    |
>> +# | 7    | 1    | END  | Y    |
>> +# |------|------|------|------|
>> +#
>> +#
>> +# Place the first brekpoint at file 2, line 22 and a second breakpoint
>> +# at file 1, line 19.  A third breakpoint is placed at file 1, line
>> +# 18, but as this line table entry will have been discarded[1] the
>> +# third breakpoint will actually be placed at the same location as the
>> +# second breakpoint.
>> +#
>> +# [1] The entry for file 1, line 18 is discarded because it is at the
>> +# same address as the previous entry, but the previous entry is-stmt,
>> +# while line 18 is a non-stmt.
>> +
>> +load_lib dwarf.exp
>> +
>> +# This test can only be run on targets which support DWARF-2 and use gas.
>> +if {![dwarf2_support]} {
>> +    return 0
>> +}
>> +
>> +# The .c files use __attribute__.
>> +if [get_compiler_info] {
>> +    return -1
>> +}
>> +if !$gcc_compiled {
>> +    return 0
>> +}
>> +
>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>> +    dw2-inline-header.c dw2-inline-header.h
>> +
>> +set asm_file [standard_output_file $srcfile2]
>> +Dwarf::assemble $asm_file {
>> +    global srcdir subdir srcfile srcfile3 srcfile4
>> +    declare_labels lines_label callee_subprog_label
>> +
>> +    get_func_info main
>> +
>> +    cu {} {
>> +	compile_unit {
>> +	    {producer "gcc" }
>> +	    {language @DW_LANG_C}
>> +	    {name ${srcfile3}}
>> +	    {low_pc 0 addr}
>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>> +	} {
>> +	    callee_subprog_label: subprogram {
>> +		{external 1 flag}
>> +		{name callee}
>> +		{inline 3 data1}
>> +	    }
>> +	    subprogram {
>> +		{external 1 flag}
>> +		{name main}
>> +		{low_pc $main_start addr}
>> +		{high_pc "$main_start + $main_len" addr}
>> +	    } {
>> +		inlined_subroutine {
>> +		    {abstract_origin %$callee_subprog_label}
>> +		    {low_pc line_label_1 addr}
>> +		    {high_pc line_label_7 addr}
>> +		    {call_file 1 data1}
>> +		    {call_line 18 data1}
>> +		}
>> +	    }
>> +	}
>> +    }
>> +
>> +    lines {version 2 default_is_stmt 1} lines_label {
>> +	include_dir "${srcdir}/${subdir}"
>> +	file_name "$srcfile3" 1
>> +	file_name "$srcfile4" 1
>> +
>> +	program {
>> +	    {DW_LNE_set_address line_label_1}
>> +	    {DW_LNS_advance_line 15}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_2}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_set_file 2}
>> +	    {DW_LNE_set_address line_label_3}
>> +	    {DW_LNS_advance_line 4}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_4}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_advance_line -4}
>> +	    {DW_LNS_set_file 1}
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_5}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_6}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_7}
>> +	    {DW_LNE_end_sequence}
>> +	}
>> +    }
>> +}
>> +
>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>> +    return -1
>> +}
>> +
>> +if ![runto_main] {
>> +    return -1
>> +}
>> +
>> +# Delete all breakpoints so that the output of "info breakpoints"
>> +# below will only contain a single breakpoint.
>> +delete_breakpoints
>> +
>> +# Place a breakpoint within the function in the header file.
>> +gdb_breakpoint "${srcfile4}:22"
>> +
>> +# Check that the breakpoint was placed where we expected.  It should
>> +# appear at the requested line.  When the bug in GDB was present the
>> +# breakpoint would be placed on one of the following lines instead.
>> +gdb_test "info breakpoints" \
>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
>> +    "check for breakpoint at ${srcfile4}"
>> +
>> +# Delete all breakpoints so that the output of "info breakpoints"
>> +# below will only contain a single breakpoint.
>> +delete_breakpoints
>> +
>> +# Place a breakpoint within the function in the header file.
>> +gdb_breakpoint "${srcfile3}:19"
>> +
>> +# Check that the breakpoint was placed where we expected.  It should
>> +# appear at the requested line.  When the bug in GDB was present the
>> +# breakpoint would be placed on one of the following lines instead.
>> +gdb_test "info breakpoints" \
>> +    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
>> +    "check for breakpoint at ${srcfile3}"
>> +
>> +# Line table entry for line 18 will have been discarded, so this
>> +# brekpoint will be at the same location as line 19.
>> +gdb_test "break ${srcfile3}:18" \
>> +    "Note: breakpoint $decimal also set at pc $hex.*"
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>> new file mode 100644
>> index 00000000000..c683dc4bb8a
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>> @@ -0,0 +1,192 @@
>> +# Copyright 2020 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/>.
>> +
>> +# Setup a line table where:
>> +#
>> +# | Addr | File | Line | Stmt |
>> +# |------|------|------|------|
>> +# | 1    | 1    | 16   | Y    |
>> +# | 2    | 1    | 17   | Y    |
>> +# | 3    | 2    | 21   | Y    |
>> +# | 4    | 2    | 22   | Y    |
>> +# | 4    | 1    | 18   | N    |
>> +# | 5    | 1    | 19   | N    |
>> +# | 6    | 1    | 20   | Y    |
>> +# | 7    | 1    | END  | Y    |
>> +# |------|------|------|------|
>> +#
>> +# Break at file 2, line 22, then single instruction step forward.  We
>> +# should pass through line 19 and then encounter line 20.
>> +#
>> +# Currently we don't expect GDB to see file 1, line 18, as this is a
>> +# non-stmt line in a different file at the same address as the
>> +# previous is-stmt line.
>> +
>> +load_lib dwarf.exp
>> +
>> +# This test can only be run on targets which support DWARF-2 and use gas.
>> +if {![dwarf2_support]} {
>> +    return 0
>> +}
>> +
>> +# The .c files use __attribute__.
>> +if [get_compiler_info] {
>> +    return -1
>> +}
>> +if !$gcc_compiled {
>> +    return 0
>> +}
>> +
>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>> +    dw2-inline-header.c dw2-inline-header.h
>> +
>> +set asm_file [standard_output_file $srcfile2]
>> +Dwarf::assemble $asm_file {
>> +    global srcdir subdir srcfile srcfile3 srcfile4
>> +    declare_labels lines_label callee_subprog_label
>> +
>> +    get_func_info main
>> +
>> +    cu {} {
>> +	compile_unit {
>> +	    {producer "gcc" }
>> +	    {language @DW_LANG_C}
>> +	    {name ${srcfile3}}
>> +	    {low_pc 0 addr}
>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>> +	} {
>> +	    callee_subprog_label: subprogram {
>> +		{external 1 flag}
>> +		{name callee}
>> +		{inline 3 data1}
>> +	    }
>> +	    subprogram {
>> +		{external 1 flag}
>> +		{name main}
>> +		{low_pc $main_start addr}
>> +		{high_pc "$main_start + $main_len" addr}
>> +	    } {
>> +		inlined_subroutine {
>> +		    {abstract_origin %$callee_subprog_label}
>> +		    {low_pc line_label_1 addr}
>> +		    {high_pc line_label_7 addr}
>> +		    {call_file 1 data1}
>> +		    {call_line 18 data1}
>> +		}
>> +	    }
>> +	}
>> +    }
>> +
>> +    lines {version 2 default_is_stmt 1} lines_label {
>> +	include_dir "${srcdir}/${subdir}"
>> +	file_name "$srcfile3" 1
>> +	file_name "$srcfile4" 1
>> +
>> +	program {
>> +	    {DW_LNE_set_address line_label_1}
>> +	    {DW_LNS_advance_line 15}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_2}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_set_file 2}
>> +	    {DW_LNE_set_address line_label_3}
>> +	    {DW_LNS_advance_line 4}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_4}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_advance_line -4}
>> +	    {DW_LNS_set_file 1}
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_5}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_6}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_7}
>> +	    {DW_LNE_end_sequence}
>> +	}
>> +    }
>> +}
>> +
>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>> +    return -1
>> +}
>> +
>> +if ![runto_main] {
>> +    return -1
>> +}
>> +
>> +# Delete all breakpoints so that the output of "info breakpoints"
>> +# below will only contain a single breakpoint.
>> +delete_breakpoints
>> +
>> +# Place a breakpoint within the function in the header file.
>> +gdb_breakpoint "${srcfile4}:22"
>> +
>> +# Check that the breakpoint was placed where we expected.  It should
>> +# appear at the requested line.  When the bug in GDB was present the
>> +# breakpoint would be placed on one of the following lines instead.
>> +gdb_test "info breakpoints" \
>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
>> +
>> +gdb_continue_to_breakpoint "${srcfile4}:22" \
>> +    ".* ${srcfile4} : 22 .*"
>> +
>> +# Now single instruction step forward.  Eventually we should hit
>> +# ${srcfile3}:20, but before we do we should hit the non-statement
>> +# line ${srcfile3}:19.
>> +#
>> +# We don't know how many instructions we'll need to step, but 100
>> +# should be enough for everyone (surely), and this stops us looping
>> +# forever if something goes wrong.
>> +set found_line_19 0
>> +set found_line_20 0
>> +set keep_going 1
>> +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
>> +    set keep_going 0
>> +    gdb_test_multiple "stepi" "stepi ${i}" {
>> +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
>> +	    set found_line_19 1
>> +	    set keep_going 1
>> +	}
>> +
>> +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
>> +	    set found_line_20 1
>> +	}
>> +
>> +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
>> +	    # Not left line 22 yet.
>> +	    set keep_going 1
>> +	}
>> +    }
>> +}
>> +
>> +gdb_assert { $found_line_19 && $found_line_20 } \
>> +    "found line 19 and 20"
>> +
>> +
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>> new file mode 100644
>> index 00000000000..a1b7b17cbeb
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>> @@ -0,0 +1,46 @@
>> +/* Copyright 2020 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/>.  */
>> +
>> +/* Used to insert labels with which we can build a fake line table.  */
>> +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
>> +
>> +volatile int var;
>> +volatile int bar;
>> +
>> +/* Generate some code to take up some space.  */
>> +#define FILLER do { \
>> +    var = 99;	    \
>> +} while (0)
>> +
>> +int
>> +main ()
>> +{					/* main prologue */
>> +  asm ("main_label: .globl main_label");
>> +  LL (1);	// F1, Ln 16
>> +  FILLER;
>> +  LL (2);	// F1, Ln 17
>> +  FILLER;
>> +  LL (3);	// F2, Ln 21
>> +  FILLER;
>> +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
>> +  FILLER;
>> +  LL (5);	// F1, Ln 19 !S
>> +  FILLER;
>> +  LL (6);	// F1, Ln 20
>> +  FILLER;
>> +  LL (7);
>> +  FILLER;
>> +  return 0;				/* main end */
>> +}
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>> new file mode 100644
>> index 00000000000..a8331268a09
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>> @@ -0,0 +1,24 @@
>> +/* Copyright 2020 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/>.  */
>> +
>> +/* dw2-inline-header.c : 16 */
>> +/* dw2-inline-header.c : 17 */
>> +/* dw2-inline-header.c : 18 */
>> +/* dw2-inline-header.c : 19 */
>> +/* dw2-inline-header.c : 20 */
>> +/* dw2-inline-header.c : 21 */
>> +/* dw2-inline-header.c : 22 */
>> +/* dw2-inline-header.c : 23 */
>> +/* dw2-inline-header.c : 24 */
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>> new file mode 100644
>> index 00000000000..7233acbcd76
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>> @@ -0,0 +1,24 @@
>> +/* Copyright 2020 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/>.  */
>> +
>> +/* dw2-inline-header.h : 16 */
>> +/* dw2-inline-header.h : 17 */
>> +/* dw2-inline-header.h : 18 */
>> +/* dw2-inline-header.h : 19 */
>> +/* dw2-inline-header.h : 20 */
>> +/* dw2-inline-header.h : 21 */
>> +/* dw2-inline-header.h : 22 */
>> +/* dw2-inline-header.h : 23 */
>> +/* dw2-inline-header.h : 24 */
>>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-04 18:07               ` Bernd Edlinger
  2020-04-04 19:59                 ` Bernd Edlinger
@ 2020-04-04 22:23                 ` Andrew Burgess
  2020-04-05  0:04                   ` Bernd Edlinger
                                     ` (2 more replies)
  1 sibling, 3 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-04-04 22:23 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: gdb-patches, Tom Tromey

* Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-04-04 20:07:52 +0200]:

> Sorry, Andrew,
> 
> please hold this one.
> 
> This will definitely break all the step over inline
> stuff that was already working.

By all I assume you only mean gdb.cp/step-and-next-inline.exp, which I
specifically talk about in the commit message.

The DWARF produced in that test is horribly broken, I was tempted to
drop the whole test, but a few parts do still work, and hopefully if
GCC gets fixed we'll be back in business.

As I mentioned in the GCC bug report, if I hack GDB to "fix" the DWARF
produced by GCC, then that test passes fine with no regressions with
this patch as is.

All other tests still pass with this patch - admittedly there's not
much testing the inline behaviour, but there are a few.

If you know of a case with valid DWARF that is broken by this test
then I'm all ears.  With the setup I used for the tests in this patch
it's actually pretty easy to synthesis test of inline functions, so
all you really need to do is describe a test you think will break and
I can knock up a test case for it reasonably quickly.

> 
> Can we please go to the drawing board before we continue
> from here?

Sure.  I posted my thoughts just now.

Thanks,
Andrew

> 
> 
> Thanks
> Bernd.
> 
> On 4/4/20 12:21 AM, Andrew Burgess wrote:
> > After the is-stmt support commit:
> > 
> >   commit 8c95582da858ac981f689a6f599acacb8c5c490f
> >   Date:   Mon Dec 30 21:04:51 2019 +0000
> > 
> >       gdb: Add support for tracking the DWARF line table is-stmt field
> > 
> > A regression was observed where a breakpoint could no longer be placed
> > in some cases.
> > 
> > Consider a line table like this:
> > 
> >   File 1: test.c
> >   File 2: test.h
> > 
> >   | Addr | File | Line | Stmt |
> >   |------|------|------|------|
> >   | 1    | 1    | 16   | Y    |
> >   | 2    | 1    | 17   | Y    |
> >   | 3    | 2    | 21   | Y    |
> >   | 4    | 2    | 22   | Y    |
> >   | 4    | 1    | 18   | N    |
> >   | 5    | 2    | 23   | N    |
> >   | 6    | 1    | 24   | Y    |
> >   | 7    | 1    | END  | Y    |
> >   |------|------|------|------|
> > 
> > Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
> > built two line table structures:
> > 
> >   File 1                 File 2
> >   ------                 ------
> > 
> >   | Addr | Line |        | Addr | Line |
> >   |------|------|        |------|------|
> >   | 1    | 16   |        | 3    | 21   |
> >   | 2    | 17   |        | 4    | 22   |
> >   | 3    | END  |        | 6    | END  |
> >   | 6    | 24   |        |------|------|
> >   | 7    | END  |
> >   |------|------|
> > 
> > After the is-stmt patch GDB now records non-stmt lines, so the
> > generated line table structures look like this:
> > 
> >   File 1                   File 2
> >   ------                   ------
> > 
> >   | Addr | Line | Stmt |  | Addr | Line | Stmt |
> >   |------|------|------|  |------|------|------|
> >   | 1    | 16   | Y    |  | 3    | 21   | Y    |
> >   | 2    | 17   | Y    |  | 4    | 22   | Y    |
> >   | 3    | END  | Y    |  | 4    | END  | Y    |
> >   | 4    | 18   | N    |  | 5    | 23   | N    |
> >   | 5    | END  | Y    |  | 6    | END  | Y    |
> >   | 6    | 24   | Y    |  |------|------|------|
> >   | 7    | END  | Y    |
> >   |------|------|------|
> > 
> > The problem is that in 'File 2', end END marker at address 4 causes
> > the previous line table entry to be discarded, so we actually end up
> > with this:
> > 
> >   File 2
> >   ------
> > 
> >   | Addr | Line | Stmt |
> >   |------|------|------|
> >   | 3    | 21   | Y    |
> >   | 4    | END  | Y    |
> >   | 5    | 23   | N    |
> >   | 6    | END  | Y    |
> >   |------|------|------|
> > 
> > When a user tries to place a breakpoint in file 2 at line 22, this is
> > no longer possible.
> > 
> > The solution I propose here is that we ignore line table entries that
> > would trigger a change of file if:
> > 
> >   1. The new line being added is at the same address as the previous
> >   line, and
> > 
> >   2. We have previously seen an is-stmt line at the current address.
> > 
> > The result of this is that GDB switches file, and knows that some line
> > entry (or entries) are going to be discarded, prefer to keep is-stmt
> > lines and discard non-stmt lines.
> > 
> > After this commit the lines tables are now:
> > 
> >   File 1                   File 2
> >   ------                   ------
> > 
> >   | Addr | Line | Stmt |  | Addr | Line | Stmt |
> >   |------|------|------|  |------|------|------|
> >   | 1    | 16   | Y    |  | 3    | 21   | Y    |
> >   | 2    | 17   | Y    |  | 4    | 22   | Y    |
> >   | 3    | END  | Y    |  | 5    | 23   | N    |
> >   | 5    | END  | Y    |  | 6    | END  | Y    |
> >   | 6    | 24   | Y    |  |------|------|------|
> >   | 7    | END  | Y    |
> >   |------|------|------|
> > 
> > We've lost the non-stmt entry for file 1, line 18, but retained the
> > is-stmt entry for file 2, line 22.  The user can now place a
> > breakpoint at that location.
> > 
> > One problem that came from this commit was the test
> > gdb.cp/step-and-next-inline.exp, which broke in several places.  After
> > looking at this test again I think that in some cases this test was
> > only ever passing by pure luck.  The debug GCC is producing for this
> > test is pretty broken.  I raised this GCC bug:
> > 
> >   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> > 
> > for this and disabled one entire half of the test.  There are still
> > some cases in here that do pass, and if/when GCC is fixed it would be
> > great to enable this test again.
> > 
> > gdb/ChangeLog:
> > 
> > 	* dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
> > 	member variable.
> > 	<m_stmt_at_address>: New member variable.
> > 	(lnp_state_machine::record_line): Don't record some lines, update
> > 	tracking of is_stmt at the same address.
> > 	(lnp_state_machine::lnp_state_machine): Initialise new member
> > 	variables.
> > 
> > gdb/testsuite/ChangeLog:
> > 
> > 	* gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
> > 	use_header case.
> > 	* gdb.dwarf2/dw2-inline-header-1.exp: New file.
> > 	* gdb.dwarf2/dw2-inline-header-2.exp: New file.
> > 	* gdb.dwarf2/dw2-inline-header-3.exp: New file.
> > 	* gdb.dwarf2/dw2-inline-header-lbls.c: New file.
> > 	* gdb.dwarf2/dw2-inline-header.c: New file.
> > 	* gdb.dwarf2/dw2-inline-header.h: New file.
> > ---
> >  gdb/ChangeLog                                     |  10 ++
> >  gdb/dwarf2/read.c                                 |  47 +++++-
> >  gdb/testsuite/ChangeLog                           |  11 ++
> >  gdb/testsuite/gdb.cp/step-and-next-inline.exp     |   7 +
> >  gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp  | 156 ++++++++++++++++++
> >  gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp  | 179 ++++++++++++++++++++
> >  gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp  | 192 ++++++++++++++++++++++
> >  gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c |  46 ++++++
> >  gdb/testsuite/gdb.dwarf2/dw2-inline-header.c      |  24 +++
> >  gdb/testsuite/gdb.dwarf2/dw2-inline-header.h      |  24 +++
> >  10 files changed, 693 insertions(+), 3 deletions(-)
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> > 
> > diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> > index f94c66b4f1b..261c8455424 100644
> > --- a/gdb/dwarf2/read.c
> > +++ b/gdb/dwarf2/read.c
> > @@ -19327,6 +19327,15 @@ class lnp_state_machine
> >    /* The last file a line number was recorded for.  */
> >    struct subfile *m_last_subfile = NULL;
> >  
> > +  /* The address of the last line entry.  */
> > +  CORE_ADDR m_last_address;
> > +
> > +  /* Set to true when a previous line at the same address (using
> > +     m_last_address) had m_is_stmt true.  This is reset to false when a
> > +     line entry at a new address (m_address different to m_last_address) is
> > +     processed.  */
> > +  bool m_stmt_at_address = false;
> > +
> >    /* When true, record the lines we decode.  */
> >    bool m_currently_recording_lines = false;
> >  
> > @@ -19520,14 +19529,34 @@ lnp_state_machine::record_line (bool end_sequence)
> >        fe->included_p = 1;
> >        if (m_record_lines_p)
> >  	{
> > -	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
> > -	      || end_sequence)
> > +	  /* When we switch files we insert an end maker in the first file,
> > +	     switch to the second file and add a new line entry.  The
> > +	     problem is that the end marker inserted in the first file will
> > +	     discard any previous line entries at the same address.  If the
> > +	     line entries in the first file are marked as is-stmt, while
> > +	     the new line in the second file is non-stmt, then this means
> > +	     the end marker will discard is-stmt lines so we can have a
> > +	     non-stmt line.  This means that there are less addresses at
> > +	     which the user can insert a breakpoint.
> > +
> > +	     To improve this we track the last address in m_last_address,
> > +	     and whether we have seen an is-stmt at this address.  Then
> > +	     when switching files, if we have seen a stmt at the current
> > +	     address, and we are switching to create a non-stmt line, then
> > +	     discard the new line.  */
> > +	  bool file_changed
> > +	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
> > +	  bool ignore_this_line
> > +	    = (file_changed && !end_sequence && m_last_address == m_address
> > +	       && !m_is_stmt && m_stmt_at_address);
> > +
> > +	  if ((file_changed && !ignore_this_line) || end_sequence)
> >  	    {
> >  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
> >  				 m_currently_recording_lines ? m_cu : nullptr);
> >  	    }
> >  
> > -	  if (!end_sequence)
> > +	  if (!end_sequence && !ignore_this_line)
> >  	    {
> >  	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
> >  
> > @@ -19546,6 +19575,15 @@ lnp_state_machine::record_line (bool end_sequence)
> >  	    }
> >  	}
> >      }
> > +
> > +  /* Track whether we have seen any m_is_stmt true at m_address in case we
> > +     have multiple line table entries all at m_address.  */
> > +  if (m_last_address != m_address)
> > +    {
> > +      m_stmt_at_address = false;
> > +      m_last_address = m_address;
> > +    }
> > +  m_stmt_at_address |= m_is_stmt;
> >  }
> >  
> >  lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
> > @@ -19565,6 +19603,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
> >    m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
> >    m_is_stmt = lh->default_is_stmt;
> >    m_discriminator = 0;
> > +
> > +  m_last_address = m_address;
> > +  m_stmt_at_addr = false;
> >  }
> >  
> >  void
> > diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> > index 3733fa75570..a95e21194f9 100644
> > --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> > +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> > @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
> >  proc do_test { use_header } {
> >      global srcfile testfile
> >  
> > +    if { $use_header } {
> > +	# This test will not pass due to poor debug information
> > +	# generated by GCC (at least upto 10.x).  See
> > +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> > +	return
> > +    }
> > +
> >      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
> >      if { $use_header } {
> >  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> > new file mode 100644
> > index 00000000000..6a1e990002c
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> > @@ -0,0 +1,156 @@
> > +# Copyright 2020 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/>.
> > +
> > +# Setup a line table where:
> > +#
> > +# | Addr | File | Line | Stmt |
> > +# |------|------|------|------|
> > +# | 1    | 1    | 16   | Y    |
> > +# | 2    | 1    | 17   | Y    |
> > +# | 3    | 2    | 21   | Y    |
> > +# | 4    | 2    | 22   | Y    |
> > +# | 4    | 1    | 18   | N    |
> > +# | 5    | 2    | 23   | N    |
> > +# | 6    | 1    | 24   | Y    |
> > +# | 7    | 1    | END  | Y    |
> > +# |------|------|------|------|
> > +#
> > +# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
> > +# the line table entry for this line due to switching files for the
> > +# file 1, line 18 non-statement line.  After patching however, GDB now
> > +# discards the file 1, line 18 entry instead, and the breakpoint at
> > +# line 22 should succeed.
> > +
> > +load_lib dwarf.exp
> > +
> > +# This test can only be run on targets which support DWARF-2 and use gas.
> > +if {![dwarf2_support]} {
> > +    return 0
> > +}
> > +
> > +# The .c files use __attribute__.
> > +if [get_compiler_info] {
> > +    return -1
> > +}
> > +if !$gcc_compiled {
> > +    return 0
> > +}
> > +
> > +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> > +    dw2-inline-header.c dw2-inline-header.h
> > +
> > +set asm_file [standard_output_file $srcfile2]
> > +Dwarf::assemble $asm_file {
> > +    global srcdir subdir srcfile srcfile3 srcfile4
> > +    declare_labels lines_label callee_subprog_label
> > +
> > +    get_func_info main
> > +
> > +    cu {} {
> > +	compile_unit {
> > +	    {producer "gcc" }
> > +	    {language @DW_LANG_C}
> > +	    {name ${srcfile3}}
> > +	    {low_pc 0 addr}
> > +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> > +	} {
> > +	    callee_subprog_label: subprogram {
> > +		{external 1 flag}
> > +		{name callee}
> > +		{inline 3 data1}
> > +	    }
> > +	    subprogram {
> > +		{external 1 flag}
> > +		{name main}
> > +		{low_pc $main_start addr}
> > +		{high_pc "$main_start + $main_len" addr}
> > +	    } {
> > +		inlined_subroutine {
> > +		    {abstract_origin %$callee_subprog_label}
> > +		    {low_pc line_label_1 addr}
> > +		    {high_pc line_label_7 addr}
> > +		    {call_file 1 data1}
> > +		    {call_line 18 data1}
> > +		}
> > +	    }
> > +	}
> > +    }
> > +
> > +    lines {version 2 default_is_stmt 1} lines_label {
> > +	include_dir "${srcdir}/${subdir}"
> > +	file_name "$srcfile3" 1
> > +	file_name "$srcfile4" 1
> > +
> > +	program {
> > +	    {DW_LNE_set_address line_label_1}
> > +	    {DW_LNS_advance_line 15}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_2}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_set_file 2}
> > +	    {DW_LNE_set_address line_label_3}
> > +	    {DW_LNS_advance_line 4}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_4}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_advance_line -4}
> > +	    {DW_LNS_set_file 1}
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_set_file 2}
> > +	    {DW_LNE_set_address line_label_5}
> > +	    {DW_LNS_advance_line 5}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_set_file 1}
> > +	    {DW_LNE_set_address line_label_6}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_7}
> > +	    {DW_LNE_end_sequence}
> > +	}
> > +    }
> > +}
> > +
> > +if { [prepare_for_testing "failed to prepare" ${testfile} \
> > +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> > +    return -1
> > +}
> > +
> > +if ![runto_main] {
> > +    return -1
> > +}
> > +
> > +# Delete all breakpoints so that the output of "info breakpoints"
> > +# below will only contain a single breakpoint.
> > +delete_breakpoints
> > +
> > +# Place a breakpoint within the function in the header file.
> > +gdb_breakpoint "${srcfile4}:22"
> > +
> > +# Check that the breakpoint was placed where we expected.  It should
> > +# appear at the requested line.  When the bug in GDB was present the
> > +# breakpoint would be placed on one of the following lines instead.
> > +gdb_test "info breakpoints" \
> > +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> > new file mode 100644
> > index 00000000000..46499919a8b
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> > @@ -0,0 +1,179 @@
> > +# Copyright 2020 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/>.
> > +
> > +# Setup a line table where:
> > +#
> > +# | Addr | File | Line | Stmt |
> > +# |------|------|------|------|
> > +# | 1    | 1    | 16   | Y    |
> > +# | 2    | 1    | 17   | Y    |
> > +# | 3    | 2    | 21   | Y    |
> > +# | 4    | 2    | 22   | Y    |
> > +# | 4    | 1    | 18   | N    |
> > +# | 5    | 1    | 19   | Y    |
> > +# | 6    | 1    | 20   | Y    |
> > +# | 7    | 1    | END  | Y    |
> > +# |------|------|------|------|
> > +#
> > +#
> > +# Place the first brekpoint at file 2, line 22 and a second breakpoint
> > +# at file 1, line 19.  A third breakpoint is placed at file 1, line
> > +# 18, but as this line table entry will have been discarded[1] the
> > +# third breakpoint will actually be placed at the same location as the
> > +# second breakpoint.
> > +#
> > +# [1] The entry for file 1, line 18 is discarded because it is at the
> > +# same address as the previous entry, but the previous entry is-stmt,
> > +# while line 18 is a non-stmt.
> > +
> > +load_lib dwarf.exp
> > +
> > +# This test can only be run on targets which support DWARF-2 and use gas.
> > +if {![dwarf2_support]} {
> > +    return 0
> > +}
> > +
> > +# The .c files use __attribute__.
> > +if [get_compiler_info] {
> > +    return -1
> > +}
> > +if !$gcc_compiled {
> > +    return 0
> > +}
> > +
> > +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> > +    dw2-inline-header.c dw2-inline-header.h
> > +
> > +set asm_file [standard_output_file $srcfile2]
> > +Dwarf::assemble $asm_file {
> > +    global srcdir subdir srcfile srcfile3 srcfile4
> > +    declare_labels lines_label callee_subprog_label
> > +
> > +    get_func_info main
> > +
> > +    cu {} {
> > +	compile_unit {
> > +	    {producer "gcc" }
> > +	    {language @DW_LANG_C}
> > +	    {name ${srcfile3}}
> > +	    {low_pc 0 addr}
> > +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> > +	} {
> > +	    callee_subprog_label: subprogram {
> > +		{external 1 flag}
> > +		{name callee}
> > +		{inline 3 data1}
> > +	    }
> > +	    subprogram {
> > +		{external 1 flag}
> > +		{name main}
> > +		{low_pc $main_start addr}
> > +		{high_pc "$main_start + $main_len" addr}
> > +	    } {
> > +		inlined_subroutine {
> > +		    {abstract_origin %$callee_subprog_label}
> > +		    {low_pc line_label_1 addr}
> > +		    {high_pc line_label_7 addr}
> > +		    {call_file 1 data1}
> > +		    {call_line 18 data1}
> > +		}
> > +	    }
> > +	}
> > +    }
> > +
> > +    lines {version 2 default_is_stmt 1} lines_label {
> > +	include_dir "${srcdir}/${subdir}"
> > +	file_name "$srcfile3" 1
> > +	file_name "$srcfile4" 1
> > +
> > +	program {
> > +	    {DW_LNE_set_address line_label_1}
> > +	    {DW_LNS_advance_line 15}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_2}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_set_file 2}
> > +	    {DW_LNE_set_address line_label_3}
> > +	    {DW_LNS_advance_line 4}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_4}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_advance_line -4}
> > +	    {DW_LNS_set_file 1}
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_5}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_6}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_7}
> > +	    {DW_LNE_end_sequence}
> > +	}
> > +    }
> > +}
> > +
> > +if { [prepare_for_testing "failed to prepare" ${testfile} \
> > +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> > +    return -1
> > +}
> > +
> > +if ![runto_main] {
> > +    return -1
> > +}
> > +
> > +# Delete all breakpoints so that the output of "info breakpoints"
> > +# below will only contain a single breakpoint.
> > +delete_breakpoints
> > +
> > +# Place a breakpoint within the function in the header file.
> > +gdb_breakpoint "${srcfile4}:22"
> > +
> > +# Check that the breakpoint was placed where we expected.  It should
> > +# appear at the requested line.  When the bug in GDB was present the
> > +# breakpoint would be placed on one of the following lines instead.
> > +gdb_test "info breakpoints" \
> > +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
> > +    "check for breakpoint at ${srcfile4}"
> > +
> > +# Delete all breakpoints so that the output of "info breakpoints"
> > +# below will only contain a single breakpoint.
> > +delete_breakpoints
> > +
> > +# Place a breakpoint within the function in the header file.
> > +gdb_breakpoint "${srcfile3}:19"
> > +
> > +# Check that the breakpoint was placed where we expected.  It should
> > +# appear at the requested line.  When the bug in GDB was present the
> > +# breakpoint would be placed on one of the following lines instead.
> > +gdb_test "info breakpoints" \
> > +    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
> > +    "check for breakpoint at ${srcfile3}"
> > +
> > +# Line table entry for line 18 will have been discarded, so this
> > +# brekpoint will be at the same location as line 19.
> > +gdb_test "break ${srcfile3}:18" \
> > +    "Note: breakpoint $decimal also set at pc $hex.*"
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> > new file mode 100644
> > index 00000000000..c683dc4bb8a
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> > @@ -0,0 +1,192 @@
> > +# Copyright 2020 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/>.
> > +
> > +# Setup a line table where:
> > +#
> > +# | Addr | File | Line | Stmt |
> > +# |------|------|------|------|
> > +# | 1    | 1    | 16   | Y    |
> > +# | 2    | 1    | 17   | Y    |
> > +# | 3    | 2    | 21   | Y    |
> > +# | 4    | 2    | 22   | Y    |
> > +# | 4    | 1    | 18   | N    |
> > +# | 5    | 1    | 19   | N    |
> > +# | 6    | 1    | 20   | Y    |
> > +# | 7    | 1    | END  | Y    |
> > +# |------|------|------|------|
> > +#
> > +# Break at file 2, line 22, then single instruction step forward.  We
> > +# should pass through line 19 and then encounter line 20.
> > +#
> > +# Currently we don't expect GDB to see file 1, line 18, as this is a
> > +# non-stmt line in a different file at the same address as the
> > +# previous is-stmt line.
> > +
> > +load_lib dwarf.exp
> > +
> > +# This test can only be run on targets which support DWARF-2 and use gas.
> > +if {![dwarf2_support]} {
> > +    return 0
> > +}
> > +
> > +# The .c files use __attribute__.
> > +if [get_compiler_info] {
> > +    return -1
> > +}
> > +if !$gcc_compiled {
> > +    return 0
> > +}
> > +
> > +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> > +    dw2-inline-header.c dw2-inline-header.h
> > +
> > +set asm_file [standard_output_file $srcfile2]
> > +Dwarf::assemble $asm_file {
> > +    global srcdir subdir srcfile srcfile3 srcfile4
> > +    declare_labels lines_label callee_subprog_label
> > +
> > +    get_func_info main
> > +
> > +    cu {} {
> > +	compile_unit {
> > +	    {producer "gcc" }
> > +	    {language @DW_LANG_C}
> > +	    {name ${srcfile3}}
> > +	    {low_pc 0 addr}
> > +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> > +	} {
> > +	    callee_subprog_label: subprogram {
> > +		{external 1 flag}
> > +		{name callee}
> > +		{inline 3 data1}
> > +	    }
> > +	    subprogram {
> > +		{external 1 flag}
> > +		{name main}
> > +		{low_pc $main_start addr}
> > +		{high_pc "$main_start + $main_len" addr}
> > +	    } {
> > +		inlined_subroutine {
> > +		    {abstract_origin %$callee_subprog_label}
> > +		    {low_pc line_label_1 addr}
> > +		    {high_pc line_label_7 addr}
> > +		    {call_file 1 data1}
> > +		    {call_line 18 data1}
> > +		}
> > +	    }
> > +	}
> > +    }
> > +
> > +    lines {version 2 default_is_stmt 1} lines_label {
> > +	include_dir "${srcdir}/${subdir}"
> > +	file_name "$srcfile3" 1
> > +	file_name "$srcfile4" 1
> > +
> > +	program {
> > +	    {DW_LNE_set_address line_label_1}
> > +	    {DW_LNS_advance_line 15}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_2}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_set_file 2}
> > +	    {DW_LNE_set_address line_label_3}
> > +	    {DW_LNS_advance_line 4}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_4}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_advance_line -4}
> > +	    {DW_LNS_set_file 1}
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_5}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_6}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_7}
> > +	    {DW_LNE_end_sequence}
> > +	}
> > +    }
> > +}
> > +
> > +if { [prepare_for_testing "failed to prepare" ${testfile} \
> > +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> > +    return -1
> > +}
> > +
> > +if ![runto_main] {
> > +    return -1
> > +}
> > +
> > +# Delete all breakpoints so that the output of "info breakpoints"
> > +# below will only contain a single breakpoint.
> > +delete_breakpoints
> > +
> > +# Place a breakpoint within the function in the header file.
> > +gdb_breakpoint "${srcfile4}:22"
> > +
> > +# Check that the breakpoint was placed where we expected.  It should
> > +# appear at the requested line.  When the bug in GDB was present the
> > +# breakpoint would be placed on one of the following lines instead.
> > +gdb_test "info breakpoints" \
> > +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> > +
> > +gdb_continue_to_breakpoint "${srcfile4}:22" \
> > +    ".* ${srcfile4} : 22 .*"
> > +
> > +# Now single instruction step forward.  Eventually we should hit
> > +# ${srcfile3}:20, but before we do we should hit the non-statement
> > +# line ${srcfile3}:19.
> > +#
> > +# We don't know how many instructions we'll need to step, but 100
> > +# should be enough for everyone (surely), and this stops us looping
> > +# forever if something goes wrong.
> > +set found_line_19 0
> > +set found_line_20 0
> > +set keep_going 1
> > +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
> > +    set keep_going 0
> > +    gdb_test_multiple "stepi" "stepi ${i}" {
> > +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
> > +	    set found_line_19 1
> > +	    set keep_going 1
> > +	}
> > +
> > +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
> > +	    set found_line_20 1
> > +	}
> > +
> > +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
> > +	    # Not left line 22 yet.
> > +	    set keep_going 1
> > +	}
> > +    }
> > +}
> > +
> > +gdb_assert { $found_line_19 && $found_line_20 } \
> > +    "found line 19 and 20"
> > +
> > +
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> > new file mode 100644
> > index 00000000000..a1b7b17cbeb
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> > @@ -0,0 +1,46 @@
> > +/* Copyright 2020 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/>.  */
> > +
> > +/* Used to insert labels with which we can build a fake line table.  */
> > +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
> > +
> > +volatile int var;
> > +volatile int bar;
> > +
> > +/* Generate some code to take up some space.  */
> > +#define FILLER do { \
> > +    var = 99;	    \
> > +} while (0)
> > +
> > +int
> > +main ()
> > +{					/* main prologue */
> > +  asm ("main_label: .globl main_label");
> > +  LL (1);	// F1, Ln 16
> > +  FILLER;
> > +  LL (2);	// F1, Ln 17
> > +  FILLER;
> > +  LL (3);	// F2, Ln 21
> > +  FILLER;
> > +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
> > +  FILLER;
> > +  LL (5);	// F1, Ln 19 !S
> > +  FILLER;
> > +  LL (6);	// F1, Ln 20
> > +  FILLER;
> > +  LL (7);
> > +  FILLER;
> > +  return 0;				/* main end */
> > +}
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> > new file mode 100644
> > index 00000000000..a8331268a09
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> > @@ -0,0 +1,24 @@
> > +/* Copyright 2020 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/>.  */
> > +
> > +/* dw2-inline-header.c : 16 */
> > +/* dw2-inline-header.c : 17 */
> > +/* dw2-inline-header.c : 18 */
> > +/* dw2-inline-header.c : 19 */
> > +/* dw2-inline-header.c : 20 */
> > +/* dw2-inline-header.c : 21 */
> > +/* dw2-inline-header.c : 22 */
> > +/* dw2-inline-header.c : 23 */
> > +/* dw2-inline-header.c : 24 */
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> > new file mode 100644
> > index 00000000000..7233acbcd76
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> > @@ -0,0 +1,24 @@
> > +/* Copyright 2020 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/>.  */
> > +
> > +/* dw2-inline-header.h : 16 */
> > +/* dw2-inline-header.h : 17 */
> > +/* dw2-inline-header.h : 18 */
> > +/* dw2-inline-header.h : 19 */
> > +/* dw2-inline-header.h : 20 */
> > +/* dw2-inline-header.h : 21 */
> > +/* dw2-inline-header.h : 22 */
> > +/* dw2-inline-header.h : 23 */
> > +/* dw2-inline-header.h : 24 */
> > 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-04 22:23                 ` Andrew Burgess
@ 2020-04-05  0:04                   ` Bernd Edlinger
  2020-04-05  0:47                   ` Bernd Edlinger
  2020-04-05  8:55                   ` Bernd Edlinger
  2 siblings, 0 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-04-05  0:04 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey



On 4/5/20 12:23 AM, Andrew Burgess wrote:
> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-04-04 20:07:52 +0200]:
> 
>> Sorry, Andrew,
>>
>> please hold this one.
>>
>> This will definitely break all the step over inline
>> stuff that was already working.
> 
> By all I assume you only mean gdb.cp/step-and-next-inline.exp, which I
> specifically talk about in the commit message.
> 
> The DWARF produced in that test is horribly broken, I was tempted to
> drop the whole test, but a few parts do still work, and hopefully if
> GCC gets fixed we'll be back in business.
> 

DWARF is broken by design, I said that already.

But I think there is no reason why the test case cannot
work.

Maybe I overlooked something, but that is what I think
in the moment.

> As I mentioned in the GCC bug report, if I hack GDB to "fix" the DWARF
> produced by GCC, then that test passes fine with no regressions with
> this patch as is.
> 

In the moment, my approach is leave GCC as is, and teach
GDB to make as much sense as possible out of the DWARF info
as it is, we have a lot of backward compatibility to maintain.

Even if the DWARF is eventually fixed it must be a new dwarf
revision v6 or so.


Bernd.

> All other tests still pass with this patch - admittedly there's not
> much testing the inline behaviour, but there are a few.
> 
> If you know of a case with valid DWARF that is broken by this test
> then I'm all ears.  With the setup I used for the tests in this patch
> it's actually pretty easy to synthesis test of inline functions, so
> all you really need to do is describe a test you think will break and
> I can knock up a test case for it reasonably quickly.
> 
>>
>> Can we please go to the drawing board before we continue
>> from here?
> 
> Sure.  I posted my thoughts just now.
> 
> Thanks,
> Andrew
> 
>>
>>
>> Thanks
>> Bernd.
>>
>> On 4/4/20 12:21 AM, Andrew Burgess wrote:
>>> After the is-stmt support commit:
>>>
>>>   commit 8c95582da858ac981f689a6f599acacb8c5c490f
>>>   Date:   Mon Dec 30 21:04:51 2019 +0000
>>>
>>>       gdb: Add support for tracking the DWARF line table is-stmt field
>>>
>>> A regression was observed where a breakpoint could no longer be placed
>>> in some cases.
>>>
>>> Consider a line table like this:
>>>
>>>   File 1: test.c
>>>   File 2: test.h
>>>
>>>   | Addr | File | Line | Stmt |
>>>   |------|------|------|------|
>>>   | 1    | 1    | 16   | Y    |
>>>   | 2    | 1    | 17   | Y    |
>>>   | 3    | 2    | 21   | Y    |
>>>   | 4    | 2    | 22   | Y    |
>>>   | 4    | 1    | 18   | N    |
>>>   | 5    | 2    | 23   | N    |
>>>   | 6    | 1    | 24   | Y    |
>>>   | 7    | 1    | END  | Y    |
>>>   |------|------|------|------|
>>>
>>> Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
>>> built two line table structures:
>>>
>>>   File 1                 File 2
>>>   ------                 ------
>>>
>>>   | Addr | Line |        | Addr | Line |
>>>   |------|------|        |------|------|
>>>   | 1    | 16   |        | 3    | 21   |
>>>   | 2    | 17   |        | 4    | 22   |
>>>   | 3    | END  |        | 6    | END  |
>>>   | 6    | 24   |        |------|------|
>>>   | 7    | END  |
>>>   |------|------|
>>>
>>> After the is-stmt patch GDB now records non-stmt lines, so the
>>> generated line table structures look like this:
>>>
>>>   File 1                   File 2
>>>   ------                   ------
>>>
>>>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>>>   |------|------|------|  |------|------|------|
>>>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>>>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>>>   | 3    | END  | Y    |  | 4    | END  | Y    |
>>>   | 4    | 18   | N    |  | 5    | 23   | N    |
>>>   | 5    | END  | Y    |  | 6    | END  | Y    |
>>>   | 6    | 24   | Y    |  |------|------|------|
>>>   | 7    | END  | Y    |
>>>   |------|------|------|
>>>
>>> The problem is that in 'File 2', end END marker at address 4 causes
>>> the previous line table entry to be discarded, so we actually end up
>>> with this:
>>>
>>>   File 2
>>>   ------
>>>
>>>   | Addr | Line | Stmt |
>>>   |------|------|------|
>>>   | 3    | 21   | Y    |
>>>   | 4    | END  | Y    |
>>>   | 5    | 23   | N    |
>>>   | 6    | END  | Y    |
>>>   |------|------|------|
>>>
>>> When a user tries to place a breakpoint in file 2 at line 22, this is
>>> no longer possible.
>>>
>>> The solution I propose here is that we ignore line table entries that
>>> would trigger a change of file if:
>>>
>>>   1. The new line being added is at the same address as the previous
>>>   line, and
>>>
>>>   2. We have previously seen an is-stmt line at the current address.
>>>
>>> The result of this is that GDB switches file, and knows that some line
>>> entry (or entries) are going to be discarded, prefer to keep is-stmt
>>> lines and discard non-stmt lines.
>>>
>>> After this commit the lines tables are now:
>>>
>>>   File 1                   File 2
>>>   ------                   ------
>>>
>>>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>>>   |------|------|------|  |------|------|------|
>>>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>>>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>>>   | 3    | END  | Y    |  | 5    | 23   | N    |
>>>   | 5    | END  | Y    |  | 6    | END  | Y    |
>>>   | 6    | 24   | Y    |  |------|------|------|
>>>   | 7    | END  | Y    |
>>>   |------|------|------|
>>>
>>> We've lost the non-stmt entry for file 1, line 18, but retained the
>>> is-stmt entry for file 2, line 22.  The user can now place a
>>> breakpoint at that location.
>>>
>>> One problem that came from this commit was the test
>>> gdb.cp/step-and-next-inline.exp, which broke in several places.  After
>>> looking at this test again I think that in some cases this test was
>>> only ever passing by pure luck.  The debug GCC is producing for this
>>> test is pretty broken.  I raised this GCC bug:
>>>
>>>   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>>>
>>> for this and disabled one entire half of the test.  There are still
>>> some cases in here that do pass, and if/when GCC is fixed it would be
>>> great to enable this test again.
>>>
>>> gdb/ChangeLog:
>>>
>>> 	* dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
>>> 	member variable.
>>> 	<m_stmt_at_address>: New member variable.
>>> 	(lnp_state_machine::record_line): Don't record some lines, update
>>> 	tracking of is_stmt at the same address.
>>> 	(lnp_state_machine::lnp_state_machine): Initialise new member
>>> 	variables.
>>>
>>> gdb/testsuite/ChangeLog:
>>>
>>> 	* gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
>>> 	use_header case.
>>> 	* gdb.dwarf2/dw2-inline-header-1.exp: New file.
>>> 	* gdb.dwarf2/dw2-inline-header-2.exp: New file.
>>> 	* gdb.dwarf2/dw2-inline-header-3.exp: New file.
>>> 	* gdb.dwarf2/dw2-inline-header-lbls.c: New file.
>>> 	* gdb.dwarf2/dw2-inline-header.c: New file.
>>> 	* gdb.dwarf2/dw2-inline-header.h: New file.
>>> ---
>>>  gdb/ChangeLog                                     |  10 ++
>>>  gdb/dwarf2/read.c                                 |  47 +++++-
>>>  gdb/testsuite/ChangeLog                           |  11 ++
>>>  gdb/testsuite/gdb.cp/step-and-next-inline.exp     |   7 +
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp  | 156 ++++++++++++++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp  | 179 ++++++++++++++++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp  | 192 ++++++++++++++++++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c |  46 ++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.c      |  24 +++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.h      |  24 +++
>>>  10 files changed, 693 insertions(+), 3 deletions(-)
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>>>
>>> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
>>> index f94c66b4f1b..261c8455424 100644
>>> --- a/gdb/dwarf2/read.c
>>> +++ b/gdb/dwarf2/read.c
>>> @@ -19327,6 +19327,15 @@ class lnp_state_machine
>>>    /* The last file a line number was recorded for.  */
>>>    struct subfile *m_last_subfile = NULL;
>>>  
>>> +  /* The address of the last line entry.  */
>>> +  CORE_ADDR m_last_address;
>>> +
>>> +  /* Set to true when a previous line at the same address (using
>>> +     m_last_address) had m_is_stmt true.  This is reset to false when a
>>> +     line entry at a new address (m_address different to m_last_address) is
>>> +     processed.  */
>>> +  bool m_stmt_at_address = false;
>>> +
>>>    /* When true, record the lines we decode.  */
>>>    bool m_currently_recording_lines = false;
>>>  
>>> @@ -19520,14 +19529,34 @@ lnp_state_machine::record_line (bool end_sequence)
>>>        fe->included_p = 1;
>>>        if (m_record_lines_p)
>>>  	{
>>> -	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
>>> -	      || end_sequence)
>>> +	  /* When we switch files we insert an end maker in the first file,
>>> +	     switch to the second file and add a new line entry.  The
>>> +	     problem is that the end marker inserted in the first file will
>>> +	     discard any previous line entries at the same address.  If the
>>> +	     line entries in the first file are marked as is-stmt, while
>>> +	     the new line in the second file is non-stmt, then this means
>>> +	     the end marker will discard is-stmt lines so we can have a
>>> +	     non-stmt line.  This means that there are less addresses at
>>> +	     which the user can insert a breakpoint.
>>> +
>>> +	     To improve this we track the last address in m_last_address,
>>> +	     and whether we have seen an is-stmt at this address.  Then
>>> +	     when switching files, if we have seen a stmt at the current
>>> +	     address, and we are switching to create a non-stmt line, then
>>> +	     discard the new line.  */
>>> +	  bool file_changed
>>> +	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
>>> +	  bool ignore_this_line
>>> +	    = (file_changed && !end_sequence && m_last_address == m_address
>>> +	       && !m_is_stmt && m_stmt_at_address);
>>> +
>>> +	  if ((file_changed && !ignore_this_line) || end_sequence)
>>>  	    {
>>>  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
>>>  				 m_currently_recording_lines ? m_cu : nullptr);
>>>  	    }
>>>  
>>> -	  if (!end_sequence)
>>> +	  if (!end_sequence && !ignore_this_line)
>>>  	    {
>>>  	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
>>>  
>>> @@ -19546,6 +19575,15 @@ lnp_state_machine::record_line (bool end_sequence)
>>>  	    }
>>>  	}
>>>      }
>>> +
>>> +  /* Track whether we have seen any m_is_stmt true at m_address in case we
>>> +     have multiple line table entries all at m_address.  */
>>> +  if (m_last_address != m_address)
>>> +    {
>>> +      m_stmt_at_address = false;
>>> +      m_last_address = m_address;
>>> +    }
>>> +  m_stmt_at_address |= m_is_stmt;
>>>  }
>>>  
>>>  lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>>> @@ -19565,6 +19603,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>>>    m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
>>>    m_is_stmt = lh->default_is_stmt;
>>>    m_discriminator = 0;
>>> +
>>> +  m_last_address = m_address;
>>> +  m_stmt_at_addr = false;
>>>  }
>>>  
>>>  void
>>> diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>>> index 3733fa75570..a95e21194f9 100644
>>> --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>>> +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>>> @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
>>>  proc do_test { use_header } {
>>>      global srcfile testfile
>>>  
>>> +    if { $use_header } {
>>> +	# This test will not pass due to poor debug information
>>> +	# generated by GCC (at least upto 10.x).  See
>>> +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>>> +	return
>>> +    }
>>> +
>>>      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
>>>      if { $use_header } {
>>>  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>>> new file mode 100644
>>> index 00000000000..6a1e990002c
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>>> @@ -0,0 +1,156 @@
>>> +# Copyright 2020 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/>.
>>> +
>>> +# Setup a line table where:
>>> +#
>>> +# | Addr | File | Line | Stmt |
>>> +# |------|------|------|------|
>>> +# | 1    | 1    | 16   | Y    |
>>> +# | 2    | 1    | 17   | Y    |
>>> +# | 3    | 2    | 21   | Y    |
>>> +# | 4    | 2    | 22   | Y    |
>>> +# | 4    | 1    | 18   | N    |
>>> +# | 5    | 2    | 23   | N    |
>>> +# | 6    | 1    | 24   | Y    |
>>> +# | 7    | 1    | END  | Y    |
>>> +# |------|------|------|------|
>>> +#
>>> +# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
>>> +# the line table entry for this line due to switching files for the
>>> +# file 1, line 18 non-statement line.  After patching however, GDB now
>>> +# discards the file 1, line 18 entry instead, and the breakpoint at
>>> +# line 22 should succeed.
>>> +
>>> +load_lib dwarf.exp
>>> +
>>> +# This test can only be run on targets which support DWARF-2 and use gas.
>>> +if {![dwarf2_support]} {
>>> +    return 0
>>> +}
>>> +
>>> +# The .c files use __attribute__.
>>> +if [get_compiler_info] {
>>> +    return -1
>>> +}
>>> +if !$gcc_compiled {
>>> +    return 0
>>> +}
>>> +
>>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>>> +    dw2-inline-header.c dw2-inline-header.h
>>> +
>>> +set asm_file [standard_output_file $srcfile2]
>>> +Dwarf::assemble $asm_file {
>>> +    global srcdir subdir srcfile srcfile3 srcfile4
>>> +    declare_labels lines_label callee_subprog_label
>>> +
>>> +    get_func_info main
>>> +
>>> +    cu {} {
>>> +	compile_unit {
>>> +	    {producer "gcc" }
>>> +	    {language @DW_LANG_C}
>>> +	    {name ${srcfile3}}
>>> +	    {low_pc 0 addr}
>>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>>> +	} {
>>> +	    callee_subprog_label: subprogram {
>>> +		{external 1 flag}
>>> +		{name callee}
>>> +		{inline 3 data1}
>>> +	    }
>>> +	    subprogram {
>>> +		{external 1 flag}
>>> +		{name main}
>>> +		{low_pc $main_start addr}
>>> +		{high_pc "$main_start + $main_len" addr}
>>> +	    } {
>>> +		inlined_subroutine {
>>> +		    {abstract_origin %$callee_subprog_label}
>>> +		    {low_pc line_label_1 addr}
>>> +		    {high_pc line_label_7 addr}
>>> +		    {call_file 1 data1}
>>> +		    {call_line 18 data1}
>>> +		}
>>> +	    }
>>> +	}
>>> +    }
>>> +
>>> +    lines {version 2 default_is_stmt 1} lines_label {
>>> +	include_dir "${srcdir}/${subdir}"
>>> +	file_name "$srcfile3" 1
>>> +	file_name "$srcfile4" 1
>>> +
>>> +	program {
>>> +	    {DW_LNE_set_address line_label_1}
>>> +	    {DW_LNS_advance_line 15}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_2}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_3}
>>> +	    {DW_LNS_advance_line 4}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_4}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_advance_line -4}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_5}
>>> +	    {DW_LNS_advance_line 5}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNE_set_address line_label_6}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_7}
>>> +	    {DW_LNE_end_sequence}
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>>> +    return -1
>>> +}
>>> +
>>> +if ![runto_main] {
>>> +    return -1
>>> +}
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile4}:22"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>>> new file mode 100644
>>> index 00000000000..46499919a8b
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>>> @@ -0,0 +1,179 @@
>>> +# Copyright 2020 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/>.
>>> +
>>> +# Setup a line table where:
>>> +#
>>> +# | Addr | File | Line | Stmt |
>>> +# |------|------|------|------|
>>> +# | 1    | 1    | 16   | Y    |
>>> +# | 2    | 1    | 17   | Y    |
>>> +# | 3    | 2    | 21   | Y    |
>>> +# | 4    | 2    | 22   | Y    |
>>> +# | 4    | 1    | 18   | N    |
>>> +# | 5    | 1    | 19   | Y    |
>>> +# | 6    | 1    | 20   | Y    |
>>> +# | 7    | 1    | END  | Y    |
>>> +# |------|------|------|------|
>>> +#
>>> +#
>>> +# Place the first brekpoint at file 2, line 22 and a second breakpoint
>>> +# at file 1, line 19.  A third breakpoint is placed at file 1, line
>>> +# 18, but as this line table entry will have been discarded[1] the
>>> +# third breakpoint will actually be placed at the same location as the
>>> +# second breakpoint.
>>> +#
>>> +# [1] The entry for file 1, line 18 is discarded because it is at the
>>> +# same address as the previous entry, but the previous entry is-stmt,
>>> +# while line 18 is a non-stmt.
>>> +
>>> +load_lib dwarf.exp
>>> +
>>> +# This test can only be run on targets which support DWARF-2 and use gas.
>>> +if {![dwarf2_support]} {
>>> +    return 0
>>> +}
>>> +
>>> +# The .c files use __attribute__.
>>> +if [get_compiler_info] {
>>> +    return -1
>>> +}
>>> +if !$gcc_compiled {
>>> +    return 0
>>> +}
>>> +
>>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>>> +    dw2-inline-header.c dw2-inline-header.h
>>> +
>>> +set asm_file [standard_output_file $srcfile2]
>>> +Dwarf::assemble $asm_file {
>>> +    global srcdir subdir srcfile srcfile3 srcfile4
>>> +    declare_labels lines_label callee_subprog_label
>>> +
>>> +    get_func_info main
>>> +
>>> +    cu {} {
>>> +	compile_unit {
>>> +	    {producer "gcc" }
>>> +	    {language @DW_LANG_C}
>>> +	    {name ${srcfile3}}
>>> +	    {low_pc 0 addr}
>>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>>> +	} {
>>> +	    callee_subprog_label: subprogram {
>>> +		{external 1 flag}
>>> +		{name callee}
>>> +		{inline 3 data1}
>>> +	    }
>>> +	    subprogram {
>>> +		{external 1 flag}
>>> +		{name main}
>>> +		{low_pc $main_start addr}
>>> +		{high_pc "$main_start + $main_len" addr}
>>> +	    } {
>>> +		inlined_subroutine {
>>> +		    {abstract_origin %$callee_subprog_label}
>>> +		    {low_pc line_label_1 addr}
>>> +		    {high_pc line_label_7 addr}
>>> +		    {call_file 1 data1}
>>> +		    {call_line 18 data1}
>>> +		}
>>> +	    }
>>> +	}
>>> +    }
>>> +
>>> +    lines {version 2 default_is_stmt 1} lines_label {
>>> +	include_dir "${srcdir}/${subdir}"
>>> +	file_name "$srcfile3" 1
>>> +	file_name "$srcfile4" 1
>>> +
>>> +	program {
>>> +	    {DW_LNE_set_address line_label_1}
>>> +	    {DW_LNS_advance_line 15}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_2}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_3}
>>> +	    {DW_LNS_advance_line 4}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_4}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_advance_line -4}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_5}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_6}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_7}
>>> +	    {DW_LNE_end_sequence}
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>>> +    return -1
>>> +}
>>> +
>>> +if ![runto_main] {
>>> +    return -1
>>> +}
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile4}:22"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
>>> +    "check for breakpoint at ${srcfile4}"
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile3}:19"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
>>> +    "check for breakpoint at ${srcfile3}"
>>> +
>>> +# Line table entry for line 18 will have been discarded, so this
>>> +# brekpoint will be at the same location as line 19.
>>> +gdb_test "break ${srcfile3}:18" \
>>> +    "Note: breakpoint $decimal also set at pc $hex.*"
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>>> new file mode 100644
>>> index 00000000000..c683dc4bb8a
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>>> @@ -0,0 +1,192 @@
>>> +# Copyright 2020 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/>.
>>> +
>>> +# Setup a line table where:
>>> +#
>>> +# | Addr | File | Line | Stmt |
>>> +# |------|------|------|------|
>>> +# | 1    | 1    | 16   | Y    |
>>> +# | 2    | 1    | 17   | Y    |
>>> +# | 3    | 2    | 21   | Y    |
>>> +# | 4    | 2    | 22   | Y    |
>>> +# | 4    | 1    | 18   | N    |
>>> +# | 5    | 1    | 19   | N    |
>>> +# | 6    | 1    | 20   | Y    |
>>> +# | 7    | 1    | END  | Y    |
>>> +# |------|------|------|------|
>>> +#
>>> +# Break at file 2, line 22, then single instruction step forward.  We
>>> +# should pass through line 19 and then encounter line 20.
>>> +#
>>> +# Currently we don't expect GDB to see file 1, line 18, as this is a
>>> +# non-stmt line in a different file at the same address as the
>>> +# previous is-stmt line.
>>> +
>>> +load_lib dwarf.exp
>>> +
>>> +# This test can only be run on targets which support DWARF-2 and use gas.
>>> +if {![dwarf2_support]} {
>>> +    return 0
>>> +}
>>> +
>>> +# The .c files use __attribute__.
>>> +if [get_compiler_info] {
>>> +    return -1
>>> +}
>>> +if !$gcc_compiled {
>>> +    return 0
>>> +}
>>> +
>>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>>> +    dw2-inline-header.c dw2-inline-header.h
>>> +
>>> +set asm_file [standard_output_file $srcfile2]
>>> +Dwarf::assemble $asm_file {
>>> +    global srcdir subdir srcfile srcfile3 srcfile4
>>> +    declare_labels lines_label callee_subprog_label
>>> +
>>> +    get_func_info main
>>> +
>>> +    cu {} {
>>> +	compile_unit {
>>> +	    {producer "gcc" }
>>> +	    {language @DW_LANG_C}
>>> +	    {name ${srcfile3}}
>>> +	    {low_pc 0 addr}
>>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>>> +	} {
>>> +	    callee_subprog_label: subprogram {
>>> +		{external 1 flag}
>>> +		{name callee}
>>> +		{inline 3 data1}
>>> +	    }
>>> +	    subprogram {
>>> +		{external 1 flag}
>>> +		{name main}
>>> +		{low_pc $main_start addr}
>>> +		{high_pc "$main_start + $main_len" addr}
>>> +	    } {
>>> +		inlined_subroutine {
>>> +		    {abstract_origin %$callee_subprog_label}
>>> +		    {low_pc line_label_1 addr}
>>> +		    {high_pc line_label_7 addr}
>>> +		    {call_file 1 data1}
>>> +		    {call_line 18 data1}
>>> +		}
>>> +	    }
>>> +	}
>>> +    }
>>> +
>>> +    lines {version 2 default_is_stmt 1} lines_label {
>>> +	include_dir "${srcdir}/${subdir}"
>>> +	file_name "$srcfile3" 1
>>> +	file_name "$srcfile4" 1
>>> +
>>> +	program {
>>> +	    {DW_LNE_set_address line_label_1}
>>> +	    {DW_LNS_advance_line 15}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_2}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_3}
>>> +	    {DW_LNS_advance_line 4}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_4}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_advance_line -4}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_5}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_6}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_7}
>>> +	    {DW_LNE_end_sequence}
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>>> +    return -1
>>> +}
>>> +
>>> +if ![runto_main] {
>>> +    return -1
>>> +}
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile4}:22"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
>>> +
>>> +gdb_continue_to_breakpoint "${srcfile4}:22" \
>>> +    ".* ${srcfile4} : 22 .*"
>>> +
>>> +# Now single instruction step forward.  Eventually we should hit
>>> +# ${srcfile3}:20, but before we do we should hit the non-statement
>>> +# line ${srcfile3}:19.
>>> +#
>>> +# We don't know how many instructions we'll need to step, but 100
>>> +# should be enough for everyone (surely), and this stops us looping
>>> +# forever if something goes wrong.
>>> +set found_line_19 0
>>> +set found_line_20 0
>>> +set keep_going 1
>>> +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
>>> +    set keep_going 0
>>> +    gdb_test_multiple "stepi" "stepi ${i}" {
>>> +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
>>> +	    set found_line_19 1
>>> +	    set keep_going 1
>>> +	}
>>> +
>>> +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
>>> +	    set found_line_20 1
>>> +	}
>>> +
>>> +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
>>> +	    # Not left line 22 yet.
>>> +	    set keep_going 1
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +gdb_assert { $found_line_19 && $found_line_20 } \
>>> +    "found line 19 and 20"
>>> +
>>> +
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>>> new file mode 100644
>>> index 00000000000..a1b7b17cbeb
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>>> @@ -0,0 +1,46 @@
>>> +/* Copyright 2020 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/>.  */
>>> +
>>> +/* Used to insert labels with which we can build a fake line table.  */
>>> +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
>>> +
>>> +volatile int var;
>>> +volatile int bar;
>>> +
>>> +/* Generate some code to take up some space.  */
>>> +#define FILLER do { \
>>> +    var = 99;	    \
>>> +} while (0)
>>> +
>>> +int
>>> +main ()
>>> +{					/* main prologue */
>>> +  asm ("main_label: .globl main_label");
>>> +  LL (1);	// F1, Ln 16
>>> +  FILLER;
>>> +  LL (2);	// F1, Ln 17
>>> +  FILLER;
>>> +  LL (3);	// F2, Ln 21
>>> +  FILLER;
>>> +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
>>> +  FILLER;
>>> +  LL (5);	// F1, Ln 19 !S
>>> +  FILLER;
>>> +  LL (6);	// F1, Ln 20
>>> +  FILLER;
>>> +  LL (7);
>>> +  FILLER;
>>> +  return 0;				/* main end */
>>> +}
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>>> new file mode 100644
>>> index 00000000000..a8331268a09
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>>> @@ -0,0 +1,24 @@
>>> +/* Copyright 2020 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/>.  */
>>> +
>>> +/* dw2-inline-header.c : 16 */
>>> +/* dw2-inline-header.c : 17 */
>>> +/* dw2-inline-header.c : 18 */
>>> +/* dw2-inline-header.c : 19 */
>>> +/* dw2-inline-header.c : 20 */
>>> +/* dw2-inline-header.c : 21 */
>>> +/* dw2-inline-header.c : 22 */
>>> +/* dw2-inline-header.c : 23 */
>>> +/* dw2-inline-header.c : 24 */
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>>> new file mode 100644
>>> index 00000000000..7233acbcd76
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>>> @@ -0,0 +1,24 @@
>>> +/* Copyright 2020 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/>.  */
>>> +
>>> +/* dw2-inline-header.h : 16 */
>>> +/* dw2-inline-header.h : 17 */
>>> +/* dw2-inline-header.h : 18 */
>>> +/* dw2-inline-header.h : 19 */
>>> +/* dw2-inline-header.h : 20 */
>>> +/* dw2-inline-header.h : 21 */
>>> +/* dw2-inline-header.h : 22 */
>>> +/* dw2-inline-header.h : 23 */
>>> +/* dw2-inline-header.h : 24 */
>>>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-04 22:23                 ` Andrew Burgess
  2020-04-05  0:04                   ` Bernd Edlinger
@ 2020-04-05  0:47                   ` Bernd Edlinger
  2020-04-05  8:55                   ` Bernd Edlinger
  2 siblings, 0 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-04-05  0:47 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey



On 4/5/20 12:23 AM, Andrew Burgess wrote:
> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-04-04 20:07:52 +0200]:
> 
>> Sorry, Andrew,
>>
>> please hold this one.
>>
>> This will definitely break all the step over inline
>> stuff that was already working.
> 
> By all I assume you only mean gdb.cp/step-and-next-inline.exp, which I
> specifically talk about in the commit message.
> 
> The DWARF produced in that test is horribly broken, I was tempted to
> drop the whole test, but a few parts do still work, and hopefully if
> GCC gets fixed we'll be back in business.
> 
> As I mentioned in the GCC bug report, if I hack GDB to "fix" the DWARF
> produced by GCC, then that test passes fine with no regressions with
> this patch as is.
> 
> All other tests still pass with this patch - admittedly there's not
> much testing the inline behaviour, but there are a few.
> 
> If you know of a case with valid DWARF that is broken by this test
> then I'm all ears.  With the setup I used for the tests in this patch
> it's actually pretty easy to synthesis test of inline functions, so
> all you really need to do is describe a test you think will break and
> I can knock up a test case for it reasonably quickly.
> 

Yes, I really need a test case with your dwarf magic, that duplicates
the aarch64 test case, which fails on current trunk.
And gets fixed if I am right :-)


Thanks
Bernd.

>>
>> Can we please go to the drawing board before we continue
>> from here?
> 
> Sure.  I posted my thoughts just now.
> 
> Thanks,
> Andrew
> 
>>
>>
>> Thanks
>> Bernd.
>>
>> On 4/4/20 12:21 AM, Andrew Burgess wrote:
>>> After the is-stmt support commit:
>>>
>>>   commit 8c95582da858ac981f689a6f599acacb8c5c490f
>>>   Date:   Mon Dec 30 21:04:51 2019 +0000
>>>
>>>       gdb: Add support for tracking the DWARF line table is-stmt field
>>>
>>> A regression was observed where a breakpoint could no longer be placed
>>> in some cases.
>>>
>>> Consider a line table like this:
>>>
>>>   File 1: test.c
>>>   File 2: test.h
>>>
>>>   | Addr | File | Line | Stmt |
>>>   |------|------|------|------|
>>>   | 1    | 1    | 16   | Y    |
>>>   | 2    | 1    | 17   | Y    |
>>>   | 3    | 2    | 21   | Y    |
>>>   | 4    | 2    | 22   | Y    |
>>>   | 4    | 1    | 18   | N    |
>>>   | 5    | 2    | 23   | N    |
>>>   | 6    | 1    | 24   | Y    |
>>>   | 7    | 1    | END  | Y    |
>>>   |------|------|------|------|
>>>
>>> Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
>>> built two line table structures:
>>>
>>>   File 1                 File 2
>>>   ------                 ------
>>>
>>>   | Addr | Line |        | Addr | Line |
>>>   |------|------|        |------|------|
>>>   | 1    | 16   |        | 3    | 21   |
>>>   | 2    | 17   |        | 4    | 22   |
>>>   | 3    | END  |        | 6    | END  |
>>>   | 6    | 24   |        |------|------|
>>>   | 7    | END  |
>>>   |------|------|
>>>
>>> After the is-stmt patch GDB now records non-stmt lines, so the
>>> generated line table structures look like this:
>>>
>>>   File 1                   File 2
>>>   ------                   ------
>>>
>>>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>>>   |------|------|------|  |------|------|------|
>>>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>>>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>>>   | 3    | END  | Y    |  | 4    | END  | Y    |
>>>   | 4    | 18   | N    |  | 5    | 23   | N    |
>>>   | 5    | END  | Y    |  | 6    | END  | Y    |
>>>   | 6    | 24   | Y    |  |------|------|------|
>>>   | 7    | END  | Y    |
>>>   |------|------|------|
>>>
>>> The problem is that in 'File 2', end END marker at address 4 causes
>>> the previous line table entry to be discarded, so we actually end up
>>> with this:
>>>
>>>   File 2
>>>   ------
>>>
>>>   | Addr | Line | Stmt |
>>>   |------|------|------|
>>>   | 3    | 21   | Y    |
>>>   | 4    | END  | Y    |
>>>   | 5    | 23   | N    |
>>>   | 6    | END  | Y    |
>>>   |------|------|------|
>>>
>>> When a user tries to place a breakpoint in file 2 at line 22, this is
>>> no longer possible.
>>>
>>> The solution I propose here is that we ignore line table entries that
>>> would trigger a change of file if:
>>>
>>>   1. The new line being added is at the same address as the previous
>>>   line, and
>>>
>>>   2. We have previously seen an is-stmt line at the current address.
>>>
>>> The result of this is that GDB switches file, and knows that some line
>>> entry (or entries) are going to be discarded, prefer to keep is-stmt
>>> lines and discard non-stmt lines.
>>>
>>> After this commit the lines tables are now:
>>>
>>>   File 1                   File 2
>>>   ------                   ------
>>>
>>>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>>>   |------|------|------|  |------|------|------|
>>>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>>>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>>>   | 3    | END  | Y    |  | 5    | 23   | N    |
>>>   | 5    | END  | Y    |  | 6    | END  | Y    |
>>>   | 6    | 24   | Y    |  |------|------|------|
>>>   | 7    | END  | Y    |
>>>   |------|------|------|
>>>
>>> We've lost the non-stmt entry for file 1, line 18, but retained the
>>> is-stmt entry for file 2, line 22.  The user can now place a
>>> breakpoint at that location.
>>>
>>> One problem that came from this commit was the test
>>> gdb.cp/step-and-next-inline.exp, which broke in several places.  After
>>> looking at this test again I think that in some cases this test was
>>> only ever passing by pure luck.  The debug GCC is producing for this
>>> test is pretty broken.  I raised this GCC bug:
>>>
>>>   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>>>
>>> for this and disabled one entire half of the test.  There are still
>>> some cases in here that do pass, and if/when GCC is fixed it would be
>>> great to enable this test again.
>>>
>>> gdb/ChangeLog:
>>>
>>> 	* dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
>>> 	member variable.
>>> 	<m_stmt_at_address>: New member variable.
>>> 	(lnp_state_machine::record_line): Don't record some lines, update
>>> 	tracking of is_stmt at the same address.
>>> 	(lnp_state_machine::lnp_state_machine): Initialise new member
>>> 	variables.
>>>
>>> gdb/testsuite/ChangeLog:
>>>
>>> 	* gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
>>> 	use_header case.
>>> 	* gdb.dwarf2/dw2-inline-header-1.exp: New file.
>>> 	* gdb.dwarf2/dw2-inline-header-2.exp: New file.
>>> 	* gdb.dwarf2/dw2-inline-header-3.exp: New file.
>>> 	* gdb.dwarf2/dw2-inline-header-lbls.c: New file.
>>> 	* gdb.dwarf2/dw2-inline-header.c: New file.
>>> 	* gdb.dwarf2/dw2-inline-header.h: New file.
>>> ---
>>>  gdb/ChangeLog                                     |  10 ++
>>>  gdb/dwarf2/read.c                                 |  47 +++++-
>>>  gdb/testsuite/ChangeLog                           |  11 ++
>>>  gdb/testsuite/gdb.cp/step-and-next-inline.exp     |   7 +
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp  | 156 ++++++++++++++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp  | 179 ++++++++++++++++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp  | 192 ++++++++++++++++++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c |  46 ++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.c      |  24 +++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.h      |  24 +++
>>>  10 files changed, 693 insertions(+), 3 deletions(-)
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>>>
>>> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
>>> index f94c66b4f1b..261c8455424 100644
>>> --- a/gdb/dwarf2/read.c
>>> +++ b/gdb/dwarf2/read.c
>>> @@ -19327,6 +19327,15 @@ class lnp_state_machine
>>>    /* The last file a line number was recorded for.  */
>>>    struct subfile *m_last_subfile = NULL;
>>>  
>>> +  /* The address of the last line entry.  */
>>> +  CORE_ADDR m_last_address;
>>> +
>>> +  /* Set to true when a previous line at the same address (using
>>> +     m_last_address) had m_is_stmt true.  This is reset to false when a
>>> +     line entry at a new address (m_address different to m_last_address) is
>>> +     processed.  */
>>> +  bool m_stmt_at_address = false;
>>> +
>>>    /* When true, record the lines we decode.  */
>>>    bool m_currently_recording_lines = false;
>>>  
>>> @@ -19520,14 +19529,34 @@ lnp_state_machine::record_line (bool end_sequence)
>>>        fe->included_p = 1;
>>>        if (m_record_lines_p)
>>>  	{
>>> -	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
>>> -	      || end_sequence)
>>> +	  /* When we switch files we insert an end maker in the first file,
>>> +	     switch to the second file and add a new line entry.  The
>>> +	     problem is that the end marker inserted in the first file will
>>> +	     discard any previous line entries at the same address.  If the
>>> +	     line entries in the first file are marked as is-stmt, while
>>> +	     the new line in the second file is non-stmt, then this means
>>> +	     the end marker will discard is-stmt lines so we can have a
>>> +	     non-stmt line.  This means that there are less addresses at
>>> +	     which the user can insert a breakpoint.
>>> +
>>> +	     To improve this we track the last address in m_last_address,
>>> +	     and whether we have seen an is-stmt at this address.  Then
>>> +	     when switching files, if we have seen a stmt at the current
>>> +	     address, and we are switching to create a non-stmt line, then
>>> +	     discard the new line.  */
>>> +	  bool file_changed
>>> +	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
>>> +	  bool ignore_this_line
>>> +	    = (file_changed && !end_sequence && m_last_address == m_address
>>> +	       && !m_is_stmt && m_stmt_at_address);
>>> +
>>> +	  if ((file_changed && !ignore_this_line) || end_sequence)
>>>  	    {
>>>  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
>>>  				 m_currently_recording_lines ? m_cu : nullptr);
>>>  	    }
>>>  
>>> -	  if (!end_sequence)
>>> +	  if (!end_sequence && !ignore_this_line)
>>>  	    {
>>>  	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
>>>  
>>> @@ -19546,6 +19575,15 @@ lnp_state_machine::record_line (bool end_sequence)
>>>  	    }
>>>  	}
>>>      }
>>> +
>>> +  /* Track whether we have seen any m_is_stmt true at m_address in case we
>>> +     have multiple line table entries all at m_address.  */
>>> +  if (m_last_address != m_address)
>>> +    {
>>> +      m_stmt_at_address = false;
>>> +      m_last_address = m_address;
>>> +    }
>>> +  m_stmt_at_address |= m_is_stmt;
>>>  }
>>>  
>>>  lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>>> @@ -19565,6 +19603,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>>>    m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
>>>    m_is_stmt = lh->default_is_stmt;
>>>    m_discriminator = 0;
>>> +
>>> +  m_last_address = m_address;
>>> +  m_stmt_at_addr = false;
>>>  }
>>>  
>>>  void
>>> diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>>> index 3733fa75570..a95e21194f9 100644
>>> --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>>> +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>>> @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
>>>  proc do_test { use_header } {
>>>      global srcfile testfile
>>>  
>>> +    if { $use_header } {
>>> +	# This test will not pass due to poor debug information
>>> +	# generated by GCC (at least upto 10.x).  See
>>> +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>>> +	return
>>> +    }
>>> +
>>>      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
>>>      if { $use_header } {
>>>  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>>> new file mode 100644
>>> index 00000000000..6a1e990002c
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>>> @@ -0,0 +1,156 @@
>>> +# Copyright 2020 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/>.
>>> +
>>> +# Setup a line table where:
>>> +#
>>> +# | Addr | File | Line | Stmt |
>>> +# |------|------|------|------|
>>> +# | 1    | 1    | 16   | Y    |
>>> +# | 2    | 1    | 17   | Y    |
>>> +# | 3    | 2    | 21   | Y    |
>>> +# | 4    | 2    | 22   | Y    |
>>> +# | 4    | 1    | 18   | N    |
>>> +# | 5    | 2    | 23   | N    |
>>> +# | 6    | 1    | 24   | Y    |
>>> +# | 7    | 1    | END  | Y    |
>>> +# |------|------|------|------|
>>> +#
>>> +# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
>>> +# the line table entry for this line due to switching files for the
>>> +# file 1, line 18 non-statement line.  After patching however, GDB now
>>> +# discards the file 1, line 18 entry instead, and the breakpoint at
>>> +# line 22 should succeed.
>>> +
>>> +load_lib dwarf.exp
>>> +
>>> +# This test can only be run on targets which support DWARF-2 and use gas.
>>> +if {![dwarf2_support]} {
>>> +    return 0
>>> +}
>>> +
>>> +# The .c files use __attribute__.
>>> +if [get_compiler_info] {
>>> +    return -1
>>> +}
>>> +if !$gcc_compiled {
>>> +    return 0
>>> +}
>>> +
>>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>>> +    dw2-inline-header.c dw2-inline-header.h
>>> +
>>> +set asm_file [standard_output_file $srcfile2]
>>> +Dwarf::assemble $asm_file {
>>> +    global srcdir subdir srcfile srcfile3 srcfile4
>>> +    declare_labels lines_label callee_subprog_label
>>> +
>>> +    get_func_info main
>>> +
>>> +    cu {} {
>>> +	compile_unit {
>>> +	    {producer "gcc" }
>>> +	    {language @DW_LANG_C}
>>> +	    {name ${srcfile3}}
>>> +	    {low_pc 0 addr}
>>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>>> +	} {
>>> +	    callee_subprog_label: subprogram {
>>> +		{external 1 flag}
>>> +		{name callee}
>>> +		{inline 3 data1}
>>> +	    }
>>> +	    subprogram {
>>> +		{external 1 flag}
>>> +		{name main}
>>> +		{low_pc $main_start addr}
>>> +		{high_pc "$main_start + $main_len" addr}
>>> +	    } {
>>> +		inlined_subroutine {
>>> +		    {abstract_origin %$callee_subprog_label}
>>> +		    {low_pc line_label_1 addr}
>>> +		    {high_pc line_label_7 addr}
>>> +		    {call_file 1 data1}
>>> +		    {call_line 18 data1}
>>> +		}
>>> +	    }
>>> +	}
>>> +    }
>>> +
>>> +    lines {version 2 default_is_stmt 1} lines_label {
>>> +	include_dir "${srcdir}/${subdir}"
>>> +	file_name "$srcfile3" 1
>>> +	file_name "$srcfile4" 1
>>> +
>>> +	program {
>>> +	    {DW_LNE_set_address line_label_1}
>>> +	    {DW_LNS_advance_line 15}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_2}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_3}
>>> +	    {DW_LNS_advance_line 4}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_4}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_advance_line -4}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_5}
>>> +	    {DW_LNS_advance_line 5}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNE_set_address line_label_6}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_7}
>>> +	    {DW_LNE_end_sequence}
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>>> +    return -1
>>> +}
>>> +
>>> +if ![runto_main] {
>>> +    return -1
>>> +}
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile4}:22"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>>> new file mode 100644
>>> index 00000000000..46499919a8b
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>>> @@ -0,0 +1,179 @@
>>> +# Copyright 2020 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/>.
>>> +
>>> +# Setup a line table where:
>>> +#
>>> +# | Addr | File | Line | Stmt |
>>> +# |------|------|------|------|
>>> +# | 1    | 1    | 16   | Y    |
>>> +# | 2    | 1    | 17   | Y    |
>>> +# | 3    | 2    | 21   | Y    |
>>> +# | 4    | 2    | 22   | Y    |
>>> +# | 4    | 1    | 18   | N    |
>>> +# | 5    | 1    | 19   | Y    |
>>> +# | 6    | 1    | 20   | Y    |
>>> +# | 7    | 1    | END  | Y    |
>>> +# |------|------|------|------|
>>> +#
>>> +#
>>> +# Place the first brekpoint at file 2, line 22 and a second breakpoint
>>> +# at file 1, line 19.  A third breakpoint is placed at file 1, line
>>> +# 18, but as this line table entry will have been discarded[1] the
>>> +# third breakpoint will actually be placed at the same location as the
>>> +# second breakpoint.
>>> +#
>>> +# [1] The entry for file 1, line 18 is discarded because it is at the
>>> +# same address as the previous entry, but the previous entry is-stmt,
>>> +# while line 18 is a non-stmt.
>>> +
>>> +load_lib dwarf.exp
>>> +
>>> +# This test can only be run on targets which support DWARF-2 and use gas.
>>> +if {![dwarf2_support]} {
>>> +    return 0
>>> +}
>>> +
>>> +# The .c files use __attribute__.
>>> +if [get_compiler_info] {
>>> +    return -1
>>> +}
>>> +if !$gcc_compiled {
>>> +    return 0
>>> +}
>>> +
>>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>>> +    dw2-inline-header.c dw2-inline-header.h
>>> +
>>> +set asm_file [standard_output_file $srcfile2]
>>> +Dwarf::assemble $asm_file {
>>> +    global srcdir subdir srcfile srcfile3 srcfile4
>>> +    declare_labels lines_label callee_subprog_label
>>> +
>>> +    get_func_info main
>>> +
>>> +    cu {} {
>>> +	compile_unit {
>>> +	    {producer "gcc" }
>>> +	    {language @DW_LANG_C}
>>> +	    {name ${srcfile3}}
>>> +	    {low_pc 0 addr}
>>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>>> +	} {
>>> +	    callee_subprog_label: subprogram {
>>> +		{external 1 flag}
>>> +		{name callee}
>>> +		{inline 3 data1}
>>> +	    }
>>> +	    subprogram {
>>> +		{external 1 flag}
>>> +		{name main}
>>> +		{low_pc $main_start addr}
>>> +		{high_pc "$main_start + $main_len" addr}
>>> +	    } {
>>> +		inlined_subroutine {
>>> +		    {abstract_origin %$callee_subprog_label}
>>> +		    {low_pc line_label_1 addr}
>>> +		    {high_pc line_label_7 addr}
>>> +		    {call_file 1 data1}
>>> +		    {call_line 18 data1}
>>> +		}
>>> +	    }
>>> +	}
>>> +    }
>>> +
>>> +    lines {version 2 default_is_stmt 1} lines_label {
>>> +	include_dir "${srcdir}/${subdir}"
>>> +	file_name "$srcfile3" 1
>>> +	file_name "$srcfile4" 1
>>> +
>>> +	program {
>>> +	    {DW_LNE_set_address line_label_1}
>>> +	    {DW_LNS_advance_line 15}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_2}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_3}
>>> +	    {DW_LNS_advance_line 4}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_4}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_advance_line -4}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_5}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_6}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_7}
>>> +	    {DW_LNE_end_sequence}
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>>> +    return -1
>>> +}
>>> +
>>> +if ![runto_main] {
>>> +    return -1
>>> +}
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile4}:22"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
>>> +    "check for breakpoint at ${srcfile4}"
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile3}:19"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
>>> +    "check for breakpoint at ${srcfile3}"
>>> +
>>> +# Line table entry for line 18 will have been discarded, so this
>>> +# brekpoint will be at the same location as line 19.
>>> +gdb_test "break ${srcfile3}:18" \
>>> +    "Note: breakpoint $decimal also set at pc $hex.*"
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>>> new file mode 100644
>>> index 00000000000..c683dc4bb8a
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>>> @@ -0,0 +1,192 @@
>>> +# Copyright 2020 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/>.
>>> +
>>> +# Setup a line table where:
>>> +#
>>> +# | Addr | File | Line | Stmt |
>>> +# |------|------|------|------|
>>> +# | 1    | 1    | 16   | Y    |
>>> +# | 2    | 1    | 17   | Y    |
>>> +# | 3    | 2    | 21   | Y    |
>>> +# | 4    | 2    | 22   | Y    |
>>> +# | 4    | 1    | 18   | N    |
>>> +# | 5    | 1    | 19   | N    |
>>> +# | 6    | 1    | 20   | Y    |
>>> +# | 7    | 1    | END  | Y    |
>>> +# |------|------|------|------|
>>> +#
>>> +# Break at file 2, line 22, then single instruction step forward.  We
>>> +# should pass through line 19 and then encounter line 20.
>>> +#
>>> +# Currently we don't expect GDB to see file 1, line 18, as this is a
>>> +# non-stmt line in a different file at the same address as the
>>> +# previous is-stmt line.
>>> +
>>> +load_lib dwarf.exp
>>> +
>>> +# This test can only be run on targets which support DWARF-2 and use gas.
>>> +if {![dwarf2_support]} {
>>> +    return 0
>>> +}
>>> +
>>> +# The .c files use __attribute__.
>>> +if [get_compiler_info] {
>>> +    return -1
>>> +}
>>> +if !$gcc_compiled {
>>> +    return 0
>>> +}
>>> +
>>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>>> +    dw2-inline-header.c dw2-inline-header.h
>>> +
>>> +set asm_file [standard_output_file $srcfile2]
>>> +Dwarf::assemble $asm_file {
>>> +    global srcdir subdir srcfile srcfile3 srcfile4
>>> +    declare_labels lines_label callee_subprog_label
>>> +
>>> +    get_func_info main
>>> +
>>> +    cu {} {
>>> +	compile_unit {
>>> +	    {producer "gcc" }
>>> +	    {language @DW_LANG_C}
>>> +	    {name ${srcfile3}}
>>> +	    {low_pc 0 addr}
>>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>>> +	} {
>>> +	    callee_subprog_label: subprogram {
>>> +		{external 1 flag}
>>> +		{name callee}
>>> +		{inline 3 data1}
>>> +	    }
>>> +	    subprogram {
>>> +		{external 1 flag}
>>> +		{name main}
>>> +		{low_pc $main_start addr}
>>> +		{high_pc "$main_start + $main_len" addr}
>>> +	    } {
>>> +		inlined_subroutine {
>>> +		    {abstract_origin %$callee_subprog_label}
>>> +		    {low_pc line_label_1 addr}
>>> +		    {high_pc line_label_7 addr}
>>> +		    {call_file 1 data1}
>>> +		    {call_line 18 data1}
>>> +		}
>>> +	    }
>>> +	}
>>> +    }
>>> +
>>> +    lines {version 2 default_is_stmt 1} lines_label {
>>> +	include_dir "${srcdir}/${subdir}"
>>> +	file_name "$srcfile3" 1
>>> +	file_name "$srcfile4" 1
>>> +
>>> +	program {
>>> +	    {DW_LNE_set_address line_label_1}
>>> +	    {DW_LNS_advance_line 15}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_2}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_3}
>>> +	    {DW_LNS_advance_line 4}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_4}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_advance_line -4}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_5}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_6}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_7}
>>> +	    {DW_LNE_end_sequence}
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>>> +    return -1
>>> +}
>>> +
>>> +if ![runto_main] {
>>> +    return -1
>>> +}
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile4}:22"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
>>> +
>>> +gdb_continue_to_breakpoint "${srcfile4}:22" \
>>> +    ".* ${srcfile4} : 22 .*"
>>> +
>>> +# Now single instruction step forward.  Eventually we should hit
>>> +# ${srcfile3}:20, but before we do we should hit the non-statement
>>> +# line ${srcfile3}:19.
>>> +#
>>> +# We don't know how many instructions we'll need to step, but 100
>>> +# should be enough for everyone (surely), and this stops us looping
>>> +# forever if something goes wrong.
>>> +set found_line_19 0
>>> +set found_line_20 0
>>> +set keep_going 1
>>> +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
>>> +    set keep_going 0
>>> +    gdb_test_multiple "stepi" "stepi ${i}" {
>>> +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
>>> +	    set found_line_19 1
>>> +	    set keep_going 1
>>> +	}
>>> +
>>> +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
>>> +	    set found_line_20 1
>>> +	}
>>> +
>>> +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
>>> +	    # Not left line 22 yet.
>>> +	    set keep_going 1
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +gdb_assert { $found_line_19 && $found_line_20 } \
>>> +    "found line 19 and 20"
>>> +
>>> +
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>>> new file mode 100644
>>> index 00000000000..a1b7b17cbeb
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>>> @@ -0,0 +1,46 @@
>>> +/* Copyright 2020 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/>.  */
>>> +
>>> +/* Used to insert labels with which we can build a fake line table.  */
>>> +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
>>> +
>>> +volatile int var;
>>> +volatile int bar;
>>> +
>>> +/* Generate some code to take up some space.  */
>>> +#define FILLER do { \
>>> +    var = 99;	    \
>>> +} while (0)
>>> +
>>> +int
>>> +main ()
>>> +{					/* main prologue */
>>> +  asm ("main_label: .globl main_label");
>>> +  LL (1);	// F1, Ln 16
>>> +  FILLER;
>>> +  LL (2);	// F1, Ln 17
>>> +  FILLER;
>>> +  LL (3);	// F2, Ln 21
>>> +  FILLER;
>>> +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
>>> +  FILLER;
>>> +  LL (5);	// F1, Ln 19 !S
>>> +  FILLER;
>>> +  LL (6);	// F1, Ln 20
>>> +  FILLER;
>>> +  LL (7);
>>> +  FILLER;
>>> +  return 0;				/* main end */
>>> +}
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>>> new file mode 100644
>>> index 00000000000..a8331268a09
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>>> @@ -0,0 +1,24 @@
>>> +/* Copyright 2020 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/>.  */
>>> +
>>> +/* dw2-inline-header.c : 16 */
>>> +/* dw2-inline-header.c : 17 */
>>> +/* dw2-inline-header.c : 18 */
>>> +/* dw2-inline-header.c : 19 */
>>> +/* dw2-inline-header.c : 20 */
>>> +/* dw2-inline-header.c : 21 */
>>> +/* dw2-inline-header.c : 22 */
>>> +/* dw2-inline-header.c : 23 */
>>> +/* dw2-inline-header.c : 24 */
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>>> new file mode 100644
>>> index 00000000000..7233acbcd76
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>>> @@ -0,0 +1,24 @@
>>> +/* Copyright 2020 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/>.  */
>>> +
>>> +/* dw2-inline-header.h : 16 */
>>> +/* dw2-inline-header.h : 17 */
>>> +/* dw2-inline-header.h : 18 */
>>> +/* dw2-inline-header.h : 19 */
>>> +/* dw2-inline-header.h : 20 */
>>> +/* dw2-inline-header.h : 21 */
>>> +/* dw2-inline-header.h : 22 */
>>> +/* dw2-inline-header.h : 23 */
>>> +/* dw2-inline-header.h : 24 */
>>>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-04 22:23                 ` Andrew Burgess
  2020-04-05  0:04                   ` Bernd Edlinger
  2020-04-05  0:47                   ` Bernd Edlinger
@ 2020-04-05  8:55                   ` Bernd Edlinger
  2 siblings, 0 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-04-05  8:55 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey



On 4/5/20 12:23 AM, Andrew Burgess wrote:
> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-04-04 20:07:52 +0200]:
> 
>> Sorry, Andrew,
>>
>> please hold this one.
>>
>> This will definitely break all the step over inline
>> stuff that was already working.
> 
> By all I assume you only mean gdb.cp/step-and-next-inline.exp, which I
> specifically talk about in the commit message.
> 
> The DWARF produced in that test is horribly broken, I was tempted to
> drop the whole test, but a few parts do still work, and hopefully if
> GCC gets fixed we'll be back in business.
> 
> As I mentioned in the GCC bug report, if I hack GDB to "fix" the DWARF
> produced by GCC, then that test passes fine with no regressions with
> this patch as is.
> 
> All other tests still pass with this patch - admittedly there's not
> much testing the inline behaviour, but there are a few.
> 
> If you know of a case with valid DWARF that is broken by this test
> then I'm all ears.  With the setup I used for the tests in this patch
> it's actually pretty easy to synthesis test of inline functions, so
> all you really need to do is describe a test you think will break and
> I can knock up a test case for it reasonably quickly.
> 
>>
>> Can we please go to the drawing board before we continue
>> from here?
> 
> Sure.  I posted my thoughts just now.
> 
> Thanks,
> Andrew
> 
>>
>>
>> Thanks
>> Bernd.
>>
>> On 4/4/20 12:21 AM, Andrew Burgess wrote:
>>> After the is-stmt support commit:
>>>
>>>   commit 8c95582da858ac981f689a6f599acacb8c5c490f
>>>   Date:   Mon Dec 30 21:04:51 2019 +0000
>>>
>>>       gdb: Add support for tracking the DWARF line table is-stmt field
>>>
>>> A regression was observed where a breakpoint could no longer be placed
>>> in some cases.
>>>
>>> Consider a line table like this:
>>>
>>>   File 1: test.c
>>>   File 2: test.h
>>>
>>>   | Addr | File | Line | Stmt |
>>>   |------|------|------|------|
>>>   | 1    | 1    | 16   | Y    |
>>>   | 2    | 1    | 17   | Y    |
>>>   | 3    | 2    | 21   | Y    |
>>>   | 4    | 2    | 22   | Y    |
>>>   | 4    | 1    | 18   | N    |
>>>   | 5    | 2    | 23   | N    |
>>>   | 6    | 1    | 24   | Y    |
>>>   | 7    | 1    | END  | Y    |
>>>   |------|------|------|------|
>>>
>>> Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
>>> built two line table structures:
>>>
>>>   File 1                 File 2
>>>   ------                 ------
>>>
>>>   | Addr | Line |        | Addr | Line |
>>>   |------|------|        |------|------|
>>>   | 1    | 16   |        | 3    | 21   |
>>>   | 2    | 17   |        | 4    | 22   |
>>>   | 3    | END  |        | 6    | END  |
>>>   | 6    | 24   |        |------|------|
>>>   | 7    | END  |
>>>   |------|------|
>>>
>>> After the is-stmt patch GDB now records non-stmt lines, so the
>>> generated line table structures look like this:
>>>

I was aware that this may happen, I pointed that out already
twice at least.  My initial patch used this effect entirely,
but I tossed it, when I saw, how nice your patch worked.


Thanks
Bernd.


>>>   File 1                   File 2
>>>   ------                   ------
>>>
>>>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>>>   |------|------|------|  |------|------|------|
>>>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>>>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>>>   | 3    | END  | Y    |  | 4    | END  | Y    |
>>>   | 4    | 18   | N    |  | 5    | 23   | N    |
>>>   | 5    | END  | Y    |  | 6    | END  | Y    |
>>>   | 6    | 24   | Y    |  |------|------|------|
>>>   | 7    | END  | Y    |
>>>   |------|------|------|
>>>
>>> The problem is that in 'File 2', end END marker at address 4 causes
>>> the previous line table entry to be discarded, so we actually end up
>>> with this:
>>>
>>>   File 2
>>>   ------
>>>
>>>   | Addr | Line | Stmt |
>>>   |------|------|------|
>>>   | 3    | 21   | Y    |
>>>   | 4    | END  | Y    |
>>>   | 5    | 23   | N    |
>>>   | 6    | END  | Y    |
>>>   |------|------|------|
>>>
>>> When a user tries to place a breakpoint in file 2 at line 22, this is
>>> no longer possible.
>>>
>>> The solution I propose here is that we ignore line table entries that
>>> would trigger a change of file if:
>>>
>>>   1. The new line being added is at the same address as the previous
>>>   line, and
>>>
>>>   2. We have previously seen an is-stmt line at the current address.
>>>
>>> The result of this is that GDB switches file, and knows that some line
>>> entry (or entries) are going to be discarded, prefer to keep is-stmt
>>> lines and discard non-stmt lines.
>>>
>>> After this commit the lines tables are now:
>>>
>>>   File 1                   File 2
>>>   ------                   ------
>>>
>>>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>>>   |------|------|------|  |------|------|------|
>>>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>>>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>>>   | 3    | END  | Y    |  | 5    | 23   | N    |
>>>   | 5    | END  | Y    |  | 6    | END  | Y    |
>>>   | 6    | 24   | Y    |  |------|------|------|
>>>   | 7    | END  | Y    |
>>>   |------|------|------|
>>>
>>> We've lost the non-stmt entry for file 1, line 18, but retained the
>>> is-stmt entry for file 2, line 22.  The user can now place a
>>> breakpoint at that location.
>>>
>>> One problem that came from this commit was the test
>>> gdb.cp/step-and-next-inline.exp, which broke in several places.  After
>>> looking at this test again I think that in some cases this test was
>>> only ever passing by pure luck.  The debug GCC is producing for this
>>> test is pretty broken.  I raised this GCC bug:
>>>
>>>   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>>>
>>> for this and disabled one entire half of the test.  There are still
>>> some cases in here that do pass, and if/when GCC is fixed it would be
>>> great to enable this test again.
>>>
>>> gdb/ChangeLog:
>>>
>>> 	* dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
>>> 	member variable.
>>> 	<m_stmt_at_address>: New member variable.
>>> 	(lnp_state_machine::record_line): Don't record some lines, update
>>> 	tracking of is_stmt at the same address.
>>> 	(lnp_state_machine::lnp_state_machine): Initialise new member
>>> 	variables.
>>>
>>> gdb/testsuite/ChangeLog:
>>>
>>> 	* gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
>>> 	use_header case.
>>> 	* gdb.dwarf2/dw2-inline-header-1.exp: New file.
>>> 	* gdb.dwarf2/dw2-inline-header-2.exp: New file.
>>> 	* gdb.dwarf2/dw2-inline-header-3.exp: New file.
>>> 	* gdb.dwarf2/dw2-inline-header-lbls.c: New file.
>>> 	* gdb.dwarf2/dw2-inline-header.c: New file.
>>> 	* gdb.dwarf2/dw2-inline-header.h: New file.
>>> ---
>>>  gdb/ChangeLog                                     |  10 ++
>>>  gdb/dwarf2/read.c                                 |  47 +++++-
>>>  gdb/testsuite/ChangeLog                           |  11 ++
>>>  gdb/testsuite/gdb.cp/step-and-next-inline.exp     |   7 +
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp  | 156 ++++++++++++++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp  | 179 ++++++++++++++++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp  | 192 ++++++++++++++++++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c |  46 ++++++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.c      |  24 +++
>>>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.h      |  24 +++
>>>  10 files changed, 693 insertions(+), 3 deletions(-)
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>>>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>>>
>>> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
>>> index f94c66b4f1b..261c8455424 100644
>>> --- a/gdb/dwarf2/read.c
>>> +++ b/gdb/dwarf2/read.c
>>> @@ -19327,6 +19327,15 @@ class lnp_state_machine
>>>    /* The last file a line number was recorded for.  */
>>>    struct subfile *m_last_subfile = NULL;
>>>  
>>> +  /* The address of the last line entry.  */
>>> +  CORE_ADDR m_last_address;
>>> +
>>> +  /* Set to true when a previous line at the same address (using
>>> +     m_last_address) had m_is_stmt true.  This is reset to false when a
>>> +     line entry at a new address (m_address different to m_last_address) is
>>> +     processed.  */
>>> +  bool m_stmt_at_address = false;
>>> +
>>>    /* When true, record the lines we decode.  */
>>>    bool m_currently_recording_lines = false;
>>>  
>>> @@ -19520,14 +19529,34 @@ lnp_state_machine::record_line (bool end_sequence)
>>>        fe->included_p = 1;
>>>        if (m_record_lines_p)
>>>  	{
>>> -	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
>>> -	      || end_sequence)
>>> +	  /* When we switch files we insert an end maker in the first file,
>>> +	     switch to the second file and add a new line entry.  The
>>> +	     problem is that the end marker inserted in the first file will
>>> +	     discard any previous line entries at the same address.  If the
>>> +	     line entries in the first file are marked as is-stmt, while
>>> +	     the new line in the second file is non-stmt, then this means
>>> +	     the end marker will discard is-stmt lines so we can have a
>>> +	     non-stmt line.  This means that there are less addresses at
>>> +	     which the user can insert a breakpoint.
>>> +
>>> +	     To improve this we track the last address in m_last_address,
>>> +	     and whether we have seen an is-stmt at this address.  Then
>>> +	     when switching files, if we have seen a stmt at the current
>>> +	     address, and we are switching to create a non-stmt line, then
>>> +	     discard the new line.  */
>>> +	  bool file_changed
>>> +	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
>>> +	  bool ignore_this_line
>>> +	    = (file_changed && !end_sequence && m_last_address == m_address
>>> +	       && !m_is_stmt && m_stmt_at_address);
>>> +
>>> +	  if ((file_changed && !ignore_this_line) || end_sequence)
>>>  	    {
>>>  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
>>>  				 m_currently_recording_lines ? m_cu : nullptr);
>>>  	    }
>>>  
>>> -	  if (!end_sequence)
>>> +	  if (!end_sequence && !ignore_this_line)
>>>  	    {
>>>  	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
>>>  
>>> @@ -19546,6 +19575,15 @@ lnp_state_machine::record_line (bool end_sequence)
>>>  	    }
>>>  	}
>>>      }
>>> +
>>> +  /* Track whether we have seen any m_is_stmt true at m_address in case we
>>> +     have multiple line table entries all at m_address.  */
>>> +  if (m_last_address != m_address)
>>> +    {
>>> +      m_stmt_at_address = false;
>>> +      m_last_address = m_address;
>>> +    }
>>> +  m_stmt_at_address |= m_is_stmt;
>>>  }
>>>  
>>>  lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>>> @@ -19565,6 +19603,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>>>    m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
>>>    m_is_stmt = lh->default_is_stmt;
>>>    m_discriminator = 0;
>>> +
>>> +  m_last_address = m_address;
>>> +  m_stmt_at_addr = false;
>>>  }
>>>  
>>>  void
>>> diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>>> index 3733fa75570..a95e21194f9 100644
>>> --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>>> +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>>> @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
>>>  proc do_test { use_header } {
>>>      global srcfile testfile
>>>  
>>> +    if { $use_header } {
>>> +	# This test will not pass due to poor debug information
>>> +	# generated by GCC (at least upto 10.x).  See
>>> +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>>> +	return
>>> +    }
>>> +
>>>      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
>>>      if { $use_header } {
>>>  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>>> new file mode 100644
>>> index 00000000000..6a1e990002c
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>>> @@ -0,0 +1,156 @@
>>> +# Copyright 2020 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/>.
>>> +
>>> +# Setup a line table where:
>>> +#
>>> +# | Addr | File | Line | Stmt |
>>> +# |------|------|------|------|
>>> +# | 1    | 1    | 16   | Y    |
>>> +# | 2    | 1    | 17   | Y    |
>>> +# | 3    | 2    | 21   | Y    |
>>> +# | 4    | 2    | 22   | Y    |
>>> +# | 4    | 1    | 18   | N    |
>>> +# | 5    | 2    | 23   | N    |
>>> +# | 6    | 1    | 24   | Y    |
>>> +# | 7    | 1    | END  | Y    |
>>> +# |------|------|------|------|
>>> +#
>>> +# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
>>> +# the line table entry for this line due to switching files for the
>>> +# file 1, line 18 non-statement line.  After patching however, GDB now
>>> +# discards the file 1, line 18 entry instead, and the breakpoint at
>>> +# line 22 should succeed.
>>> +
>>> +load_lib dwarf.exp
>>> +
>>> +# This test can only be run on targets which support DWARF-2 and use gas.
>>> +if {![dwarf2_support]} {
>>> +    return 0
>>> +}
>>> +
>>> +# The .c files use __attribute__.
>>> +if [get_compiler_info] {
>>> +    return -1
>>> +}
>>> +if !$gcc_compiled {
>>> +    return 0
>>> +}
>>> +
>>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>>> +    dw2-inline-header.c dw2-inline-header.h
>>> +
>>> +set asm_file [standard_output_file $srcfile2]
>>> +Dwarf::assemble $asm_file {
>>> +    global srcdir subdir srcfile srcfile3 srcfile4
>>> +    declare_labels lines_label callee_subprog_label
>>> +
>>> +    get_func_info main
>>> +
>>> +    cu {} {
>>> +	compile_unit {
>>> +	    {producer "gcc" }
>>> +	    {language @DW_LANG_C}
>>> +	    {name ${srcfile3}}
>>> +	    {low_pc 0 addr}
>>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>>> +	} {
>>> +	    callee_subprog_label: subprogram {
>>> +		{external 1 flag}
>>> +		{name callee}
>>> +		{inline 3 data1}
>>> +	    }
>>> +	    subprogram {
>>> +		{external 1 flag}
>>> +		{name main}
>>> +		{low_pc $main_start addr}
>>> +		{high_pc "$main_start + $main_len" addr}
>>> +	    } {
>>> +		inlined_subroutine {
>>> +		    {abstract_origin %$callee_subprog_label}
>>> +		    {low_pc line_label_1 addr}
>>> +		    {high_pc line_label_7 addr}
>>> +		    {call_file 1 data1}
>>> +		    {call_line 18 data1}
>>> +		}
>>> +	    }
>>> +	}
>>> +    }
>>> +
>>> +    lines {version 2 default_is_stmt 1} lines_label {
>>> +	include_dir "${srcdir}/${subdir}"
>>> +	file_name "$srcfile3" 1
>>> +	file_name "$srcfile4" 1
>>> +
>>> +	program {
>>> +	    {DW_LNE_set_address line_label_1}
>>> +	    {DW_LNS_advance_line 15}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_2}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_3}
>>> +	    {DW_LNS_advance_line 4}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_4}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_advance_line -4}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_5}
>>> +	    {DW_LNS_advance_line 5}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNE_set_address line_label_6}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_7}
>>> +	    {DW_LNE_end_sequence}
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>>> +    return -1
>>> +}
>>> +
>>> +if ![runto_main] {
>>> +    return -1
>>> +}
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile4}:22"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>>> new file mode 100644
>>> index 00000000000..46499919a8b
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>>> @@ -0,0 +1,179 @@
>>> +# Copyright 2020 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/>.
>>> +
>>> +# Setup a line table where:
>>> +#
>>> +# | Addr | File | Line | Stmt |
>>> +# |------|------|------|------|
>>> +# | 1    | 1    | 16   | Y    |
>>> +# | 2    | 1    | 17   | Y    |
>>> +# | 3    | 2    | 21   | Y    |
>>> +# | 4    | 2    | 22   | Y    |
>>> +# | 4    | 1    | 18   | N    |
>>> +# | 5    | 1    | 19   | Y    |
>>> +# | 6    | 1    | 20   | Y    |
>>> +# | 7    | 1    | END  | Y    |
>>> +# |------|------|------|------|
>>> +#
>>> +#
>>> +# Place the first brekpoint at file 2, line 22 and a second breakpoint
>>> +# at file 1, line 19.  A third breakpoint is placed at file 1, line
>>> +# 18, but as this line table entry will have been discarded[1] the
>>> +# third breakpoint will actually be placed at the same location as the
>>> +# second breakpoint.
>>> +#
>>> +# [1] The entry for file 1, line 18 is discarded because it is at the
>>> +# same address as the previous entry, but the previous entry is-stmt,
>>> +# while line 18 is a non-stmt.
>>> +
>>> +load_lib dwarf.exp
>>> +
>>> +# This test can only be run on targets which support DWARF-2 and use gas.
>>> +if {![dwarf2_support]} {
>>> +    return 0
>>> +}
>>> +
>>> +# The .c files use __attribute__.
>>> +if [get_compiler_info] {
>>> +    return -1
>>> +}
>>> +if !$gcc_compiled {
>>> +    return 0
>>> +}
>>> +
>>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>>> +    dw2-inline-header.c dw2-inline-header.h
>>> +
>>> +set asm_file [standard_output_file $srcfile2]
>>> +Dwarf::assemble $asm_file {
>>> +    global srcdir subdir srcfile srcfile3 srcfile4
>>> +    declare_labels lines_label callee_subprog_label
>>> +
>>> +    get_func_info main
>>> +
>>> +    cu {} {
>>> +	compile_unit {
>>> +	    {producer "gcc" }
>>> +	    {language @DW_LANG_C}
>>> +	    {name ${srcfile3}}
>>> +	    {low_pc 0 addr}
>>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>>> +	} {
>>> +	    callee_subprog_label: subprogram {
>>> +		{external 1 flag}
>>> +		{name callee}
>>> +		{inline 3 data1}
>>> +	    }
>>> +	    subprogram {
>>> +		{external 1 flag}
>>> +		{name main}
>>> +		{low_pc $main_start addr}
>>> +		{high_pc "$main_start + $main_len" addr}
>>> +	    } {
>>> +		inlined_subroutine {
>>> +		    {abstract_origin %$callee_subprog_label}
>>> +		    {low_pc line_label_1 addr}
>>> +		    {high_pc line_label_7 addr}
>>> +		    {call_file 1 data1}
>>> +		    {call_line 18 data1}
>>> +		}
>>> +	    }
>>> +	}
>>> +    }
>>> +
>>> +    lines {version 2 default_is_stmt 1} lines_label {
>>> +	include_dir "${srcdir}/${subdir}"
>>> +	file_name "$srcfile3" 1
>>> +	file_name "$srcfile4" 1
>>> +
>>> +	program {
>>> +	    {DW_LNE_set_address line_label_1}
>>> +	    {DW_LNS_advance_line 15}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_2}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_3}
>>> +	    {DW_LNS_advance_line 4}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_4}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_advance_line -4}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_5}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_6}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_7}
>>> +	    {DW_LNE_end_sequence}
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>>> +    return -1
>>> +}
>>> +
>>> +if ![runto_main] {
>>> +    return -1
>>> +}
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile4}:22"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
>>> +    "check for breakpoint at ${srcfile4}"
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile3}:19"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
>>> +    "check for breakpoint at ${srcfile3}"
>>> +
>>> +# Line table entry for line 18 will have been discarded, so this
>>> +# brekpoint will be at the same location as line 19.
>>> +gdb_test "break ${srcfile3}:18" \
>>> +    "Note: breakpoint $decimal also set at pc $hex.*"
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>>> new file mode 100644
>>> index 00000000000..c683dc4bb8a
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>>> @@ -0,0 +1,192 @@
>>> +# Copyright 2020 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/>.
>>> +
>>> +# Setup a line table where:
>>> +#
>>> +# | Addr | File | Line | Stmt |
>>> +# |------|------|------|------|
>>> +# | 1    | 1    | 16   | Y    |
>>> +# | 2    | 1    | 17   | Y    |
>>> +# | 3    | 2    | 21   | Y    |
>>> +# | 4    | 2    | 22   | Y    |
>>> +# | 4    | 1    | 18   | N    |
>>> +# | 5    | 1    | 19   | N    |
>>> +# | 6    | 1    | 20   | Y    |
>>> +# | 7    | 1    | END  | Y    |
>>> +# |------|------|------|------|
>>> +#
>>> +# Break at file 2, line 22, then single instruction step forward.  We
>>> +# should pass through line 19 and then encounter line 20.
>>> +#
>>> +# Currently we don't expect GDB to see file 1, line 18, as this is a
>>> +# non-stmt line in a different file at the same address as the
>>> +# previous is-stmt line.
>>> +
>>> +load_lib dwarf.exp
>>> +
>>> +# This test can only be run on targets which support DWARF-2 and use gas.
>>> +if {![dwarf2_support]} {
>>> +    return 0
>>> +}
>>> +
>>> +# The .c files use __attribute__.
>>> +if [get_compiler_info] {
>>> +    return -1
>>> +}
>>> +if !$gcc_compiled {
>>> +    return 0
>>> +}
>>> +
>>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>>> +    dw2-inline-header.c dw2-inline-header.h
>>> +
>>> +set asm_file [standard_output_file $srcfile2]
>>> +Dwarf::assemble $asm_file {
>>> +    global srcdir subdir srcfile srcfile3 srcfile4
>>> +    declare_labels lines_label callee_subprog_label
>>> +
>>> +    get_func_info main
>>> +
>>> +    cu {} {
>>> +	compile_unit {
>>> +	    {producer "gcc" }
>>> +	    {language @DW_LANG_C}
>>> +	    {name ${srcfile3}}
>>> +	    {low_pc 0 addr}
>>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>>> +	} {
>>> +	    callee_subprog_label: subprogram {
>>> +		{external 1 flag}
>>> +		{name callee}
>>> +		{inline 3 data1}
>>> +	    }
>>> +	    subprogram {
>>> +		{external 1 flag}
>>> +		{name main}
>>> +		{low_pc $main_start addr}
>>> +		{high_pc "$main_start + $main_len" addr}
>>> +	    } {
>>> +		inlined_subroutine {
>>> +		    {abstract_origin %$callee_subprog_label}
>>> +		    {low_pc line_label_1 addr}
>>> +		    {high_pc line_label_7 addr}
>>> +		    {call_file 1 data1}
>>> +		    {call_line 18 data1}
>>> +		}
>>> +	    }
>>> +	}
>>> +    }
>>> +
>>> +    lines {version 2 default_is_stmt 1} lines_label {
>>> +	include_dir "${srcdir}/${subdir}"
>>> +	file_name "$srcfile3" 1
>>> +	file_name "$srcfile4" 1
>>> +
>>> +	program {
>>> +	    {DW_LNE_set_address line_label_1}
>>> +	    {DW_LNS_advance_line 15}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_2}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_set_file 2}
>>> +	    {DW_LNE_set_address line_label_3}
>>> +	    {DW_LNS_advance_line 4}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_4}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNS_advance_line -4}
>>> +	    {DW_LNS_set_file 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_5}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_6}
>>> +	    {DW_LNS_advance_line 1}
>>> +	    {DW_LNS_negate_stmt}
>>> +	    {DW_LNS_copy}
>>> +
>>> +	    {DW_LNE_set_address line_label_7}
>>> +	    {DW_LNE_end_sequence}
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>>> +    return -1
>>> +}
>>> +
>>> +if ![runto_main] {
>>> +    return -1
>>> +}
>>> +
>>> +# Delete all breakpoints so that the output of "info breakpoints"
>>> +# below will only contain a single breakpoint.
>>> +delete_breakpoints
>>> +
>>> +# Place a breakpoint within the function in the header file.
>>> +gdb_breakpoint "${srcfile4}:22"
>>> +
>>> +# Check that the breakpoint was placed where we expected.  It should
>>> +# appear at the requested line.  When the bug in GDB was present the
>>> +# breakpoint would be placed on one of the following lines instead.
>>> +gdb_test "info breakpoints" \
>>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
>>> +
>>> +gdb_continue_to_breakpoint "${srcfile4}:22" \
>>> +    ".* ${srcfile4} : 22 .*"
>>> +
>>> +# Now single instruction step forward.  Eventually we should hit
>>> +# ${srcfile3}:20, but before we do we should hit the non-statement
>>> +# line ${srcfile3}:19.
>>> +#
>>> +# We don't know how many instructions we'll need to step, but 100
>>> +# should be enough for everyone (surely), and this stops us looping
>>> +# forever if something goes wrong.
>>> +set found_line_19 0
>>> +set found_line_20 0
>>> +set keep_going 1
>>> +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
>>> +    set keep_going 0
>>> +    gdb_test_multiple "stepi" "stepi ${i}" {
>>> +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
>>> +	    set found_line_19 1
>>> +	    set keep_going 1
>>> +	}
>>> +
>>> +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
>>> +	    set found_line_20 1
>>> +	}
>>> +
>>> +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
>>> +	    # Not left line 22 yet.
>>> +	    set keep_going 1
>>> +	}
>>> +    }
>>> +}
>>> +
>>> +gdb_assert { $found_line_19 && $found_line_20 } \
>>> +    "found line 19 and 20"
>>> +
>>> +
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>>> new file mode 100644
>>> index 00000000000..a1b7b17cbeb
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>>> @@ -0,0 +1,46 @@
>>> +/* Copyright 2020 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/>.  */
>>> +
>>> +/* Used to insert labels with which we can build a fake line table.  */
>>> +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
>>> +
>>> +volatile int var;
>>> +volatile int bar;
>>> +
>>> +/* Generate some code to take up some space.  */
>>> +#define FILLER do { \
>>> +    var = 99;	    \
>>> +} while (0)
>>> +
>>> +int
>>> +main ()
>>> +{					/* main prologue */
>>> +  asm ("main_label: .globl main_label");
>>> +  LL (1);	// F1, Ln 16
>>> +  FILLER;
>>> +  LL (2);	// F1, Ln 17
>>> +  FILLER;
>>> +  LL (3);	// F2, Ln 21
>>> +  FILLER;
>>> +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
>>> +  FILLER;
>>> +  LL (5);	// F1, Ln 19 !S
>>> +  FILLER;
>>> +  LL (6);	// F1, Ln 20
>>> +  FILLER;
>>> +  LL (7);
>>> +  FILLER;
>>> +  return 0;				/* main end */
>>> +}
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>>> new file mode 100644
>>> index 00000000000..a8331268a09
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>>> @@ -0,0 +1,24 @@
>>> +/* Copyright 2020 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/>.  */
>>> +
>>> +/* dw2-inline-header.c : 16 */
>>> +/* dw2-inline-header.c : 17 */
>>> +/* dw2-inline-header.c : 18 */
>>> +/* dw2-inline-header.c : 19 */
>>> +/* dw2-inline-header.c : 20 */
>>> +/* dw2-inline-header.c : 21 */
>>> +/* dw2-inline-header.c : 22 */
>>> +/* dw2-inline-header.c : 23 */
>>> +/* dw2-inline-header.c : 24 */
>>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>>> new file mode 100644
>>> index 00000000000..7233acbcd76
>>> --- /dev/null
>>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>>> @@ -0,0 +1,24 @@
>>> +/* Copyright 2020 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/>.  */
>>> +
>>> +/* dw2-inline-header.h : 16 */
>>> +/* dw2-inline-header.h : 17 */
>>> +/* dw2-inline-header.h : 18 */
>>> +/* dw2-inline-header.h : 19 */
>>> +/* dw2-inline-header.h : 20 */
>>> +/* dw2-inline-header.h : 21 */
>>> +/* dw2-inline-header.h : 22 */
>>> +/* dw2-inline-header.h : 23 */
>>> +/* dw2-inline-header.h : 24 */
>>>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 1/2] gdb/testsuite: Move helper function into lib/dwarf.exp
  2020-04-03 22:21             ` [PATCH 1/2] gdb/testsuite: Move helper function into lib/dwarf.exp Andrew Burgess
@ 2020-04-06 20:18               ` Tom Tromey
  2020-04-14 11:18                 ` Andrew Burgess
  0 siblings, 1 reply; 79+ messages in thread
From: Tom Tromey @ 2020-04-06 20:18 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, Bernd Edlinger, Tom Tromey

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

Andrew> gdb/testsuite/ChangeLog:

Andrew> 	* gdb.dwarf2/dw2-inline-many-frames.exp (get_func_info): Delete.
Andrew> 	* gdb.dwarf2/dw2-inline-small-func.exp: Pass options to
Andrew> 	get_func_info.
Andrew> 	(get_func_info): Delete.
Andrew> 	* gdb.dwarf2/dw2-is-stmt-2.exp (get_func_info): Delete.
Andrew> 	* gdb.dwarf2/dw2-is-stmt.exp (get_func_info): Delete.
Andrew> 	* lib/dwarf.exp (get_func_info): New function.

Thanks for doing this.  I didn't realize this function was copied so
many times, or I would have merged them myself.  Looks good.

Tom

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-03 22:21             ` [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files Andrew Burgess
  2020-04-04 18:07               ` Bernd Edlinger
@ 2020-04-11  3:52               ` Bernd Edlinger
  2020-04-12 17:13                 ` Bernd Edlinger
  1 sibling, 1 reply; 79+ messages in thread
From: Bernd Edlinger @ 2020-04-11  3:52 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Tom Tromey

Andrew,

Just, a minor nit:

/home/ed/gnu/binutils-gdb/.git/rebase-apply/patch:665: new blank line at EOF.

I am trying to find out which patch is better.
So I applied your patch, an try to see where the differences are.


Please be patient..


Bernd.



On 4/4/20 12:21 AM, Andrew Burgess wrote:
> After the is-stmt support commit:
> 
>   commit 8c95582da858ac981f689a6f599acacb8c5c490f
>   Date:   Mon Dec 30 21:04:51 2019 +0000
> 
>       gdb: Add support for tracking the DWARF line table is-stmt field
> 
> A regression was observed where a breakpoint could no longer be placed
> in some cases.
> 
> Consider a line table like this:
> 
>   File 1: test.c
>   File 2: test.h
> 
>   | Addr | File | Line | Stmt |
>   |------|------|------|------|
>   | 1    | 1    | 16   | Y    |
>   | 2    | 1    | 17   | Y    |
>   | 3    | 2    | 21   | Y    |
>   | 4    | 2    | 22   | Y    |
>   | 4    | 1    | 18   | N    |
>   | 5    | 2    | 23   | N    |
>   | 6    | 1    | 24   | Y    |
>   | 7    | 1    | END  | Y    |
>   |------|------|------|------|
> 
> Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
> built two line table structures:
> 
>   File 1                 File 2
>   ------                 ------
> 
>   | Addr | Line |        | Addr | Line |
>   |------|------|        |------|------|
>   | 1    | 16   |        | 3    | 21   |
>   | 2    | 17   |        | 4    | 22   |
>   | 3    | END  |        | 6    | END  |
>   | 6    | 24   |        |------|------|
>   | 7    | END  |
>   |------|------|
> 
> After the is-stmt patch GDB now records non-stmt lines, so the
> generated line table structures look like this:
> 
>   File 1                   File 2
>   ------                   ------
> 
>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>   |------|------|------|  |------|------|------|
>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>   | 3    | END  | Y    |  | 4    | END  | Y    |
>   | 4    | 18   | N    |  | 5    | 23   | N    |
>   | 5    | END  | Y    |  | 6    | END  | Y    |
>   | 6    | 24   | Y    |  |------|------|------|
>   | 7    | END  | Y    |
>   |------|------|------|
> 
> The problem is that in 'File 2', end END marker at address 4 causes
> the previous line table entry to be discarded, so we actually end up
> with this:
> 
>   File 2
>   ------
> 
>   | Addr | Line | Stmt |
>   |------|------|------|
>   | 3    | 21   | Y    |
>   | 4    | END  | Y    |
>   | 5    | 23   | N    |
>   | 6    | END  | Y    |
>   |------|------|------|
> 
> When a user tries to place a breakpoint in file 2 at line 22, this is
> no longer possible.
> 
> The solution I propose here is that we ignore line table entries that
> would trigger a change of file if:
> 
>   1. The new line being added is at the same address as the previous
>   line, and
> 
>   2. We have previously seen an is-stmt line at the current address.
> 
> The result of this is that GDB switches file, and knows that some line
> entry (or entries) are going to be discarded, prefer to keep is-stmt
> lines and discard non-stmt lines.
> 
> After this commit the lines tables are now:
> 
>   File 1                   File 2
>   ------                   ------
> 
>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>   |------|------|------|  |------|------|------|
>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>   | 3    | END  | Y    |  | 5    | 23   | N    |
>   | 5    | END  | Y    |  | 6    | END  | Y    |
>   | 6    | 24   | Y    |  |------|------|------|
>   | 7    | END  | Y    |
>   |------|------|------|
> 
> We've lost the non-stmt entry for file 1, line 18, but retained the
> is-stmt entry for file 2, line 22.  The user can now place a
> breakpoint at that location.
> 
> One problem that came from this commit was the test
> gdb.cp/step-and-next-inline.exp, which broke in several places.  After
> looking at this test again I think that in some cases this test was
> only ever passing by pure luck.  The debug GCC is producing for this
> test is pretty broken.  I raised this GCC bug:
> 
>   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> 
> for this and disabled one entire half of the test.  There are still
> some cases in here that do pass, and if/when GCC is fixed it would be
> great to enable this test again.
> 
> gdb/ChangeLog:
> 
> 	* dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
> 	member variable.
> 	<m_stmt_at_address>: New member variable.
> 	(lnp_state_machine::record_line): Don't record some lines, update
> 	tracking of is_stmt at the same address.
> 	(lnp_state_machine::lnp_state_machine): Initialise new member
> 	variables.
> 
> gdb/testsuite/ChangeLog:
> 
> 	* gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
> 	use_header case.
> 	* gdb.dwarf2/dw2-inline-header-1.exp: New file.
> 	* gdb.dwarf2/dw2-inline-header-2.exp: New file.
> 	* gdb.dwarf2/dw2-inline-header-3.exp: New file.
> 	* gdb.dwarf2/dw2-inline-header-lbls.c: New file.
> 	* gdb.dwarf2/dw2-inline-header.c: New file.
> 	* gdb.dwarf2/dw2-inline-header.h: New file.
> ---
>  gdb/ChangeLog                                     |  10 ++
>  gdb/dwarf2/read.c                                 |  47 +++++-
>  gdb/testsuite/ChangeLog                           |  11 ++
>  gdb/testsuite/gdb.cp/step-and-next-inline.exp     |   7 +
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp  | 156 ++++++++++++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp  | 179 ++++++++++++++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp  | 192 ++++++++++++++++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c |  46 ++++++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.c      |  24 +++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.h      |  24 +++
>  10 files changed, 693 insertions(+), 3 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> 
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index f94c66b4f1b..261c8455424 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -19327,6 +19327,15 @@ class lnp_state_machine
>    /* The last file a line number was recorded for.  */
>    struct subfile *m_last_subfile = NULL;
>  
> +  /* The address of the last line entry.  */
> +  CORE_ADDR m_last_address;
> +
> +  /* Set to true when a previous line at the same address (using
> +     m_last_address) had m_is_stmt true.  This is reset to false when a
> +     line entry at a new address (m_address different to m_last_address) is
> +     processed.  */
> +  bool m_stmt_at_address = false;
> +
>    /* When true, record the lines we decode.  */
>    bool m_currently_recording_lines = false;
>  
> @@ -19520,14 +19529,34 @@ lnp_state_machine::record_line (bool end_sequence)
>        fe->included_p = 1;
>        if (m_record_lines_p)
>  	{
> -	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
> -	      || end_sequence)
> +	  /* When we switch files we insert an end maker in the first file,
> +	     switch to the second file and add a new line entry.  The
> +	     problem is that the end marker inserted in the first file will
> +	     discard any previous line entries at the same address.  If the
> +	     line entries in the first file are marked as is-stmt, while
> +	     the new line in the second file is non-stmt, then this means
> +	     the end marker will discard is-stmt lines so we can have a
> +	     non-stmt line.  This means that there are less addresses at
> +	     which the user can insert a breakpoint.
> +
> +	     To improve this we track the last address in m_last_address,
> +	     and whether we have seen an is-stmt at this address.  Then
> +	     when switching files, if we have seen a stmt at the current
> +	     address, and we are switching to create a non-stmt line, then
> +	     discard the new line.  */
> +	  bool file_changed
> +	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
> +	  bool ignore_this_line
> +	    = (file_changed && !end_sequence && m_last_address == m_address
> +	       && !m_is_stmt && m_stmt_at_address);
> +
> +	  if ((file_changed && !ignore_this_line) || end_sequence)
>  	    {
>  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
>  				 m_currently_recording_lines ? m_cu : nullptr);
>  	    }
>  
> -	  if (!end_sequence)
> +	  if (!end_sequence && !ignore_this_line)
>  	    {
>  	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
>  
> @@ -19546,6 +19575,15 @@ lnp_state_machine::record_line (bool end_sequence)
>  	    }
>  	}
>      }
> +
> +  /* Track whether we have seen any m_is_stmt true at m_address in case we
> +     have multiple line table entries all at m_address.  */
> +  if (m_last_address != m_address)
> +    {
> +      m_stmt_at_address = false;
> +      m_last_address = m_address;
> +    }
> +  m_stmt_at_address |= m_is_stmt;
>  }
>  
>  lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
> @@ -19565,6 +19603,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>    m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
>    m_is_stmt = lh->default_is_stmt;
>    m_discriminator = 0;
> +
> +  m_last_address = m_address;
> +  m_stmt_at_addr = false;
>  }
>  
>  void
> diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> index 3733fa75570..a95e21194f9 100644
> --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
>  proc do_test { use_header } {
>      global srcfile testfile
>  
> +    if { $use_header } {
> +	# This test will not pass due to poor debug information
> +	# generated by GCC (at least upto 10.x).  See
> +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> +	return
> +    }
> +
>      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
>      if { $use_header } {
>  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> new file mode 100644
> index 00000000000..6a1e990002c
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> @@ -0,0 +1,156 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt |
> +# |------|------|------|------|
> +# | 1    | 1    | 16   | Y    |
> +# | 2    | 1    | 17   | Y    |
> +# | 3    | 2    | 21   | Y    |
> +# | 4    | 2    | 22   | Y    |
> +# | 4    | 1    | 18   | N    |
> +# | 5    | 2    | 23   | N    |
> +# | 6    | 1    | 24   | Y    |
> +# | 7    | 1    | END  | Y    |
> +# |------|------|------|------|
> +#
> +# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
> +# the line table entry for this line due to switching files for the
> +# file 1, line 18 non-statement line.  After patching however, GDB now
> +# discards the file 1, line 18 entry instead, and the breakpoint at
> +# line 22 should succeed.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_1 addr}
> +		    {high_pc line_label_7 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 5}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> new file mode 100644
> index 00000000000..46499919a8b
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> @@ -0,0 +1,179 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt |
> +# |------|------|------|------|
> +# | 1    | 1    | 16   | Y    |
> +# | 2    | 1    | 17   | Y    |
> +# | 3    | 2    | 21   | Y    |
> +# | 4    | 2    | 22   | Y    |
> +# | 4    | 1    | 18   | N    |
> +# | 5    | 1    | 19   | Y    |
> +# | 6    | 1    | 20   | Y    |
> +# | 7    | 1    | END  | Y    |
> +# |------|------|------|------|
> +#
> +#
> +# Place the first brekpoint at file 2, line 22 and a second breakpoint
> +# at file 1, line 19.  A third breakpoint is placed at file 1, line
> +# 18, but as this line table entry will have been discarded[1] the
> +# third breakpoint will actually be placed at the same location as the
> +# second breakpoint.
> +#
> +# [1] The entry for file 1, line 18 is discarded because it is at the
> +# same address as the previous entry, but the previous entry is-stmt,
> +# while line 18 is a non-stmt.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_1 addr}
> +		    {high_pc line_label_7 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
> +    "check for breakpoint at ${srcfile4}"
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile3}:19"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
> +    "check for breakpoint at ${srcfile3}"
> +
> +# Line table entry for line 18 will have been discarded, so this
> +# brekpoint will be at the same location as line 19.
> +gdb_test "break ${srcfile3}:18" \
> +    "Note: breakpoint $decimal also set at pc $hex.*"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> new file mode 100644
> index 00000000000..c683dc4bb8a
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> @@ -0,0 +1,192 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt |
> +# |------|------|------|------|
> +# | 1    | 1    | 16   | Y    |
> +# | 2    | 1    | 17   | Y    |
> +# | 3    | 2    | 21   | Y    |
> +# | 4    | 2    | 22   | Y    |
> +# | 4    | 1    | 18   | N    |
> +# | 5    | 1    | 19   | N    |
> +# | 6    | 1    | 20   | Y    |
> +# | 7    | 1    | END  | Y    |
> +# |------|------|------|------|
> +#
> +# Break at file 2, line 22, then single instruction step forward.  We
> +# should pass through line 19 and then encounter line 20.
> +#
> +# Currently we don't expect GDB to see file 1, line 18, as this is a
> +# non-stmt line in a different file at the same address as the
> +# previous is-stmt line.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_1 addr}
> +		    {high_pc line_label_7 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> +
> +gdb_continue_to_breakpoint "${srcfile4}:22" \
> +    ".* ${srcfile4} : 22 .*"
> +
> +# Now single instruction step forward.  Eventually we should hit
> +# ${srcfile3}:20, but before we do we should hit the non-statement
> +# line ${srcfile3}:19.
> +#
> +# We don't know how many instructions we'll need to step, but 100
> +# should be enough for everyone (surely), and this stops us looping
> +# forever if something goes wrong.
> +set found_line_19 0
> +set found_line_20 0
> +set keep_going 1
> +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
> +    set keep_going 0
> +    gdb_test_multiple "stepi" "stepi ${i}" {
> +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
> +	    set found_line_19 1
> +	    set keep_going 1
> +	}
> +
> +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
> +	    set found_line_20 1
> +	}
> +
> +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
> +	    # Not left line 22 yet.
> +	    set keep_going 1
> +	}
> +    }
> +}
> +
> +gdb_assert { $found_line_19 && $found_line_20 } \
> +    "found line 19 and 20"
> +
> +
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> new file mode 100644
> index 00000000000..a1b7b17cbeb
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> @@ -0,0 +1,46 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* Used to insert labels with which we can build a fake line table.  */
> +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
> +
> +volatile int var;
> +volatile int bar;
> +
> +/* Generate some code to take up some space.  */
> +#define FILLER do { \
> +    var = 99;	    \
> +} while (0)
> +
> +int
> +main ()
> +{					/* main prologue */
> +  asm ("main_label: .globl main_label");
> +  LL (1);	// F1, Ln 16
> +  FILLER;
> +  LL (2);	// F1, Ln 17
> +  FILLER;
> +  LL (3);	// F2, Ln 21
> +  FILLER;
> +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
> +  FILLER;
> +  LL (5);	// F1, Ln 19 !S
> +  FILLER;
> +  LL (6);	// F1, Ln 20
> +  FILLER;
> +  LL (7);
> +  FILLER;
> +  return 0;				/* main end */
> +}
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> new file mode 100644
> index 00000000000..a8331268a09
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> @@ -0,0 +1,24 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* dw2-inline-header.c : 16 */
> +/* dw2-inline-header.c : 17 */
> +/* dw2-inline-header.c : 18 */
> +/* dw2-inline-header.c : 19 */
> +/* dw2-inline-header.c : 20 */
> +/* dw2-inline-header.c : 21 */
> +/* dw2-inline-header.c : 22 */
> +/* dw2-inline-header.c : 23 */
> +/* dw2-inline-header.c : 24 */
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> new file mode 100644
> index 00000000000..7233acbcd76
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> @@ -0,0 +1,24 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* dw2-inline-header.h : 16 */
> +/* dw2-inline-header.h : 17 */
> +/* dw2-inline-header.h : 18 */
> +/* dw2-inline-header.h : 19 */
> +/* dw2-inline-header.h : 20 */
> +/* dw2-inline-header.h : 21 */
> +/* dw2-inline-header.h : 22 */
> +/* dw2-inline-header.h : 23 */
> +/* dw2-inline-header.h : 24 */
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-11  3:52               ` Bernd Edlinger
@ 2020-04-12 17:13                 ` Bernd Edlinger
  2020-04-14 11:28                   ` Andrew Burgess
  0 siblings, 1 reply; 79+ messages in thread
From: Bernd Edlinger @ 2020-04-12 17:13 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Tom Tromey

On 4/11/20 5:52 AM, Bernd Edlinger wrote:
> Andrew,
> 
> Just, a minor nit:
> 
> /home/ed/gnu/binutils-gdb/.git/rebase-apply/patch:665: new blank line at EOF.

Hmm, I hope you did not think that is me beginning to be insane, that was just the
incomprehensible output of "git am" on your patch part 2/2, funny isn't it?

I think that means:
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
index c683dc4..161a1fc 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
@@ -188,5 +188,3 @@ for { set i 0 } { $i < 100 && $keep_going } { incr i } {
 
 gdb_assert { $found_line_19 && $found_line_20 } \
     "found line 19 and 20"
-
-
-- 

Another bit I had to fix in order to make it compile

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index a146ed1..36e8b24 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -19786,7 +19786,7 @@ class lnp_state_machine
   m_discriminator = 0;
 
   m_last_address = m_address;
-  m_stmt_at_addr = false;
+  m_stmt_at_address = false;
 }
 
 void

Which makes me wonder if you compiled that at all, what you sent to me.
Or if that was actually a different version than intended, so please
double-check that.

If that is the case, please re-submit your test case patches as extra
patch files, and the code change also extra, since I intend to use
your test cases in my patch, and not having to dissect the patches
would be great.

And in order to compare the test results better, I restored the
step-and-next-inline.exp test:

diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
index a95e211..e3a5793 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -24,13 +24,6 @@ if { ![supports_statement_frontiers] } {
 proc do_test { use_header } {
     global srcfile testfile
 
-    if { $use_header } {
-	# This test will not pass due to poor debug information
-	# generated by GCC (at least upto 10.x).  See
-	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
-	return
-    }
-
     set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
     if { $use_header } {
 	lappend options additional_flags=-DUSE_NEXT_INLINE_H
@@ -59,37 +52,20 @@ proc do_test { use_header } {
     gdb_test "step" ".*" "step into get_alias_set"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 1"
-    # It's possible that this first failure (when not using a header
-    # file) is GCC's fault, though the remaining failures would best
-    # be fixed by adding location views support (though it could be
-    # that some easier heuristic could be figured out).  Still, it is
-    # not certain that the first failure wouldn't also be fixed by
-    # having location view support, so for now it is tagged as such.
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" ".*TREE_TYPE.*" "next step 1"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 2"
     gdb_test "next" ".*TREE_TYPE.*" "next step 2"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 3"
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" ".*TREE_TYPE.*" "next step 3"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 4"
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" "return 0.*" "next step 4"
     gdb_test "bt" \
 	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
 	"not in inline 5"
 
-    if {!$use_header} {
-	# With the debug from GCC 10.x (and earlier) GDB is currently
-	# unable to successfully complete the following tests when we
-	# are not using a header file.
-	kfail symtab/25507 "stepping tests"
-	return
-    }
-
     clean_restart ${executable}
 
     if ![runto_main] {


> 
> I am trying to find out which patch is better.
> So I applied your patch, an try to see where the differences are.
> 
> 
> Please be patient..
> 

Hmm, okay, I compared the two patches now.

First please don't take that personally :-)
but I think mine is a tiny bit better, because it
fixes the step-and-next-inline test case entirely.

And fixes your test case also almost completely:
 /gdb/testsuite/../../../binutils-gdb/gdb/testsuite/gdb.cp/step-and-next-inline.exp ...
 PASS: gdb.cp/step-and-next-inline.exp: no_header: in get_alias_set pass 2
-PASS: gdb.cp/step-and-next-inline.exp: no_header: in inline 1 pass 2
-PASS: gdb.cp/step-and-next-inline.exp: no_header: in inline 2 pass 2
+FAIL: gdb.cp/step-and-next-inline.exp: no_header: in inline 1 pass 2
+FAIL: gdb.cp/step-and-next-inline.exp: no_header: in inline 2 pass 2
 PASS: gdb.cp/step-and-next-inline.exp: no_header: in inline 3 pass 2
 PASS: gdb.cp/step-and-next-inline.exp: no_header: in main
 PASS: gdb.cp/step-and-next-inline.exp: no_header: in main pass 2
-PASS: gdb.cp/step-and-next-inline.exp: no_header: next step 1
+FAIL: gdb.cp/step-and-next-inline.exp: no_header: next step 1
 PASS: gdb.cp/step-and-next-inline.exp: no_header: next step 2
-PASS: gdb.cp/step-and-next-inline.exp: no_header: next step 3
-PASS: gdb.cp/step-and-next-inline.exp: no_header: next step 4
+FAIL: gdb.cp/step-and-next-inline.exp: no_header: next step 3
+FAIL: gdb.cp/step-and-next-inline.exp: no_header: next step 4
 PASS: gdb.cp/step-and-next-inline.exp: no_header: not in inline 1
 PASS: gdb.cp/step-and-next-inline.exp: no_header: not in inline 1 pass 2
 PASS: gdb.cp/step-and-next-inline.exp: no_header: not in inline 2
-PASS: gdb.cp/step-and-next-inline.exp: no_header: not in inline 2 pass 2
+FAIL: gdb.cp/step-and-next-inline.exp: no_header: not in inline 2 pass 2
 PASS: gdb.cp/step-and-next-inline.exp: no_header: not in inline 3
 PASS: gdb.cp/step-and-next-inline.exp: no_header: not in inline 3 pass 2
 PASS: gdb.cp/step-and-next-inline.exp: no_header: not in inline 4
 PASS: gdb.cp/step-and-next-inline.exp: no_header: not in inline 4 pass 2
 PASS: gdb.cp/step-and-next-inline.exp: no_header: not in inline 5
-PASS: gdb.cp/step-and-next-inline.exp: no_header: step 1
-PASS: gdb.cp/step-and-next-inline.exp: no_header: step 2
-PASS: gdb.cp/step-and-next-inline.exp: no_header: step 3
-PASS: gdb.cp/step-and-next-inline.exp: no_header: step 4
+FAIL: gdb.cp/step-and-next-inline.exp: no_header: step 1
+FAIL: gdb.cp/step-and-next-inline.exp: no_header: step 2
+FAIL: gdb.cp/step-and-next-inline.exp: no_header: step 3
+FAIL: gdb.cp/step-and-next-inline.exp: no_header: step 4
 PASS: gdb.cp/step-and-next-inline.exp: no_header: step 5
 PASS: gdb.cp/step-and-next-inline.exp: no_header: step 6
-PASS: gdb.cp/step-and-next-inline.exp: no_header: step 7
+FAIL: gdb.cp/step-and-next-inline.exp: no_header: step 7
 PASS: gdb.cp/step-and-next-inline.exp: no_header: step into get_alias_set
 PASS: gdb.cp/step-and-next-inline.exp: no_header: step into get_alias_set pass 2
 PASS: gdb.cp/step-and-next-inline.exp: use_header: in get_alias_set pass 2
-PASS: gdb.cp/step-and-next-inline.exp: use_header: in inline 1 pass 2
-PASS: gdb.cp/step-and-next-inline.exp: use_header: in inline 2 pass 2
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: in inline 1 pass 2
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: in inline 2 pass 2
 PASS: gdb.cp/step-and-next-inline.exp: use_header: in inline 3 pass 2
 PASS: gdb.cp/step-and-next-inline.exp: use_header: in main
 PASS: gdb.cp/step-and-next-inline.exp: use_header: in main pass 2
-PASS: gdb.cp/step-and-next-inline.exp: use_header: next step 1
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: next step 1
 PASS: gdb.cp/step-and-next-inline.exp: use_header: next step 2
-PASS: gdb.cp/step-and-next-inline.exp: use_header: next step 3
-PASS: gdb.cp/step-and-next-inline.exp: use_header: next step 4
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: next step 3
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: next step 4
 PASS: gdb.cp/step-and-next-inline.exp: use_header: not in inline 1
-PASS: gdb.cp/step-and-next-inline.exp: use_header: not in inline 1 pass 2
-PASS: gdb.cp/step-and-next-inline.exp: use_header: not in inline 2
-PASS: gdb.cp/step-and-next-inline.exp: use_header: not in inline 2 pass 2
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: not in inline 1 pass 2
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: not in inline 2
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: not in inline 2 pass 2
 PASS: gdb.cp/step-and-next-inline.exp: use_header: not in inline 3
 PASS: gdb.cp/step-and-next-inline.exp: use_header: not in inline 3 pass 2
-PASS: gdb.cp/step-and-next-inline.exp: use_header: not in inline 4
-PASS: gdb.cp/step-and-next-inline.exp: use_header: not in inline 4 pass 2
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: not in inline 4
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: not in inline 4 pass 2
 PASS: gdb.cp/step-and-next-inline.exp: use_header: not in inline 5
-PASS: gdb.cp/step-and-next-inline.exp: use_header: step 1
-PASS: gdb.cp/step-and-next-inline.exp: use_header: step 2
-PASS: gdb.cp/step-and-next-inline.exp: use_header: step 3
-PASS: gdb.cp/step-and-next-inline.exp: use_header: step 4
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: step 1
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: step 2
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: step 3
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: step 4
 PASS: gdb.cp/step-and-next-inline.exp: use_header: step 5
 PASS: gdb.cp/step-and-next-inline.exp: use_header: step 6
-PASS: gdb.cp/step-and-next-inline.exp: use_header: step 7
+FAIL: gdb.cp/step-and-next-inline.exp: use_header: step 7
 PASS: gdb.cp/step-and-next-inline.exp: use_header: step into get_alias_set
 PASS: gdb.cp/step-and-next-inline.exp: use_header: step into get_alias_set pass 2
 /gdb/testsuite/../../../binutils-gdb/gdb/testsuite/gdb.cp/subtypes.exp ...
@@ -52391,7 +52381,7 @@
 PASS: gdb.dwarf2/dw2-inline-header-2.exp: check for breakpoint at dw2-inline-header.c
 PASS: gdb.dwarf2/dw2-inline-header-2.exp: check for breakpoint at dw2-inline-header.h
 /gdb/testsuite/../../../binutils-gdb/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp ...
-FAIL: gdb.dwarf2/dw2-inline-header-3.exp: continue to breakpoint: dw2-inline-header.h:22
+PASS: gdb.dwarf2/dw2-inline-header-3.exp: continue to breakpoint: dw2-inline-header.h:22
 PASS: gdb.dwarf2/dw2-inline-header-3.exp: found line 19 and 20
 PASS: gdb.dwarf2/dw2-inline-header-3.exp: info breakpoints

So if it would be a socker game that would be 26:1 :-/

Regarding the one test where my patch is still able to be improved,
gdb.dwarf2/dw2-inline-header-3.exp: continue to breakpoint: dw2-inline-header.h:22

I think the issue there is that dw2-inline-header.h:22
and dw2-inline-header.c:18 is at the same adress.
To me it appears a bit arbitrary, which of the two lines the stack trace shows,
but on the other hand, if there is a breakpoint on dw2-inline-header.h:22
that is a clear indication that the user want to see  dw2-inline-header.h:22
while if there is a breakpoint on dw2-inline-header.c:18 the user would
certainly want to see dw2-inline-header.c:18 even if both are only different
views of the same address.  Yes.  I wonder if we can choose the SAL entry
which is presented, in a was that prefers SAL where there is a breakpoint.
That can't be impossible given all we alread achieved in this cursade ;-)
for better debug experience, don't you agree?

Looking at the debug info in the test case, I see a wrong subroutine
range here:

 <2><52>: Abbrev Number: 5 (DW_TAG_inlined_subroutine)
    <53>   DW_AT_abstract_origin: <0x31>
    <57>   DW_AT_low_pc      : 0x401122
    <5f>   DW_AT_high_pc     : 0x40115e
    <67>   DW_AT_call_file   : 1
    <68>   DW_AT_call_line   : 18

See the line table the subroutine is from 0x401136 to 0x401140:

  [0x000000ba]  Set File Name to entry 2 in the File Name Table
  [0x000000bc]  Extended opcode 2: set Address to 0x401136
  [0x000000c7]  Advance Line by 4 to 21
  [0x000000c9]  Copy
  [0x000000ca]  Extended opcode 2: set Address to 0x401140
  [0x000000d5]  Advance Line by 1 to 22
  [0x000000d7]  Copy
  [0x000000d8]  Advance Line by -4 to 18
  [0x000000da]  Set File Name to entry 1 in the File Name Table


with that wrong subroutine table I think my patch cas not a chance
to give correct results.  Could you please fix the subroutine
table, and then we see what I have to fix on my patch in order
to make it pass?

Also please check the subroutine entries on the other
dw2-inline-header tests, if the subroutine range info have similar
problems, I need correct ranges, otherwise my patch is unable to
produce correct output.


Thanks
Bernd.

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 1/2] gdb/testsuite: Move helper function into lib/dwarf.exp
  2020-04-06 20:18               ` Tom Tromey
@ 2020-04-14 11:18                 ` Andrew Burgess
  0 siblings, 0 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-04-14 11:18 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, Bernd Edlinger

* Tom Tromey <tom@tromey.com> [2020-04-06 14:18:28 -0600]:

> >>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
> 
> Andrew> gdb/testsuite/ChangeLog:
> 
> Andrew> 	* gdb.dwarf2/dw2-inline-many-frames.exp (get_func_info): Delete.
> Andrew> 	* gdb.dwarf2/dw2-inline-small-func.exp: Pass options to
> Andrew> 	get_func_info.
> Andrew> 	(get_func_info): Delete.
> Andrew> 	* gdb.dwarf2/dw2-is-stmt-2.exp (get_func_info): Delete.
> Andrew> 	* gdb.dwarf2/dw2-is-stmt.exp (get_func_info): Delete.
> Andrew> 	* lib/dwarf.exp (get_func_info): New function.
> 
> Thanks for doing this.  I didn't realize this function was copied so
> many times, or I would have merged them myself.  Looks good.

Thanks for reviewing this.

I've gone ahead and pushed this patch as it is a nice clean up
independent of the second patch, which is still under discussion.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-12 17:13                 ` Bernd Edlinger
@ 2020-04-14 11:28                   ` Andrew Burgess
  2020-04-14 11:37                     ` Bernd Edlinger
  2020-04-16 17:18                     ` Andrew Burgess
  0 siblings, 2 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-04-14 11:28 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: gdb-patches, Tom Tromey

Bernd,

As requested, rebased version of this patch for your feedback.  Sorry
for the delay, I took a short break over Easter.

Apologies for the previous build failure the fix you found was the
only issue, and was caused by a last second choice to rename
'm_stmt_at_addr' to 'm_stmt_at_address', combined with me making a
dumb mistake generating the patch from a slightly broken tree.  Please
be assured the patch was fully tested before I did the rename.

The trailing blank lines I have no excuse for.

Changes since previous version:

  - Rebase
  - Fix m_stmt_at_addr typo
  - Delete some trailing blank lines.

I look forward to hearing your thoughts.

Thanks,
Andrew

---

commit cf4bab4672356e190b166e063d2539ddad5eb542
Author: Andrew Burgess <andrew.burgess@embecosm.com>
Date:   Fri Apr 3 20:32:38 2020 +0100

    gdb: Preserve is-stmt lines when switch between files
    
    After the is-stmt support commit:
    
      commit 8c95582da858ac981f689a6f599acacb8c5c490f
      Date:   Mon Dec 30 21:04:51 2019 +0000
    
          gdb: Add support for tracking the DWARF line table is-stmt field
    
    A regression was observed where a breakpoint could no longer be placed
    in some cases.
    
    Consider a line table like this:
    
      File 1: test.c
      File 2: test.h
    
      | Addr | File | Line | Stmt |
      |------|------|------|------|
      | 1    | 1    | 16   | Y    |
      | 2    | 1    | 17   | Y    |
      | 3    | 2    | 21   | Y    |
      | 4    | 2    | 22   | Y    |
      | 4    | 1    | 18   | N    |
      | 5    | 2    | 23   | N    |
      | 6    | 1    | 24   | Y    |
      | 7    | 1    | END  | Y    |
      |------|------|------|------|
    
    Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
    built two line table structures:
    
      File 1                 File 2
      ------                 ------
    
      | Addr | Line |        | Addr | Line |
      |------|------|        |------|------|
      | 1    | 16   |        | 3    | 21   |
      | 2    | 17   |        | 4    | 22   |
      | 3    | END  |        | 6    | END  |
      | 6    | 24   |        |------|------|
      | 7    | END  |
      |------|------|
    
    After the is-stmt patch GDB now records non-stmt lines, so the
    generated line table structures look like this:
    
      File 1                   File 2
      ------                   ------
    
      | Addr | Line | Stmt |  | Addr | Line | Stmt |
      |------|------|------|  |------|------|------|
      | 1    | 16   | Y    |  | 3    | 21   | Y    |
      | 2    | 17   | Y    |  | 4    | 22   | Y    |
      | 3    | END  | Y    |  | 4    | END  | Y    |
      | 4    | 18   | N    |  | 5    | 23   | N    |
      | 5    | END  | Y    |  | 6    | END  | Y    |
      | 6    | 24   | Y    |  |------|------|------|
      | 7    | END  | Y    |
      |------|------|------|
    
    The problem is that in 'File 2', end END marker at address 4 causes
    the previous line table entry to be discarded, so we actually end up
    with this:
    
      File 2
      ------
    
      | Addr | Line | Stmt |
      |------|------|------|
      | 3    | 21   | Y    |
      | 4    | END  | Y    |
      | 5    | 23   | N    |
      | 6    | END  | Y    |
      |------|------|------|
    
    When a user tries to place a breakpoint in file 2 at line 22, this is
    no longer possible.
    
    The solution I propose here is that we ignore line table entries that
    would trigger a change of file if:
    
      1. The new line being added is at the same address as the previous
      line, and
    
      2. We have previously seen an is-stmt line at the current address.
    
    The result of this is that GDB switches file, and knows that some line
    entry (or entries) are going to be discarded, prefer to keep is-stmt
    lines and discard non-stmt lines.
    
    After this commit the lines tables are now:
    
      File 1                   File 2
      ------                   ------
    
      | Addr | Line | Stmt |  | Addr | Line | Stmt |
      |------|------|------|  |------|------|------|
      | 1    | 16   | Y    |  | 3    | 21   | Y    |
      | 2    | 17   | Y    |  | 4    | 22   | Y    |
      | 3    | END  | Y    |  | 5    | 23   | N    |
      | 5    | END  | Y    |  | 6    | END  | Y    |
      | 6    | 24   | Y    |  |------|------|------|
      | 7    | END  | Y    |
      |------|------|------|
    
    We've lost the non-stmt entry for file 1, line 18, but retained the
    is-stmt entry for file 2, line 22.  The user can now place a
    breakpoint at that location.
    
    One problem that came from this commit was the test
    gdb.cp/step-and-next-inline.exp, which broke in several places.  After
    looking at this test again I think that in some cases this test was
    only ever passing by pure luck.  The debug GCC is producing for this
    test is pretty broken.  I raised this GCC bug:
    
      https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
    
    for this and disabled one entire half of the test.  There are still
    some cases in here that do pass, and if/when GCC is fixed it would be
    great to enable this test again.
    
    gdb/ChangeLog:
    
            * dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
            member variable.
            <m_stmt_at_address>: New member variable.
            (lnp_state_machine::record_line): Don't record some lines, update
            tracking of is_stmt at the same address.
            (lnp_state_machine::lnp_state_machine): Initialise new member
            variables.
    
    gdb/testsuite/ChangeLog:
    
            * gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
            use_header case.
            * gdb.dwarf2/dw2-inline-header-1.exp: New file.
            * gdb.dwarf2/dw2-inline-header-2.exp: New file.
            * gdb.dwarf2/dw2-inline-header-3.exp: New file.
            * gdb.dwarf2/dw2-inline-header-lbls.c: New file.
            * gdb.dwarf2/dw2-inline-header.c: New file.
            * gdb.dwarf2/dw2-inline-header.h: New file.

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index da702205c60..36e8b24a29a 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -19508,6 +19508,15 @@ class lnp_state_machine
   /* The last file a line number was recorded for.  */
   struct subfile *m_last_subfile = NULL;
 
+  /* The address of the last line entry.  */
+  CORE_ADDR m_last_address;
+
+  /* Set to true when a previous line at the same address (using
+     m_last_address) had m_is_stmt true.  This is reset to false when a
+     line entry at a new address (m_address different to m_last_address) is
+     processed.  */
+  bool m_stmt_at_address = false;
+
   /* When true, record the lines we decode.  */
   bool m_currently_recording_lines = false;
 
@@ -19701,14 +19710,34 @@ lnp_state_machine::record_line (bool end_sequence)
       fe->included_p = 1;
       if (m_record_lines_p)
 	{
-	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
-	      || end_sequence)
+	  /* When we switch files we insert an end maker in the first file,
+	     switch to the second file and add a new line entry.  The
+	     problem is that the end marker inserted in the first file will
+	     discard any previous line entries at the same address.  If the
+	     line entries in the first file are marked as is-stmt, while
+	     the new line in the second file is non-stmt, then this means
+	     the end marker will discard is-stmt lines so we can have a
+	     non-stmt line.  This means that there are less addresses at
+	     which the user can insert a breakpoint.
+
+	     To improve this we track the last address in m_last_address,
+	     and whether we have seen an is-stmt at this address.  Then
+	     when switching files, if we have seen a stmt at the current
+	     address, and we are switching to create a non-stmt line, then
+	     discard the new line.  */
+	  bool file_changed
+	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
+	  bool ignore_this_line
+	    = (file_changed && !end_sequence && m_last_address == m_address
+	       && !m_is_stmt && m_stmt_at_address);
+
+	  if ((file_changed && !ignore_this_line) || end_sequence)
 	    {
 	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
 				 m_currently_recording_lines ? m_cu : nullptr);
 	    }
 
-	  if (!end_sequence)
+	  if (!end_sequence && !ignore_this_line)
 	    {
 	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
 
@@ -19727,6 +19756,15 @@ lnp_state_machine::record_line (bool end_sequence)
 	    }
 	}
     }
+
+  /* Track whether we have seen any m_is_stmt true at m_address in case we
+     have multiple line table entries all at m_address.  */
+  if (m_last_address != m_address)
+    {
+      m_stmt_at_address = false;
+      m_last_address = m_address;
+    }
+  m_stmt_at_address |= m_is_stmt;
 }
 
 lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
@@ -19746,6 +19784,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
   m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
   m_is_stmt = lh->default_is_stmt;
   m_discriminator = 0;
+
+  m_last_address = m_address;
+  m_stmt_at_address = false;
 }
 
 void
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
index 3733fa75570..a95e21194f9 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
 proc do_test { use_header } {
     global srcfile testfile
 
+    if { $use_header } {
+	# This test will not pass due to poor debug information
+	# generated by GCC (at least upto 10.x).  See
+	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
+	return
+    }
+
     set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
     if { $use_header } {
 	lappend options additional_flags=-DUSE_NEXT_INLINE_H
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
new file mode 100644
index 00000000000..6a1e990002c
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
@@ -0,0 +1,156 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 2    | 23   | N    |
+# | 6    | 1    | 24   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
+# the line table entry for this line due to switching files for the
+# file 1, line 18 non-statement line.  After patching however, GDB now
+# discards the file 1, line 18 entry instead, and the breakpoint at
+# line 22 should succeed.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 5}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_set_file 1}
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
new file mode 100644
index 00000000000..46499919a8b
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
@@ -0,0 +1,179 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 1    | 19   | Y    |
+# | 6    | 1    | 20   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+#
+# Place the first brekpoint at file 2, line 22 and a second breakpoint
+# at file 1, line 19.  A third breakpoint is placed at file 1, line
+# 18, but as this line table entry will have been discarded[1] the
+# third breakpoint will actually be placed at the same location as the
+# second breakpoint.
+#
+# [1] The entry for file 1, line 18 is discarded because it is at the
+# same address as the previous entry, but the previous entry is-stmt,
+# while line 18 is a non-stmt.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
+    "check for breakpoint at ${srcfile4}"
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile3}:19"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
+    "check for breakpoint at ${srcfile3}"
+
+# Line table entry for line 18 will have been discarded, so this
+# brekpoint will be at the same location as line 19.
+gdb_test "break ${srcfile3}:18" \
+    "Note: breakpoint $decimal also set at pc $hex.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
new file mode 100644
index 00000000000..161a1fc2aea
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
@@ -0,0 +1,190 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 1    | 19   | N    |
+# | 6    | 1    | 20   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+# Break at file 2, line 22, then single instruction step forward.  We
+# should pass through line 19 and then encounter line 20.
+#
+# Currently we don't expect GDB to see file 1, line 18, as this is a
+# non-stmt line in a different file at the same address as the
+# previous is-stmt line.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
+
+gdb_continue_to_breakpoint "${srcfile4}:22" \
+    ".* ${srcfile4} : 22 .*"
+
+# Now single instruction step forward.  Eventually we should hit
+# ${srcfile3}:20, but before we do we should hit the non-statement
+# line ${srcfile3}:19.
+#
+# We don't know how many instructions we'll need to step, but 100
+# should be enough for everyone (surely), and this stops us looping
+# forever if something goes wrong.
+set found_line_19 0
+set found_line_20 0
+set keep_going 1
+for { set i 0 } { $i < 100 && $keep_going } { incr i } {
+    set keep_going 0
+    gdb_test_multiple "stepi" "stepi ${i}" {
+	-re "${srcfile3} : 19 .*${gdb_prompt} " {
+	    set found_line_19 1
+	    set keep_going 1
+	}
+
+	-re "${srcfile3} : 20 .*${gdb_prompt} " {
+	    set found_line_20 1
+	}
+
+	-re "${srcfile4} : 22 .*${gdb_prompt} " {
+	    # Not left line 22 yet.
+	    set keep_going 1
+	}
+    }
+}
+
+gdb_assert { $found_line_19 && $found_line_20 } \
+    "found line 19 and 20"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
new file mode 100644
index 00000000000..a1b7b17cbeb
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
@@ -0,0 +1,46 @@
+/* Copyright 2020 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/>.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+volatile int var;
+volatile int bar;
+
+/* Generate some code to take up some space.  */
+#define FILLER do { \
+    var = 99;	    \
+} while (0)
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+  LL (1);	// F1, Ln 16
+  FILLER;
+  LL (2);	// F1, Ln 17
+  FILLER;
+  LL (3);	// F2, Ln 21
+  FILLER;
+  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
+  FILLER;
+  LL (5);	// F1, Ln 19 !S
+  FILLER;
+  LL (6);	// F1, Ln 20
+  FILLER;
+  LL (7);
+  FILLER;
+  return 0;				/* main end */
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
new file mode 100644
index 00000000000..a8331268a09
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.c : 16 */
+/* dw2-inline-header.c : 17 */
+/* dw2-inline-header.c : 18 */
+/* dw2-inline-header.c : 19 */
+/* dw2-inline-header.c : 20 */
+/* dw2-inline-header.c : 21 */
+/* dw2-inline-header.c : 22 */
+/* dw2-inline-header.c : 23 */
+/* dw2-inline-header.c : 24 */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
new file mode 100644
index 00000000000..7233acbcd76
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.h : 16 */
+/* dw2-inline-header.h : 17 */
+/* dw2-inline-header.h : 18 */
+/* dw2-inline-header.h : 19 */
+/* dw2-inline-header.h : 20 */
+/* dw2-inline-header.h : 21 */
+/* dw2-inline-header.h : 22 */
+/* dw2-inline-header.h : 23 */
+/* dw2-inline-header.h : 24 */


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-14 11:28                   ` Andrew Burgess
@ 2020-04-14 11:37                     ` Bernd Edlinger
  2020-04-14 11:41                       ` Bernd Edlinger
  2020-04-14 13:08                       ` Andrew Burgess
  2020-04-16 17:18                     ` Andrew Burgess
  1 sibling, 2 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-04-14 11:37 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey



On 4/14/20 1:28 PM, Andrew Burgess wrote:
> Bernd,
> 
> As requested, rebased version of this patch for your feedback.  Sorry
> for the delay, I took a short break over Easter.
> 
> Apologies for the previous build failure the fix you found was the
> only issue, and was caused by a last second choice to rename
> 'm_stmt_at_addr' to 'm_stmt_at_address', combined with me making a
> dumb mistake generating the patch from a slightly broken tree.  Please
> be assured the patch was fully tested before I did the rename.
> 
> The trailing blank lines I have no excuse for.
> 
> Changes since previous version:
> 
>   - Rebase
>   - Fix m_stmt_at_addr typo
>   - Delete some trailing blank lines.
> 
> I look forward to hearing your thoughts.
> 

Did you look at fixing the subrouting ranges.
My patch depends entirely on correct range info.

It is probably not very difficult for you to fix.


Thanks
Bernd.

> Thanks,
> Andrew
> 
> ---
> 
> commit cf4bab4672356e190b166e063d2539ddad5eb542
> Author: Andrew Burgess <andrew.burgess@embecosm.com>
> Date:   Fri Apr 3 20:32:38 2020 +0100
> 
>     gdb: Preserve is-stmt lines when switch between files
>     
>     After the is-stmt support commit:
>     
>       commit 8c95582da858ac981f689a6f599acacb8c5c490f
>       Date:   Mon Dec 30 21:04:51 2019 +0000
>     
>           gdb: Add support for tracking the DWARF line table is-stmt field
>     
>     A regression was observed where a breakpoint could no longer be placed
>     in some cases.
>     
>     Consider a line table like this:
>     
>       File 1: test.c
>       File 2: test.h
>     
>       | Addr | File | Line | Stmt |
>       |------|------|------|------|
>       | 1    | 1    | 16   | Y    |
>       | 2    | 1    | 17   | Y    |
>       | 3    | 2    | 21   | Y    |
>       | 4    | 2    | 22   | Y    |
>       | 4    | 1    | 18   | N    |
>       | 5    | 2    | 23   | N    |
>       | 6    | 1    | 24   | Y    |
>       | 7    | 1    | END  | Y    |
>       |------|------|------|------|
>     
>     Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
>     built two line table structures:
>     
>       File 1                 File 2
>       ------                 ------
>     
>       | Addr | Line |        | Addr | Line |
>       |------|------|        |------|------|
>       | 1    | 16   |        | 3    | 21   |
>       | 2    | 17   |        | 4    | 22   |
>       | 3    | END  |        | 6    | END  |
>       | 6    | 24   |        |------|------|
>       | 7    | END  |
>       |------|------|
>     
>     After the is-stmt patch GDB now records non-stmt lines, so the
>     generated line table structures look like this:
>     
>       File 1                   File 2
>       ------                   ------
>     
>       | Addr | Line | Stmt |  | Addr | Line | Stmt |
>       |------|------|------|  |------|------|------|
>       | 1    | 16   | Y    |  | 3    | 21   | Y    |
>       | 2    | 17   | Y    |  | 4    | 22   | Y    |
>       | 3    | END  | Y    |  | 4    | END  | Y    |
>       | 4    | 18   | N    |  | 5    | 23   | N    |
>       | 5    | END  | Y    |  | 6    | END  | Y    |
>       | 6    | 24   | Y    |  |------|------|------|
>       | 7    | END  | Y    |
>       |------|------|------|
>     
>     The problem is that in 'File 2', end END marker at address 4 causes
>     the previous line table entry to be discarded, so we actually end up
>     with this:
>     
>       File 2
>       ------
>     
>       | Addr | Line | Stmt |
>       |------|------|------|
>       | 3    | 21   | Y    |
>       | 4    | END  | Y    |
>       | 5    | 23   | N    |
>       | 6    | END  | Y    |
>       |------|------|------|
>     
>     When a user tries to place a breakpoint in file 2 at line 22, this is
>     no longer possible.
>     
>     The solution I propose here is that we ignore line table entries that
>     would trigger a change of file if:
>     
>       1. The new line being added is at the same address as the previous
>       line, and
>     
>       2. We have previously seen an is-stmt line at the current address.
>     
>     The result of this is that GDB switches file, and knows that some line
>     entry (or entries) are going to be discarded, prefer to keep is-stmt
>     lines and discard non-stmt lines.
>     
>     After this commit the lines tables are now:
>     
>       File 1                   File 2
>       ------                   ------
>     
>       | Addr | Line | Stmt |  | Addr | Line | Stmt |
>       |------|------|------|  |------|------|------|
>       | 1    | 16   | Y    |  | 3    | 21   | Y    |
>       | 2    | 17   | Y    |  | 4    | 22   | Y    |
>       | 3    | END  | Y    |  | 5    | 23   | N    |
>       | 5    | END  | Y    |  | 6    | END  | Y    |
>       | 6    | 24   | Y    |  |------|------|------|
>       | 7    | END  | Y    |
>       |------|------|------|
>     
>     We've lost the non-stmt entry for file 1, line 18, but retained the
>     is-stmt entry for file 2, line 22.  The user can now place a
>     breakpoint at that location.
>     
>     One problem that came from this commit was the test
>     gdb.cp/step-and-next-inline.exp, which broke in several places.  After
>     looking at this test again I think that in some cases this test was
>     only ever passing by pure luck.  The debug GCC is producing for this
>     test is pretty broken.  I raised this GCC bug:
>     
>       https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>     
>     for this and disabled one entire half of the test.  There are still
>     some cases in here that do pass, and if/when GCC is fixed it would be
>     great to enable this test again.
>     
>     gdb/ChangeLog:
>     
>             * dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
>             member variable.
>             <m_stmt_at_address>: New member variable.
>             (lnp_state_machine::record_line): Don't record some lines, update
>             tracking of is_stmt at the same address.
>             (lnp_state_machine::lnp_state_machine): Initialise new member
>             variables.
>     
>     gdb/testsuite/ChangeLog:
>     
>             * gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
>             use_header case.
>             * gdb.dwarf2/dw2-inline-header-1.exp: New file.
>             * gdb.dwarf2/dw2-inline-header-2.exp: New file.
>             * gdb.dwarf2/dw2-inline-header-3.exp: New file.
>             * gdb.dwarf2/dw2-inline-header-lbls.c: New file.
>             * gdb.dwarf2/dw2-inline-header.c: New file.
>             * gdb.dwarf2/dw2-inline-header.h: New file.
> 
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index da702205c60..36e8b24a29a 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -19508,6 +19508,15 @@ class lnp_state_machine
>    /* The last file a line number was recorded for.  */
>    struct subfile *m_last_subfile = NULL;
>  
> +  /* The address of the last line entry.  */
> +  CORE_ADDR m_last_address;
> +
> +  /* Set to true when a previous line at the same address (using
> +     m_last_address) had m_is_stmt true.  This is reset to false when a
> +     line entry at a new address (m_address different to m_last_address) is
> +     processed.  */
> +  bool m_stmt_at_address = false;
> +
>    /* When true, record the lines we decode.  */
>    bool m_currently_recording_lines = false;
>  
> @@ -19701,14 +19710,34 @@ lnp_state_machine::record_line (bool end_sequence)
>        fe->included_p = 1;
>        if (m_record_lines_p)
>  	{
> -	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
> -	      || end_sequence)
> +	  /* When we switch files we insert an end maker in the first file,
> +	     switch to the second file and add a new line entry.  The
> +	     problem is that the end marker inserted in the first file will
> +	     discard any previous line entries at the same address.  If the
> +	     line entries in the first file are marked as is-stmt, while
> +	     the new line in the second file is non-stmt, then this means
> +	     the end marker will discard is-stmt lines so we can have a
> +	     non-stmt line.  This means that there are less addresses at
> +	     which the user can insert a breakpoint.
> +
> +	     To improve this we track the last address in m_last_address,
> +	     and whether we have seen an is-stmt at this address.  Then
> +	     when switching files, if we have seen a stmt at the current
> +	     address, and we are switching to create a non-stmt line, then
> +	     discard the new line.  */
> +	  bool file_changed
> +	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
> +	  bool ignore_this_line
> +	    = (file_changed && !end_sequence && m_last_address == m_address
> +	       && !m_is_stmt && m_stmt_at_address);
> +
> +	  if ((file_changed && !ignore_this_line) || end_sequence)
>  	    {
>  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
>  				 m_currently_recording_lines ? m_cu : nullptr);
>  	    }
>  
> -	  if (!end_sequence)
> +	  if (!end_sequence && !ignore_this_line)
>  	    {
>  	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
>  
> @@ -19727,6 +19756,15 @@ lnp_state_machine::record_line (bool end_sequence)
>  	    }
>  	}
>      }
> +
> +  /* Track whether we have seen any m_is_stmt true at m_address in case we
> +     have multiple line table entries all at m_address.  */
> +  if (m_last_address != m_address)
> +    {
> +      m_stmt_at_address = false;
> +      m_last_address = m_address;
> +    }
> +  m_stmt_at_address |= m_is_stmt;
>  }
>  
>  lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
> @@ -19746,6 +19784,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>    m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
>    m_is_stmt = lh->default_is_stmt;
>    m_discriminator = 0;
> +
> +  m_last_address = m_address;
> +  m_stmt_at_address = false;
>  }
>  
>  void
> diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> index 3733fa75570..a95e21194f9 100644
> --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
>  proc do_test { use_header } {
>      global srcfile testfile
>  
> +    if { $use_header } {
> +	# This test will not pass due to poor debug information
> +	# generated by GCC (at least upto 10.x).  See
> +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> +	return
> +    }
> +
>      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
>      if { $use_header } {
>  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> new file mode 100644
> index 00000000000..6a1e990002c
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> @@ -0,0 +1,156 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt |
> +# |------|------|------|------|
> +# | 1    | 1    | 16   | Y    |
> +# | 2    | 1    | 17   | Y    |
> +# | 3    | 2    | 21   | Y    |
> +# | 4    | 2    | 22   | Y    |
> +# | 4    | 1    | 18   | N    |
> +# | 5    | 2    | 23   | N    |
> +# | 6    | 1    | 24   | Y    |
> +# | 7    | 1    | END  | Y    |
> +# |------|------|------|------|
> +#
> +# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
> +# the line table entry for this line due to switching files for the
> +# file 1, line 18 non-statement line.  After patching however, GDB now
> +# discards the file 1, line 18 entry instead, and the breakpoint at
> +# line 22 should succeed.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_1 addr}
> +		    {high_pc line_label_7 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 5}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> new file mode 100644
> index 00000000000..46499919a8b
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> @@ -0,0 +1,179 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt |
> +# |------|------|------|------|
> +# | 1    | 1    | 16   | Y    |
> +# | 2    | 1    | 17   | Y    |
> +# | 3    | 2    | 21   | Y    |
> +# | 4    | 2    | 22   | Y    |
> +# | 4    | 1    | 18   | N    |
> +# | 5    | 1    | 19   | Y    |
> +# | 6    | 1    | 20   | Y    |
> +# | 7    | 1    | END  | Y    |
> +# |------|------|------|------|
> +#
> +#
> +# Place the first brekpoint at file 2, line 22 and a second breakpoint
> +# at file 1, line 19.  A third breakpoint is placed at file 1, line
> +# 18, but as this line table entry will have been discarded[1] the
> +# third breakpoint will actually be placed at the same location as the
> +# second breakpoint.
> +#
> +# [1] The entry for file 1, line 18 is discarded because it is at the
> +# same address as the previous entry, but the previous entry is-stmt,
> +# while line 18 is a non-stmt.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_1 addr}
> +		    {high_pc line_label_7 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
> +    "check for breakpoint at ${srcfile4}"
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile3}:19"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
> +    "check for breakpoint at ${srcfile3}"
> +
> +# Line table entry for line 18 will have been discarded, so this
> +# brekpoint will be at the same location as line 19.
> +gdb_test "break ${srcfile3}:18" \
> +    "Note: breakpoint $decimal also set at pc $hex.*"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> new file mode 100644
> index 00000000000..161a1fc2aea
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> @@ -0,0 +1,190 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt |
> +# |------|------|------|------|
> +# | 1    | 1    | 16   | Y    |
> +# | 2    | 1    | 17   | Y    |
> +# | 3    | 2    | 21   | Y    |
> +# | 4    | 2    | 22   | Y    |
> +# | 4    | 1    | 18   | N    |
> +# | 5    | 1    | 19   | N    |
> +# | 6    | 1    | 20   | Y    |
> +# | 7    | 1    | END  | Y    |
> +# |------|------|------|------|
> +#
> +# Break at file 2, line 22, then single instruction step forward.  We
> +# should pass through line 19 and then encounter line 20.
> +#
> +# Currently we don't expect GDB to see file 1, line 18, as this is a
> +# non-stmt line in a different file at the same address as the
> +# previous is-stmt line.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_1 addr}
> +		    {high_pc line_label_7 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> +
> +gdb_continue_to_breakpoint "${srcfile4}:22" \
> +    ".* ${srcfile4} : 22 .*"
> +
> +# Now single instruction step forward.  Eventually we should hit
> +# ${srcfile3}:20, but before we do we should hit the non-statement
> +# line ${srcfile3}:19.
> +#
> +# We don't know how many instructions we'll need to step, but 100
> +# should be enough for everyone (surely), and this stops us looping
> +# forever if something goes wrong.
> +set found_line_19 0
> +set found_line_20 0
> +set keep_going 1
> +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
> +    set keep_going 0
> +    gdb_test_multiple "stepi" "stepi ${i}" {
> +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
> +	    set found_line_19 1
> +	    set keep_going 1
> +	}
> +
> +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
> +	    set found_line_20 1
> +	}
> +
> +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
> +	    # Not left line 22 yet.
> +	    set keep_going 1
> +	}
> +    }
> +}
> +
> +gdb_assert { $found_line_19 && $found_line_20 } \
> +    "found line 19 and 20"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> new file mode 100644
> index 00000000000..a1b7b17cbeb
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> @@ -0,0 +1,46 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* Used to insert labels with which we can build a fake line table.  */
> +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
> +
> +volatile int var;
> +volatile int bar;
> +
> +/* Generate some code to take up some space.  */
> +#define FILLER do { \
> +    var = 99;	    \
> +} while (0)
> +
> +int
> +main ()
> +{					/* main prologue */
> +  asm ("main_label: .globl main_label");
> +  LL (1);	// F1, Ln 16
> +  FILLER;
> +  LL (2);	// F1, Ln 17
> +  FILLER;
> +  LL (3);	// F2, Ln 21
> +  FILLER;
> +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
> +  FILLER;
> +  LL (5);	// F1, Ln 19 !S
> +  FILLER;
> +  LL (6);	// F1, Ln 20
> +  FILLER;
> +  LL (7);
> +  FILLER;
> +  return 0;				/* main end */
> +}
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> new file mode 100644
> index 00000000000..a8331268a09
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> @@ -0,0 +1,24 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* dw2-inline-header.c : 16 */
> +/* dw2-inline-header.c : 17 */
> +/* dw2-inline-header.c : 18 */
> +/* dw2-inline-header.c : 19 */
> +/* dw2-inline-header.c : 20 */
> +/* dw2-inline-header.c : 21 */
> +/* dw2-inline-header.c : 22 */
> +/* dw2-inline-header.c : 23 */
> +/* dw2-inline-header.c : 24 */
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> new file mode 100644
> index 00000000000..7233acbcd76
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> @@ -0,0 +1,24 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* dw2-inline-header.h : 16 */
> +/* dw2-inline-header.h : 17 */
> +/* dw2-inline-header.h : 18 */
> +/* dw2-inline-header.h : 19 */
> +/* dw2-inline-header.h : 20 */
> +/* dw2-inline-header.h : 21 */
> +/* dw2-inline-header.h : 22 */
> +/* dw2-inline-header.h : 23 */
> +/* dw2-inline-header.h : 24 */
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-14 11:37                     ` Bernd Edlinger
@ 2020-04-14 11:41                       ` Bernd Edlinger
  2020-04-14 13:08                       ` Andrew Burgess
  1 sibling, 0 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-04-14 11:41 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey



On 4/14/20 1:37 PM, Bernd Edlinger wrote:
> 
> 
> On 4/14/20 1:28 PM, Andrew Burgess wrote:
>> Bernd,
>>
>> As requested, rebased version of this patch for your feedback.  Sorry
>> for the delay, I took a short break over Easter.
>>
>> Apologies for the previous build failure the fix you found was the
>> only issue, and was caused by a last second choice to rename
>> 'm_stmt_at_addr' to 'm_stmt_at_address', combined with me making a
>> dumb mistake generating the patch from a slightly broken tree.  Please
>> be assured the patch was fully tested before I did the rename.
>>
>> The trailing blank lines I have no excuse for.
>>
>> Changes since previous version:
>>
>>   - Rebase
>>   - Fix m_stmt_at_addr typo
>>   - Delete some trailing blank lines.
>>
>> I look forward to hearing your thoughts.
>>
> 
> Did you look at fixing the subrouting ranges.

Sorry for typo, I blame my keyborad for it :)
I meant: subroutine ranges.

> My patch depends entirely on correct range info.
> 
> It is probably not very difficult for you to fix.
> 
> 
> Thanks
> Bernd.
> 
>> Thanks,
>> Andrew
>>
>> ---
>>
>> commit cf4bab4672356e190b166e063d2539ddad5eb542
>> Author: Andrew Burgess <andrew.burgess@embecosm.com>
>> Date:   Fri Apr 3 20:32:38 2020 +0100
>>
>>     gdb: Preserve is-stmt lines when switch between files
>>     
>>     After the is-stmt support commit:
>>     
>>       commit 8c95582da858ac981f689a6f599acacb8c5c490f
>>       Date:   Mon Dec 30 21:04:51 2019 +0000
>>     
>>           gdb: Add support for tracking the DWARF line table is-stmt field
>>     
>>     A regression was observed where a breakpoint could no longer be placed
>>     in some cases.
>>     
>>     Consider a line table like this:
>>     
>>       File 1: test.c
>>       File 2: test.h
>>     
>>       | Addr | File | Line | Stmt |
>>       |------|------|------|------|
>>       | 1    | 1    | 16   | Y    |
>>       | 2    | 1    | 17   | Y    |
>>       | 3    | 2    | 21   | Y    |
>>       | 4    | 2    | 22   | Y    |
>>       | 4    | 1    | 18   | N    |
>>       | 5    | 2    | 23   | N    |
>>       | 6    | 1    | 24   | Y    |
>>       | 7    | 1    | END  | Y    |
>>       |------|------|------|------|
>>     
>>     Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
>>     built two line table structures:
>>     
>>       File 1                 File 2
>>       ------                 ------
>>     
>>       | Addr | Line |        | Addr | Line |
>>       |------|------|        |------|------|
>>       | 1    | 16   |        | 3    | 21   |
>>       | 2    | 17   |        | 4    | 22   |
>>       | 3    | END  |        | 6    | END  |
>>       | 6    | 24   |        |------|------|
>>       | 7    | END  |
>>       |------|------|
>>     
>>     After the is-stmt patch GDB now records non-stmt lines, so the
>>     generated line table structures look like this:
>>     
>>       File 1                   File 2
>>       ------                   ------
>>     
>>       | Addr | Line | Stmt |  | Addr | Line | Stmt |
>>       |------|------|------|  |------|------|------|
>>       | 1    | 16   | Y    |  | 3    | 21   | Y    |
>>       | 2    | 17   | Y    |  | 4    | 22   | Y    |
>>       | 3    | END  | Y    |  | 4    | END  | Y    |
>>       | 4    | 18   | N    |  | 5    | 23   | N    |
>>       | 5    | END  | Y    |  | 6    | END  | Y    |
>>       | 6    | 24   | Y    |  |------|------|------|
>>       | 7    | END  | Y    |
>>       |------|------|------|
>>     
>>     The problem is that in 'File 2', end END marker at address 4 causes
>>     the previous line table entry to be discarded, so we actually end up
>>     with this:
>>     
>>       File 2
>>       ------
>>     
>>       | Addr | Line | Stmt |
>>       |------|------|------|
>>       | 3    | 21   | Y    |
>>       | 4    | END  | Y    |
>>       | 5    | 23   | N    |
>>       | 6    | END  | Y    |
>>       |------|------|------|
>>     
>>     When a user tries to place a breakpoint in file 2 at line 22, this is
>>     no longer possible.
>>     
>>     The solution I propose here is that we ignore line table entries that
>>     would trigger a change of file if:
>>     
>>       1. The new line being added is at the same address as the previous
>>       line, and
>>     
>>       2. We have previously seen an is-stmt line at the current address.
>>     
>>     The result of this is that GDB switches file, and knows that some line
>>     entry (or entries) are going to be discarded, prefer to keep is-stmt
>>     lines and discard non-stmt lines.
>>     
>>     After this commit the lines tables are now:
>>     
>>       File 1                   File 2
>>       ------                   ------
>>     
>>       | Addr | Line | Stmt |  | Addr | Line | Stmt |
>>       |------|------|------|  |------|------|------|
>>       | 1    | 16   | Y    |  | 3    | 21   | Y    |
>>       | 2    | 17   | Y    |  | 4    | 22   | Y    |
>>       | 3    | END  | Y    |  | 5    | 23   | N    |
>>       | 5    | END  | Y    |  | 6    | END  | Y    |
>>       | 6    | 24   | Y    |  |------|------|------|
>>       | 7    | END  | Y    |
>>       |------|------|------|
>>     
>>     We've lost the non-stmt entry for file 1, line 18, but retained the
>>     is-stmt entry for file 2, line 22.  The user can now place a
>>     breakpoint at that location.
>>     
>>     One problem that came from this commit was the test
>>     gdb.cp/step-and-next-inline.exp, which broke in several places.  After
>>     looking at this test again I think that in some cases this test was
>>     only ever passing by pure luck.  The debug GCC is producing for this
>>     test is pretty broken.  I raised this GCC bug:
>>     
>>       https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>>     
>>     for this and disabled one entire half of the test.  There are still
>>     some cases in here that do pass, and if/when GCC is fixed it would be
>>     great to enable this test again.
>>     
>>     gdb/ChangeLog:
>>     
>>             * dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
>>             member variable.
>>             <m_stmt_at_address>: New member variable.
>>             (lnp_state_machine::record_line): Don't record some lines, update
>>             tracking of is_stmt at the same address.
>>             (lnp_state_machine::lnp_state_machine): Initialise new member
>>             variables.
>>     
>>     gdb/testsuite/ChangeLog:
>>     
>>             * gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
>>             use_header case.
>>             * gdb.dwarf2/dw2-inline-header-1.exp: New file.
>>             * gdb.dwarf2/dw2-inline-header-2.exp: New file.
>>             * gdb.dwarf2/dw2-inline-header-3.exp: New file.
>>             * gdb.dwarf2/dw2-inline-header-lbls.c: New file.
>>             * gdb.dwarf2/dw2-inline-header.c: New file.
>>             * gdb.dwarf2/dw2-inline-header.h: New file.
>>
>> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
>> index da702205c60..36e8b24a29a 100644
>> --- a/gdb/dwarf2/read.c
>> +++ b/gdb/dwarf2/read.c
>> @@ -19508,6 +19508,15 @@ class lnp_state_machine
>>    /* The last file a line number was recorded for.  */
>>    struct subfile *m_last_subfile = NULL;
>>  
>> +  /* The address of the last line entry.  */
>> +  CORE_ADDR m_last_address;
>> +
>> +  /* Set to true when a previous line at the same address (using
>> +     m_last_address) had m_is_stmt true.  This is reset to false when a
>> +     line entry at a new address (m_address different to m_last_address) is
>> +     processed.  */
>> +  bool m_stmt_at_address = false;
>> +
>>    /* When true, record the lines we decode.  */
>>    bool m_currently_recording_lines = false;
>>  
>> @@ -19701,14 +19710,34 @@ lnp_state_machine::record_line (bool end_sequence)
>>        fe->included_p = 1;
>>        if (m_record_lines_p)
>>  	{
>> -	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
>> -	      || end_sequence)
>> +	  /* When we switch files we insert an end maker in the first file,
>> +	     switch to the second file and add a new line entry.  The
>> +	     problem is that the end marker inserted in the first file will
>> +	     discard any previous line entries at the same address.  If the
>> +	     line entries in the first file are marked as is-stmt, while
>> +	     the new line in the second file is non-stmt, then this means
>> +	     the end marker will discard is-stmt lines so we can have a
>> +	     non-stmt line.  This means that there are less addresses at
>> +	     which the user can insert a breakpoint.
>> +
>> +	     To improve this we track the last address in m_last_address,
>> +	     and whether we have seen an is-stmt at this address.  Then
>> +	     when switching files, if we have seen a stmt at the current
>> +	     address, and we are switching to create a non-stmt line, then
>> +	     discard the new line.  */
>> +	  bool file_changed
>> +	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
>> +	  bool ignore_this_line
>> +	    = (file_changed && !end_sequence && m_last_address == m_address
>> +	       && !m_is_stmt && m_stmt_at_address);
>> +
>> +	  if ((file_changed && !ignore_this_line) || end_sequence)
>>  	    {
>>  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
>>  				 m_currently_recording_lines ? m_cu : nullptr);
>>  	    }
>>  
>> -	  if (!end_sequence)
>> +	  if (!end_sequence && !ignore_this_line)
>>  	    {
>>  	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
>>  
>> @@ -19727,6 +19756,15 @@ lnp_state_machine::record_line (bool end_sequence)
>>  	    }
>>  	}
>>      }
>> +
>> +  /* Track whether we have seen any m_is_stmt true at m_address in case we
>> +     have multiple line table entries all at m_address.  */
>> +  if (m_last_address != m_address)
>> +    {
>> +      m_stmt_at_address = false;
>> +      m_last_address = m_address;
>> +    }
>> +  m_stmt_at_address |= m_is_stmt;
>>  }
>>  
>>  lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>> @@ -19746,6 +19784,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>>    m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
>>    m_is_stmt = lh->default_is_stmt;
>>    m_discriminator = 0;
>> +
>> +  m_last_address = m_address;
>> +  m_stmt_at_address = false;
>>  }
>>  
>>  void
>> diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>> index 3733fa75570..a95e21194f9 100644
>> --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>> +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
>> @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
>>  proc do_test { use_header } {
>>      global srcfile testfile
>>  
>> +    if { $use_header } {
>> +	# This test will not pass due to poor debug information
>> +	# generated by GCC (at least upto 10.x).  See
>> +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>> +	return
>> +    }
>> +
>>      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
>>      if { $use_header } {
>>  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>> new file mode 100644
>> index 00000000000..6a1e990002c
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>> @@ -0,0 +1,156 @@
>> +# Copyright 2020 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/>.
>> +
>> +# Setup a line table where:
>> +#
>> +# | Addr | File | Line | Stmt |
>> +# |------|------|------|------|
>> +# | 1    | 1    | 16   | Y    |
>> +# | 2    | 1    | 17   | Y    |
>> +# | 3    | 2    | 21   | Y    |
>> +# | 4    | 2    | 22   | Y    |
>> +# | 4    | 1    | 18   | N    |
>> +# | 5    | 2    | 23   | N    |
>> +# | 6    | 1    | 24   | Y    |
>> +# | 7    | 1    | END  | Y    |
>> +# |------|------|------|------|
>> +#
>> +# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
>> +# the line table entry for this line due to switching files for the
>> +# file 1, line 18 non-statement line.  After patching however, GDB now
>> +# discards the file 1, line 18 entry instead, and the breakpoint at
>> +# line 22 should succeed.
>> +
>> +load_lib dwarf.exp
>> +
>> +# This test can only be run on targets which support DWARF-2 and use gas.
>> +if {![dwarf2_support]} {
>> +    return 0
>> +}
>> +
>> +# The .c files use __attribute__.
>> +if [get_compiler_info] {
>> +    return -1
>> +}
>> +if !$gcc_compiled {
>> +    return 0
>> +}
>> +
>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>> +    dw2-inline-header.c dw2-inline-header.h
>> +
>> +set asm_file [standard_output_file $srcfile2]
>> +Dwarf::assemble $asm_file {
>> +    global srcdir subdir srcfile srcfile3 srcfile4
>> +    declare_labels lines_label callee_subprog_label
>> +
>> +    get_func_info main
>> +
>> +    cu {} {
>> +	compile_unit {
>> +	    {producer "gcc" }
>> +	    {language @DW_LANG_C}
>> +	    {name ${srcfile3}}
>> +	    {low_pc 0 addr}
>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>> +	} {
>> +	    callee_subprog_label: subprogram {
>> +		{external 1 flag}
>> +		{name callee}
>> +		{inline 3 data1}
>> +	    }
>> +	    subprogram {
>> +		{external 1 flag}
>> +		{name main}
>> +		{low_pc $main_start addr}
>> +		{high_pc "$main_start + $main_len" addr}
>> +	    } {
>> +		inlined_subroutine {
>> +		    {abstract_origin %$callee_subprog_label}
>> +		    {low_pc line_label_1 addr}
>> +		    {high_pc line_label_7 addr}
>> +		    {call_file 1 data1}
>> +		    {call_line 18 data1}
>> +		}
>> +	    }
>> +	}
>> +    }
>> +
>> +    lines {version 2 default_is_stmt 1} lines_label {
>> +	include_dir "${srcdir}/${subdir}"
>> +	file_name "$srcfile3" 1
>> +	file_name "$srcfile4" 1
>> +
>> +	program {
>> +	    {DW_LNE_set_address line_label_1}
>> +	    {DW_LNS_advance_line 15}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_2}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_set_file 2}
>> +	    {DW_LNE_set_address line_label_3}
>> +	    {DW_LNS_advance_line 4}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_4}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_advance_line -4}
>> +	    {DW_LNS_set_file 1}
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_set_file 2}
>> +	    {DW_LNE_set_address line_label_5}
>> +	    {DW_LNS_advance_line 5}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_set_file 1}
>> +	    {DW_LNE_set_address line_label_6}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_7}
>> +	    {DW_LNE_end_sequence}
>> +	}
>> +    }
>> +}
>> +
>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>> +    return -1
>> +}
>> +
>> +if ![runto_main] {
>> +    return -1
>> +}
>> +
>> +# Delete all breakpoints so that the output of "info breakpoints"
>> +# below will only contain a single breakpoint.
>> +delete_breakpoints
>> +
>> +# Place a breakpoint within the function in the header file.
>> +gdb_breakpoint "${srcfile4}:22"
>> +
>> +# Check that the breakpoint was placed where we expected.  It should
>> +# appear at the requested line.  When the bug in GDB was present the
>> +# breakpoint would be placed on one of the following lines instead.
>> +gdb_test "info breakpoints" \
>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>> new file mode 100644
>> index 00000000000..46499919a8b
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>> @@ -0,0 +1,179 @@
>> +# Copyright 2020 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/>.
>> +
>> +# Setup a line table where:
>> +#
>> +# | Addr | File | Line | Stmt |
>> +# |------|------|------|------|
>> +# | 1    | 1    | 16   | Y    |
>> +# | 2    | 1    | 17   | Y    |
>> +# | 3    | 2    | 21   | Y    |
>> +# | 4    | 2    | 22   | Y    |
>> +# | 4    | 1    | 18   | N    |
>> +# | 5    | 1    | 19   | Y    |
>> +# | 6    | 1    | 20   | Y    |
>> +# | 7    | 1    | END  | Y    |
>> +# |------|------|------|------|
>> +#
>> +#
>> +# Place the first brekpoint at file 2, line 22 and a second breakpoint
>> +# at file 1, line 19.  A third breakpoint is placed at file 1, line
>> +# 18, but as this line table entry will have been discarded[1] the
>> +# third breakpoint will actually be placed at the same location as the
>> +# second breakpoint.
>> +#
>> +# [1] The entry for file 1, line 18 is discarded because it is at the
>> +# same address as the previous entry, but the previous entry is-stmt,
>> +# while line 18 is a non-stmt.
>> +
>> +load_lib dwarf.exp
>> +
>> +# This test can only be run on targets which support DWARF-2 and use gas.
>> +if {![dwarf2_support]} {
>> +    return 0
>> +}
>> +
>> +# The .c files use __attribute__.
>> +if [get_compiler_info] {
>> +    return -1
>> +}
>> +if !$gcc_compiled {
>> +    return 0
>> +}
>> +
>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>> +    dw2-inline-header.c dw2-inline-header.h
>> +
>> +set asm_file [standard_output_file $srcfile2]
>> +Dwarf::assemble $asm_file {
>> +    global srcdir subdir srcfile srcfile3 srcfile4
>> +    declare_labels lines_label callee_subprog_label
>> +
>> +    get_func_info main
>> +
>> +    cu {} {
>> +	compile_unit {
>> +	    {producer "gcc" }
>> +	    {language @DW_LANG_C}
>> +	    {name ${srcfile3}}
>> +	    {low_pc 0 addr}
>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>> +	} {
>> +	    callee_subprog_label: subprogram {
>> +		{external 1 flag}
>> +		{name callee}
>> +		{inline 3 data1}
>> +	    }
>> +	    subprogram {
>> +		{external 1 flag}
>> +		{name main}
>> +		{low_pc $main_start addr}
>> +		{high_pc "$main_start + $main_len" addr}
>> +	    } {
>> +		inlined_subroutine {
>> +		    {abstract_origin %$callee_subprog_label}
>> +		    {low_pc line_label_1 addr}
>> +		    {high_pc line_label_7 addr}
>> +		    {call_file 1 data1}
>> +		    {call_line 18 data1}
>> +		}
>> +	    }
>> +	}
>> +    }
>> +
>> +    lines {version 2 default_is_stmt 1} lines_label {
>> +	include_dir "${srcdir}/${subdir}"
>> +	file_name "$srcfile3" 1
>> +	file_name "$srcfile4" 1
>> +
>> +	program {
>> +	    {DW_LNE_set_address line_label_1}
>> +	    {DW_LNS_advance_line 15}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_2}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_set_file 2}
>> +	    {DW_LNE_set_address line_label_3}
>> +	    {DW_LNS_advance_line 4}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_4}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_advance_line -4}
>> +	    {DW_LNS_set_file 1}
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_5}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_6}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_7}
>> +	    {DW_LNE_end_sequence}
>> +	}
>> +    }
>> +}
>> +
>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>> +    return -1
>> +}
>> +
>> +if ![runto_main] {
>> +    return -1
>> +}
>> +
>> +# Delete all breakpoints so that the output of "info breakpoints"
>> +# below will only contain a single breakpoint.
>> +delete_breakpoints
>> +
>> +# Place a breakpoint within the function in the header file.
>> +gdb_breakpoint "${srcfile4}:22"
>> +
>> +# Check that the breakpoint was placed where we expected.  It should
>> +# appear at the requested line.  When the bug in GDB was present the
>> +# breakpoint would be placed on one of the following lines instead.
>> +gdb_test "info breakpoints" \
>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
>> +    "check for breakpoint at ${srcfile4}"
>> +
>> +# Delete all breakpoints so that the output of "info breakpoints"
>> +# below will only contain a single breakpoint.
>> +delete_breakpoints
>> +
>> +# Place a breakpoint within the function in the header file.
>> +gdb_breakpoint "${srcfile3}:19"
>> +
>> +# Check that the breakpoint was placed where we expected.  It should
>> +# appear at the requested line.  When the bug in GDB was present the
>> +# breakpoint would be placed on one of the following lines instead.
>> +gdb_test "info breakpoints" \
>> +    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
>> +    "check for breakpoint at ${srcfile3}"
>> +
>> +# Line table entry for line 18 will have been discarded, so this
>> +# brekpoint will be at the same location as line 19.
>> +gdb_test "break ${srcfile3}:18" \
>> +    "Note: breakpoint $decimal also set at pc $hex.*"
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>> new file mode 100644
>> index 00000000000..161a1fc2aea
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>> @@ -0,0 +1,190 @@
>> +# Copyright 2020 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/>.
>> +
>> +# Setup a line table where:
>> +#
>> +# | Addr | File | Line | Stmt |
>> +# |------|------|------|------|
>> +# | 1    | 1    | 16   | Y    |
>> +# | 2    | 1    | 17   | Y    |
>> +# | 3    | 2    | 21   | Y    |
>> +# | 4    | 2    | 22   | Y    |
>> +# | 4    | 1    | 18   | N    |
>> +# | 5    | 1    | 19   | N    |
>> +# | 6    | 1    | 20   | Y    |
>> +# | 7    | 1    | END  | Y    |
>> +# |------|------|------|------|
>> +#
>> +# Break at file 2, line 22, then single instruction step forward.  We
>> +# should pass through line 19 and then encounter line 20.
>> +#
>> +# Currently we don't expect GDB to see file 1, line 18, as this is a
>> +# non-stmt line in a different file at the same address as the
>> +# previous is-stmt line.
>> +
>> +load_lib dwarf.exp
>> +
>> +# This test can only be run on targets which support DWARF-2 and use gas.
>> +if {![dwarf2_support]} {
>> +    return 0
>> +}
>> +
>> +# The .c files use __attribute__.
>> +if [get_compiler_info] {
>> +    return -1
>> +}
>> +if !$gcc_compiled {
>> +    return 0
>> +}
>> +
>> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
>> +    dw2-inline-header.c dw2-inline-header.h
>> +
>> +set asm_file [standard_output_file $srcfile2]
>> +Dwarf::assemble $asm_file {
>> +    global srcdir subdir srcfile srcfile3 srcfile4
>> +    declare_labels lines_label callee_subprog_label
>> +
>> +    get_func_info main
>> +
>> +    cu {} {
>> +	compile_unit {
>> +	    {producer "gcc" }
>> +	    {language @DW_LANG_C}
>> +	    {name ${srcfile3}}
>> +	    {low_pc 0 addr}
>> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
>> +	} {
>> +	    callee_subprog_label: subprogram {
>> +		{external 1 flag}
>> +		{name callee}
>> +		{inline 3 data1}
>> +	    }
>> +	    subprogram {
>> +		{external 1 flag}
>> +		{name main}
>> +		{low_pc $main_start addr}
>> +		{high_pc "$main_start + $main_len" addr}
>> +	    } {
>> +		inlined_subroutine {
>> +		    {abstract_origin %$callee_subprog_label}
>> +		    {low_pc line_label_1 addr}
>> +		    {high_pc line_label_7 addr}
>> +		    {call_file 1 data1}
>> +		    {call_line 18 data1}
>> +		}
>> +	    }
>> +	}
>> +    }
>> +
>> +    lines {version 2 default_is_stmt 1} lines_label {
>> +	include_dir "${srcdir}/${subdir}"
>> +	file_name "$srcfile3" 1
>> +	file_name "$srcfile4" 1
>> +
>> +	program {
>> +	    {DW_LNE_set_address line_label_1}
>> +	    {DW_LNS_advance_line 15}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_2}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_set_file 2}
>> +	    {DW_LNE_set_address line_label_3}
>> +	    {DW_LNS_advance_line 4}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_4}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNS_advance_line -4}
>> +	    {DW_LNS_set_file 1}
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_5}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_6}
>> +	    {DW_LNS_advance_line 1}
>> +	    {DW_LNS_negate_stmt}
>> +	    {DW_LNS_copy}
>> +
>> +	    {DW_LNE_set_address line_label_7}
>> +	    {DW_LNE_end_sequence}
>> +	}
>> +    }
>> +}
>> +
>> +if { [prepare_for_testing "failed to prepare" ${testfile} \
>> +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
>> +    return -1
>> +}
>> +
>> +if ![runto_main] {
>> +    return -1
>> +}
>> +
>> +# Delete all breakpoints so that the output of "info breakpoints"
>> +# below will only contain a single breakpoint.
>> +delete_breakpoints
>> +
>> +# Place a breakpoint within the function in the header file.
>> +gdb_breakpoint "${srcfile4}:22"
>> +
>> +# Check that the breakpoint was placed where we expected.  It should
>> +# appear at the requested line.  When the bug in GDB was present the
>> +# breakpoint would be placed on one of the following lines instead.
>> +gdb_test "info breakpoints" \
>> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
>> +
>> +gdb_continue_to_breakpoint "${srcfile4}:22" \
>> +    ".* ${srcfile4} : 22 .*"
>> +
>> +# Now single instruction step forward.  Eventually we should hit
>> +# ${srcfile3}:20, but before we do we should hit the non-statement
>> +# line ${srcfile3}:19.
>> +#
>> +# We don't know how many instructions we'll need to step, but 100
>> +# should be enough for everyone (surely), and this stops us looping
>> +# forever if something goes wrong.
>> +set found_line_19 0
>> +set found_line_20 0
>> +set keep_going 1
>> +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
>> +    set keep_going 0
>> +    gdb_test_multiple "stepi" "stepi ${i}" {
>> +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
>> +	    set found_line_19 1
>> +	    set keep_going 1
>> +	}
>> +
>> +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
>> +	    set found_line_20 1
>> +	}
>> +
>> +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
>> +	    # Not left line 22 yet.
>> +	    set keep_going 1
>> +	}
>> +    }
>> +}
>> +
>> +gdb_assert { $found_line_19 && $found_line_20 } \
>> +    "found line 19 and 20"
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>> new file mode 100644
>> index 00000000000..a1b7b17cbeb
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>> @@ -0,0 +1,46 @@
>> +/* Copyright 2020 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/>.  */
>> +
>> +/* Used to insert labels with which we can build a fake line table.  */
>> +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
>> +
>> +volatile int var;
>> +volatile int bar;
>> +
>> +/* Generate some code to take up some space.  */
>> +#define FILLER do { \
>> +    var = 99;	    \
>> +} while (0)
>> +
>> +int
>> +main ()
>> +{					/* main prologue */
>> +  asm ("main_label: .globl main_label");
>> +  LL (1);	// F1, Ln 16
>> +  FILLER;
>> +  LL (2);	// F1, Ln 17
>> +  FILLER;
>> +  LL (3);	// F2, Ln 21
>> +  FILLER;
>> +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
>> +  FILLER;
>> +  LL (5);	// F1, Ln 19 !S
>> +  FILLER;
>> +  LL (6);	// F1, Ln 20
>> +  FILLER;
>> +  LL (7);
>> +  FILLER;
>> +  return 0;				/* main end */
>> +}
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>> new file mode 100644
>> index 00000000000..a8331268a09
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>> @@ -0,0 +1,24 @@
>> +/* Copyright 2020 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/>.  */
>> +
>> +/* dw2-inline-header.c : 16 */
>> +/* dw2-inline-header.c : 17 */
>> +/* dw2-inline-header.c : 18 */
>> +/* dw2-inline-header.c : 19 */
>> +/* dw2-inline-header.c : 20 */
>> +/* dw2-inline-header.c : 21 */
>> +/* dw2-inline-header.c : 22 */
>> +/* dw2-inline-header.c : 23 */
>> +/* dw2-inline-header.c : 24 */
>> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>> new file mode 100644
>> index 00000000000..7233acbcd76
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
>> @@ -0,0 +1,24 @@
>> +/* Copyright 2020 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/>.  */
>> +
>> +/* dw2-inline-header.h : 16 */
>> +/* dw2-inline-header.h : 17 */
>> +/* dw2-inline-header.h : 18 */
>> +/* dw2-inline-header.h : 19 */
>> +/* dw2-inline-header.h : 20 */
>> +/* dw2-inline-header.h : 21 */
>> +/* dw2-inline-header.h : 22 */
>> +/* dw2-inline-header.h : 23 */
>> +/* dw2-inline-header.h : 24 */
>>

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-14 11:37                     ` Bernd Edlinger
  2020-04-14 11:41                       ` Bernd Edlinger
@ 2020-04-14 13:08                       ` Andrew Burgess
  1 sibling, 0 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-04-14 13:08 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: gdb-patches, Tom Tromey

* Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-04-14 13:37:56 +0200]:

> 
> 
> On 4/14/20 1:28 PM, Andrew Burgess wrote:
> > Bernd,
> > 
> > As requested, rebased version of this patch for your feedback.  Sorry
> > for the delay, I took a short break over Easter.
> > 
> > Apologies for the previous build failure the fix you found was the
> > only issue, and was caused by a last second choice to rename
> > 'm_stmt_at_addr' to 'm_stmt_at_address', combined with me making a
> > dumb mistake generating the patch from a slightly broken tree.  Please
> > be assured the patch was fully tested before I did the rename.
> > 
> > The trailing blank lines I have no excuse for.
> > 
> > Changes since previous version:
> > 
> >   - Rebase
> >   - Fix m_stmt_at_addr typo
> >   - Delete some trailing blank lines.
> > 
> > I look forward to hearing your thoughts.
> > 
> 
> Did you look at fixing the subrouting ranges.
> My patch depends entirely on correct range info.
> 
> It is probably not very difficult for you to fix.

I think you're referring here to the invalid range information
produced by GCC, but maybe I'm wrong and you mean something else?

If you are talking about the invalid ranges, as produced in
gdb.cp/step-and-next-inline.exp, then no, I haven't tried to fix this
as I don't think we can.  Unless we can show that GCC is always wrong,
and always wrong in the same say then I don't see how we can know that
GCC has cut a range short.  That's why I raised GCC bug:

  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474

with the hope that we'd get some clarity from the GCC maintainers.

I did hack GDB to "fix" the ranges, but this was done using fixed
addresses, basically, "if (range_end == 0x......) range_end =
0x......", which obviously isn't a real fix, but did show me that iff
GCC got the range information correct, then the test would basically
pass just fine.

Let me know if you were actually referencing something else.

Thanks
Andrew








> 
> 
> Thanks
> Bernd.
> 
> > Thanks,
> > Andrew
> > 
> > ---
> > 
> > commit cf4bab4672356e190b166e063d2539ddad5eb542
> > Author: Andrew Burgess <andrew.burgess@embecosm.com>
> > Date:   Fri Apr 3 20:32:38 2020 +0100
> > 
> >     gdb: Preserve is-stmt lines when switch between files
> >     
> >     After the is-stmt support commit:
> >     
> >       commit 8c95582da858ac981f689a6f599acacb8c5c490f
> >       Date:   Mon Dec 30 21:04:51 2019 +0000
> >     
> >           gdb: Add support for tracking the DWARF line table is-stmt field
> >     
> >     A regression was observed where a breakpoint could no longer be placed
> >     in some cases.
> >     
> >     Consider a line table like this:
> >     
> >       File 1: test.c
> >       File 2: test.h
> >     
> >       | Addr | File | Line | Stmt |
> >       |------|------|------|------|
> >       | 1    | 1    | 16   | Y    |
> >       | 2    | 1    | 17   | Y    |
> >       | 3    | 2    | 21   | Y    |
> >       | 4    | 2    | 22   | Y    |
> >       | 4    | 1    | 18   | N    |
> >       | 5    | 2    | 23   | N    |
> >       | 6    | 1    | 24   | Y    |
> >       | 7    | 1    | END  | Y    |
> >       |------|------|------|------|
> >     
> >     Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
> >     built two line table structures:
> >     
> >       File 1                 File 2
> >       ------                 ------
> >     
> >       | Addr | Line |        | Addr | Line |
> >       |------|------|        |------|------|
> >       | 1    | 16   |        | 3    | 21   |
> >       | 2    | 17   |        | 4    | 22   |
> >       | 3    | END  |        | 6    | END  |
> >       | 6    | 24   |        |------|------|
> >       | 7    | END  |
> >       |------|------|
> >     
> >     After the is-stmt patch GDB now records non-stmt lines, so the
> >     generated line table structures look like this:
> >     
> >       File 1                   File 2
> >       ------                   ------
> >     
> >       | Addr | Line | Stmt |  | Addr | Line | Stmt |
> >       |------|------|------|  |------|------|------|
> >       | 1    | 16   | Y    |  | 3    | 21   | Y    |
> >       | 2    | 17   | Y    |  | 4    | 22   | Y    |
> >       | 3    | END  | Y    |  | 4    | END  | Y    |
> >       | 4    | 18   | N    |  | 5    | 23   | N    |
> >       | 5    | END  | Y    |  | 6    | END  | Y    |
> >       | 6    | 24   | Y    |  |------|------|------|
> >       | 7    | END  | Y    |
> >       |------|------|------|
> >     
> >     The problem is that in 'File 2', end END marker at address 4 causes
> >     the previous line table entry to be discarded, so we actually end up
> >     with this:
> >     
> >       File 2
> >       ------
> >     
> >       | Addr | Line | Stmt |
> >       |------|------|------|
> >       | 3    | 21   | Y    |
> >       | 4    | END  | Y    |
> >       | 5    | 23   | N    |
> >       | 6    | END  | Y    |
> >       |------|------|------|
> >     
> >     When a user tries to place a breakpoint in file 2 at line 22, this is
> >     no longer possible.
> >     
> >     The solution I propose here is that we ignore line table entries that
> >     would trigger a change of file if:
> >     
> >       1. The new line being added is at the same address as the previous
> >       line, and
> >     
> >       2. We have previously seen an is-stmt line at the current address.
> >     
> >     The result of this is that GDB switches file, and knows that some line
> >     entry (or entries) are going to be discarded, prefer to keep is-stmt
> >     lines and discard non-stmt lines.
> >     
> >     After this commit the lines tables are now:
> >     
> >       File 1                   File 2
> >       ------                   ------
> >     
> >       | Addr | Line | Stmt |  | Addr | Line | Stmt |
> >       |------|------|------|  |------|------|------|
> >       | 1    | 16   | Y    |  | 3    | 21   | Y    |
> >       | 2    | 17   | Y    |  | 4    | 22   | Y    |
> >       | 3    | END  | Y    |  | 5    | 23   | N    |
> >       | 5    | END  | Y    |  | 6    | END  | Y    |
> >       | 6    | 24   | Y    |  |------|------|------|
> >       | 7    | END  | Y    |
> >       |------|------|------|
> >     
> >     We've lost the non-stmt entry for file 1, line 18, but retained the
> >     is-stmt entry for file 2, line 22.  The user can now place a
> >     breakpoint at that location.
> >     
> >     One problem that came from this commit was the test
> >     gdb.cp/step-and-next-inline.exp, which broke in several places.  After
> >     looking at this test again I think that in some cases this test was
> >     only ever passing by pure luck.  The debug GCC is producing for this
> >     test is pretty broken.  I raised this GCC bug:
> >     
> >       https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> >     
> >     for this and disabled one entire half of the test.  There are still
> >     some cases in here that do pass, and if/when GCC is fixed it would be
> >     great to enable this test again.
> >     
> >     gdb/ChangeLog:
> >     
> >             * dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
> >             member variable.
> >             <m_stmt_at_address>: New member variable.
> >             (lnp_state_machine::record_line): Don't record some lines, update
> >             tracking of is_stmt at the same address.
> >             (lnp_state_machine::lnp_state_machine): Initialise new member
> >             variables.
> >     
> >     gdb/testsuite/ChangeLog:
> >     
> >             * gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
> >             use_header case.
> >             * gdb.dwarf2/dw2-inline-header-1.exp: New file.
> >             * gdb.dwarf2/dw2-inline-header-2.exp: New file.
> >             * gdb.dwarf2/dw2-inline-header-3.exp: New file.
> >             * gdb.dwarf2/dw2-inline-header-lbls.c: New file.
> >             * gdb.dwarf2/dw2-inline-header.c: New file.
> >             * gdb.dwarf2/dw2-inline-header.h: New file.
> > 
> > diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> > index da702205c60..36e8b24a29a 100644
> > --- a/gdb/dwarf2/read.c
> > +++ b/gdb/dwarf2/read.c
> > @@ -19508,6 +19508,15 @@ class lnp_state_machine
> >    /* The last file a line number was recorded for.  */
> >    struct subfile *m_last_subfile = NULL;
> >  
> > +  /* The address of the last line entry.  */
> > +  CORE_ADDR m_last_address;
> > +
> > +  /* Set to true when a previous line at the same address (using
> > +     m_last_address) had m_is_stmt true.  This is reset to false when a
> > +     line entry at a new address (m_address different to m_last_address) is
> > +     processed.  */
> > +  bool m_stmt_at_address = false;
> > +
> >    /* When true, record the lines we decode.  */
> >    bool m_currently_recording_lines = false;
> >  
> > @@ -19701,14 +19710,34 @@ lnp_state_machine::record_line (bool end_sequence)
> >        fe->included_p = 1;
> >        if (m_record_lines_p)
> >  	{
> > -	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
> > -	      || end_sequence)
> > +	  /* When we switch files we insert an end maker in the first file,
> > +	     switch to the second file and add a new line entry.  The
> > +	     problem is that the end marker inserted in the first file will
> > +	     discard any previous line entries at the same address.  If the
> > +	     line entries in the first file are marked as is-stmt, while
> > +	     the new line in the second file is non-stmt, then this means
> > +	     the end marker will discard is-stmt lines so we can have a
> > +	     non-stmt line.  This means that there are less addresses at
> > +	     which the user can insert a breakpoint.
> > +
> > +	     To improve this we track the last address in m_last_address,
> > +	     and whether we have seen an is-stmt at this address.  Then
> > +	     when switching files, if we have seen a stmt at the current
> > +	     address, and we are switching to create a non-stmt line, then
> > +	     discard the new line.  */
> > +	  bool file_changed
> > +	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
> > +	  bool ignore_this_line
> > +	    = (file_changed && !end_sequence && m_last_address == m_address
> > +	       && !m_is_stmt && m_stmt_at_address);
> > +
> > +	  if ((file_changed && !ignore_this_line) || end_sequence)
> >  	    {
> >  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
> >  				 m_currently_recording_lines ? m_cu : nullptr);
> >  	    }
> >  
> > -	  if (!end_sequence)
> > +	  if (!end_sequence && !ignore_this_line)
> >  	    {
> >  	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
> >  
> > @@ -19727,6 +19756,15 @@ lnp_state_machine::record_line (bool end_sequence)
> >  	    }
> >  	}
> >      }
> > +
> > +  /* Track whether we have seen any m_is_stmt true at m_address in case we
> > +     have multiple line table entries all at m_address.  */
> > +  if (m_last_address != m_address)
> > +    {
> > +      m_stmt_at_address = false;
> > +      m_last_address = m_address;
> > +    }
> > +  m_stmt_at_address |= m_is_stmt;
> >  }
> >  
> >  lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
> > @@ -19746,6 +19784,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
> >    m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
> >    m_is_stmt = lh->default_is_stmt;
> >    m_discriminator = 0;
> > +
> > +  m_last_address = m_address;
> > +  m_stmt_at_address = false;
> >  }
> >  
> >  void
> > diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> > index 3733fa75570..a95e21194f9 100644
> > --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> > +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> > @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
> >  proc do_test { use_header } {
> >      global srcfile testfile
> >  
> > +    if { $use_header } {
> > +	# This test will not pass due to poor debug information
> > +	# generated by GCC (at least upto 10.x).  See
> > +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> > +	return
> > +    }
> > +
> >      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
> >      if { $use_header } {
> >  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> > new file mode 100644
> > index 00000000000..6a1e990002c
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> > @@ -0,0 +1,156 @@
> > +# Copyright 2020 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/>.
> > +
> > +# Setup a line table where:
> > +#
> > +# | Addr | File | Line | Stmt |
> > +# |------|------|------|------|
> > +# | 1    | 1    | 16   | Y    |
> > +# | 2    | 1    | 17   | Y    |
> > +# | 3    | 2    | 21   | Y    |
> > +# | 4    | 2    | 22   | Y    |
> > +# | 4    | 1    | 18   | N    |
> > +# | 5    | 2    | 23   | N    |
> > +# | 6    | 1    | 24   | Y    |
> > +# | 7    | 1    | END  | Y    |
> > +# |------|------|------|------|
> > +#
> > +# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
> > +# the line table entry for this line due to switching files for the
> > +# file 1, line 18 non-statement line.  After patching however, GDB now
> > +# discards the file 1, line 18 entry instead, and the breakpoint at
> > +# line 22 should succeed.
> > +
> > +load_lib dwarf.exp
> > +
> > +# This test can only be run on targets which support DWARF-2 and use gas.
> > +if {![dwarf2_support]} {
> > +    return 0
> > +}
> > +
> > +# The .c files use __attribute__.
> > +if [get_compiler_info] {
> > +    return -1
> > +}
> > +if !$gcc_compiled {
> > +    return 0
> > +}
> > +
> > +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> > +    dw2-inline-header.c dw2-inline-header.h
> > +
> > +set asm_file [standard_output_file $srcfile2]
> > +Dwarf::assemble $asm_file {
> > +    global srcdir subdir srcfile srcfile3 srcfile4
> > +    declare_labels lines_label callee_subprog_label
> > +
> > +    get_func_info main
> > +
> > +    cu {} {
> > +	compile_unit {
> > +	    {producer "gcc" }
> > +	    {language @DW_LANG_C}
> > +	    {name ${srcfile3}}
> > +	    {low_pc 0 addr}
> > +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> > +	} {
> > +	    callee_subprog_label: subprogram {
> > +		{external 1 flag}
> > +		{name callee}
> > +		{inline 3 data1}
> > +	    }
> > +	    subprogram {
> > +		{external 1 flag}
> > +		{name main}
> > +		{low_pc $main_start addr}
> > +		{high_pc "$main_start + $main_len" addr}
> > +	    } {
> > +		inlined_subroutine {
> > +		    {abstract_origin %$callee_subprog_label}
> > +		    {low_pc line_label_1 addr}
> > +		    {high_pc line_label_7 addr}
> > +		    {call_file 1 data1}
> > +		    {call_line 18 data1}
> > +		}
> > +	    }
> > +	}
> > +    }
> > +
> > +    lines {version 2 default_is_stmt 1} lines_label {
> > +	include_dir "${srcdir}/${subdir}"
> > +	file_name "$srcfile3" 1
> > +	file_name "$srcfile4" 1
> > +
> > +	program {
> > +	    {DW_LNE_set_address line_label_1}
> > +	    {DW_LNS_advance_line 15}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_2}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_set_file 2}
> > +	    {DW_LNE_set_address line_label_3}
> > +	    {DW_LNS_advance_line 4}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_4}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_advance_line -4}
> > +	    {DW_LNS_set_file 1}
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_set_file 2}
> > +	    {DW_LNE_set_address line_label_5}
> > +	    {DW_LNS_advance_line 5}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_set_file 1}
> > +	    {DW_LNE_set_address line_label_6}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_7}
> > +	    {DW_LNE_end_sequence}
> > +	}
> > +    }
> > +}
> > +
> > +if { [prepare_for_testing "failed to prepare" ${testfile} \
> > +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> > +    return -1
> > +}
> > +
> > +if ![runto_main] {
> > +    return -1
> > +}
> > +
> > +# Delete all breakpoints so that the output of "info breakpoints"
> > +# below will only contain a single breakpoint.
> > +delete_breakpoints
> > +
> > +# Place a breakpoint within the function in the header file.
> > +gdb_breakpoint "${srcfile4}:22"
> > +
> > +# Check that the breakpoint was placed where we expected.  It should
> > +# appear at the requested line.  When the bug in GDB was present the
> > +# breakpoint would be placed on one of the following lines instead.
> > +gdb_test "info breakpoints" \
> > +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> > new file mode 100644
> > index 00000000000..46499919a8b
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> > @@ -0,0 +1,179 @@
> > +# Copyright 2020 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/>.
> > +
> > +# Setup a line table where:
> > +#
> > +# | Addr | File | Line | Stmt |
> > +# |------|------|------|------|
> > +# | 1    | 1    | 16   | Y    |
> > +# | 2    | 1    | 17   | Y    |
> > +# | 3    | 2    | 21   | Y    |
> > +# | 4    | 2    | 22   | Y    |
> > +# | 4    | 1    | 18   | N    |
> > +# | 5    | 1    | 19   | Y    |
> > +# | 6    | 1    | 20   | Y    |
> > +# | 7    | 1    | END  | Y    |
> > +# |------|------|------|------|
> > +#
> > +#
> > +# Place the first brekpoint at file 2, line 22 and a second breakpoint
> > +# at file 1, line 19.  A third breakpoint is placed at file 1, line
> > +# 18, but as this line table entry will have been discarded[1] the
> > +# third breakpoint will actually be placed at the same location as the
> > +# second breakpoint.
> > +#
> > +# [1] The entry for file 1, line 18 is discarded because it is at the
> > +# same address as the previous entry, but the previous entry is-stmt,
> > +# while line 18 is a non-stmt.
> > +
> > +load_lib dwarf.exp
> > +
> > +# This test can only be run on targets which support DWARF-2 and use gas.
> > +if {![dwarf2_support]} {
> > +    return 0
> > +}
> > +
> > +# The .c files use __attribute__.
> > +if [get_compiler_info] {
> > +    return -1
> > +}
> > +if !$gcc_compiled {
> > +    return 0
> > +}
> > +
> > +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> > +    dw2-inline-header.c dw2-inline-header.h
> > +
> > +set asm_file [standard_output_file $srcfile2]
> > +Dwarf::assemble $asm_file {
> > +    global srcdir subdir srcfile srcfile3 srcfile4
> > +    declare_labels lines_label callee_subprog_label
> > +
> > +    get_func_info main
> > +
> > +    cu {} {
> > +	compile_unit {
> > +	    {producer "gcc" }
> > +	    {language @DW_LANG_C}
> > +	    {name ${srcfile3}}
> > +	    {low_pc 0 addr}
> > +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> > +	} {
> > +	    callee_subprog_label: subprogram {
> > +		{external 1 flag}
> > +		{name callee}
> > +		{inline 3 data1}
> > +	    }
> > +	    subprogram {
> > +		{external 1 flag}
> > +		{name main}
> > +		{low_pc $main_start addr}
> > +		{high_pc "$main_start + $main_len" addr}
> > +	    } {
> > +		inlined_subroutine {
> > +		    {abstract_origin %$callee_subprog_label}
> > +		    {low_pc line_label_1 addr}
> > +		    {high_pc line_label_7 addr}
> > +		    {call_file 1 data1}
> > +		    {call_line 18 data1}
> > +		}
> > +	    }
> > +	}
> > +    }
> > +
> > +    lines {version 2 default_is_stmt 1} lines_label {
> > +	include_dir "${srcdir}/${subdir}"
> > +	file_name "$srcfile3" 1
> > +	file_name "$srcfile4" 1
> > +
> > +	program {
> > +	    {DW_LNE_set_address line_label_1}
> > +	    {DW_LNS_advance_line 15}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_2}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_set_file 2}
> > +	    {DW_LNE_set_address line_label_3}
> > +	    {DW_LNS_advance_line 4}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_4}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_advance_line -4}
> > +	    {DW_LNS_set_file 1}
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_5}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_6}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_7}
> > +	    {DW_LNE_end_sequence}
> > +	}
> > +    }
> > +}
> > +
> > +if { [prepare_for_testing "failed to prepare" ${testfile} \
> > +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> > +    return -1
> > +}
> > +
> > +if ![runto_main] {
> > +    return -1
> > +}
> > +
> > +# Delete all breakpoints so that the output of "info breakpoints"
> > +# below will only contain a single breakpoint.
> > +delete_breakpoints
> > +
> > +# Place a breakpoint within the function in the header file.
> > +gdb_breakpoint "${srcfile4}:22"
> > +
> > +# Check that the breakpoint was placed where we expected.  It should
> > +# appear at the requested line.  When the bug in GDB was present the
> > +# breakpoint would be placed on one of the following lines instead.
> > +gdb_test "info breakpoints" \
> > +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
> > +    "check for breakpoint at ${srcfile4}"
> > +
> > +# Delete all breakpoints so that the output of "info breakpoints"
> > +# below will only contain a single breakpoint.
> > +delete_breakpoints
> > +
> > +# Place a breakpoint within the function in the header file.
> > +gdb_breakpoint "${srcfile3}:19"
> > +
> > +# Check that the breakpoint was placed where we expected.  It should
> > +# appear at the requested line.  When the bug in GDB was present the
> > +# breakpoint would be placed on one of the following lines instead.
> > +gdb_test "info breakpoints" \
> > +    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
> > +    "check for breakpoint at ${srcfile3}"
> > +
> > +# Line table entry for line 18 will have been discarded, so this
> > +# brekpoint will be at the same location as line 19.
> > +gdb_test "break ${srcfile3}:18" \
> > +    "Note: breakpoint $decimal also set at pc $hex.*"
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> > new file mode 100644
> > index 00000000000..161a1fc2aea
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> > @@ -0,0 +1,190 @@
> > +# Copyright 2020 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/>.
> > +
> > +# Setup a line table where:
> > +#
> > +# | Addr | File | Line | Stmt |
> > +# |------|------|------|------|
> > +# | 1    | 1    | 16   | Y    |
> > +# | 2    | 1    | 17   | Y    |
> > +# | 3    | 2    | 21   | Y    |
> > +# | 4    | 2    | 22   | Y    |
> > +# | 4    | 1    | 18   | N    |
> > +# | 5    | 1    | 19   | N    |
> > +# | 6    | 1    | 20   | Y    |
> > +# | 7    | 1    | END  | Y    |
> > +# |------|------|------|------|
> > +#
> > +# Break at file 2, line 22, then single instruction step forward.  We
> > +# should pass through line 19 and then encounter line 20.
> > +#
> > +# Currently we don't expect GDB to see file 1, line 18, as this is a
> > +# non-stmt line in a different file at the same address as the
> > +# previous is-stmt line.
> > +
> > +load_lib dwarf.exp
> > +
> > +# This test can only be run on targets which support DWARF-2 and use gas.
> > +if {![dwarf2_support]} {
> > +    return 0
> > +}
> > +
> > +# The .c files use __attribute__.
> > +if [get_compiler_info] {
> > +    return -1
> > +}
> > +if !$gcc_compiled {
> > +    return 0
> > +}
> > +
> > +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> > +    dw2-inline-header.c dw2-inline-header.h
> > +
> > +set asm_file [standard_output_file $srcfile2]
> > +Dwarf::assemble $asm_file {
> > +    global srcdir subdir srcfile srcfile3 srcfile4
> > +    declare_labels lines_label callee_subprog_label
> > +
> > +    get_func_info main
> > +
> > +    cu {} {
> > +	compile_unit {
> > +	    {producer "gcc" }
> > +	    {language @DW_LANG_C}
> > +	    {name ${srcfile3}}
> > +	    {low_pc 0 addr}
> > +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> > +	} {
> > +	    callee_subprog_label: subprogram {
> > +		{external 1 flag}
> > +		{name callee}
> > +		{inline 3 data1}
> > +	    }
> > +	    subprogram {
> > +		{external 1 flag}
> > +		{name main}
> > +		{low_pc $main_start addr}
> > +		{high_pc "$main_start + $main_len" addr}
> > +	    } {
> > +		inlined_subroutine {
> > +		    {abstract_origin %$callee_subprog_label}
> > +		    {low_pc line_label_1 addr}
> > +		    {high_pc line_label_7 addr}
> > +		    {call_file 1 data1}
> > +		    {call_line 18 data1}
> > +		}
> > +	    }
> > +	}
> > +    }
> > +
> > +    lines {version 2 default_is_stmt 1} lines_label {
> > +	include_dir "${srcdir}/${subdir}"
> > +	file_name "$srcfile3" 1
> > +	file_name "$srcfile4" 1
> > +
> > +	program {
> > +	    {DW_LNE_set_address line_label_1}
> > +	    {DW_LNS_advance_line 15}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_2}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_set_file 2}
> > +	    {DW_LNE_set_address line_label_3}
> > +	    {DW_LNS_advance_line 4}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_4}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNS_advance_line -4}
> > +	    {DW_LNS_set_file 1}
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_5}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_6}
> > +	    {DW_LNS_advance_line 1}
> > +	    {DW_LNS_negate_stmt}
> > +	    {DW_LNS_copy}
> > +
> > +	    {DW_LNE_set_address line_label_7}
> > +	    {DW_LNE_end_sequence}
> > +	}
> > +    }
> > +}
> > +
> > +if { [prepare_for_testing "failed to prepare" ${testfile} \
> > +	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
> > +    return -1
> > +}
> > +
> > +if ![runto_main] {
> > +    return -1
> > +}
> > +
> > +# Delete all breakpoints so that the output of "info breakpoints"
> > +# below will only contain a single breakpoint.
> > +delete_breakpoints
> > +
> > +# Place a breakpoint within the function in the header file.
> > +gdb_breakpoint "${srcfile4}:22"
> > +
> > +# Check that the breakpoint was placed where we expected.  It should
> > +# appear at the requested line.  When the bug in GDB was present the
> > +# breakpoint would be placed on one of the following lines instead.
> > +gdb_test "info breakpoints" \
> > +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> > +
> > +gdb_continue_to_breakpoint "${srcfile4}:22" \
> > +    ".* ${srcfile4} : 22 .*"
> > +
> > +# Now single instruction step forward.  Eventually we should hit
> > +# ${srcfile3}:20, but before we do we should hit the non-statement
> > +# line ${srcfile3}:19.
> > +#
> > +# We don't know how many instructions we'll need to step, but 100
> > +# should be enough for everyone (surely), and this stops us looping
> > +# forever if something goes wrong.
> > +set found_line_19 0
> > +set found_line_20 0
> > +set keep_going 1
> > +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
> > +    set keep_going 0
> > +    gdb_test_multiple "stepi" "stepi ${i}" {
> > +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
> > +	    set found_line_19 1
> > +	    set keep_going 1
> > +	}
> > +
> > +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
> > +	    set found_line_20 1
> > +	}
> > +
> > +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
> > +	    # Not left line 22 yet.
> > +	    set keep_going 1
> > +	}
> > +    }
> > +}
> > +
> > +gdb_assert { $found_line_19 && $found_line_20 } \
> > +    "found line 19 and 20"
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> > new file mode 100644
> > index 00000000000..a1b7b17cbeb
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> > @@ -0,0 +1,46 @@
> > +/* Copyright 2020 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/>.  */
> > +
> > +/* Used to insert labels with which we can build a fake line table.  */
> > +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
> > +
> > +volatile int var;
> > +volatile int bar;
> > +
> > +/* Generate some code to take up some space.  */
> > +#define FILLER do { \
> > +    var = 99;	    \
> > +} while (0)
> > +
> > +int
> > +main ()
> > +{					/* main prologue */
> > +  asm ("main_label: .globl main_label");
> > +  LL (1);	// F1, Ln 16
> > +  FILLER;
> > +  LL (2);	// F1, Ln 17
> > +  FILLER;
> > +  LL (3);	// F2, Ln 21
> > +  FILLER;
> > +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
> > +  FILLER;
> > +  LL (5);	// F1, Ln 19 !S
> > +  FILLER;
> > +  LL (6);	// F1, Ln 20
> > +  FILLER;
> > +  LL (7);
> > +  FILLER;
> > +  return 0;				/* main end */
> > +}
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> > new file mode 100644
> > index 00000000000..a8331268a09
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> > @@ -0,0 +1,24 @@
> > +/* Copyright 2020 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/>.  */
> > +
> > +/* dw2-inline-header.c : 16 */
> > +/* dw2-inline-header.c : 17 */
> > +/* dw2-inline-header.c : 18 */
> > +/* dw2-inline-header.c : 19 */
> > +/* dw2-inline-header.c : 20 */
> > +/* dw2-inline-header.c : 21 */
> > +/* dw2-inline-header.c : 22 */
> > +/* dw2-inline-header.c : 23 */
> > +/* dw2-inline-header.c : 24 */
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> > new file mode 100644
> > index 00000000000..7233acbcd76
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> > @@ -0,0 +1,24 @@
> > +/* Copyright 2020 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/>.  */
> > +
> > +/* dw2-inline-header.h : 16 */
> > +/* dw2-inline-header.h : 17 */
> > +/* dw2-inline-header.h : 18 */
> > +/* dw2-inline-header.h : 19 */
> > +/* dw2-inline-header.h : 20 */
> > +/* dw2-inline-header.h : 21 */
> > +/* dw2-inline-header.h : 22 */
> > +/* dw2-inline-header.h : 23 */
> > +/* dw2-inline-header.h : 24 */
> > 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-14 11:28                   ` Andrew Burgess
  2020-04-14 11:37                     ` Bernd Edlinger
@ 2020-04-16 17:18                     ` Andrew Burgess
  2020-04-22 21:13                       ` Tom Tromey
  1 sibling, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-04-16 17:18 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: gdb-patches, Tom Tromey

Tested this patch on another machine threw up another issue with the
tests.  I was compiling the test file at -O0 to extract label
locations, and -O1 when creating the final build.  On my older machine
this (apparently) wasn't a problem, but with a newer GCC this causes a
test failure.

The only change in this version is in the
dw2-inline-header-{1,2,3}.exp files.

Thanks,
Andrew

---

commit 05c134477fa87759d018d59dc579382582dc6005
Author: Andrew Burgess <andrew.burgess@embecosm.com>
Date:   Fri Apr 3 20:32:38 2020 +0100

    gdb: Preserve is-stmt lines when switch between files
    
    After the is-stmt support commit:
    
      commit 8c95582da858ac981f689a6f599acacb8c5c490f
      Date:   Mon Dec 30 21:04:51 2019 +0000
    
          gdb: Add support for tracking the DWARF line table is-stmt field
    
    A regression was observed where a breakpoint could no longer be placed
    in some cases.
    
    Consider a line table like this:
    
      File 1: test.c
      File 2: test.h
    
      | Addr | File | Line | Stmt |
      |------|------|------|------|
      | 1    | 1    | 16   | Y    |
      | 2    | 1    | 17   | Y    |
      | 3    | 2    | 21   | Y    |
      | 4    | 2    | 22   | Y    |
      | 4    | 1    | 18   | N    |
      | 5    | 2    | 23   | N    |
      | 6    | 1    | 24   | Y    |
      | 7    | 1    | END  | Y    |
      |------|------|------|------|
    
    Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
    built two line table structures:
    
      File 1                 File 2
      ------                 ------
    
      | Addr | Line |        | Addr | Line |
      |------|------|        |------|------|
      | 1    | 16   |        | 3    | 21   |
      | 2    | 17   |        | 4    | 22   |
      | 3    | END  |        | 6    | END  |
      | 6    | 24   |        |------|------|
      | 7    | END  |
      |------|------|
    
    After the is-stmt patch GDB now records non-stmt lines, so the
    generated line table structures look like this:
    
      File 1                   File 2
      ------                   ------
    
      | Addr | Line | Stmt |  | Addr | Line | Stmt |
      |------|------|------|  |------|------|------|
      | 1    | 16   | Y    |  | 3    | 21   | Y    |
      | 2    | 17   | Y    |  | 4    | 22   | Y    |
      | 3    | END  | Y    |  | 4    | END  | Y    |
      | 4    | 18   | N    |  | 5    | 23   | N    |
      | 5    | END  | Y    |  | 6    | END  | Y    |
      | 6    | 24   | Y    |  |------|------|------|
      | 7    | END  | Y    |
      |------|------|------|
    
    The problem is that in 'File 2', end END marker at address 4 causes
    the previous line table entry to be discarded, so we actually end up
    with this:
    
      File 2
      ------
    
      | Addr | Line | Stmt |
      |------|------|------|
      | 3    | 21   | Y    |
      | 4    | END  | Y    |
      | 5    | 23   | N    |
      | 6    | END  | Y    |
      |------|------|------|
    
    When a user tries to place a breakpoint in file 2 at line 22, this is
    no longer possible.
    
    The solution I propose here is that we ignore line table entries that
    would trigger a change of file if:
    
      1. The new line being added is at the same address as the previous
      line, and
    
      2. We have previously seen an is-stmt line at the current address.
    
    The result of this is that GDB switches file, and knows that some line
    entry (or entries) are going to be discarded, prefer to keep is-stmt
    lines and discard non-stmt lines.
    
    After this commit the lines tables are now:
    
      File 1                   File 2
      ------                   ------
    
      | Addr | Line | Stmt |  | Addr | Line | Stmt |
      |------|------|------|  |------|------|------|
      | 1    | 16   | Y    |  | 3    | 21   | Y    |
      | 2    | 17   | Y    |  | 4    | 22   | Y    |
      | 3    | END  | Y    |  | 5    | 23   | N    |
      | 5    | END  | Y    |  | 6    | END  | Y    |
      | 6    | 24   | Y    |  |------|------|------|
      | 7    | END  | Y    |
      |------|------|------|
    
    We've lost the non-stmt entry for file 1, line 18, but retained the
    is-stmt entry for file 2, line 22.  The user can now place a
    breakpoint at that location.
    
    One problem that came from this commit was the test
    gdb.cp/step-and-next-inline.exp, which broke in several places.  After
    looking at this test again I think that in some cases this test was
    only ever passing by pure luck.  The debug GCC is producing for this
    test is pretty broken.  I raised this GCC bug:
    
      https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
    
    for this and disabled one entire half of the test.  There are still
    some cases in here that do pass, and if/when GCC is fixed it would be
    great to enable this test again.
    
    gdb/ChangeLog:
    
            * dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
            member variable.
            <m_stmt_at_address>: New member variable.
            (lnp_state_machine::record_line): Don't record some lines, update
            tracking of is_stmt at the same address.
            (lnp_state_machine::lnp_state_machine): Initialise new member
            variables.
    
    gdb/testsuite/ChangeLog:
    
            * gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
            use_header case.
            * gdb.dwarf2/dw2-inline-header-1.exp: New file.
            * gdb.dwarf2/dw2-inline-header-2.exp: New file.
            * gdb.dwarf2/dw2-inline-header-3.exp: New file.
            * gdb.dwarf2/dw2-inline-header-lbls.c: New file.
            * gdb.dwarf2/dw2-inline-header.c: New file.
            * gdb.dwarf2/dw2-inline-header.h: New file.

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index da702205c60..36e8b24a29a 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -19508,6 +19508,15 @@ class lnp_state_machine
   /* The last file a line number was recorded for.  */
   struct subfile *m_last_subfile = NULL;
 
+  /* The address of the last line entry.  */
+  CORE_ADDR m_last_address;
+
+  /* Set to true when a previous line at the same address (using
+     m_last_address) had m_is_stmt true.  This is reset to false when a
+     line entry at a new address (m_address different to m_last_address) is
+     processed.  */
+  bool m_stmt_at_address = false;
+
   /* When true, record the lines we decode.  */
   bool m_currently_recording_lines = false;
 
@@ -19701,14 +19710,34 @@ lnp_state_machine::record_line (bool end_sequence)
       fe->included_p = 1;
       if (m_record_lines_p)
 	{
-	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
-	      || end_sequence)
+	  /* When we switch files we insert an end maker in the first file,
+	     switch to the second file and add a new line entry.  The
+	     problem is that the end marker inserted in the first file will
+	     discard any previous line entries at the same address.  If the
+	     line entries in the first file are marked as is-stmt, while
+	     the new line in the second file is non-stmt, then this means
+	     the end marker will discard is-stmt lines so we can have a
+	     non-stmt line.  This means that there are less addresses at
+	     which the user can insert a breakpoint.
+
+	     To improve this we track the last address in m_last_address,
+	     and whether we have seen an is-stmt at this address.  Then
+	     when switching files, if we have seen a stmt at the current
+	     address, and we are switching to create a non-stmt line, then
+	     discard the new line.  */
+	  bool file_changed
+	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
+	  bool ignore_this_line
+	    = (file_changed && !end_sequence && m_last_address == m_address
+	       && !m_is_stmt && m_stmt_at_address);
+
+	  if ((file_changed && !ignore_this_line) || end_sequence)
 	    {
 	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
 				 m_currently_recording_lines ? m_cu : nullptr);
 	    }
 
-	  if (!end_sequence)
+	  if (!end_sequence && !ignore_this_line)
 	    {
 	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
 
@@ -19727,6 +19756,15 @@ lnp_state_machine::record_line (bool end_sequence)
 	    }
 	}
     }
+
+  /* Track whether we have seen any m_is_stmt true at m_address in case we
+     have multiple line table entries all at m_address.  */
+  if (m_last_address != m_address)
+    {
+      m_stmt_at_address = false;
+      m_last_address = m_address;
+    }
+  m_stmt_at_address |= m_is_stmt;
 }
 
 lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
@@ -19746,6 +19784,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
   m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
   m_is_stmt = lh->default_is_stmt;
   m_discriminator = 0;
+
+  m_last_address = m_address;
+  m_stmt_at_address = false;
 }
 
 void
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
index 3733fa75570..a95e21194f9 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
 proc do_test { use_header } {
     global srcfile testfile
 
+    if { $use_header } {
+	# This test will not pass due to poor debug information
+	# generated by GCC (at least upto 10.x).  See
+	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
+	return
+    }
+
     set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
     if { $use_header } {
 	lappend options additional_flags=-DUSE_NEXT_INLINE_H
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
new file mode 100644
index 00000000000..0ba481ad4b7
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
@@ -0,0 +1,159 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 2    | 23   | N    |
+# | 6    | 1    | 24   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
+# the line table entry for this line due to switching files for the
+# file 1, line 18 non-statement line.  After patching however, GDB now
+# discards the file 1, line 18 entry instead, and the breakpoint at
+# line 22 should succeed.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set build_options {nodebug optimize=-O1}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    global build_options
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main $build_options
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 5}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_set_file 1}
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] $build_options] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
new file mode 100644
index 00000000000..b8ce88ce32e
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
@@ -0,0 +1,182 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 1    | 19   | Y    |
+# | 6    | 1    | 20   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+#
+# Place the first brekpoint at file 2, line 22 and a second breakpoint
+# at file 1, line 19.  A third breakpoint is placed at file 1, line
+# 18, but as this line table entry will have been discarded[1] the
+# third breakpoint will actually be placed at the same location as the
+# second breakpoint.
+#
+# [1] The entry for file 1, line 18 is discarded because it is at the
+# same address as the previous entry, but the previous entry is-stmt,
+# while line 18 is a non-stmt.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set build_options {nodebug optimize=-O1}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    global build_options
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main $build_options
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] $build_options] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
+    "check for breakpoint at ${srcfile4}"
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile3}:19"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
+    "check for breakpoint at ${srcfile3}"
+
+# Line table entry for line 18 will have been discarded, so this
+# brekpoint will be at the same location as line 19.
+gdb_test "break ${srcfile3}:18" \
+    "Note: breakpoint $decimal also set at pc $hex.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
new file mode 100644
index 00000000000..a2a2dfd07bd
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
@@ -0,0 +1,193 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 1    | 19   | N    |
+# | 6    | 1    | 20   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+# Break at file 2, line 22, then single instruction step forward.  We
+# should pass through line 19 and then encounter line 20.
+#
+# Currently we don't expect GDB to see file 1, line 18, as this is a
+# non-stmt line in a different file at the same address as the
+# previous is-stmt line.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set build_options {nodebug optimize=-O1}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    global build_options
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main $build_options
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] $build_options] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
+
+gdb_continue_to_breakpoint "${srcfile4}:22" \
+    ".* ${srcfile4} : 22 .*"
+
+# Now single instruction step forward.  Eventually we should hit
+# ${srcfile3}:20, but before we do we should hit the non-statement
+# line ${srcfile3}:19.
+#
+# We don't know how many instructions we'll need to step, but 100
+# should be enough for everyone (surely), and this stops us looping
+# forever if something goes wrong.
+set found_line_19 0
+set found_line_20 0
+set keep_going 1
+for { set i 0 } { $i < 100 && $keep_going } { incr i } {
+    set keep_going 0
+    gdb_test_multiple "stepi" "stepi ${i}" {
+	-re "${srcfile3} : 19 .*${gdb_prompt} " {
+	    set found_line_19 1
+	    set keep_going 1
+	}
+
+	-re "${srcfile3} : 20 .*${gdb_prompt} " {
+	    set found_line_20 1
+	}
+
+	-re "${srcfile4} : 22 .*${gdb_prompt} " {
+	    # Not left line 22 yet.
+	    set keep_going 1
+	}
+    }
+}
+
+gdb_assert { $found_line_19 && $found_line_20 } \
+    "found line 19 and 20"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
new file mode 100644
index 00000000000..a1b7b17cbeb
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
@@ -0,0 +1,46 @@
+/* Copyright 2020 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/>.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+volatile int var;
+volatile int bar;
+
+/* Generate some code to take up some space.  */
+#define FILLER do { \
+    var = 99;	    \
+} while (0)
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+  LL (1);	// F1, Ln 16
+  FILLER;
+  LL (2);	// F1, Ln 17
+  FILLER;
+  LL (3);	// F2, Ln 21
+  FILLER;
+  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
+  FILLER;
+  LL (5);	// F1, Ln 19 !S
+  FILLER;
+  LL (6);	// F1, Ln 20
+  FILLER;
+  LL (7);
+  FILLER;
+  return 0;				/* main end */
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
new file mode 100644
index 00000000000..a8331268a09
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.c : 16 */
+/* dw2-inline-header.c : 17 */
+/* dw2-inline-header.c : 18 */
+/* dw2-inline-header.c : 19 */
+/* dw2-inline-header.c : 20 */
+/* dw2-inline-header.c : 21 */
+/* dw2-inline-header.c : 22 */
+/* dw2-inline-header.c : 23 */
+/* dw2-inline-header.c : 24 */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
new file mode 100644
index 00000000000..7233acbcd76
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.h : 16 */
+/* dw2-inline-header.h : 17 */
+/* dw2-inline-header.h : 18 */
+/* dw2-inline-header.h : 19 */
+/* dw2-inline-header.h : 20 */
+/* dw2-inline-header.h : 21 */
+/* dw2-inline-header.h : 22 */
+/* dw2-inline-header.h : 23 */
+/* dw2-inline-header.h : 24 */

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-16 17:18                     ` Andrew Burgess
@ 2020-04-22 21:13                       ` Tom Tromey
  2020-04-25  7:06                         ` Bernd Edlinger
  0 siblings, 1 reply; 79+ messages in thread
From: Tom Tromey @ 2020-04-22 21:13 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Bernd Edlinger, Tom Tromey, gdb-patches

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

I read through the various threads on this topic, though I think maybe
not very well.

IIUC, the issue at hand is that there are two proposed patches to fix
various issues in this area.


Andrew's patch takes us roughly to the status quo from before is-stmt
landed.  This patch takes the view that GCC is currently buggy, so one
test is partly disabled; and there's a new bug so we can fix GDB once
GCC is fixed.

Bernd's patch takes the view that GCC won't be fixed and tries to work
around the problem; it drops at least one "FILE:LINE" case that
currently works.


Is that a fair summary?  There were many messages and details, so I'm
not sure I either understood it all or that it can be summarized like
this.

If that's fair, then my inclination is to move forward with Andrew's
patch for the time being, with the primary justification being
conservatism.  Bernd's patch can always land if/when we have some
clarification from GCC about whether the bugs there will be fixed.

Let me know what you think.

Tom

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-22 21:13                       ` Tom Tromey
@ 2020-04-25  7:06                         ` Bernd Edlinger
  2020-04-27 10:34                           ` Andrew Burgess
  0 siblings, 1 reply; 79+ messages in thread
From: Bernd Edlinger @ 2020-04-25  7:06 UTC (permalink / raw)
  To: Tom Tromey, Andrew Burgess; +Cc: gdb-patches, Alexandre Oliva

CC Alexandre Oliva, who maintains this part of GCC.


On 4/22/20 11:13 PM, Tom Tromey wrote:
>>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
> 
> I read through the various threads on this topic, though I think maybe
> not very well.
> 
> IIUC, the issue at hand is that there are two proposed patches to fix
> various issues in this area.
> 
> 
> Andrew's patch takes us roughly to the status quo from before is-stmt
> landed.  This patch takes the view that GCC is currently buggy, so one
> test is partly disabled; and there's a new bug so we can fix GDB once
> GCC is fixed.
> 
> Bernd's patch takes the view that GCC won't be fixed and tries to work
> around the problem; it drops at least one "FILE:LINE" case that
> currently works.
> 
> 
> Is that a fair summary?  There were many messages and details, so I'm
> not sure I either understood it all or that it can be summarized like
> this.
> 

Yes, I agree with this summary.

> If that's fair, then my inclination is to move forward with Andrew's
> patch for the time being, with the primary justification being
> conservatism.  Bernd's patch can always land if/when we have some
> clarification from GCC about whether the bugs there will be fixed.
> 
> Let me know what you think.
> 

I think if we take Andrew's patch the only possible outcome
will be that gcc will disable that statement frontiers debug
option by default, because they do not understand what we
want from them.  (I am one of them, but I came here to understand
you better)


Bernd.

> Tom
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-25  7:06                         ` Bernd Edlinger
@ 2020-04-27 10:34                           ` Andrew Burgess
  2020-05-14 20:18                             ` Tom Tromey
  0 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-04-27 10:34 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: Tom Tromey, gdb-patches, Alexandre Oliva

* Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-04-25 09:06:35 +0200]:

> CC Alexandre Oliva, who maintains this part of GCC.
> 
> 
> On 4/22/20 11:13 PM, Tom Tromey wrote:
> >>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
> > 
> > I read through the various threads on this topic, though I think maybe
> > not very well.
> > 
> > IIUC, the issue at hand is that there are two proposed patches to fix
> > various issues in this area.
> > 
> > 
> > Andrew's patch takes us roughly to the status quo from before is-stmt
> > landed.  This patch takes the view that GCC is currently buggy, so one
> > test is partly disabled; and there's a new bug so we can fix GDB once
> > GCC is fixed.
> > 
> > Bernd's patch takes the view that GCC won't be fixed and tries to work
> > around the problem; it drops at least one "FILE:LINE" case that
> > currently works.
> > 
> > 
> > Is that a fair summary?  There were many messages and details, so I'm
> > not sure I either understood it all or that it can be summarized like
> > this.
> > 
> 
> Yes, I agree with this summary.
> 
> > If that's fair, then my inclination is to move forward with Andrew's
> > patch for the time being, with the primary justification being
> > conservatism.  Bernd's patch can always land if/when we have some
> > clarification from GCC about whether the bugs there will be fixed.
> > 
> > Let me know what you think.
> > 
> 
> I think if we take Andrew's patch the only possible outcome
> will be that gcc will disable that statement frontiers debug
> option by default, because they do not understand what we
> want from them.  (I am one of them, but I came here to understand
> you better)

I tried to describe the two current issues I see with GCC in this bug
report[1] but after a stupid mistake by me of using an older version
of binutils the conversation got side-tracked into a discussion about
view numbers and GDB.

It is my position that neither of the bugs described in [1] require
view number changes, nor additional DWARF features, they could be
fixed entirely withing GCC (in theory at least).

I'm not entirely sure which bit of debug statement frontiers is
responsible for.

After reading one of your other email[2], in which you said this bug
was present from GCC-7 I went and ran some tests, as I thought
gdb.cp/step-and-next-inline.exp doesn't run on GCC-7 as GCC that old
doesn't support -gstatement-frontiers.  I was right, that flag doesn't
exist on GCC-7, and your were correct that some of the range bugs (at
least) do still exist on GCC-7.

After reading[2] I'd also be interest to understand what flaw in DWARF
you feel makes a difference in this case.  Given some of your feedback
in [1] I think that you might be talking about improvements (the
addition of?) to the view number support, but while I agree with you
that this might (would?) allow for improved debug experience, I don't
think it would be needed in order for GCC to do better in this case.

I think it is great Bernd, that you are reaching out from the GCC
community to engage with GDB, this is certainly the best way to ensure
that we can work together as communities to give the best possible
debug experience, and I'm sorry you feel that I have not been clear
enough about the issues I'm seeing here.

I do still think that merging my patch is the correct way to move
forward, however, I don't think the future has to be as bleak as you
describe above.  I am happy to continue discussing this issue for as
long as you are in order to try and find a solution that makes
everyone happy.  That said, here's what I think would need to happen
so that _I_ (personally) was happy to accept your patch:

  1. We need to agree what we're working around.  In [2] you describe
  this issue as a flaw in DWARF, while in [1] I describe this as an
  issue in GCC.  I think we need to first need to agree on what the
  spec says, what GCC can (currently) do in an ideal world, and what
  GCC can be expected to do (currently) in the real world.

  2. We should change GDB assuming that any bugs in GCC or DWARF will
  one day be fixed, and that when that happens we would, ideally, not
  have to apply the work around.  So fixes should be guarded with a
  check of either the producer or the DWARF version.

  3. Currently (to me) it appears that we are crafting a work around
  based on one test case (gdb.cp/step-and-next-inline.exp).  My
  concern here is that when we talk about changing the line table, or
  range handling, these are pretty core aspects of GDB.

  As a GCC developer you probably have more insight, but it doesn't
  feel clear to me yet, do we know that GCC always misbehaves in the
  same say across all, or most, compiled code?

  I don't know how we address this without merging your patch,
  releasing GDB and seeing how it works in the wild.  However, if we
  did decide to "just try it", I would still prefer we staged things
  as:
    (a) Merge my patch, targeted regression fix, then
    (b) Your patch, new functionality GCC/DWARF ranges work around.
  In this way, if we end up backing out some or all of (b) we still
  have (a) in place that fixes the regression.  I'm more than happy
  for a rebase of (b) in include full removal of (a).

Thanks for your continued input.

Andrew

[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
[2] https://sourceware.org/pipermail/gdb-patches/2020-April/167933.html

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-04-27 10:34                           ` Andrew Burgess
@ 2020-05-14 20:18                             ` Tom Tromey
  2020-05-14 22:39                               ` Andrew Burgess
  0 siblings, 1 reply; 79+ messages in thread
From: Tom Tromey @ 2020-05-14 20:18 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Bernd Edlinger, Tom Tromey, gdb-patches

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

Resurrecting this again ... we have some internal tests that have been
failing, and I want to land at least one of these patches to resolve
this.

Andrew> After reading[2] I'd also be interest to understand what flaw in
Andrew> DWARF you feel makes a difference in this case.

I also don't understand this.

Andrew> I think it is great Bernd, that you are reaching out from the GCC
Andrew> community to engage with GDB, this is certainly the best way to ensure
Andrew> that we can work together as communities to give the best possible
Andrew> debug experience, and I'm sorry you feel that I have not been clear
Andrew> enough about the issues I'm seeing here.

+1

Andrew>   I don't know how we address this without merging your patch,
Andrew>   releasing GDB and seeing how it works in the wild.  However, if we
Andrew>   did decide to "just try it", I would still prefer we staged things
Andrew>   as:
Andrew>     (a) Merge my patch, targeted regression fix, then
Andrew>     (b) Your patch, new functionality GCC/DWARF ranges work around.
Andrew>   In this way, if we end up backing out some or all of (b) we still
Andrew>   have (a) in place that fixes the regression.  I'm more than happy
Andrew>   for a rebase of (b) in include full removal of (a).

I think landing your patch is safe to do while we discuss part (b).
How about we move forward with that part?  Then if we come to agreement
on where the bug lies we can decide about that.

Tom

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-05-14 20:18                             ` Tom Tromey
@ 2020-05-14 22:39                               ` Andrew Burgess
  2020-05-15  3:35                                 ` Bernd Edlinger
  0 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-05-14 22:39 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Bernd Edlinger, gdb-patches

* Tom Tromey <tom@tromey.com> [2020-05-14 14:18:44 -0600]:

> >>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
> 
> Resurrecting this again ... we have some internal tests that have been
> failing, and I want to land at least one of these patches to resolve
> this.
> 
> Andrew> After reading[2] I'd also be interest to understand what flaw in
> Andrew> DWARF you feel makes a difference in this case.
> 
> I also don't understand this.
> 
> Andrew> I think it is great Bernd, that you are reaching out from the GCC
> Andrew> community to engage with GDB, this is certainly the best way to ensure
> Andrew> that we can work together as communities to give the best possible
> Andrew> debug experience, and I'm sorry you feel that I have not been clear
> Andrew> enough about the issues I'm seeing here.
> 
> +1
> 
> Andrew>   I don't know how we address this without merging your patch,
> Andrew>   releasing GDB and seeing how it works in the wild.  However, if we
> Andrew>   did decide to "just try it", I would still prefer we staged things
> Andrew>   as:
> Andrew>     (a) Merge my patch, targeted regression fix, then
> Andrew>     (b) Your patch, new functionality GCC/DWARF ranges work around.
> Andrew>   In this way, if we end up backing out some or all of (b) we still
> Andrew>   have (a) in place that fixes the regression.  I'm more than happy
> Andrew>   for a rebase of (b) in include full removal of (a).
> 
> I think landing your patch is safe to do while we discuss part (b).
> How about we move forward with that part?  Then if we come to agreement
> on where the bug lies we can decide about that.

I agree. I already rebased this and retested it.  Unless someone
shouts out between now and tomorrow I plan to merge my patch.

Bernd (or anyone else) - I'm more than happy that if a better patch is
put forward it can revert some or all of my patch, as needed.  Like
Tom, I'd like to see the regressions squashed.

Thanks,
Andrew

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-05-14 22:39                               ` Andrew Burgess
@ 2020-05-15  3:35                                 ` Bernd Edlinger
  2020-05-15 14:46                                   ` Andrew Burgess
                                                     ` (2 more replies)
  0 siblings, 3 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-05-15  3:35 UTC (permalink / raw)
  To: Andrew Burgess, Tom Tromey; +Cc: gdb-patches, Alexandre Oliva

[-- Attachment #1: Type: text/plain, Size: 7491 bytes --]

Hi Andrew, and Tom

I'll try to help, if I can.
I must admit I was busy with other things, so I have
not much new info, but I can add my view.

First I want to make you aware, of my last e-mails
on this issue, I wonder if you got them.

I added  Alexandre Oliva who maintains the debug info
in GCC to CC then, and unfortunately although I was
hoping that he gives his view on this matter, I think
I have not received any response from him so far.

https://sourceware.org/pipermail/gdb-patches/2020-April/167933.html
https://sourceware.org/pipermail/gdb-patches/2020-April/167932.html

I must note that it is impossible to find out the
e-mail addresses which were in CC on the mailing list
archives since the unfortunate move to the new
maling list archive format.  But I added oliva@gnu.org
in CC, probably you should do that as well, since
I think we cannot get anything changed in the gcc
debug output unless we can convince him about it.

Andrew, as far as I remember the range info of the
inlined subroutines were not correct, in your patch.

GCC tells us where the code originating from the
inlined code starts and ends.  The byte addresses.
These must be the same addresses where the line numbers
switch from the calling procedure to the inlined procedure
but the range info in your dwarf code is artificially
and it is having start and end addresses where they
should be.

Can you please fix the range infos in your patch
and re-post it, before we decide how to proceed?

I will probably not have time to do much today,
since this is my son's birthday, and I will probably
bake a birthday cake instead :-)

But I would say that I see a way how I can adjust my
patch to fix the issues which were raised by your test
case (I added the test case from your patch to mine,
and most of it seems to be fine with my latest patch,
one remaining issue is a bit nasty but fixable).
I'll attach the patch file where I am at here, so
you can see what I have now.
It fails only one test case in dw2-inline-header-3.exp
but I would like how it compares to your patch
when Tom would like to try it out on the
internal test cases, he mentioned.

And, yes, of course my approach is that the debug info
is not optimal at all as it is emitted by gcc, but 
gdb can probably work around it until it is fixed
which should happen when a new updated dwarf extension
is invented which tells us the view number where
the inlined subroutine ends.  The line table program
has view numbers, but not the range info of the
subroutine.  That is the deficiency in the dwarf
format I think causes the trouble, apart from the
fact that dwarf is already too complicate to understand.

I must also say, that since if I remember correctly
Andrew's patch makes

gdb.cp/step-and-next-inline.exp

fail, I consider my attempt to make gdb work around
the debug info of inlined subroutines a failure.

While my patch did make

gdb.cp/step-and-next-inline.exp

succeed completely, also the case where the inline
function is not in a header file.

I must also say, and that is not because I have
emotions about it, but just because I consider it
the only possible option when we consider gcc's
debug output format completely broken, then
I will probably just send a one-line patch to
gcc-patches, which reverts the default of
-gstatement-frontiers, that would be safe for
backporting to all active branches.

And it would prevent the !is_stmt line infos
completely, the debug format will then
probably work as before gcc-8 implemented
that for the first time.

So I attached my latest patch version, together
with the Test case from Andrews patch.




On 5/15/20 12:39 AM, Andrew Burgess wrote:
> * Tom Tromey <tom@tromey.com> [2020-05-14 14:18:44 -0600]:
> 
>>>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
>>
>> Resurrecting this again ... we have some internal tests that have been
>> failing, and I want to land at least one of these patches to resolve
>> this.
>>
>> Andrew> After reading[2] I'd also be interest to understand what flaw in
>> Andrew> DWARF you feel makes a difference in this case.
>>
>> I also don't understand this.
>>

When you only know where an inline function ends at the byte address.
You can habe line table entries at the same address from the inline
funcion and from the calling function.  It is impossible to distinguish
between those, however there is a view number which would be different,
and as Alexandre explains it, these can be consider an extension to the
byte addresses, so if a range info would add a view number we would
know which line infos are from the subroutine and which are not, even
if the byte addresses are identical.

>> Andrew> I think it is great Bernd, that you are reaching out from the GCC
>> Andrew> community to engage with GDB, this is certainly the best way to ensure
>> Andrew> that we can work together as communities to give the best possible
>> Andrew> debug experience, and I'm sorry you feel that I have not been clear
>> Andrew> enough about the issues I'm seeing here.
>>
>> +1
>>

I still think this is possible, I think we need to understand each other
better.  Of course stress from release cycles might this even harder.
Are we (gdb maintainers) already under stress to reach the next release version?

>> Andrew>   I don't know how we address this without merging your patch,
>> Andrew>   releasing GDB and seeing how it works in the wild.  However, if we
>> Andrew>   did decide to "just try it", I would still prefer we staged things
>> Andrew>   as:
>> Andrew>     (a) Merge my patch, targeted regression fix, then
>> Andrew>     (b) Your patch, new functionality GCC/DWARF ranges work around.
>> Andrew>   In this way, if we end up backing out some or all of (b) we still
>> Andrew>   have (a) in place that fixes the regression.  I'm more than happy
>> Andrew>   for a rebase of (b) in include full removal of (a).
>>
>> I think landing your patch is safe to do while we discuss part (b).
>> How about we move forward with that part?  Then if we come to agreement
>> on where the bug lies we can decide about that.
> 
> I agree. I already rebased this and retested it.  Unless someone
> shouts out between now and tomorrow I plan to merge my patch.
> 
> Bernd (or anyone else) - I'm more than happy that if a better patch is
> put forward it can revert some or all of my patch, as needed.  Like
> Tom, I'd like to see the regressions squashed.
> 

No, I am not able to do this once we decide that gcc's debug info
is impossible to implement, I will send a patch to the gcc-patches
which restores the gcc-7 debug info.  My patch was based on the
assumption that there is a way to work around the issues, but if
that turns out to be a failure, I will not go back and revert
your patch again, since it seems to revert my work on that issue
completely (see how this patch breaks the step-and-next-inline.exp
test case).  And once again, I feel not at all emotional, just that
I want the issue fixed, and next time I will try a completely
different approach which is gcc was wrong, and therefore we will
revert Alexandre Oliva's patch, since it is the least effort and
Alexandre already signaled he also had considered that already.

Alexandre Oliva wrote on 11/19/19:
https://gcc.gnu.org/pipermail/gcc-patches/2019-November/534845.html
> Perhaps we should change our defaults, if the situation with GDB does
> not change :-(


Andrew:
Have you attached your *latest* patch?
Have you done the changes I requested?

Thanks
Bernd.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-range-end-handling-of-inlined-subroutines.patch --]
[-- Type: text/x-patch; name="0001-Fix-range-end-handling-of-inlined-subroutines.patch", Size: 14360 bytes --]

From 1ae05f16f284d2367fcb233070b8664913a3b086 Mon Sep 17 00:00:00 2001
From: Bernd Edlinger <bernd.edlinger@hotmail.de>
Date: Sun, 9 Feb 2020 21:13:17 +0100
Subject: [PATCH 1/2] Fix range end handling of inlined subroutines

Since the is_stmt is now handled, it becomes
possible to locate dubious is_stmt line entries
at the end of an inlined function, even if the
called inline function is in the same subfile.

When there is a sequence of line entries at the
same address where an inline range ends, and the
last item has is_stmt = 0, we force all previous
items to have is_stmt = 0 as well.

If the last line at that address has is_stmt = 1,
there is no need to change anything, since a step
over will always stop at that last line from the
same address, which is fine, since it is outside
the subroutine.

With this change we lose the ability to set
breakpoints on some lines using file:line syntax,
but the data is not completely lost, as the
line table is still holding those lines, just
with the is_stmt flag reset.

This is necessary as breakpoints on these lines
are problematic, as the call stack is often
wrong.  From the block info they appear to be
located in the caller, but they are actually meant
to be part of the subroutine, therefore usually one
frame is missing from the callstack when the
execution stops there.

This is about the best we can do at the moment,
unless location view information are added to the
block ranges debug info structure, and location
views are implemented in gdb in general.

This restores the previous behavior of
record_line that deletes lines at the end of
a function.

gdb:
2020-04-05  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	* buildsym.c (buildsym_compunit::record_line): Delete
	previous lines at the same PC as the end sequence marker,
	but don't do that for a fake end sequence marker.
	(buildsym_compunit::record_inline_range_end,
	patch_inline_end_pos): New helper functions.
	(buildsym_compunit::end_symtab_with_blockvector): Patch line table.
	(buildsym_compunit::~buildsym_compunit): Cleanup m_inline_end_vector.
	* buildsym.h (buildsym_compunit::record_inline_range_end): Declare.
	(buildsym_compunit::m_inline_end_vector,
	buildsym_compunit::m_inline_end_vector_length,
	buildsym_compunit::m_inline_end_vector_nitems): New data items.
	* dwarf2/read.c (dwarf2_rnglists_process,
	dwarf2_ranges_process): Don't ignore empty ranges here.
	(dwarf2_ranges_read): Ignore empty ranges here.
	(dwarf2_record_block_ranges): Pass end of range PC to
	record_inline_range_end for inline functions.
	(dwarf_finish_line): Add new parameter end_sequence.
	(lnp_state_machine::record_line): Pass end_sequence to
	dwarf_finish_line.

gdb/testsuite:
2020-04-05  Bernd Edlinger  <bernd.edlinger@hotmail.de>

	* gdb.cp/step-and-next-inline.exp: Adjust test.
---
 gdb/buildsym.c                                | 115 +++++++++++++++++++++++---
 gdb/buildsym.h                                |  11 +++
 gdb/dwarf2/read.c                             |  30 ++++---
 gdb/testsuite/gdb.cp/step-and-next-inline.exp |  17 ----
 4 files changed, 131 insertions(+), 42 deletions(-)

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index b9bcc33..ae90175 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -113,6 +113,8 @@ struct pending_block
       next1 = next->next;
       xfree ((void *) next);
     }
+
+  xfree (m_inline_end_vector);
 }
 
 struct macro_table *
@@ -691,19 +693,16 @@ struct blockvector *
 		      * sizeof (struct linetable_entry))));
     }
 
-  /* Normally, we treat lines as unsorted.  But the end of sequence
-     marker is special.  We sort line markers at the same PC by line
-     number, so end of sequence markers (which have line == 0) appear
-     first.  This is right if the marker ends the previous function,
-     and there is no padding before the next function.  But it is
-     wrong if the previous line was empty and we are now marking a
-     switch to a different subfile.  We must leave the end of sequence
-     marker at the end of this group of lines, not sort the empty line
-     to after the marker.  The easiest way to accomplish this is to
-     delete any empty lines from our table, if they are followed by
-     end of sequence markers.  All we lose is the ability to set
-     breakpoints at some lines which contain no instructions
-     anyway.  */
+  /* The end of sequence marker is special.  We need to delete any
+     previous lines at the same PC, otherwise these lines may cause
+     problems since they might be at the same address as the following
+     function.  For instance suppose a function calls abort there is no
+     reason to emit a ret after that point (no joke).
+     So the label may be at the same address where the following
+     function begins.  There is also a fake end of sequence marker (-1)
+     that we emit internally when switching between different CUs
+     In this case, duplicate line table entries shall not be deleted.
+     But the line table entry looks the same in both cases.  */
   if (line == 0)
     {
       while (subfile->line_vector->nitems > 0)
@@ -714,6 +713,8 @@ struct blockvector *
 	  subfile->line_vector->nitems--;
 	}
     }
+  else if (line == -1)
+    line = 0;
 
   e = subfile->line_vector->item + subfile->line_vector->nitems++;
   e->line = line;
@@ -722,6 +723,90 @@ struct blockvector *
 }
 
 \f
+/* Record a PC where a inlined subroutine ends.  */
+
+void
+buildsym_compunit::record_inline_range_end (CORE_ADDR end)
+{
+  /* The performance of this function is very important,
+     it shall be O(n*log(n)) therefore we do not use std::vector
+     here since some compilers, e.g. visual studio, do not
+     guarantee that for vector::push_back.  */
+  if (m_inline_end_vector == nullptr)
+    {
+      m_inline_end_vector_length = INITIAL_LINE_VECTOR_LENGTH;
+      m_inline_end_vector = (CORE_ADDR *)
+	xmalloc (sizeof (CORE_ADDR) * m_inline_end_vector_length);
+      m_inline_end_vector_nitems = 0;
+    }
+  else if (m_inline_end_vector_nitems == m_inline_end_vector_length)
+    {
+      m_inline_end_vector_length *= 2;
+      m_inline_end_vector = (CORE_ADDR *)
+	xrealloc ((char *) m_inline_end_vector,
+		  sizeof (CORE_ADDR) * m_inline_end_vector_length);
+    }
+
+  m_inline_end_vector[m_inline_end_vector_nitems++] = end;
+}
+
+\f
+/* Patch the is_stmt bits at the given inline end address.
+   The line table has to be already sorted.  */
+
+static void
+patch_inline_end_pos (struct linetable *table, CORE_ADDR end)
+{
+  int a = 2, b = table->nitems - 1;
+  struct linetable_entry *items = table->item;
+
+  /* We need at least two items with pc = end in the table.
+     The lowest usable items are at pos 0 and 1, the highest
+     usable items are at pos b - 2 and b - 1.  */
+  if (a > b || end < items[1].pc || end > items[b - 2].pc)
+    return;
+
+  /* Look for the first item with pc > end in the range [a,b].
+     The previous element has pc = end or there is no match.
+     We set a = 2, since we need at least two consecutive elements
+     with pc = end to do anything useful.
+     We set b = nitems - 1, since we are not interested in the last
+     element which should be an end of sequence marker with line = 0
+     and is_stmt = 1.  */
+  while (a < b)
+    {
+      int c = (a + b) / 2;
+
+      if (end < items[c].pc)
+	b = c;
+      else
+	a = c + 1;
+    }
+
+  a--;
+  if (items[a].pc != end || items[a].is_stmt)
+    return;
+
+  /* When there is a sequence of line entries at the same address
+     where an inline range ends, and the last item has is_stmt = 0,
+     we force all previous items to have is_stmt = 0 as well.
+     Setting breakpoints at those addresses is currently not
+     supported, since it is unclear if the previous addresses are
+     part of the subroutine or the calling program.  */
+  do
+    {
+      /* We stop at the first line entry with a different address,
+	 or when we see an end of sequence marker.  */
+      a--;
+      if (items[a].pc != end || items[a].line == 0)
+	break;
+
+      items[a].is_stmt = 0;
+    }
+  while (a > 0);
+}
+
+\f
 /* Subroutine of end_symtab to simplify it.  Look for a subfile that
    matches the main source file's basename.  If there is only one, and
    if the main source file doesn't have any symbol or line number
@@ -955,6 +1040,10 @@ struct compunit_symtab *
 			      subfile->line_vector->item
 			      + subfile->line_vector->nitems,
 			      lte_is_less_than);
+
+	   for (int i = 0; i < m_inline_end_vector_nitems; i++)
+	     patch_inline_end_pos (subfile->line_vector,
+				   m_inline_end_vector[i]);
 	}
 
       /* Allocate a symbol table if necessary.  */
diff --git a/gdb/buildsym.h b/gdb/buildsym.h
index c768a4c..2845789 100644
--- a/gdb/buildsym.h
+++ b/gdb/buildsym.h
@@ -190,6 +190,8 @@ struct buildsym_compunit
   void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
 		    bool is_stmt);
 
+  void record_inline_range_end (CORE_ADDR end);
+
   struct compunit_symtab *get_compunit_symtab ()
   {
     return m_compunit_symtab;
@@ -397,6 +399,15 @@ struct buildsym_compunit
 
   /* Pending symbols that are local to the lexical context.  */
   struct pending *m_local_symbols = nullptr;
+
+  /* Pending inline end range addresses.  */
+  CORE_ADDR * m_inline_end_vector = nullptr;
+
+  /* Number of allocated inline end range addresses.  */
+  int m_inline_end_vector_length = 0;
+
+  /* Number of pending inline end range addresses.  */
+  int m_inline_end_vector_nitems = 0;
 };
 
 \f
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index ab21ab0..3c54a83 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -13703,10 +13703,6 @@ class process_die_scope
 	  return false;
 	}
 
-      /* Empty range entries have no effect.  */
-      if (range_beginning == range_end)
-	continue;
-
       range_beginning += *base;
       range_end += *base;
 
@@ -13814,10 +13810,6 @@ class process_die_scope
 	  return 0;
 	}
 
-      /* Empty range entries have no effect.  */
-      if (range_beginning == range_end)
-	continue;
-
       range_beginning += *base;
       range_end += *base;
 
@@ -13857,6 +13849,10 @@ class process_die_scope
   retval = dwarf2_ranges_process (offset, cu,
     [&] (CORE_ADDR range_beginning, CORE_ADDR range_end)
     {
+      /* Empty range entries have no effect.  */
+      if (range_beginning == range_end)
+	return;
+
       if (ranges_pst != NULL)
 	{
 	  CORE_ADDR lowpc;
@@ -14094,6 +14090,7 @@ class process_die_scope
   struct gdbarch *gdbarch = objfile->arch ();
   struct attribute *attr;
   struct attribute *attr_high;
+  bool inlined_subroutine = (die->tag == DW_TAG_inlined_subroutine);
 
   attr_high = dwarf2_attr (die, DW_AT_high_pc, cu);
   if (attr_high)
@@ -14109,7 +14106,10 @@ class process_die_scope
 
 	  low = gdbarch_adjust_dwarf2_addr (gdbarch, low + baseaddr);
 	  high = gdbarch_adjust_dwarf2_addr (gdbarch, high + baseaddr);
-	  cu->get_builder ()->record_block_range (block, low, high - 1);
+	  if (inlined_subroutine)
+	    cu->get_builder ()->record_inline_range_end (high);
+	  if (low < high)
+	    cu->get_builder ()->record_block_range (block, low, high - 1);
         }
     }
 
@@ -14134,6 +14134,10 @@ class process_die_scope
 	  end += baseaddr;
 	  start = gdbarch_adjust_dwarf2_addr (gdbarch, start);
 	  end = gdbarch_adjust_dwarf2_addr (gdbarch, end);
+	  if (inlined_subroutine)
+	    cu->get_builder ()->record_inline_range_end (end);
+	  if (start == end)
+	    return;
 	  cu->get_builder ()->record_block_range (block, start, end - 1);
 	  blockvec.emplace_back (start, end);
 	});
@@ -20031,7 +20035,7 @@ class lnp_state_machine
 
 static void
 dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
-		   CORE_ADDR address, struct dwarf2_cu *cu)
+		   CORE_ADDR address, struct dwarf2_cu *cu, bool end_sequence)
 {
   if (subfile == NULL)
     return;
@@ -20044,7 +20048,8 @@ class lnp_state_machine
 			  paddress (gdbarch, address));
     }
 
-  dwarf_record_line_1 (gdbarch, subfile, 0, address, true, cu);
+  dwarf_record_line_1 (gdbarch, subfile, end_sequence ? 0 : -1, address,
+		       true, cu);
 }
 
 void
@@ -20077,7 +20082,8 @@ class lnp_state_machine
 	      || end_sequence)
 	    {
 	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
-				 m_currently_recording_lines ? m_cu : nullptr);
+				 m_currently_recording_lines ? m_cu : nullptr,
+				 end_sequence);
 	    }
 
 	  if (!end_sequence)
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
index 3733fa7..e3a5793 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -52,37 +52,20 @@ proc do_test { use_header } {
     gdb_test "step" ".*" "step into get_alias_set"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 1"
-    # It's possible that this first failure (when not using a header
-    # file) is GCC's fault, though the remaining failures would best
-    # be fixed by adding location views support (though it could be
-    # that some easier heuristic could be figured out).  Still, it is
-    # not certain that the first failure wouldn't also be fixed by
-    # having location view support, so for now it is tagged as such.
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" ".*TREE_TYPE.*" "next step 1"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 2"
     gdb_test "next" ".*TREE_TYPE.*" "next step 2"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 3"
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" ".*TREE_TYPE.*" "next step 3"
     gdb_test "bt" "\\s*\\#0\\s+get_alias_set\[^\r\]*${srcfile}:.*" \
 	"not in inline 4"
-    if {!$use_header} { setup_kfail "*-*-*" symtab/25507 }
     gdb_test "next" "return 0.*" "next step 4"
     gdb_test "bt" \
 	"\\s*\\#0\\s+(main|get_alias_set)\[^\r\]*${srcfile}:.*" \
 	"not in inline 5"
 
-    if {!$use_header} {
-	# With the debug from GCC 10.x (and earlier) GDB is currently
-	# unable to successfully complete the following tests when we
-	# are not using a header file.
-	kfail symtab/25507 "stepping tests"
-	return
-    }
-
     clean_restart ${executable}
 
     if ![runto_main] {
-- 
1.9.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-gdb-Preserve-is-stmt-lines-when-switch-between-files.patch --]
[-- Type: text/x-patch; name="0002-gdb-Preserve-is-stmt-lines-when-switch-between-files.patch", Size: 25012 bytes --]

From 804bd32133d585d3dd3d707baf7d14ab1ea659d1 Mon Sep 17 00:00:00 2001
From: Andrew Burgess <andrew.burgess@embecosm.com>
Date: Fri, 3 Apr 2020 23:21:04 +0100
Subject: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files

After the is-stmt support commit:

  commit 8c95582da858ac981f689a6f599acacb8c5c490f
  Date:   Mon Dec 30 21:04:51 2019 +0000

      gdb: Add support for tracking the DWARF line table is-stmt field

A regression was observed where a breakpoint could no longer be placed
in some cases.

Consider a line table like this:

  File 1: test.c
  File 2: test.h

  | Addr | File | Line | Stmt |
  |------|------|------|------|
  | 1    | 1    | 16   | Y    |
  | 2    | 1    | 17   | Y    |
  | 3    | 2    | 21   | Y    |
  | 4    | 2    | 22   | Y    |
  | 4    | 1    | 18   | N    |
  | 5    | 2    | 23   | N    |
  | 6    | 1    | 24   | Y    |
  | 7    | 1    | END  | Y    |
  |------|------|------|------|

Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
built two line table structures:

  File 1                 File 2
  ------                 ------

  | Addr | Line |        | Addr | Line |
  |------|------|        |------|------|
  | 1    | 16   |        | 3    | 21   |
  | 2    | 17   |        | 4    | 22   |
  | 3    | END  |        | 6    | END  |
  | 6    | 24   |        |------|------|
  | 7    | END  |
  |------|------|

After the is-stmt patch GDB now records non-stmt lines, so the
generated line table structures look like this:

  File 1                   File 2
  ------                   ------

  | Addr | Line | Stmt |  | Addr | Line | Stmt |
  |------|------|------|  |------|------|------|
  | 1    | 16   | Y    |  | 3    | 21   | Y    |
  | 2    | 17   | Y    |  | 4    | 22   | Y    |
  | 3    | END  | Y    |  | 4    | END  | Y    |
  | 4    | 18   | N    |  | 5    | 23   | N    |
  | 5    | END  | Y    |  | 6    | END  | Y    |
  | 6    | 24   | Y    |  |------|------|------|
  | 7    | END  | Y    |
  |------|------|------|

The problem is that in 'File 2', end END marker at address 4 causes
the previous line table entry to be discarded, so we actually end up
with this:

  File 2
  ------

  | Addr | Line | Stmt |
  |------|------|------|
  | 3    | 21   | Y    |
  | 4    | END  | Y    |
  | 5    | 23   | N    |
  | 6    | END  | Y    |
  |------|------|------|

When a user tries to place a breakpoint in file 2 at line 22, this is
no longer possible.

The solution I propose here is that we ignore line table entries that
would trigger a change of file if:

  1. The new line being added is at the same address as the previous
  line, and

  2. We have previously seen an is-stmt line at the current address.

The result of this is that GDB switches file, and knows that some line
entry (or entries) are going to be discarded, prefer to keep is-stmt
lines and discard non-stmt lines.

After this commit the lines tables are now:

  File 1                   File 2
  ------                   ------

  | Addr | Line | Stmt |  | Addr | Line | Stmt |
  |------|------|------|  |------|------|------|
  | 1    | 16   | Y    |  | 3    | 21   | Y    |
  | 2    | 17   | Y    |  | 4    | 22   | Y    |
  | 3    | END  | Y    |  | 5    | 23   | N    |
  | 5    | END  | Y    |  | 6    | END  | Y    |
  | 6    | 24   | Y    |  |------|------|------|
  | 7    | END  | Y    |
  |------|------|------|

We've lost the non-stmt entry for file 1, line 18, but retained the
is-stmt entry for file 2, line 22.  The user can now place a
breakpoint at that location.

One problem that came from this commit was the test
gdb.cp/step-and-next-inline.exp, which broke in several places.  After
looking at this test again I think that in some cases this test was
only ever passing by pure luck.  The debug GCC is producing for this
test is pretty broken.  I raised this GCC bug:

  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474

for this and disabled one entire half of the test.  There are still
some cases in here that do pass, and if/when GCC is fixed it would be
great to enable this test again.

gdb/testsuite/ChangeLog:

	* gdb.dwarf2/dw2-inline-header-1.exp: New file.
	* gdb.dwarf2/dw2-inline-header-2.exp: New file.
	* gdb.dwarf2/dw2-inline-header-3.exp: New file.
	* gdb.dwarf2/dw2-inline-header-lbls.c: New file.
	* gdb.dwarf2/dw2-inline-header.c: New file.
	* gdb.dwarf2/dw2-inline-header.h: New file.
---
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp  | 156 ++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp  | 179 ++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp  | 192 ++++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c |  46 ++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c      |  24 +++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h      |  24 +++
 6 files changed, 621 insertions(+)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h

diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
new file mode 100644
index 0000000..6a1e990
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
@@ -0,0 +1,156 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 2    | 23   | N    |
+# | 6    | 1    | 24   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+# Places a brekpoint at file 2, line 22.  Previously GDB would discrad
+# the line table entry for this line due to switching files for the
+# file 1, line 18 non-statement line.  After patching however, GDB now
+# discards the file 1, line 18 entry instead, and the breakpoint at
+# line 22 should succeed.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 5}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_set_file 1}
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
new file mode 100644
index 0000000..4649991
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
@@ -0,0 +1,179 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 1    | 19   | Y    |
+# | 6    | 1    | 20   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+#
+# Place the first brekpoint at file 2, line 22 and a second breakpoint
+# at file 1, line 19.  A third breakpoint is placed at file 1, line
+# 18, but as this line table entry will have been discarded[1] the
+# third breakpoint will actually be placed at the same location as the
+# second breakpoint.
+#
+# [1] The entry for file 1, line 18 is discarded because it is at the
+# same address as the previous entry, but the previous entry is-stmt,
+# while line 18 is a non-stmt.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
+    "check for breakpoint at ${srcfile4}"
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile3}:19"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile3}:19\\y.*" \
+    "check for breakpoint at ${srcfile3}"
+
+# Line table entry for line 18 will have been discarded, so this
+# brekpoint will be at the same location as line 19.
+gdb_test "break ${srcfile3}:18" \
+    "Note: breakpoint $decimal also set at pc $hex.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
new file mode 100644
index 0000000..c683dc4
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
@@ -0,0 +1,192 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt |
+# |------|------|------|------|
+# | 1    | 1    | 16   | Y    |
+# | 2    | 1    | 17   | Y    |
+# | 3    | 2    | 21   | Y    |
+# | 4    | 2    | 22   | Y    |
+# | 4    | 1    | 18   | N    |
+# | 5    | 1    | 19   | N    |
+# | 6    | 1    | 20   | Y    |
+# | 7    | 1    | END  | Y    |
+# |------|------|------|------|
+#
+# Break at file 2, line 22, then single instruction step forward.  We
+# should pass through line 19 and then encounter line 20.
+#
+# Currently we don't expect GDB to see file 1, line 18, as this is a
+# non-stmt line in a different file at the same address as the
+# previous is-stmt line.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_1 addr}
+		    {high_pc line_label_7 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug optimize=-O1}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
+
+gdb_continue_to_breakpoint "${srcfile4}:22" \
+    ".* ${srcfile4} : 22 .*"
+
+# Now single instruction step forward.  Eventually we should hit
+# ${srcfile3}:20, but before we do we should hit the non-statement
+# line ${srcfile3}:19.
+#
+# We don't know how many instructions we'll need to step, but 100
+# should be enough for everyone (surely), and this stops us looping
+# forever if something goes wrong.
+set found_line_19 0
+set found_line_20 0
+set keep_going 1
+for { set i 0 } { $i < 100 && $keep_going } { incr i } {
+    set keep_going 0
+    gdb_test_multiple "stepi" "stepi ${i}" {
+	-re "${srcfile3} : 19 .*${gdb_prompt} " {
+	    set found_line_19 1
+	    set keep_going 1
+	}
+
+	-re "${srcfile3} : 20 .*${gdb_prompt} " {
+	    set found_line_20 1
+	}
+
+	-re "${srcfile4} : 22 .*${gdb_prompt} " {
+	    # Not left line 22 yet.
+	    set keep_going 1
+	}
+    }
+}
+
+gdb_assert { $found_line_19 && $found_line_20 } \
+    "found line 19 and 20"
+
+
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
new file mode 100644
index 0000000..a1b7b17
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
@@ -0,0 +1,46 @@
+/* Copyright 2020 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/>.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+volatile int var;
+volatile int bar;
+
+/* Generate some code to take up some space.  */
+#define FILLER do { \
+    var = 99;	    \
+} while (0)
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+  LL (1);	// F1, Ln 16
+  FILLER;
+  LL (2);	// F1, Ln 17
+  FILLER;
+  LL (3);	// F2, Ln 21
+  FILLER;
+  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
+  FILLER;
+  LL (5);	// F1, Ln 19 !S
+  FILLER;
+  LL (6);	// F1, Ln 20
+  FILLER;
+  LL (7);
+  FILLER;
+  return 0;				/* main end */
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
new file mode 100644
index 0000000..a833126
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.c : 16 */
+/* dw2-inline-header.c : 17 */
+/* dw2-inline-header.c : 18 */
+/* dw2-inline-header.c : 19 */
+/* dw2-inline-header.c : 20 */
+/* dw2-inline-header.c : 21 */
+/* dw2-inline-header.c : 22 */
+/* dw2-inline-header.c : 23 */
+/* dw2-inline-header.c : 24 */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
new file mode 100644
index 0000000..7233acb
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.h : 16 */
+/* dw2-inline-header.h : 17 */
+/* dw2-inline-header.h : 18 */
+/* dw2-inline-header.h : 19 */
+/* dw2-inline-header.h : 20 */
+/* dw2-inline-header.h : 21 */
+/* dw2-inline-header.h : 22 */
+/* dw2-inline-header.h : 23 */
+/* dw2-inline-header.h : 24 */
-- 
1.9.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-05-15  3:35                                 ` Bernd Edlinger
@ 2020-05-15 14:46                                   ` Andrew Burgess
  2020-05-16  8:12                                     ` Bernd Edlinger
  2020-05-17 17:26                                   ` Bernd Edlinger
  2020-05-20 18:26                                   ` Andrew Burgess
  2 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-05-15 14:46 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: Tom Tromey, gdb-patches, Alexandre Oliva

* Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-05-15 05:35:43 +0200]:

<snip>

> 
> Andrew, as far as I remember the range info of the
> inlined subroutines were not correct, in your patch.

Thanks for pointing this out, I eventually figured out the problem
you'd seen and fixed this.

> Can you please fix the range infos in your patch
> and re-post it, before we decide how to proceed?

Rebased and latest version of the patch is attached.  As the bug fixes
are minor I don't think we should spend much longer discussing this.

> But I would say that I see a way how I can adjust my
> patch to fix the issues which were raised by your test
> case (I added the test case from your patch to mine,
> and most of it seems to be fine with my latest patch,
> one remaining issue is a bit nasty but fixable).
> I'll attach the patch file where I am at here, so
> you can see what I have now.
> It fails only one test case in dw2-inline-header-3.exp
> but I would like how it compares to your patch
> when Tom would like to try it out on the
> internal test cases, he mentioned.
> 
> And, yes, of course my approach is that the debug info
> is not optimal at all as it is emitted by gcc, but 
> gdb can probably work around it until it is fixed
> which should happen when a new updated dwarf extension
> is invented which tells us the view number where
> the inlined subroutine ends.  The line table program
> has view numbers, but not the range info of the
> subroutine.  That is the deficiency in the dwarf
> format I think causes the trouble, apart from the
> fact that dwarf is already too complicate to understand.

I think this is one of our sticking points.  I agree 100% with you
that adding view support to the range information would allow GCC to
be more expressive.

But I feel that, just because DWARF lacks a certain feature, this
doesn't mean we can't build a valid debug picture with the features we
do have.  Sure, me may have to compromise, but we can still create
something that is internally consistent.  GCC isn't doing this right
now.

Maybe it is the opinion of the GCC devs that they'll not fix GCC until
DWARF gains the extra features they'd like.  That's something you'd
have to take up on the GCC list.

Adding support to work around compiler bugs is absolutely fine, so
long as we understand exactly what issues we're working around, and
that the issue (hopefully) will get fixed eventually, so any work
around should be (I think) applied conservatively, only when we are
sure the work around is appropriate (maybe based on producer strings,
for example).

<snip>

> 
> On 5/15/20 12:39 AM, Andrew Burgess wrote:
> > * Tom Tromey <tom@tromey.com> [2020-05-14 14:18:44 -0600]:
> > 
> >>>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
> >>
> >> Resurrecting this again ... we have some internal tests that have been
> >> failing, and I want to land at least one of these patches to resolve
> >> this.
> >>
> >> Andrew> After reading[2] I'd also be interest to understand what flaw in
> >> Andrew> DWARF you feel makes a difference in this case.
> >>
> >> I also don't understand this.
> >>
> 
> When you only know where an inline function ends at the byte address.
> You can habe line table entries at the same address from the inline
> funcion and from the calling function.  It is impossible to distinguish
> between those, however there is a view number which would be different,
> and as Alexandre explains it, these can be consider an extension to the
> byte addresses, so if a range info would add a view number we would
> know which line infos are from the subroutine and which are not, even
> if the byte addresses are identical.

Please correct me if I'm wrong, but as for view support in ranges,
there's not even a GCC only extension for this yet (I did look, but
could easily have missed something).

I think we'd probably want to see this feature as at least a GCC
extension, then we could add support in GDB.

>
> >> Andrew>   I don't know how we address this without merging your patch,
> >> Andrew>   releasing GDB and seeing how it works in the wild.  However, if we
> >> Andrew>   did decide to "just try it", I would still prefer we staged things
> >> Andrew>   as:
> >> Andrew>     (a) Merge my patch, targeted regression fix, then
> >> Andrew>     (b) Your patch, new functionality GCC/DWARF ranges work around.
> >> Andrew>   In this way, if we end up backing out some or all of (b) we still
> >> Andrew>   have (a) in place that fixes the regression.  I'm more than happy
> >> Andrew>   for a rebase of (b) in include full removal of (a).
> >>
> >> I think landing your patch is safe to do while we discuss part (b).
> >> How about we move forward with that part?  Then if we come to agreement
> >> on where the bug lies we can decide about that.
> > 
> > I agree. I already rebased this and retested it.  Unless someone
> > shouts out between now and tomorrow I plan to merge my patch.
> > 
> > Bernd (or anyone else) - I'm more than happy that if a better patch is
> > put forward it can revert some or all of my patch, as needed.  Like
> > Tom, I'd like to see the regressions squashed.
> > 
> 
> No, I am not able to do this once we decide that gcc's debug info
> is impossible to implement, I will send a patch to the gcc-patches
> which restores the gcc-7 debug info.

I am certainly not suggesting, and having re-read many mails in this
thread, I don't think anyone else is suggesting, that it is impossible
to craft a work around for GCC's broken debug information.

I think that all we're suggesting is that your work around patch isn't
ready just yet, so instead of rushing that in to fix the is-stmt
regression, lets just merge my conservative regression fix patch, then
we can take our time and get your work around GCC patch correct.

This would be an easy discussion if it wasn't for
gdb.cp/step-and-next-inline.exp, which my regression fix patch
breaks.

That test is what motivated your original patch last year, so I
completely understand why my breaking it leaves you feeling
frustrated.

My position here is that this test is really an example of the GCC
broken debug issue and really should never have been merged with the
is-stmt patch, as such I would like to see it disabled until your
patch lands later.

>                                       My patch was based on the
> assumption that there is a way to work around the issues, but if
> that turns out to be a failure, I will not go back and revert
> your patch again, since it seems to revert my work on that issue
> completely

No you've misunderstood, when your patch succeeds you will (maybe)
revert this patch as part of your successful patch.

If your patch were to never land (and I'm sure we _can_ get it over
the line) then no, the patch I propose here wouldn't be reverted, but
that's exactly the point of pushing this patch first.

This patch is a low risk regression fix.

You patch is (slightly) higher risk, new functionality.

All Tom and I are suggesting is lets just merge them in that order,
rather than rushing into the high risk fix.

>             (see how this patch breaks the step-and-next-inline.exp
> test case).  And once again, I feel not at all emotional, just that
> I want the issue fixed, and next time I will try a completely
> different approach which is gcc was wrong, and therefore we will
> revert Alexandre Oliva's patch, since it is the least effort and
> Alexandre already signaled he also had considered that already.
> 
> Alexandre Oliva wrote on 11/19/19:
> https://gcc.gnu.org/pipermail/gcc-patches/2019-November/534845.html
> > Perhaps we should change our defaults, if the situation with GDB does
> > not change :-(

There is some view information emitted by GCC now, but for
gdb.cp/step-and-next-inline.exp I don't think any of the view
information would help even if we did consume it.  The problem with
that test is entirely GCC emitting broken DWARF.

I interpret the last two paragraphs above as saying, it's easier to
disable a broken feature in GCC than to merge a partially complete
work around into GDB.  And that's as it should be.

If you can point to some non-broken DWARF that GDB doesn't currently
parse correctly, that would help gdb.cp/step-and-next-inline.exp pass,
then I would certainly be prepared to invest some time trying to help
improve GDB in that area.

Bernd, you asked for some time to follow up on this thread, but this
issue has dragged on for long enough now I think.  It is my intention
to merge the patch below on Monday unless I hear a compelling argument
against it.

After that it should be trivial for your to rebase your patch and
revert those parts of my patch you no longer need.  Repost the update
and we can discuss how to get it merged.

Thanks,
Andrew

----

From c5d89ffa3032c401cc79c241097006a12b306d02 Mon Sep 17 00:00:00 2001
From: Andrew Burgess <andrew.burgess@embecosm.com>
Date: Fri, 3 Apr 2020 20:32:38 +0100
Subject: [PATCH] gdb: Preserve is-stmt lines when switch between files

After the is-stmt support commit:

  commit 8c95582da858ac981f689a6f599acacb8c5c490f
  Date:   Mon Dec 30 21:04:51 2019 +0000

      gdb: Add support for tracking the DWARF line table is-stmt field

A regression was observed where a breakpoint could no longer be placed
in some cases.

Consider a line table like this:

  File 1: test.c
  File 2: test.h

  | Addr | File | Line | Stmt |
  |------|------|------|------|
  | 1    | 1    | 16   | Y    |
  | 2    | 1    | 17   | Y    |
  | 3    | 2    | 21   | Y    |
  | 4    | 2    | 22   | Y    |
  | 4    | 1    | 18   | N    |
  | 5    | 2    | 23   | N    |
  | 6    | 1    | 24   | Y    |
  | 7    | 1    | END  | Y    |
  |------|------|------|------|

Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
built two line table structures:

  File 1                 File 2
  ------                 ------

  | Addr | Line |        | Addr | Line |
  |------|------|        |------|------|
  | 1    | 16   |        | 3    | 21   |
  | 2    | 17   |        | 4    | 22   |
  | 3    | END  |        | 6    | END  |
  | 6    | 24   |        |------|------|
  | 7    | END  |
  |------|------|

After the is-stmt patch GDB now records non-stmt lines, so the
generated line table structures look like this:

  File 1                   File 2
  ------                   ------

  | Addr | Line | Stmt |  | Addr | Line | Stmt |
  |------|------|------|  |------|------|------|
  | 1    | 16   | Y    |  | 3    | 21   | Y    |
  | 2    | 17   | Y    |  | 4    | 22   | Y    |
  | 3    | END  | Y    |  | 4    | END  | Y    |
  | 4    | 18   | N    |  | 5    | 23   | N    |
  | 5    | END  | Y    |  | 6    | END  | Y    |
  | 6    | 24   | Y    |  |------|------|------|
  | 7    | END  | Y    |
  |------|------|------|

The problem is that in 'File 2', end END marker at address 4 causes
the previous line table entry to be discarded, so we actually end up
with this:

  File 2
  ------

  | Addr | Line | Stmt |
  |------|------|------|
  | 3    | 21   | Y    |
  | 4    | END  | Y    |
  | 5    | 23   | N    |
  | 6    | END  | Y    |
  |------|------|------|

When a user tries to place a breakpoint in file 2 at line 22, this is
no longer possible.

The solution I propose here is that we ignore line table entries that
would trigger a change of file if:

  1. The new line being added is at the same address as the previous
  line, and

  2. We have previously seen an is-stmt line at the current address.

The result of this is that GDB switches file, and knows that some line
entry (or entries) are going to be discarded, prefer to keep is-stmt
lines and discard non-stmt lines.

After this commit the lines tables are now:

  File 1                   File 2
  ------                   ------

  | Addr | Line | Stmt |  | Addr | Line | Stmt |
  |------|------|------|  |------|------|------|
  | 1    | 16   | Y    |  | 3    | 21   | Y    |
  | 2    | 17   | Y    |  | 4    | 22   | Y    |
  | 3    | END  | Y    |  | 5    | 23   | N    |
  | 5    | END  | Y    |  | 6    | END  | Y    |
  | 6    | 24   | Y    |  |------|------|------|
  | 7    | END  | Y    |
  |------|------|------|

We've lost the non-stmt entry for file 1, line 18, but retained the
is-stmt entry for file 2, line 22.  The user can now place a
breakpoint at that location.

One problem that came from this commit was the test
gdb.cp/step-and-next-inline.exp, which broke in several places.  After
looking at this test again I think that in some cases this test was
only ever passing by pure luck.  The debug GCC is producing for this
test is pretty broken.  I raised this GCC bug:

  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474

for this and disabled one entire half of the test.  There are still
some cases in here that do pass, and if/when GCC is fixed it would be
great to enable this test again.

gdb/ChangeLog:

	* dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
	member variable.
	<m_stmt_at_address>: New member variable.
	(lnp_state_machine::record_line): Don't record some lines, update
	tracking of is_stmt at the same address.
	(lnp_state_machine::lnp_state_machine): Initialise new member
	variables.

gdb/testsuite/ChangeLog:

	* gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
	use_header case.
	* gdb.dwarf2/dw2-inline-header-1.exp: New file.
	* gdb.dwarf2/dw2-inline-header-2.exp: New file.
	* gdb.dwarf2/dw2-inline-header-3.exp: New file.
	* gdb.dwarf2/dw2-inline-header-lbls.c: New file.
	* gdb.dwarf2/dw2-inline-header.c: New file.
	* gdb.dwarf2/dw2-inline-header.h: New file.
---
 gdb/ChangeLog                                 |  10 +
 gdb/dwarf2/read.c                             |  47 ++++-
 gdb/testsuite/ChangeLog                       |  11 +
 gdb/testsuite/gdb.cp/step-and-next-inline.exp |   7 +
 .../gdb.dwarf2/dw2-inline-header-1.exp        | 186 +++++++++++++++++
 .../gdb.dwarf2/dw2-inline-header-2.exp        | 189 +++++++++++++++++
 .../gdb.dwarf2/dw2-inline-header-3.exp        | 193 ++++++++++++++++++
 .../gdb.dwarf2/dw2-inline-header-lbls.c       |  46 +++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c  |  24 +++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h  |  24 +++
 10 files changed, 734 insertions(+), 3 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index ab21ab0d13a..fea980d0a33 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -19880,6 +19880,15 @@ class lnp_state_machine
   /* The last file a line number was recorded for.  */
   struct subfile *m_last_subfile = NULL;
 
+  /* The address of the last line entry.  */
+  CORE_ADDR m_last_address;
+
+  /* Set to true when a previous line at the same address (using
+     m_last_address) had m_is_stmt true.  This is reset to false when a
+     line entry at a new address (m_address different to m_last_address) is
+     processed.  */
+  bool m_stmt_at_address = false;
+
   /* When true, record the lines we decode.  */
   bool m_currently_recording_lines = false;
 
@@ -20073,14 +20082,34 @@ lnp_state_machine::record_line (bool end_sequence)
       fe->included_p = 1;
       if (m_record_lines_p)
 	{
-	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
-	      || end_sequence)
+	  /* When we switch files we insert an end maker in the first file,
+	     switch to the second file and add a new line entry.  The
+	     problem is that the end marker inserted in the first file will
+	     discard any previous line entries at the same address.  If the
+	     line entries in the first file are marked as is-stmt, while
+	     the new line in the second file is non-stmt, then this means
+	     the end marker will discard is-stmt lines so we can have a
+	     non-stmt line.  This means that there are less addresses at
+	     which the user can insert a breakpoint.
+
+	     To improve this we track the last address in m_last_address,
+	     and whether we have seen an is-stmt at this address.  Then
+	     when switching files, if we have seen a stmt at the current
+	     address, and we are switching to create a non-stmt line, then
+	     discard the new line.  */
+	  bool file_changed
+	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
+	  bool ignore_this_line
+	    = (file_changed && !end_sequence && m_last_address == m_address
+	       && !m_is_stmt && m_stmt_at_address);
+
+	  if ((file_changed && !ignore_this_line) || end_sequence)
 	    {
 	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
 				 m_currently_recording_lines ? m_cu : nullptr);
 	    }
 
-	  if (!end_sequence)
+	  if (!end_sequence && !ignore_this_line)
 	    {
 	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
 
@@ -20099,6 +20128,15 @@ lnp_state_machine::record_line (bool end_sequence)
 	    }
 	}
     }
+
+  /* Track whether we have seen any m_is_stmt true at m_address in case we
+     have multiple line table entries all at m_address.  */
+  if (m_last_address != m_address)
+    {
+      m_stmt_at_address = false;
+      m_last_address = m_address;
+    }
+  m_stmt_at_address |= m_is_stmt;
 }
 
 lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
@@ -20118,6 +20156,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
   m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
   m_is_stmt = lh->default_is_stmt;
   m_discriminator = 0;
+
+  m_last_address = m_address;
+  m_stmt_at_address = false;
 }
 
 void
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
index 3733fa75570..a95e21194f9 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
 proc do_test { use_header } {
     global srcfile testfile
 
+    if { $use_header } {
+	# This test will not pass due to poor debug information
+	# generated by GCC (at least upto 10.x).  See
+	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
+	return
+    }
+
     set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
     if { $use_header } {
 	lappend options additional_flags=-DUSE_NEXT_INLINE_H
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
new file mode 100644
index 00000000000..dc7ec929236
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
@@ -0,0 +1,186 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# |      |      |      |      | Inline | Inline |
+# | Addr | File | Line | Stmt | Rng A  | Rng B  |
+# |------|------|------|------|--------|--------|
+# | 1    | 1    | 16   | Y    |        |        |
+# | 2    | 1    | 17   | Y    |        |        |
+# | 3    | 2    | 21   | Y    | X      |        |
+# | 4    | 2    | 22   | Y    | X      |        |
+# | 4    | 1    | 18   | N    | X      |        |
+# | 5    | 2    | 23   | N    | X      | X      |
+# | 6    | 1    | 24   | Y    |        |        |
+# | 7    | 1    | END  | Y    |        |        |
+# |------|------|------|------|--------|--------|
+#
+# Places a brekpoint at file 2, line 22.  Previously GDB would discard
+# the line table entry for this line due to switching files for the
+# file 1, line 18 non-statement line.  After patching however, GDB now
+# discards the file 1, line 18 entry instead, and the breakpoint at
+# line 22 should succeed.
+#
+# The two inlined subroutine ranges 'A' and 'B' represent two possible
+# ways that a compiler might represent this siuatio in the DWARF.
+#
+# Range 'B' is something that has been seen in the wild using GCC 8.2.
+# In this case the compilers range information is clearly wrong, but
+# this shouldn't impact the main point of the test.
+#
+# Range 'A' is a hypothetical case of how the compiler might choose to
+# represent this range, this has never been seen in the wild, but is
+# an improved debug experiece over range 'B'.  However, if we ever run
+# in to the situation where GDB can support the range 'A' test, or
+# support some real DWARF seen in the wild, then the range 'A' case
+# should be dropped in favour of supporting real world cases.  This is
+# included here as it "just worked" once the range 'B' case was
+# working.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+# Prepare and run the test.
+proc do_test { start_label func_name tag } {
+    global srcfile srcfile2 srcfile3 srcfile4 testfile
+
+    standard_testfile dw2-inline-header-lbls.c dw2-inline-header-${tag}.S \
+	dw2-inline-header.c dw2-inline-header.h
+
+    set build_options {nodebug optimize=-O1}
+
+    set asm_file [standard_output_file $srcfile2]
+    Dwarf::assemble $asm_file {
+	global srcdir subdir srcfile srcfile3 srcfile4 testfile
+	upvar build_options build_options
+	upvar start_label start_label
+	declare_labels lines_label callee_subprog_label
+
+	get_func_info main $build_options
+
+	cu {} {
+	    compile_unit {
+		{producer "gcc" }
+		{language @DW_LANG_C}
+		{name ${srcfile3}}
+		{low_pc 0 addr}
+		{stmt_list ${lines_label} DW_FORM_sec_offset}
+	    } {
+		callee_subprog_label: subprogram {
+		    {external 1 flag}
+		    {name callee}
+		    {inline 3 data1}
+		}
+		subprogram {
+		    {external 1 flag}
+		    {name main}
+		    {low_pc $main_start addr}
+		    {high_pc "$main_start + $main_len" addr}
+		} {
+		    inlined_subroutine {
+			{abstract_origin %$callee_subprog_label}
+			{low_pc $start_label addr}
+			{high_pc line_label_6 addr}
+			{call_file 1 data1}
+			{call_line 18 data1}
+		    }
+		}
+	    }
+	}
+
+	lines {version 2 default_is_stmt 1} lines_label {
+	    include_dir "${srcdir}/${subdir}"
+	    file_name "$srcfile3" 1
+	    file_name "$srcfile4" 1
+
+	    program {
+		{DW_LNE_set_address line_label_1}
+		{DW_LNS_advance_line 15}
+		{DW_LNS_copy}
+
+		{DW_LNE_set_address line_label_2}
+		{DW_LNS_advance_line 1}
+		{DW_LNS_copy}
+
+		{DW_LNS_set_file 2}
+		{DW_LNE_set_address line_label_3}
+		{DW_LNS_advance_line 4}
+		{DW_LNS_copy}
+
+		{DW_LNE_set_address line_label_4}
+		{DW_LNS_advance_line 1}
+		{DW_LNS_copy}
+
+		{DW_LNS_advance_line -4}
+		{DW_LNS_set_file 1}
+		{DW_LNS_negate_stmt}
+		{DW_LNS_copy}
+
+		{DW_LNS_set_file 2}
+		{DW_LNE_set_address line_label_5}
+		{DW_LNS_advance_line 5}
+		{DW_LNS_copy}
+
+		{DW_LNS_negate_stmt}
+		{DW_LNS_set_file 1}
+		{DW_LNE_set_address line_label_6}
+		{DW_LNS_advance_line 1}
+		{DW_LNS_copy}
+
+		{DW_LNE_set_address line_label_7}
+		{DW_LNE_end_sequence}
+	    }
+	}
+    }
+
+    if { [prepare_for_testing "failed to prepare" ${testfile}-${tag} \
+	      [list $srcfile $asm_file] $build_options] } {
+	return -1
+    }
+
+    if ![runto_main] {
+	return -1
+    }
+
+    # Delete all breakpoints so that the output of "info breakpoints"
+    # below will only contain a single breakpoint.
+    delete_breakpoints
+
+    # Place a breakpoint within the function in the header file.
+    gdb_breakpoint "${srcfile4}:22"
+
+    # Check that the breakpoint was placed where we expected.  It should
+    # appear at the requested line.  When the bug in GDB was present the
+    # breakpoint would be placed on one of the following lines instead.
+    gdb_test "info breakpoints" \
+	".* in $func_name at \[^\r\n\]+${srcfile4}:22\\y.*" \
+	"info breakpoints, $tag"
+}
+
+do_test line_label_3 "callee" "range-a"
+do_test line_label_5 "main" "range-b"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
new file mode 100644
index 00000000000..9f09f353273
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
@@ -0,0 +1,189 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt | Inline |
+# |------|------|------|------|--------|
+# | 1    | 1    | 16   | Y    |        |
+# | 2    | 1    | 17   | Y    |        |
+# | 3    | 2    | 21   | Y    | X      |
+# | 4    | 2    | 22   | Y    | X      |
+# | 4    | 1    | 18   | N    | X      |
+# | 5    | 1    | 19   | Y    |        |
+# | 6    | 1    | 20   | Y    |        |
+# | 7    | 1    | END  | Y    |        |
+# |------|------|------|------|--------|
+#
+#
+# Place the first brekpoint at file 2, line 22 and a second breakpoint
+# at file 1, line 19.  A third breakpoint is placed at file 1, line
+# 18, but as this line table entry will have been discarded[1] the
+# third breakpoint will actually be placed at the same location as the
+# second breakpoint.
+#
+# This test is designed to test GDB's internal behaviour with respect
+# to discarding particular line table entries.  GCC and DWARF are
+# starting to introduce the idea of line table views.  As the views
+# information becomes better supported within GDB it is likely that
+# this will become out of date.  This is fine, the test will have
+# served its purpose by that point and can be deleted.
+#
+# [1] The entry for file 1, line 18 is discarded because it is at the
+# same address as the previous entry, but the previous entry is-stmt,
+# while line 18 is a non-stmt.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set build_options {nodebug optimize=-O1}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    global build_options
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main $build_options
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_3 addr}
+		    {high_pc line_label_5 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] $build_options] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
+    "check for breakpoint at ${srcfile4}"
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile3}:19"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in main at \[^\r\n\]+${srcfile3}:19\\y.*" \
+    "check for breakpoint at ${srcfile3}"
+
+# Line table entry for line 18 will have been discarded, so this
+# brekpoint will be at the same location as line 19.
+gdb_test "break ${srcfile3}:18" \
+    "Note: breakpoint $decimal also set at pc $hex.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
new file mode 100644
index 00000000000..a3820f16d57
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
@@ -0,0 +1,193 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt | Inline |
+# |------|------|------|------|--------|
+# | 1    | 1    | 16   | Y    |        |
+# | 2    | 1    | 17   | Y    |        |
+# | 3    | 2    | 21   | Y    | X      |
+# | 4    | 2    | 22   | Y    | X      |
+# | 4    | 1    | 18   | N    |        |
+# | 5    | 1    | 19   | N    |        |
+# | 6    | 1    | 20   | Y    |        |
+# | 7    | 1    | END  | Y    |        |
+# |------|------|------|------|--------|
+#
+# Break at file 2, line 22, then single instruction step forward.  We
+# should pass through line 19 and then encounter line 20.
+#
+# Currently we don't expect GDB to see file 1, line 18, as this is a
+# non-stmt line in a different file at the same address as the
+# previous is-stmt line.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set build_options {nodebug optimize=-O1}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    global build_options
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main $build_options
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_3 addr}
+		    {high_pc line_label_5 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] $build_options] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
+
+gdb_continue_to_breakpoint "${srcfile4}:22" \
+    ".* ${srcfile4} : 22 .*"
+
+# Now single instruction step forward.  Eventually we should hit
+# ${srcfile3}:20, but before we do we should hit the non-statement
+# line ${srcfile3}:19.
+#
+# We don't know how many instructions we'll need to step, but 100
+# should be enough for everyone (surely), and this stops us looping
+# forever if something goes wrong.
+set found_line_19 0
+set found_line_20 0
+set keep_going 1
+for { set i 0 } { $i < 100 && $keep_going } { incr i } {
+    set keep_going 0
+    gdb_test_multiple "stepi" "stepi ${i}" {
+	-re "${srcfile3} : 19 .*${gdb_prompt} " {
+	    set found_line_19 1
+	    set keep_going 1
+	}
+
+	-re "${srcfile3} : 20 .*${gdb_prompt} " {
+	    set found_line_20 1
+	}
+
+	-re "${srcfile4} : 22 .*${gdb_prompt} " {
+	    # Not left line 22 yet.
+	    set keep_going 1
+	}
+    }
+}
+
+gdb_assert { $found_line_19 && $found_line_20 } \
+    "found line 19 and 20"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
new file mode 100644
index 00000000000..a1b7b17cbeb
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
@@ -0,0 +1,46 @@
+/* Copyright 2020 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/>.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+volatile int var;
+volatile int bar;
+
+/* Generate some code to take up some space.  */
+#define FILLER do { \
+    var = 99;	    \
+} while (0)
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+  LL (1);	// F1, Ln 16
+  FILLER;
+  LL (2);	// F1, Ln 17
+  FILLER;
+  LL (3);	// F2, Ln 21
+  FILLER;
+  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
+  FILLER;
+  LL (5);	// F1, Ln 19 !S
+  FILLER;
+  LL (6);	// F1, Ln 20
+  FILLER;
+  LL (7);
+  FILLER;
+  return 0;				/* main end */
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
new file mode 100644
index 00000000000..a8331268a09
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.c : 16 */
+/* dw2-inline-header.c : 17 */
+/* dw2-inline-header.c : 18 */
+/* dw2-inline-header.c : 19 */
+/* dw2-inline-header.c : 20 */
+/* dw2-inline-header.c : 21 */
+/* dw2-inline-header.c : 22 */
+/* dw2-inline-header.c : 23 */
+/* dw2-inline-header.c : 24 */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
new file mode 100644
index 00000000000..7233acbcd76
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.h : 16 */
+/* dw2-inline-header.h : 17 */
+/* dw2-inline-header.h : 18 */
+/* dw2-inline-header.h : 19 */
+/* dw2-inline-header.h : 20 */
+/* dw2-inline-header.h : 21 */
+/* dw2-inline-header.h : 22 */
+/* dw2-inline-header.h : 23 */
+/* dw2-inline-header.h : 24 */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-05-15 14:46                                   ` Andrew Burgess
@ 2020-05-16  8:12                                     ` Bernd Edlinger
  0 siblings, 0 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-05-16  8:12 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Tom Tromey, gdb-patches, Alexandre Oliva

Thanks Andrew,

I tried you updated test case, and it does still have only one
failure:

FAIL: gdb.dwarf2/dw2-inline-header-3.exp: continue to breakpoint: dw2-inline-header.h:22

		=== gdb Summary ===

# of expected passes		2
# of unexpected failures	1

That is not a given thing as my patch depends completely on the inline end positions.

Also just for later reference Tom, you said there are internal tests failing, currently,
which are fixed by Andrew's patch, but did you ever try my latest patch ?
I sent it yesterday?

This information would be really helpful as it would at least be a proof of concept
for this attempt to fix the debug info.



On 5/15/20 4:46 PM, Andrew Burgess wrote:
> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2020-05-15 05:35:43 +0200]:
> 
> <snip>
> 
>>
>> Andrew, as far as I remember the range info of the
>> inlined subroutines were not correct, in your patch.
> 
> Thanks for pointing this out, I eventually figured out the problem
> you'd seen and fixed this.
> 
>> Can you please fix the range infos in your patch
>> and re-post it, before we decide how to proceed?
> 
> Rebased and latest version of the patch is attached.  As the bug fixes
> are minor I don't think we should spend much longer discussing this.
> 
>> But I would say that I see a way how I can adjust my
>> patch to fix the issues which were raised by your test
>> case (I added the test case from your patch to mine,
>> and most of it seems to be fine with my latest patch,
>> one remaining issue is a bit nasty but fixable).
>> I'll attach the patch file where I am at here, so
>> you can see what I have now.
>> It fails only one test case in dw2-inline-header-3.exp
>> but I would like how it compares to your patch
>> when Tom would like to try it out on the
>> internal test cases, he mentioned.
>>
>> And, yes, of course my approach is that the debug info
>> is not optimal at all as it is emitted by gcc, but 
>> gdb can probably work around it until it is fixed
>> which should happen when a new updated dwarf extension
>> is invented which tells us the view number where
>> the inlined subroutine ends.  The line table program
>> has view numbers, but not the range info of the
>> subroutine.  That is the deficiency in the dwarf
>> format I think causes the trouble, apart from the
>> fact that dwarf is already too complicate to understand.
> 
> I think this is one of our sticking points.  I agree 100% with you
> that adding view support to the range information would allow GCC to
> be more expressive.
> 
> But I feel that, just because DWARF lacks a certain feature, this
> doesn't mean we can't build a valid debug picture with the features we
> do have.  Sure, me may have to compromise, but we can still create
> something that is internally consistent.  GCC isn't doing this right
> now.
> 
> Maybe it is the opinion of the GCC devs that they'll not fix GCC until
> DWARF gains the extra features they'd like.  That's something you'd
> have to take up on the GCC list.
> 
> Adding support to work around compiler bugs is absolutely fine, so
> long as we understand exactly what issues we're working around, and
> that the issue (hopefully) will get fixed eventually, so any work
> around should be (I think) applied conservatively, only when we are
> sure the work around is appropriate (maybe based on producer strings,
> for example).
> 
> <snip>
> 
>>
>> On 5/15/20 12:39 AM, Andrew Burgess wrote:
>>> * Tom Tromey <tom@tromey.com> [2020-05-14 14:18:44 -0600]:
>>>
>>>>>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
>>>>
>>>> Resurrecting this again ... we have some internal tests that have been
>>>> failing, and I want to land at least one of these patches to resolve
>>>> this.
>>>>
>>>> Andrew> After reading[2] I'd also be interest to understand what flaw in
>>>> Andrew> DWARF you feel makes a difference in this case.
>>>>
>>>> I also don't understand this.
>>>>
>>
>> When you only know where an inline function ends at the byte address.
>> You can habe line table entries at the same address from the inline
>> funcion and from the calling function.  It is impossible to distinguish
>> between those, however there is a view number which would be different,
>> and as Alexandre explains it, these can be consider an extension to the
>> byte addresses, so if a range info would add a view number we would
>> know which line infos are from the subroutine and which are not, even
>> if the byte addresses are identical.
> 
> Please correct me if I'm wrong, but as for view support in ranges,
> there's not even a GCC only extension for this yet (I did look, but
> could easily have missed something).
> 
> I think we'd probably want to see this feature as at least a GCC
> extension, then we could add support in GDB.
> 
>>
>>>> Andrew>   I don't know how we address this without merging your patch,
>>>> Andrew>   releasing GDB and seeing how it works in the wild.  However, if we
>>>> Andrew>   did decide to "just try it", I would still prefer we staged things
>>>> Andrew>   as:
>>>> Andrew>     (a) Merge my patch, targeted regression fix, then
>>>> Andrew>     (b) Your patch, new functionality GCC/DWARF ranges work around.
>>>> Andrew>   In this way, if we end up backing out some or all of (b) we still
>>>> Andrew>   have (a) in place that fixes the regression.  I'm more than happy
>>>> Andrew>   for a rebase of (b) in include full removal of (a).
>>>>
>>>> I think landing your patch is safe to do while we discuss part (b).
>>>> How about we move forward with that part?  Then if we come to agreement
>>>> on where the bug lies we can decide about that.
>>>
>>> I agree. I already rebased this and retested it.  Unless someone
>>> shouts out between now and tomorrow I plan to merge my patch.
>>>
>>> Bernd (or anyone else) - I'm more than happy that if a better patch is
>>> put forward it can revert some or all of my patch, as needed.  Like
>>> Tom, I'd like to see the regressions squashed.
>>>
>>
>> No, I am not able to do this once we decide that gcc's debug info
>> is impossible to implement, I will send a patch to the gcc-patches
>> which restores the gcc-7 debug info.
> 
> I am certainly not suggesting, and having re-read many mails in this
> thread, I don't think anyone else is suggesting, that it is impossible
> to craft a work around for GCC's broken debug information.
> 
> I think that all we're suggesting is that your work around patch isn't
> ready just yet, so instead of rushing that in to fix the is-stmt
> regression, lets just merge my conservative regression fix patch, then
> we can take our time and get your work around GCC patch correct.
> 
> This would be an easy discussion if it wasn't for
> gdb.cp/step-and-next-inline.exp, which my regression fix patch
> breaks.
> 
> That test is what motivated your original patch last year, so I
> completely understand why my breaking it leaves you feeling
> frustrated.
> 
> My position here is that this test is really an example of the GCC
> broken debug issue and really should never have been merged with the
> is-stmt patch, as such I would like to see it disabled until your
> patch lands later.
> 
>>                                       My patch was based on the
>> assumption that there is a way to work around the issues, but if
>> that turns out to be a failure, I will not go back and revert
>> your patch again, since it seems to revert my work on that issue
>> completely
> 
> No you've misunderstood, when your patch succeeds you will (maybe)
> revert this patch as part of your successful patch.
> 
> If your patch were to never land (and I'm sure we _can_ get it over
> the line) then no, the patch I propose here wouldn't be reverted, but
> that's exactly the point of pushing this patch first.
> 
> This patch is a low risk regression fix.
> 
> You patch is (slightly) higher risk, new functionality.
> 
> All Tom and I are suggesting is lets just merge them in that order,
> rather than rushing into the high risk fix.
> 
>>             (see how this patch breaks the step-and-next-inline.exp
>> test case).  And once again, I feel not at all emotional, just that
>> I want the issue fixed, and next time I will try a completely
>> different approach which is gcc was wrong, and therefore we will
>> revert Alexandre Oliva's patch, since it is the least effort and
>> Alexandre already signaled he also had considered that already.
>>
>> Alexandre Oliva wrote on 11/19/19:
>> https://gcc.gnu.org/pipermail/gcc-patches/2019-November/534845.html
>>> Perhaps we should change our defaults, if the situation with GDB does
>>> not change :-(
> 
> There is some view information emitted by GCC now, but for
> gdb.cp/step-and-next-inline.exp I don't think any of the view
> information would help even if we did consume it.  The problem with
> that test is entirely GCC emitting broken DWARF.
> 
> I interpret the last two paragraphs above as saying, it's easier to
> disable a broken feature in GCC than to merge a partially complete
> work around into GDB.  And that's as it should be.
> 
> If you can point to some non-broken DWARF that GDB doesn't currently
> parse correctly, that would help gdb.cp/step-and-next-inline.exp pass,
> then I would certainly be prepared to invest some time trying to help
> improve GDB in that area.
> 
> Bernd, you asked for some time to follow up on this thread, but this
> issue has dragged on for long enough now I think.  It is my intention
> to merge the patch below on Monday unless I hear a compelling argument
> against it.
> 
> After that it should be trivial for your to rebase your patch and
> revert those parts of my patch you no longer need.  Repost the update
> and we can discuss how to get it merged.
> 
> Thanks,
> Andrew
> 
> ----
> 
> From c5d89ffa3032c401cc79c241097006a12b306d02 Mon Sep 17 00:00:00 2001
> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Date: Fri, 3 Apr 2020 20:32:38 +0100
> Subject: [PATCH] gdb: Preserve is-stmt lines when switch between files
> 
> After the is-stmt support commit:
> 
>   commit 8c95582da858ac981f689a6f599acacb8c5c490f
>   Date:   Mon Dec 30 21:04:51 2019 +0000
> 
>       gdb: Add support for tracking the DWARF line table is-stmt field
> 
> A regression was observed where a breakpoint could no longer be placed
> in some cases.
> 
> Consider a line table like this:
> 
>   File 1: test.c
>   File 2: test.h
> 
>   | Addr | File | Line | Stmt |
>   |------|------|------|------|
>   | 1    | 1    | 16   | Y    |
>   | 2    | 1    | 17   | Y    |
>   | 3    | 2    | 21   | Y    |
>   | 4    | 2    | 22   | Y    |
>   | 4    | 1    | 18   | N    |
>   | 5    | 2    | 23   | N    |
>   | 6    | 1    | 24   | Y    |
>   | 7    | 1    | END  | Y    |
>   |------|------|------|------|
> 
> Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
> built two line table structures:
> 
>   File 1                 File 2
>   ------                 ------
> 
>   | Addr | Line |        | Addr | Line |
>   |------|------|        |------|------|
>   | 1    | 16   |        | 3    | 21   |
>   | 2    | 17   |        | 4    | 22   |
>   | 3    | END  |        | 6    | END  |
>   | 6    | 24   |        |------|------|
>   | 7    | END  |
>   |------|------|
> 
> After the is-stmt patch GDB now records non-stmt lines, so the
> generated line table structures look like this:
> 
>   File 1                   File 2
>   ------                   ------
> 
>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>   |------|------|------|  |------|------|------|
>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>   | 3    | END  | Y    |  | 4    | END  | Y    |
>   | 4    | 18   | N    |  | 5    | 23   | N    |
>   | 5    | END  | Y    |  | 6    | END  | Y    |
>   | 6    | 24   | Y    |  |------|------|------|
>   | 7    | END  | Y    |
>   |------|------|------|
> 
> The problem is that in 'File 2', end END marker at address 4 causes
> the previous line table entry to be discarded, so we actually end up
> with this:
> 
>   File 2
>   ------
> 
>   | Addr | Line | Stmt |
>   |------|------|------|
>   | 3    | 21   | Y    |
>   | 4    | END  | Y    |
>   | 5    | 23   | N    |
>   | 6    | END  | Y    |
>   |------|------|------|
> 
> When a user tries to place a breakpoint in file 2 at line 22, this is
> no longer possible.
> 
> The solution I propose here is that we ignore line table entries that
> would trigger a change of file if:
> 
>   1. The new line being added is at the same address as the previous
>   line, and
> 
>   2. We have previously seen an is-stmt line at the current address.
> 
> The result of this is that GDB switches file, and knows that some line
> entry (or entries) are going to be discarded, prefer to keep is-stmt
> lines and discard non-stmt lines.
> 
> After this commit the lines tables are now:
> 
>   File 1                   File 2
>   ------                   ------
> 
>   | Addr | Line | Stmt |  | Addr | Line | Stmt |
>   |------|------|------|  |------|------|------|
>   | 1    | 16   | Y    |  | 3    | 21   | Y    |
>   | 2    | 17   | Y    |  | 4    | 22   | Y    |
>   | 3    | END  | Y    |  | 5    | 23   | N    |
>   | 5    | END  | Y    |  | 6    | END  | Y    |
>   | 6    | 24   | Y    |  |------|------|------|
>   | 7    | END  | Y    |
>   |------|------|------|
> 
> We've lost the non-stmt entry for file 1, line 18, but retained the
> is-stmt entry for file 2, line 22.  The user can now place a
> breakpoint at that location.
> 
> One problem that came from this commit was the test
> gdb.cp/step-and-next-inline.exp, which broke in several places.  After
> looking at this test again I think that in some cases this test was
> only ever passing by pure luck.  The debug GCC is producing for this
> test is pretty broken.  I raised this GCC bug:
> 
>   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> 
> for this and disabled one entire half of the test.  There are still
> some cases in here that do pass, and if/when GCC is fixed it would be
> great to enable this test again.
> 
> gdb/ChangeLog:
> 
> 	* dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
> 	member variable.
> 	<m_stmt_at_address>: New member variable.
> 	(lnp_state_machine::record_line): Don't record some lines, update
> 	tracking of is_stmt at the same address.
> 	(lnp_state_machine::lnp_state_machine): Initialise new member
> 	variables.
> 
> gdb/testsuite/ChangeLog:
> 
> 	* gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
> 	use_header case.
> 	* gdb.dwarf2/dw2-inline-header-1.exp: New file.
> 	* gdb.dwarf2/dw2-inline-header-2.exp: New file.
> 	* gdb.dwarf2/dw2-inline-header-3.exp: New file.
> 	* gdb.dwarf2/dw2-inline-header-lbls.c: New file.
> 	* gdb.dwarf2/dw2-inline-header.c: New file.
> 	* gdb.dwarf2/dw2-inline-header.h: New file.
> ---
>  gdb/ChangeLog                                 |  10 +
>  gdb/dwarf2/read.c                             |  47 ++++-
>  gdb/testsuite/ChangeLog                       |  11 +
>  gdb/testsuite/gdb.cp/step-and-next-inline.exp |   7 +
>  .../gdb.dwarf2/dw2-inline-header-1.exp        | 186 +++++++++++++++++
>  .../gdb.dwarf2/dw2-inline-header-2.exp        | 189 +++++++++++++++++
>  .../gdb.dwarf2/dw2-inline-header-3.exp        | 193 ++++++++++++++++++
>  .../gdb.dwarf2/dw2-inline-header-lbls.c       |  46 +++++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.c  |  24 +++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-header.h  |  24 +++
>  10 files changed, 734 insertions(+), 3 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> 
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index ab21ab0d13a..fea980d0a33 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -19880,6 +19880,15 @@ class lnp_state_machine
>    /* The last file a line number was recorded for.  */
>    struct subfile *m_last_subfile = NULL;
>  
> +  /* The address of the last line entry.  */
> +  CORE_ADDR m_last_address;
> +
> +  /* Set to true when a previous line at the same address (using
> +     m_last_address) had m_is_stmt true.  This is reset to false when a
> +     line entry at a new address (m_address different to m_last_address) is
> +     processed.  */
> +  bool m_stmt_at_address = false;
> +
>    /* When true, record the lines we decode.  */
>    bool m_currently_recording_lines = false;
>  
> @@ -20073,14 +20082,34 @@ lnp_state_machine::record_line (bool end_sequence)
>        fe->included_p = 1;
>        if (m_record_lines_p)
>  	{
> -	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
> -	      || end_sequence)
> +	  /* When we switch files we insert an end maker in the first file,
> +	     switch to the second file and add a new line entry.  The
> +	     problem is that the end marker inserted in the first file will
> +	     discard any previous line entries at the same address.  If the
> +	     line entries in the first file are marked as is-stmt, while
> +	     the new line in the second file is non-stmt, then this means
> +	     the end marker will discard is-stmt lines so we can have a
> +	     non-stmt line.  This means that there are less addresses at
> +	     which the user can insert a breakpoint.
> +
> +	     To improve this we track the last address in m_last_address,
> +	     and whether we have seen an is-stmt at this address.  Then
> +	     when switching files, if we have seen a stmt at the current
> +	     address, and we are switching to create a non-stmt line, then
> +	     discard the new line.  */
> +	  bool file_changed
> +	    = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
> +	  bool ignore_this_line
> +	    = (file_changed && !end_sequence && m_last_address == m_address
> +	       && !m_is_stmt && m_stmt_at_address);
> +
> +	  if ((file_changed && !ignore_this_line) || end_sequence)
>  	    {
>  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
>  				 m_currently_recording_lines ? m_cu : nullptr);
>  	    }
>  
> -	  if (!end_sequence)
> +	  if (!end_sequence && !ignore_this_line)
>  	    {
>  	      bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
>  
> @@ -20099,6 +20128,15 @@ lnp_state_machine::record_line (bool end_sequence)
>  	    }
>  	}
>      }
> +
> +  /* Track whether we have seen any m_is_stmt true at m_address in case we
> +     have multiple line table entries all at m_address.  */
> +  if (m_last_address != m_address)
> +    {
> +      m_stmt_at_address = false;
> +      m_last_address = m_address;
> +    }
> +  m_stmt_at_address |= m_is_stmt;
>  }
>  
>  lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
> @@ -20118,6 +20156,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
>    m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
>    m_is_stmt = lh->default_is_stmt;
>    m_discriminator = 0;
> +
> +  m_last_address = m_address;
> +  m_stmt_at_address = false;
>  }
>  
>  void
> diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> index 3733fa75570..a95e21194f9 100644
> --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
>  proc do_test { use_header } {
>      global srcfile testfile
>  
> +    if { $use_header } {
> +	# This test will not pass due to poor debug information
> +	# generated by GCC (at least upto 10.x).  See
> +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> +	return
> +    }
> +
>      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
>      if { $use_header } {
>  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> new file mode 100644
> index 00000000000..dc7ec929236
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> @@ -0,0 +1,186 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# |      |      |      |      | Inline | Inline |
> +# | Addr | File | Line | Stmt | Rng A  | Rng B  |
> +# |------|------|------|------|--------|--------|
> +# | 1    | 1    | 16   | Y    |        |        |
> +# | 2    | 1    | 17   | Y    |        |        |
> +# | 3    | 2    | 21   | Y    | X      |        |
> +# | 4    | 2    | 22   | Y    | X      |        |
> +# | 4    | 1    | 18   | N    | X      |        |
> +# | 5    | 2    | 23   | N    | X      | X      |
> +# | 6    | 1    | 24   | Y    |        |        |
> +# | 7    | 1    | END  | Y    |        |        |
> +# |------|------|------|------|--------|--------|
> +#
> +# Places a brekpoint at file 2, line 22.  Previously GDB would discard
> +# the line table entry for this line due to switching files for the
> +# file 1, line 18 non-statement line.  After patching however, GDB now
> +# discards the file 1, line 18 entry instead, and the breakpoint at
> +# line 22 should succeed.
> +#
> +# The two inlined subroutine ranges 'A' and 'B' represent two possible
> +# ways that a compiler might represent this siuatio in the DWARF.
> +#
> +# Range 'B' is something that has been seen in the wild using GCC 8.2.
> +# In this case the compilers range information is clearly wrong, but
> +# this shouldn't impact the main point of the test.
> +#
> +# Range 'A' is a hypothetical case of how the compiler might choose to
> +# represent this range, this has never been seen in the wild, but is
> +# an improved debug experiece over range 'B'.  However, if we ever run
> +# in to the situation where GDB can support the range 'A' test, or
> +# support some real DWARF seen in the wild, then the range 'A' case
> +# should be dropped in favour of supporting real world cases.  This is
> +# included here as it "just worked" once the range 'B' case was
> +# working.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +# Prepare and run the test.
> +proc do_test { start_label func_name tag } {
> +    global srcfile srcfile2 srcfile3 srcfile4 testfile
> +
> +    standard_testfile dw2-inline-header-lbls.c dw2-inline-header-${tag}.S \
> +	dw2-inline-header.c dw2-inline-header.h
> +
> +    set build_options {nodebug optimize=-O1}
> +
> +    set asm_file [standard_output_file $srcfile2]
> +    Dwarf::assemble $asm_file {
> +	global srcdir subdir srcfile srcfile3 srcfile4 testfile
> +	upvar build_options build_options
> +	upvar start_label start_label
> +	declare_labels lines_label callee_subprog_label
> +
> +	get_func_info main $build_options
> +
> +	cu {} {
> +	    compile_unit {
> +		{producer "gcc" }
> +		{language @DW_LANG_C}
> +		{name ${srcfile3}}
> +		{low_pc 0 addr}
> +		{stmt_list ${lines_label} DW_FORM_sec_offset}
> +	    } {
> +		callee_subprog_label: subprogram {
> +		    {external 1 flag}
> +		    {name callee}
> +		    {inline 3 data1}
> +		}
> +		subprogram {
> +		    {external 1 flag}
> +		    {name main}
> +		    {low_pc $main_start addr}
> +		    {high_pc "$main_start + $main_len" addr}
> +		} {
> +		    inlined_subroutine {
> +			{abstract_origin %$callee_subprog_label}
> +			{low_pc $start_label addr}
> +			{high_pc line_label_6 addr}
> +			{call_file 1 data1}
> +			{call_line 18 data1}
> +		    }
> +		}
> +	    }
> +	}
> +
> +	lines {version 2 default_is_stmt 1} lines_label {
> +	    include_dir "${srcdir}/${subdir}"
> +	    file_name "$srcfile3" 1
> +	    file_name "$srcfile4" 1
> +
> +	    program {
> +		{DW_LNE_set_address line_label_1}
> +		{DW_LNS_advance_line 15}
> +		{DW_LNS_copy}
> +
> +		{DW_LNE_set_address line_label_2}
> +		{DW_LNS_advance_line 1}
> +		{DW_LNS_copy}
> +
> +		{DW_LNS_set_file 2}
> +		{DW_LNE_set_address line_label_3}
> +		{DW_LNS_advance_line 4}
> +		{DW_LNS_copy}
> +
> +		{DW_LNE_set_address line_label_4}
> +		{DW_LNS_advance_line 1}
> +		{DW_LNS_copy}
> +
> +		{DW_LNS_advance_line -4}
> +		{DW_LNS_set_file 1}
> +		{DW_LNS_negate_stmt}
> +		{DW_LNS_copy}
> +
> +		{DW_LNS_set_file 2}
> +		{DW_LNE_set_address line_label_5}
> +		{DW_LNS_advance_line 5}
> +		{DW_LNS_copy}
> +
> +		{DW_LNS_negate_stmt}
> +		{DW_LNS_set_file 1}
> +		{DW_LNE_set_address line_label_6}
> +		{DW_LNS_advance_line 1}
> +		{DW_LNS_copy}
> +
> +		{DW_LNE_set_address line_label_7}
> +		{DW_LNE_end_sequence}
> +	    }
> +	}
> +    }
> +
> +    if { [prepare_for_testing "failed to prepare" ${testfile}-${tag} \
> +	      [list $srcfile $asm_file] $build_options] } {
> +	return -1
> +    }
> +
> +    if ![runto_main] {
> +	return -1
> +    }
> +
> +    # Delete all breakpoints so that the output of "info breakpoints"
> +    # below will only contain a single breakpoint.
> +    delete_breakpoints
> +
> +    # Place a breakpoint within the function in the header file.
> +    gdb_breakpoint "${srcfile4}:22"
> +
> +    # Check that the breakpoint was placed where we expected.  It should
> +    # appear at the requested line.  When the bug in GDB was present the
> +    # breakpoint would be placed on one of the following lines instead.
> +    gdb_test "info breakpoints" \
> +	".* in $func_name at \[^\r\n\]+${srcfile4}:22\\y.*" \
> +	"info breakpoints, $tag"
> +}
> +
> +do_test line_label_3 "callee" "range-a"
> +do_test line_label_5 "main" "range-b"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> new file mode 100644
> index 00000000000..9f09f353273
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> @@ -0,0 +1,189 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt | Inline |
> +# |------|------|------|------|--------|
> +# | 1    | 1    | 16   | Y    |        |
> +# | 2    | 1    | 17   | Y    |        |
> +# | 3    | 2    | 21   | Y    | X      |
> +# | 4    | 2    | 22   | Y    | X      |
> +# | 4    | 1    | 18   | N    | X      |
> +# | 5    | 1    | 19   | Y    |        |
> +# | 6    | 1    | 20   | Y    |        |
> +# | 7    | 1    | END  | Y    |        |
> +# |------|------|------|------|--------|
> +#
> +#
> +# Place the first brekpoint at file 2, line 22 and a second breakpoint
> +# at file 1, line 19.  A third breakpoint is placed at file 1, line
> +# 18, but as this line table entry will have been discarded[1] the
> +# third breakpoint will actually be placed at the same location as the
> +# second breakpoint.
> +#
> +# This test is designed to test GDB's internal behaviour with respect
> +# to discarding particular line table entries.  GCC and DWARF are
> +# starting to introduce the idea of line table views.  As the views
> +# information becomes better supported within GDB it is likely that
> +# this will become out of date.  This is fine, the test will have
> +# served its purpose by that point and can be deleted.
> +#
> +# [1] The entry for file 1, line 18 is discarded because it is at the
> +# same address as the previous entry, but the previous entry is-stmt,
> +# while line 18 is a non-stmt.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set build_options {nodebug optimize=-O1}
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    global build_options
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main $build_options
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_3 addr}
> +		    {high_pc line_label_5 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] $build_options] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
> +    "check for breakpoint at ${srcfile4}"
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile3}:19"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in main at \[^\r\n\]+${srcfile3}:19\\y.*" \
> +    "check for breakpoint at ${srcfile3}"
> +
> +# Line table entry for line 18 will have been discarded, so this
> +# brekpoint will be at the same location as line 19.
> +gdb_test "break ${srcfile3}:18" \
> +    "Note: breakpoint $decimal also set at pc $hex.*"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> new file mode 100644
> index 00000000000..a3820f16d57
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> @@ -0,0 +1,193 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt | Inline |
> +# |------|------|------|------|--------|
> +# | 1    | 1    | 16   | Y    |        |
> +# | 2    | 1    | 17   | Y    |        |
> +# | 3    | 2    | 21   | Y    | X      |
> +# | 4    | 2    | 22   | Y    | X      |
> +# | 4    | 1    | 18   | N    |        |
> +# | 5    | 1    | 19   | N    |        |
> +# | 6    | 1    | 20   | Y    |        |
> +# | 7    | 1    | END  | Y    |        |
> +# |------|------|------|------|--------|
> +#
> +# Break at file 2, line 22, then single instruction step forward.  We
> +# should pass through line 19 and then encounter line 20.
> +#
> +# Currently we don't expect GDB to see file 1, line 18, as this is a
> +# non-stmt line in a different file at the same address as the
> +# previous is-stmt line.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set build_options {nodebug optimize=-O1}
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    global build_options
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main $build_options
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_3 addr}
> +		    {high_pc line_label_5 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] $build_options] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> +
> +gdb_continue_to_breakpoint "${srcfile4}:22" \
> +    ".* ${srcfile4} : 22 .*"
> +
> +# Now single instruction step forward.  Eventually we should hit
> +# ${srcfile3}:20, but before we do we should hit the non-statement
> +# line ${srcfile3}:19.
> +#
> +# We don't know how many instructions we'll need to step, but 100
> +# should be enough for everyone (surely), and this stops us looping
> +# forever if something goes wrong.
> +set found_line_19 0
> +set found_line_20 0
> +set keep_going 1
> +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
> +    set keep_going 0
> +    gdb_test_multiple "stepi" "stepi ${i}" {
> +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
> +	    set found_line_19 1
> +	    set keep_going 1
> +	}
> +
> +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
> +	    set found_line_20 1
> +	}
> +
> +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
> +	    # Not left line 22 yet.
> +	    set keep_going 1
> +	}
> +    }
> +}
> +
> +gdb_assert { $found_line_19 && $found_line_20 } \
> +    "found line 19 and 20"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> new file mode 100644
> index 00000000000..a1b7b17cbeb
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> @@ -0,0 +1,46 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* Used to insert labels with which we can build a fake line table.  */
> +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
> +
> +volatile int var;
> +volatile int bar;
> +
> +/* Generate some code to take up some space.  */
> +#define FILLER do { \
> +    var = 99;	    \
> +} while (0)
> +
> +int
> +main ()
> +{					/* main prologue */
> +  asm ("main_label: .globl main_label");
> +  LL (1);	// F1, Ln 16
> +  FILLER;
> +  LL (2);	// F1, Ln 17
> +  FILLER;
> +  LL (3);	// F2, Ln 21
> +  FILLER;
> +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
> +  FILLER;
> +  LL (5);	// F1, Ln 19 !S
> +  FILLER;
> +  LL (6);	// F1, Ln 20
> +  FILLER;
> +  LL (7);
> +  FILLER;
> +  return 0;				/* main end */
> +}
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> new file mode 100644
> index 00000000000..a8331268a09
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> @@ -0,0 +1,24 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* dw2-inline-header.c : 16 */
> +/* dw2-inline-header.c : 17 */
> +/* dw2-inline-header.c : 18 */
> +/* dw2-inline-header.c : 19 */
> +/* dw2-inline-header.c : 20 */
> +/* dw2-inline-header.c : 21 */
> +/* dw2-inline-header.c : 22 */
> +/* dw2-inline-header.c : 23 */
> +/* dw2-inline-header.c : 24 */
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> new file mode 100644
> index 00000000000..7233acbcd76
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> @@ -0,0 +1,24 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* dw2-inline-header.h : 16 */
> +/* dw2-inline-header.h : 17 */
> +/* dw2-inline-header.h : 18 */
> +/* dw2-inline-header.h : 19 */
> +/* dw2-inline-header.h : 20 */
> +/* dw2-inline-header.h : 21 */
> +/* dw2-inline-header.h : 22 */
> +/* dw2-inline-header.h : 23 */
> +/* dw2-inline-header.h : 24 */
> 

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-05-15  3:35                                 ` Bernd Edlinger
  2020-05-15 14:46                                   ` Andrew Burgess
@ 2020-05-17 17:26                                   ` Bernd Edlinger
  2020-05-20 18:26                                   ` Andrew Burgess
  2 siblings, 0 replies; 79+ messages in thread
From: Bernd Edlinger @ 2020-05-17 17:26 UTC (permalink / raw)
  To: Andrew Burgess, Tom Tromey; +Cc: gdb-patches, Alexandre Oliva

[-- Attachment #1: Type: text/plain, Size: 2702 bytes --]

Hi Tom, Andrew,

> On 5/15/20 12:39 AM, Andrew Burgess wrote:
>> * Tom Tromey <tom@tromey.com> [2020-05-14 14:18:44 -0600]:
>>
>>>>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
>>>
>>> Resurrecting this again ... we have some internal tests that have been
>>> failing, and I want to land at least one of these patches to resolve
>>> this.
>>>
>>> Andrew> After reading[2] I'd also be interest to understand what flaw in
>>> Andrew> DWARF you feel makes a difference in this case.
>>>
>>> I also don't understand this.
>>>
> 
> When you only know where an inline function ends at the byte address.
> You can habe line table entries at the same address from the inline
> funcion and from the calling function.  It is impossible to distinguish
> between those, however there is a view number which would be different,
> and as Alexandre explains it, these can be consider an extension to the
> byte addresses, so if a range info would add a view number we would
> know which line infos are from the subroutine and which are not, even
> if the byte addresses are identical.
> 
>>> Andrew> I think it is great Bernd, that you are reaching out from the GCC
>>> Andrew> community to engage with GDB, this is certainly the best way to ensure
>>> Andrew> that we can work together as communities to give the best possible
>>> Andrew> debug experience, and I'm sorry you feel that I have not been clear
>>> Andrew> enough about the issues I'm seeing here.
>>>
>>> +1
>>>

I am always happy to help.

Andrew, and/or Tom, could you please test my patch, obviously one new test
case is still failing, however I would not rule out the possibility, that
we can work together to even fix that, I must say that our cooperation made
things possible, that I would not have expected, like fixing the inline functions
that are not in a header file.

How does this patch work in practice, I would be happy to know what I have
to fix even when Andrews patch lands tomorrow, probably due to a release
schedule?  Attached are again the patch from myself, and test cases from
Andrew's patch.

By the way when is the next gdb release planned?

And let me say this, honestly, I am happy to take the blame for any GCC BUG :-),
that certainly still exists, and ideally those will be fixed in a way that
gdb can figure out that it is fixed, ideally by some kind of dwarf-version
info in the debug info.

What is a given thing that this broken debug info is already there for years,
and historic gcc version will continue to exist for quite a while.  So my goal
is to work around it, communicate with each other, look for solutions, but not
make the users unhappy unless absolutely necessary.



Thanks
Bernd.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-gdb.multi-multi-re-run.exp-with-native-gdbserver.patch --]
[-- Type: text/x-patch; name="0001-Fix-gdb.multi-multi-re-run.exp-with-native-gdbserver.patch", Size: 3722 bytes --]

From 7cfd74cfc6e14034779e6cc048c68877b7a08f88 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Sat, 16 May 2020 18:59:10 +0100
Subject: [PATCH 1/2] Fix gdb.multi/multi-re-run.exp with native-gdbserver

The new exec-file-mismatch feature reveals that when running
gdb.multi/multi-re-run.exp against --target_board=native-gdbserver,
we've been starting gdbserver with the wrong program:

 (gdb) spawn /home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/../../gdbserver/gdbserver --once localhost:2347 /home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/te
 stsuite/outputs/gdb.multi/multi-re-run/multi-re-run-2
 Process /home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.multi/multi-re-run/multi-re-run-2 created; pid = 6280
 Listening on port 2347
 target remote localhost:2347
 Remote debugging using localhost:2347
 warning: Mismatch between current exec-file /home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.multi/multi-re-run/multi-re-run-1
 and automatically determined exec-file /home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.multi/multi-re-run/multi-re-run-2
 exec-file-mismatch handling is currently "ask"
 Load new symbol table from "/home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.multi/multi-re-run/multi-re-run-2"? (y or n) Quit
 (gdb) FAIL: gdb.multi/multi-re-run.exp: re_run_inf=1: iter=1: running to all_started in runto

The problem is that gdb_reload uses the last loaded file as binary to
spawn, but we load the program for inferior 2 and then switch to
inferior 1 and run it, so the last loaded file is the program for
inferior 2.

Fix this by tweaking last_loaded_file.

gdb/testsuite/ChangeLog:
2020-05-16  Pedro Alves  <palves@redhat.com>

	* gdb.multi/multi-re-run.exp (test_re_run): Switch
	LAST_LOADED_FILE accordingly.
---
 gdb/testsuite/ChangeLog                  | 5 +++++
 gdb/testsuite/gdb.multi/multi-re-run.exp | 7 +++++++
 2 files changed, 12 insertions(+)

diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index c57ddf5..0be2fd3 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2020-05-16  Pedro Alves  <palves@redhat.com>
+
+	* gdb.multi/multi-re-run.exp (test_re_run): Switch
+	LAST_LOADED_FILE accordingly.
+
 2020-05-15  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
 
 	* gdb.base/alias.exp: Verify 'help aliases' shows user defined aliases.
diff --git a/gdb/testsuite/gdb.multi/multi-re-run.exp b/gdb/testsuite/gdb.multi/multi-re-run.exp
index 93cd709..a243293 100644
--- a/gdb/testsuite/gdb.multi/multi-re-run.exp
+++ b/gdb/testsuite/gdb.multi/multi-re-run.exp
@@ -51,6 +51,7 @@ proc test_re_run {re_run_inf} {
     global binfile1 binfile2
     global inferior_exited_re
     global gdb_prompt
+    global last_loaded_file
 
     clean_restart ${binfile1}
 
@@ -65,12 +66,17 @@ proc test_re_run {re_run_inf} {
 
     if {$re_run_inf == 1} {
 	set steady_inf 2
+	set steady_binfile $binfile2
+	set re_run_binfile $binfile1
     } else {
 	set steady_inf 1
+	set steady_binfile $binfile1
+	set re_run_binfile $binfile2
     }
 
     gdb_test "inferior $steady_inf" "Switching to inferior $steady_inf.*" \
 	"switch to steady inferior"
+    set last_loaded_file $steady_binfile
 
     # Run the steady inferior to a breakpoint, and let it stay stopped
     # there.
@@ -81,6 +87,7 @@ proc test_re_run {re_run_inf} {
 
     gdb_test "inferior $re_run_inf" "Switching to inferior $re_run_inf.*" \
 	"switch to re-run inferior"
+    set last_loaded_file $re_run_binfile
 
     # Now run the RE_RUN_INF inferior a couple times.  GDB used to
     # have a bug that caused the second run to fail to load
-- 
1.9.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-Automatic-date-update-in-version.in.patch --]
[-- Type: text/x-patch; name="0002-Automatic-date-update-in-version.in.patch", Size: 741 bytes --]

From 966dc1a27c55ccb298cb8c7c41c9cc2985cc321a Mon Sep 17 00:00:00 2001
From: GDB Administrator <gdbadmin@sourceware.org>
Date: Sun, 17 May 2020 00:00:06 +0000
Subject: [PATCH 2/2] Automatic date update in version.in

---
 bfd/version.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bfd/version.h b/bfd/version.h
index 810a82e..08a6344 100644
--- a/bfd/version.h
+++ b/bfd/version.h
@@ -16,7 +16,7 @@
 
    In releases, the date is not included in either version strings or
    sonames.  */
-#define BFD_VERSION_DATE 20200516
+#define BFD_VERSION_DATE 20200517
 #define BFD_VERSION @bfd_version@
 #define BFD_VERSION_STRING  @bfd_version_package@ @bfd_version_string@
 #define REPORT_BUGS_TO @report_bugs_to@
-- 
1.9.1


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-05-15  3:35                                 ` Bernd Edlinger
  2020-05-15 14:46                                   ` Andrew Burgess
  2020-05-17 17:26                                   ` Bernd Edlinger
@ 2020-05-20 18:26                                   ` Andrew Burgess
  2020-05-27 13:10                                     ` Andrew Burgess
  2 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-05-20 18:26 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: Tom Tromey, gdb-patches, Alexandre Oliva

Bernd,

Below is a first draft of a currently WIP patch that is an alternative
to what I have previously proposed.

This patch is based on parts of your patch, specifically, those bits
related to having two different end-of-sequence type markers 0 or -1.

I have fixed the issue that prevented my new dw2-inline-header-3.exp
test from passing.

This does not include any of the inline range handling changes you
were proposing, but as this is a sub-set of your proposal, then I'm
hoping you might feel .... less bad .... about this patch, adding the
rest of your work should be much more building on top of this, rather
than backing this out and starting again.

The one contentious area where I have had to stick with my original
patch is in the treatment of gdb.cp/step-and-next-inline.exp, this
test still regresses with this patch, so I have still basically
disabled this test.  As we've discussed (at great length) I believe
the regressions here are due to GCC bugs, which your patch when its
completed will aim to address, so hopefully that will not be too much
to accept for now.

Like I said, this is still WIP, its just I'll likely not get to work
on this until Friday or the weekend now, so I wanted to share this
early so you could give me your thoughts.

Look forward to your feedback,

Thanks,
Andrew

----

From 009671f35e232d80f61fd7d86251a84be2d965db Mon Sep 17 00:00:00 2001
From: Andrew Burgess <andrew.burgess@embecosm.com>
Date: Wed, 20 May 2020 19:16:05 +0100
Subject: [PATCH] WIP: gdb: Don't delete empty lines when changing symtabs

Based on Bernd's patch found here:

  https://sourceware.org/pipermail/gdb-patches/2020-May/168591.html

TODO:

 - More testing,
 - Changelogs
 - Split out addition of linetable_entry::end_of_sequence
---
 gdb/buildsym.c                                |  24 ++-
 gdb/dwarf2/read.c                             |   8 +-
 gdb/symtab.c                                  |   4 +-
 gdb/symtab.h                                  |  16 +-
 gdb/testsuite/gdb.cp/step-and-next-inline.exp |   7 +
 .../gdb.dwarf2/dw2-inline-header-1.exp        | 186 +++++++++++++++++
 .../gdb.dwarf2/dw2-inline-header-2.exp        | 189 +++++++++++++++++
 .../gdb.dwarf2/dw2-inline-header-3.exp        | 193 ++++++++++++++++++
 .../gdb.dwarf2/dw2-inline-header-lbls.c       |  46 +++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c  |  24 +++
 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h  |  24 +++
 11 files changed, 714 insertions(+), 7 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-header.h

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index b9bcc33080a..2ff2a5455d0 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -662,7 +662,24 @@ buildsym_compunit::pop_subfile ()
 }
 \f
 /* Add a linetable entry for line number LINE and address PC to the
-   line vector for SUBFILE.  */
+   line vector for SUBFILE.
+
+   LINE is usually an integer greater than 0 representing the line number
+   within the file, however two special values also exist.  Pass 0 to
+   indicate an end of sequence marker found within the actual line table,
+   or -1 to indicate an artificial end of sequence generated during
+   parsing of the line table to indicate a switch between subfiles.
+
+   The difference between these values is how GDB treats apparently empty
+   lines at the end marker, with the end marker value 0 this function
+   deletes any empty lines at the same address as the end marker, while
+   when switching subfiles any empty lines are preserved.  The reason is
+   that a 0 style end of sequence most likely means the end of a function,
+   so trying to place a breakpoint on any empty lines at this address will
+   result in a breakpoint being placed within the next function.  A -1
+   style end of sequence marker will appear within a function when code
+   has been inlined, in this case placing a breakpoint at the address of
+   an end of sequence should be fine.  */
 
 void
 buildsym_compunit::record_line (struct subfile *subfile, int line,
@@ -704,7 +721,7 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
      end of sequence markers.  All we lose is the ability to set
      breakpoints at some lines which contain no instructions
      anyway.  */
-  if (line == 0)
+  if (line == linetable_entry::end_of_sequence)
     {
       while (subfile->line_vector->nitems > 0)
 	{
@@ -714,8 +731,11 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
 	  subfile->line_vector->nitems--;
 	}
     }
+  else if (line == linetable_entry::change_of_symtab)
+    line = linetable_entry::end_of_sequence;
 
   e = subfile->line_vector->item + subfile->line_vector->nitems++;
+  gdb_assert (line >= 0);
   e->line = line;
   e->is_stmt = is_stmt ? 1 : 0;
   e->pc = pc;
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index ab21ab0d13a..cbea38f9b1a 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -20031,7 +20031,7 @@ dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
 
 static void
 dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
-		   CORE_ADDR address, struct dwarf2_cu *cu)
+		   CORE_ADDR address, struct dwarf2_cu *cu, bool end_sequence)
 {
   if (subfile == NULL)
     return;
@@ -20044,7 +20044,8 @@ dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
 			  paddress (gdbarch, address));
     }
 
-  dwarf_record_line_1 (gdbarch, subfile, 0, address, true, cu);
+  dwarf_record_line_1 (gdbarch, subfile, end_sequence ? 0 : -1, address,
+		       true, cu);
 }
 
 void
@@ -20077,7 +20078,8 @@ lnp_state_machine::record_line (bool end_sequence)
 	      || end_sequence)
 	    {
 	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
-				 m_currently_recording_lines ? m_cu : nullptr);
+				 m_currently_recording_lines ? m_cu : nullptr,
+				 end_sequence);
 	    }
 
 	  if (!end_sequence)
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 2043d084140..070a3311a16 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3270,7 +3270,9 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
          save prev if it represents the end of a function (i.e. line number
          0) instead of a real line.  */
 
-      if (prev && prev->line && (!best || prev->pc > best->pc))
+      if (prev && prev->line && (!best || prev->pc > best->pc
+				 || (prev->pc == best->pc && !best->is_stmt
+				     && prev->is_stmt)))
 	{
 	  best = prev;
 	  best_symtab = iter_s;
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 764c567a90b..ebb0c818bc8 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1304,7 +1304,10 @@ struct rust_vtable_symbol : public symbol
 
 struct linetable_entry
 {
-  /* The line number for this entry.  */
+  /* The line number for this entry.  This should be a value greater than
+     0 to indicate an actual line number, or the magic value 0 is used to
+     indicate the end of a sequence of linetable_entry structures in a
+     list.  */
   int line;
 
   /* True if this PC is a good location to place a breakpoint for LINE.  */
@@ -1312,6 +1315,17 @@ struct linetable_entry
 
   /* The address for this entry.  */
   CORE_ADDR pc;
+
+  /* Constant value that can be placed into the LINE field to indicate the
+     end of a sequence of line table entries.  */
+  static const int end_of_sequence = 0;
+
+  /* Constant that can be passed to buildsym_compunit::record_line when
+     creating the line table.  This indicates an artificial end of sequence
+     and is transformed into END_OF_SEQUENCE before being stored into the
+     line table, consequently CHANGE_OF_SYMTAB will never actually appear
+     in the LINE field.  */
+  static const int change_of_symtab = -1;
 };
 
 /* The order of entries in the linetable is significant.  They should
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
index 3733fa75570..a95e21194f9 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
 proc do_test { use_header } {
     global srcfile testfile
 
+    if { $use_header } {
+	# This test will not pass due to poor debug information
+	# generated by GCC (at least upto 10.x).  See
+	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
+	return
+    }
+
     set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
     if { $use_header } {
 	lappend options additional_flags=-DUSE_NEXT_INLINE_H
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
new file mode 100644
index 00000000000..dc7ec929236
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
@@ -0,0 +1,186 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# |      |      |      |      | Inline | Inline |
+# | Addr | File | Line | Stmt | Rng A  | Rng B  |
+# |------|------|------|------|--------|--------|
+# | 1    | 1    | 16   | Y    |        |        |
+# | 2    | 1    | 17   | Y    |        |        |
+# | 3    | 2    | 21   | Y    | X      |        |
+# | 4    | 2    | 22   | Y    | X      |        |
+# | 4    | 1    | 18   | N    | X      |        |
+# | 5    | 2    | 23   | N    | X      | X      |
+# | 6    | 1    | 24   | Y    |        |        |
+# | 7    | 1    | END  | Y    |        |        |
+# |------|------|------|------|--------|--------|
+#
+# Places a brekpoint at file 2, line 22.  Previously GDB would discard
+# the line table entry for this line due to switching files for the
+# file 1, line 18 non-statement line.  After patching however, GDB now
+# discards the file 1, line 18 entry instead, and the breakpoint at
+# line 22 should succeed.
+#
+# The two inlined subroutine ranges 'A' and 'B' represent two possible
+# ways that a compiler might represent this siuatio in the DWARF.
+#
+# Range 'B' is something that has been seen in the wild using GCC 8.2.
+# In this case the compilers range information is clearly wrong, but
+# this shouldn't impact the main point of the test.
+#
+# Range 'A' is a hypothetical case of how the compiler might choose to
+# represent this range, this has never been seen in the wild, but is
+# an improved debug experiece over range 'B'.  However, if we ever run
+# in to the situation where GDB can support the range 'A' test, or
+# support some real DWARF seen in the wild, then the range 'A' case
+# should be dropped in favour of supporting real world cases.  This is
+# included here as it "just worked" once the range 'B' case was
+# working.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+# Prepare and run the test.
+proc do_test { start_label func_name tag } {
+    global srcfile srcfile2 srcfile3 srcfile4 testfile
+
+    standard_testfile dw2-inline-header-lbls.c dw2-inline-header-${tag}.S \
+	dw2-inline-header.c dw2-inline-header.h
+
+    set build_options {nodebug optimize=-O1}
+
+    set asm_file [standard_output_file $srcfile2]
+    Dwarf::assemble $asm_file {
+	global srcdir subdir srcfile srcfile3 srcfile4 testfile
+	upvar build_options build_options
+	upvar start_label start_label
+	declare_labels lines_label callee_subprog_label
+
+	get_func_info main $build_options
+
+	cu {} {
+	    compile_unit {
+		{producer "gcc" }
+		{language @DW_LANG_C}
+		{name ${srcfile3}}
+		{low_pc 0 addr}
+		{stmt_list ${lines_label} DW_FORM_sec_offset}
+	    } {
+		callee_subprog_label: subprogram {
+		    {external 1 flag}
+		    {name callee}
+		    {inline 3 data1}
+		}
+		subprogram {
+		    {external 1 flag}
+		    {name main}
+		    {low_pc $main_start addr}
+		    {high_pc "$main_start + $main_len" addr}
+		} {
+		    inlined_subroutine {
+			{abstract_origin %$callee_subprog_label}
+			{low_pc $start_label addr}
+			{high_pc line_label_6 addr}
+			{call_file 1 data1}
+			{call_line 18 data1}
+		    }
+		}
+	    }
+	}
+
+	lines {version 2 default_is_stmt 1} lines_label {
+	    include_dir "${srcdir}/${subdir}"
+	    file_name "$srcfile3" 1
+	    file_name "$srcfile4" 1
+
+	    program {
+		{DW_LNE_set_address line_label_1}
+		{DW_LNS_advance_line 15}
+		{DW_LNS_copy}
+
+		{DW_LNE_set_address line_label_2}
+		{DW_LNS_advance_line 1}
+		{DW_LNS_copy}
+
+		{DW_LNS_set_file 2}
+		{DW_LNE_set_address line_label_3}
+		{DW_LNS_advance_line 4}
+		{DW_LNS_copy}
+
+		{DW_LNE_set_address line_label_4}
+		{DW_LNS_advance_line 1}
+		{DW_LNS_copy}
+
+		{DW_LNS_advance_line -4}
+		{DW_LNS_set_file 1}
+		{DW_LNS_negate_stmt}
+		{DW_LNS_copy}
+
+		{DW_LNS_set_file 2}
+		{DW_LNE_set_address line_label_5}
+		{DW_LNS_advance_line 5}
+		{DW_LNS_copy}
+
+		{DW_LNS_negate_stmt}
+		{DW_LNS_set_file 1}
+		{DW_LNE_set_address line_label_6}
+		{DW_LNS_advance_line 1}
+		{DW_LNS_copy}
+
+		{DW_LNE_set_address line_label_7}
+		{DW_LNE_end_sequence}
+	    }
+	}
+    }
+
+    if { [prepare_for_testing "failed to prepare" ${testfile}-${tag} \
+	      [list $srcfile $asm_file] $build_options] } {
+	return -1
+    }
+
+    if ![runto_main] {
+	return -1
+    }
+
+    # Delete all breakpoints so that the output of "info breakpoints"
+    # below will only contain a single breakpoint.
+    delete_breakpoints
+
+    # Place a breakpoint within the function in the header file.
+    gdb_breakpoint "${srcfile4}:22"
+
+    # Check that the breakpoint was placed where we expected.  It should
+    # appear at the requested line.  When the bug in GDB was present the
+    # breakpoint would be placed on one of the following lines instead.
+    gdb_test "info breakpoints" \
+	".* in $func_name at \[^\r\n\]+${srcfile4}:22\\y.*" \
+	"info breakpoints, $tag"
+}
+
+do_test line_label_3 "callee" "range-a"
+do_test line_label_5 "main" "range-b"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
new file mode 100644
index 00000000000..9f09f353273
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
@@ -0,0 +1,189 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt | Inline |
+# |------|------|------|------|--------|
+# | 1    | 1    | 16   | Y    |        |
+# | 2    | 1    | 17   | Y    |        |
+# | 3    | 2    | 21   | Y    | X      |
+# | 4    | 2    | 22   | Y    | X      |
+# | 4    | 1    | 18   | N    | X      |
+# | 5    | 1    | 19   | Y    |        |
+# | 6    | 1    | 20   | Y    |        |
+# | 7    | 1    | END  | Y    |        |
+# |------|------|------|------|--------|
+#
+#
+# Place the first brekpoint at file 2, line 22 and a second breakpoint
+# at file 1, line 19.  A third breakpoint is placed at file 1, line
+# 18, but as this line table entry will have been discarded[1] the
+# third breakpoint will actually be placed at the same location as the
+# second breakpoint.
+#
+# This test is designed to test GDB's internal behaviour with respect
+# to discarding particular line table entries.  GCC and DWARF are
+# starting to introduce the idea of line table views.  As the views
+# information becomes better supported within GDB it is likely that
+# this will become out of date.  This is fine, the test will have
+# served its purpose by that point and can be deleted.
+#
+# [1] The entry for file 1, line 18 is discarded because it is at the
+# same address as the previous entry, but the previous entry is-stmt,
+# while line 18 is a non-stmt.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set build_options {nodebug optimize=-O1}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    global build_options
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main $build_options
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_3 addr}
+		    {high_pc line_label_5 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] $build_options] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
+    "check for breakpoint at ${srcfile4}"
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile3}:19"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in main at \[^\r\n\]+${srcfile3}:19\\y.*" \
+    "check for breakpoint at ${srcfile3}"
+
+# Line table entry for line 18 will have been discarded, so this
+# brekpoint will be at the same location as line 19.
+gdb_test "break ${srcfile3}:18" \
+    "Note: breakpoint $decimal also set at pc $hex.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
new file mode 100644
index 00000000000..a3820f16d57
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
@@ -0,0 +1,193 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt | Inline |
+# |------|------|------|------|--------|
+# | 1    | 1    | 16   | Y    |        |
+# | 2    | 1    | 17   | Y    |        |
+# | 3    | 2    | 21   | Y    | X      |
+# | 4    | 2    | 22   | Y    | X      |
+# | 4    | 1    | 18   | N    |        |
+# | 5    | 1    | 19   | N    |        |
+# | 6    | 1    | 20   | Y    |        |
+# | 7    | 1    | END  | Y    |        |
+# |------|------|------|------|--------|
+#
+# Break at file 2, line 22, then single instruction step forward.  We
+# should pass through line 19 and then encounter line 20.
+#
+# Currently we don't expect GDB to see file 1, line 18, as this is a
+# non-stmt line in a different file at the same address as the
+# previous is-stmt line.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set build_options {nodebug optimize=-O1}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    global build_options
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main $build_options
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_3 addr}
+		    {high_pc line_label_5 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] $build_options] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
+
+gdb_continue_to_breakpoint "${srcfile4}:22" \
+    ".* ${srcfile4} : 22 .*"
+
+# Now single instruction step forward.  Eventually we should hit
+# ${srcfile3}:20, but before we do we should hit the non-statement
+# line ${srcfile3}:19.
+#
+# We don't know how many instructions we'll need to step, but 100
+# should be enough for everyone (surely), and this stops us looping
+# forever if something goes wrong.
+set found_line_19 0
+set found_line_20 0
+set keep_going 1
+for { set i 0 } { $i < 100 && $keep_going } { incr i } {
+    set keep_going 0
+    gdb_test_multiple "stepi" "stepi ${i}" {
+	-re "${srcfile3} : 19 .*${gdb_prompt} " {
+	    set found_line_19 1
+	    set keep_going 1
+	}
+
+	-re "${srcfile3} : 20 .*${gdb_prompt} " {
+	    set found_line_20 1
+	}
+
+	-re "${srcfile4} : 22 .*${gdb_prompt} " {
+	    # Not left line 22 yet.
+	    set keep_going 1
+	}
+    }
+}
+
+gdb_assert { $found_line_19 && $found_line_20 } \
+    "found line 19 and 20"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
new file mode 100644
index 00000000000..a1b7b17cbeb
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
@@ -0,0 +1,46 @@
+/* Copyright 2020 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/>.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+volatile int var;
+volatile int bar;
+
+/* Generate some code to take up some space.  */
+#define FILLER do { \
+    var = 99;	    \
+} while (0)
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+  LL (1);	// F1, Ln 16
+  FILLER;
+  LL (2);	// F1, Ln 17
+  FILLER;
+  LL (3);	// F2, Ln 21
+  FILLER;
+  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
+  FILLER;
+  LL (5);	// F1, Ln 19 !S
+  FILLER;
+  LL (6);	// F1, Ln 20
+  FILLER;
+  LL (7);
+  FILLER;
+  return 0;				/* main end */
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
new file mode 100644
index 00000000000..a8331268a09
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.c : 16 */
+/* dw2-inline-header.c : 17 */
+/* dw2-inline-header.c : 18 */
+/* dw2-inline-header.c : 19 */
+/* dw2-inline-header.c : 20 */
+/* dw2-inline-header.c : 21 */
+/* dw2-inline-header.c : 22 */
+/* dw2-inline-header.c : 23 */
+/* dw2-inline-header.c : 24 */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
new file mode 100644
index 00000000000..7233acbcd76
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.h : 16 */
+/* dw2-inline-header.h : 17 */
+/* dw2-inline-header.h : 18 */
+/* dw2-inline-header.h : 19 */
+/* dw2-inline-header.h : 20 */
+/* dw2-inline-header.h : 21 */
+/* dw2-inline-header.h : 22 */
+/* dw2-inline-header.h : 23 */
+/* dw2-inline-header.h : 24 */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-05-20 18:26                                   ` Andrew Burgess
@ 2020-05-27 13:10                                     ` Andrew Burgess
  2020-06-01  9:05                                       ` Andrew Burgess
  0 siblings, 1 reply; 79+ messages in thread
From: Andrew Burgess @ 2020-05-27 13:10 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: Tom Tromey, gdb-patches, Alexandre Oliva

Below is an updated version of the previous work in progress patch.
I'm posting it here so there's a record, however, I don't think this
patch should be merged into upstream.  While working on this solution
the problems with it became apparent to me.  I've tried to describe my
issues with this approach in the commit message of the patch.

It is now my intention to merge the patch I posted here:

   https://sourceware.org/pipermail/gdb-patches/2020-May/168611.html

which I'll do in a couple of days unless anyone would like to discuss
this further.

Thanks,
Andrew

---

commit 79719063c19b3906fda9d9e7fa0b8c009716b53a
Author: Andrew Burgess <andrew.burgess@embecosm.com>
Date:   Wed May 20 19:16:05 2020 +0100

    gdb: Don't delete empty lines when changing symtabs
    
    ***** NOT FOR MERGING INTO UPSTREAM *****
    
    This patch is based on the work of Bernd Edlinger here:
    
      https://sourceware.org/pipermail/gdb-patches/2020-May/168591.html
    
    After the is-stmt support commit:
    
        commit 8c95582da858ac981f689a6f599acacb8c5c490f
        Date:   Mon Dec 30 21:04:51 2019 +0000
    
            gdb: Add support for tracking the DWARF line table is-stmt field
    
    A regression was observed where a breakpoint could no longer be placed
    in some cases.
    
    Consider a line table like this:
    
      File 1: test.c
      File 2: test.h
    
      | Addr | File | Line | Stmt |
      |------|------|------|------|
      | 1    | 1    | 16   | Y    |
      | 2    | 1    | 17   | Y    |
      | 3    | 2    | 21   | Y    |
      | 4    | 2    | 22   | Y    |
      | 4    | 1    | 18   | N    |
      | 5    | 2    | 23   | N    |
      | 6    | 1    | 24   | Y    |
      | 7    | 1    | END  | Y    |
      |------|------|------|------|
    
    Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
    built two line table structures:
    
      File 1                 File 2
      ------                 ------
    
      | Addr | Line |        | Addr | Line |
      |------|------|        |------|------|
      | 1    | 16   |        | 3    | 21   |
      | 2    | 17   |        | 4    | 22   |
      | 3    | END  |        | 6    | END  |
      | 6    | 24   |        |------|------|
      | 7    | END  |
      |------|------|
    
    After the is-stmt patch GDB now records non-stmt lines, so the
    generated line table structures look like this:
    
      File 1                   File 2
      ------                   ------
    
      | Addr | Line | Stmt |  | Addr | Line | Stmt |
      |------|------|------|  |------|------|------|
      | 1    | 16   | Y    |  | 3    | 21   | Y    |
      | 2    | 17   | Y    |  | 4    | 22   | Y    |
      | 3    | END  | Y    |  | 4    | END  | Y    |
      | 4    | 18   | N    |  | 5    | 23   | N    |
      | 5    | END  | Y    |  | 6    | END  | Y    |
      | 6    | 24   | Y    |  |------|------|------|
      | 7    | END  | Y    |
      |------|------|------|
    
    The problem is that in 'File 2', end END marker at address 4 causes
    the previous line table entry to be discarded, so we actually end up
    with this:
    
      File 2
      ------
    
      | Addr | Line | Stmt |
      |------|------|------|
      | 3    | 21   | Y    |
      | 4    | END  | Y    |
      | 5    | 23   | N    |
      | 6    | END  | Y    |
      |------|------|------|
    
    When a user tries to place a breakpoint in file 2 at line 22, this is
    no longer possible.
    
    In order to understand the solution presented here, we must first
    remind ourselves why we delete line table entries that occur at the
    same address as an END marker.
    
    The problem this (deleting lines) is trying to solve is that if the
    last real line within a line table is empty (maybe due to
    optimisation) then the line will be at the same address as the end
    marker.  If the user tries to place a breakpoint at this address then
    we actually end up with a breakpoint at whatever is _after_ the
    location of that empty line, which is likely some other random
    function.
    
    However, when a line table switches subfiles we also insert end
    markers, this is because the GDB internal line table is split
    per-subfile, so the end markers allow us to see and "end" for the line
    just before the switch.  Without this, the line table entry just
    before the we switch subfiles would appear to extend over the entire
    region covered by the subfile we switch too.
    
    If we further consider the end markers inserted for switching
    subfiles, there are two common cases when this can occur, these are,
    first when one file is fully included inside another, and secondly
    when a function from one (header) file is inlined within a function
    in a different file.
    
    In the first of these situations GDB is exposed to the same risk as
    for the classic end marker case, if the last line before the subfile
    switch is empty, then when we switch subfiles we should be deleting
    the empty line, otherwise a user placing a breakpoint on this line,
    will end up with a breakpoint in a potentially different function.
    
    However, for the case of the inlined function the argument for
    deleting lines is harder to make, indeed if we consider a function
    inlined from another file compared to a function inlined from within
    the same file then it is obvious that in the case where the inlined
    callee is within the same file there will be no subfile switch, and
    hence no end of sequence markers inserted, and as a consequence no
    deleted line entries.
    
    It is tempting then to think that, when we switch subfiles as part of
    an inlined function we would like to preserve line table entries where
    possible.
    
    The problem is how to distinguish between the case of an inlined
    function subfile switch, and a general inclusion subfile switch?
    Though I'm sure we can solve this problem, I think it is worth
    considering if this is even a problem worth solving.
    
    First, the act of deleting line table entries only occurs when entries
    in the original debug information "pile up" and occur at the same
    address, consider this imagined input line table:
    
      | Addr | File | Line | Stmt |
      |------|------|------|------|
      | 1    | 1    | 10   | Y    |
      | 2    | 2    | 100  | Y    |
      | 3    | 1    | 11   | Y    |
    
    In this case no entries will be deleted, as every line table item
    occurs at a separate address.  Compare it to this:
    
      | Addr | File | Line | Stmt |
      |------|------|------|------|
      | 1    | 1    | 10   | Y    |
      | 1    | 2    | 100  | Y    |
      | 2    | 1    | 11   | Y    |
    
    Here, the switch to subfile 2 will cause the line item from subfile
    1 (also at address 1) to be deleted.  This kind of makes sense, the
    debug is claiming the address 1 represents both file 1, line 10 and
    file 2, line 100.  If we stop at this address which line should GDB
    report?  If the same situation was imagined within a single subfile,
    like this:
    
      | Addr | File | Line | Stmt |
      |------|------|------|------|
      | 1    | 1    | 10   | Y    |
      | 1    | 1    | 100  | Y    |
      | 2    | 1    | 11   | Y    |
    
    GDB would report line 100 at address 1, and line 11 at address 2.  By
    deleting the line in the two subfile case we get the same behaviour,
    but we protect against the empty line at the end of a subfile
    problem.
    
    It is for this reason, that I think this commit should not be merged
    with GDB.
    
    One problem that came from this commit was the test
    gdb.cp/step-and-next-inline.exp, which broke in several places.  After
    looking at this test again I think that in some cases this test was
    only ever passing by pure luck.  The debug GCC is producing for this
    test is pretty broken.  I raised this GCC bug:
    
      https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
    
    for this and disabled one entire half of the test.  There are still
    some cases in here that do pass, and if/when GCC is fixed it would be
    great to enable this test again.  Also the possibility of extending
    GDB to handle GCC's broken debug information has been discussed.
    
    gdb/ChangeLog:
    
            * buildsym.c (buildsym_compunit::record_line): Extend header
            comment.  Add some static assertions.  Handle different end of
            sequence types.
            * buildsym.h (struct buildsym_compunit) <end_of_sequence>: New
            constant.
            <change_of_subfile>: New constant.
            * dwarf2/read.c (dwarf_finish_line): Add extra parameter, use this
            to decide which end of sequence marker to send through as the line
            number.
            (lnp_state_machine::record_line): Pass extra parameter to
            dwarf_finish_line.
            * symtab.c (find_pc_sect_line): The previous line is better if it
            was marked is-stmt, and the new possible best is not.
            * symtab.h (struct linetable_entry) <line>: Extend comment.
    
    gdb/testsuite/ChangeLog:
    
            * gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
            use_header case.
            * gdb.dwarf2/dw2-inline-header-1.exp: New file.
            * gdb.dwarf2/dw2-inline-header-2.exp: New file.
            * gdb.dwarf2/dw2-inline-header-3.exp: New file.
            * gdb.dwarf2/dw2-inline-header-lbls.c: New file.
            * gdb.dwarf2/dw2-inline-header.c: New file.
            * gdb.dwarf2/dw2-inline-header.h: New file.

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 33bf6523e90..37fec4f53b6 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -663,7 +663,42 @@ buildsym_compunit::pop_subfile ()
 }
 \f
 /* Add a linetable entry for line number LINE and address PC to the
-   line vector for SUBFILE.  */
+   line vector for SUBFILE.
+
+   Pass the constant end_of_sequence for LINE when an end of sequence
+   marker is encountered in the original debug information.  If this case
+   any previous line table entries already at this address will be deleted,
+   these lines are by definition empty, and attempting to place a
+   breakpoint at this address will result in a breakpoint being placed on
+   whatever is next in the executable, which is never going to be the
+   correct behaviour.
+
+   Alternatively, pass the constant change_of_subfile for LINE to indicate
+   that the original debug line table switched to a different subfile.
+   This still results in an end of sequence marker being placed in GDB's
+   internal line table, but in this case we don't delete previous line
+   entries at this address.  This case arises from two common situations,
+   the most common is inlined functions, in this case the switching between
+   subfiles occurs within a single function.  If a real line entry and a
+   subfile switch occur at the same address we don't want to delete the
+   real line entry in this case as we end up loosing too much useful debug
+   information.
+
+   The other situation where a subfile switch is commonly seen is when one
+   source file is included into another, in this case the original line
+   table will include information for one subfile before switching to the
+   second subfile.  If the last line of the last function in the first
+   subfile is at the same address as the subfile switch then it is possible
+   that a breakpoint placed on this line would actually be placed in the
+   subsequent function by mistake.
+
+   We could improve on this second situation by entering all end of
+   sequence markers here without deleting any line table entries, then
+   cross check the line table with the list of function blocks, we would
+   then delete any line table entries that occurred at an end of sequence
+   marker that was also at the end of a function.  Right now we don't do
+   this, and live with the (small) risk of a breakpoint being misplaced;
+   the benefit of preserving the extra line table entries is worth it.  */
 
 void
 buildsym_compunit::record_line (struct subfile *subfile, int line,
@@ -671,6 +706,13 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
 {
   struct linetable_entry *e;
 
+  /* The END_OF_SEQUENCE must be 0 as the constant 0 is used when
+     processing the line table as the magic end of sequence marker.  The
+     CHANGE_OF_SUBFILE marker must be less than 0 as any value greater than
+     0 is an actual line number.  */
+  gdb_static_assert (buildsym_compunit::end_of_sequence == 0);
+  gdb_static_assert (buildsym_compunit::change_of_subfile < 0);
+
   /* Make sure line vector exists and is big enough.  */
   if (!subfile->line_vector)
     {
@@ -705,7 +747,7 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
      end of sequence markers.  All we lose is the ability to set
      breakpoints at some lines which contain no instructions
      anyway.  */
-  if (line == 0)
+  if (line == buildsym_compunit::end_of_sequence)
     {
       while (subfile->line_vector->nitems > 0)
 	{
@@ -715,8 +757,11 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
 	  subfile->line_vector->nitems--;
 	}
     }
+  else if (line == buildsym_compunit::change_of_subfile)
+    line = buildsym_compunit::end_of_sequence;
 
   e = subfile->line_vector->item + subfile->line_vector->nitems++;
+  gdb_assert (line >= 0);
   e->line = line;
   e->is_stmt = is_stmt ? 1 : 0;
   e->pc = pc;
diff --git a/gdb/buildsym.h b/gdb/buildsym.h
index c768a4c2dae..3e22d0b4494 100644
--- a/gdb/buildsym.h
+++ b/gdb/buildsym.h
@@ -190,6 +190,14 @@ struct buildsym_compunit
   void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
 		    bool is_stmt);
 
+  /* These two constants can be passed to RECORD_LINE as the value of LINE
+     to indicate special events in the line table.  END_OF_SEQUENCE marks
+     an actual end of sequence marked in the original debug's line table,
+     while CHANGE_OF_SUBFILE indicates an end marker due to switching
+     between subfiles.  */
+  static const int end_of_sequence = 0;
+  static const int change_of_subfile = -1;
+
   struct compunit_symtab *get_compunit_symtab ()
   {
     return m_compunit_symtab;
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index ec3844188ee..ed0f247b6a1 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -20067,7 +20067,7 @@ dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
 
 static void
 dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
-		   CORE_ADDR address, struct dwarf2_cu *cu)
+		   CORE_ADDR address, struct dwarf2_cu *cu, bool end_sequence)
 {
   if (subfile == NULL)
     return;
@@ -20080,7 +20080,10 @@ dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
 			  paddress (gdbarch, address));
     }
 
-  dwarf_record_line_1 (gdbarch, subfile, 0, address, true, cu);
+  int lineno = (end_sequence
+		? buildsym_compunit::end_of_sequence
+		: buildsym_compunit::change_of_subfile);
+  dwarf_record_line_1 (gdbarch, subfile, lineno, address, true, cu);
 }
 
 void
@@ -20113,7 +20116,8 @@ lnp_state_machine::record_line (bool end_sequence)
 	      || end_sequence)
 	    {
 	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
-				 m_currently_recording_lines ? m_cu : nullptr);
+				 m_currently_recording_lines ? m_cu : nullptr,
+				 end_sequence);
 	    }
 
 	  if (!end_sequence)
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 5c4e282c024..f6ebae45b27 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3254,7 +3254,9 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
          save prev if it represents the end of a function (i.e. line number
          0) instead of a real line.  */
 
-      if (prev && prev->line && (!best || prev->pc > best->pc))
+      if (prev && prev->line && (!best || prev->pc > best->pc
+				 || (prev->pc == best->pc && !best->is_stmt
+				     && prev->is_stmt)))
 	{
 	  best = prev;
 	  best_symtab = iter_s;
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 9972e8125ba..62bedb45de7 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1306,7 +1306,10 @@ struct rust_vtable_symbol : public symbol
 
 struct linetable_entry
 {
-  /* The line number for this entry.  */
+  /* The line number for this entry.  This should be a value greater than
+     0 to indicate an actual line number, or the magic value 0 is used to
+     indicate the end of a sequence of linetable_entry structures in a
+     list.  */
   int line;
 
   /* True if this PC is a good location to place a breakpoint for LINE.  */
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
index 3733fa75570..a95e21194f9 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
 proc do_test { use_header } {
     global srcfile testfile
 
+    if { $use_header } {
+	# This test will not pass due to poor debug information
+	# generated by GCC (at least upto 10.x).  See
+	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
+	return
+    }
+
     set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
     if { $use_header } {
 	lappend options additional_flags=-DUSE_NEXT_INLINE_H
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
new file mode 100644
index 00000000000..dc7ec929236
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
@@ -0,0 +1,186 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# |      |      |      |      | Inline | Inline |
+# | Addr | File | Line | Stmt | Rng A  | Rng B  |
+# |------|------|------|------|--------|--------|
+# | 1    | 1    | 16   | Y    |        |        |
+# | 2    | 1    | 17   | Y    |        |        |
+# | 3    | 2    | 21   | Y    | X      |        |
+# | 4    | 2    | 22   | Y    | X      |        |
+# | 4    | 1    | 18   | N    | X      |        |
+# | 5    | 2    | 23   | N    | X      | X      |
+# | 6    | 1    | 24   | Y    |        |        |
+# | 7    | 1    | END  | Y    |        |        |
+# |------|------|------|------|--------|--------|
+#
+# Places a brekpoint at file 2, line 22.  Previously GDB would discard
+# the line table entry for this line due to switching files for the
+# file 1, line 18 non-statement line.  After patching however, GDB now
+# discards the file 1, line 18 entry instead, and the breakpoint at
+# line 22 should succeed.
+#
+# The two inlined subroutine ranges 'A' and 'B' represent two possible
+# ways that a compiler might represent this siuatio in the DWARF.
+#
+# Range 'B' is something that has been seen in the wild using GCC 8.2.
+# In this case the compilers range information is clearly wrong, but
+# this shouldn't impact the main point of the test.
+#
+# Range 'A' is a hypothetical case of how the compiler might choose to
+# represent this range, this has never been seen in the wild, but is
+# an improved debug experiece over range 'B'.  However, if we ever run
+# in to the situation where GDB can support the range 'A' test, or
+# support some real DWARF seen in the wild, then the range 'A' case
+# should be dropped in favour of supporting real world cases.  This is
+# included here as it "just worked" once the range 'B' case was
+# working.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+# Prepare and run the test.
+proc do_test { start_label func_name tag } {
+    global srcfile srcfile2 srcfile3 srcfile4 testfile
+
+    standard_testfile dw2-inline-header-lbls.c dw2-inline-header-${tag}.S \
+	dw2-inline-header.c dw2-inline-header.h
+
+    set build_options {nodebug optimize=-O1}
+
+    set asm_file [standard_output_file $srcfile2]
+    Dwarf::assemble $asm_file {
+	global srcdir subdir srcfile srcfile3 srcfile4 testfile
+	upvar build_options build_options
+	upvar start_label start_label
+	declare_labels lines_label callee_subprog_label
+
+	get_func_info main $build_options
+
+	cu {} {
+	    compile_unit {
+		{producer "gcc" }
+		{language @DW_LANG_C}
+		{name ${srcfile3}}
+		{low_pc 0 addr}
+		{stmt_list ${lines_label} DW_FORM_sec_offset}
+	    } {
+		callee_subprog_label: subprogram {
+		    {external 1 flag}
+		    {name callee}
+		    {inline 3 data1}
+		}
+		subprogram {
+		    {external 1 flag}
+		    {name main}
+		    {low_pc $main_start addr}
+		    {high_pc "$main_start + $main_len" addr}
+		} {
+		    inlined_subroutine {
+			{abstract_origin %$callee_subprog_label}
+			{low_pc $start_label addr}
+			{high_pc line_label_6 addr}
+			{call_file 1 data1}
+			{call_line 18 data1}
+		    }
+		}
+	    }
+	}
+
+	lines {version 2 default_is_stmt 1} lines_label {
+	    include_dir "${srcdir}/${subdir}"
+	    file_name "$srcfile3" 1
+	    file_name "$srcfile4" 1
+
+	    program {
+		{DW_LNE_set_address line_label_1}
+		{DW_LNS_advance_line 15}
+		{DW_LNS_copy}
+
+		{DW_LNE_set_address line_label_2}
+		{DW_LNS_advance_line 1}
+		{DW_LNS_copy}
+
+		{DW_LNS_set_file 2}
+		{DW_LNE_set_address line_label_3}
+		{DW_LNS_advance_line 4}
+		{DW_LNS_copy}
+
+		{DW_LNE_set_address line_label_4}
+		{DW_LNS_advance_line 1}
+		{DW_LNS_copy}
+
+		{DW_LNS_advance_line -4}
+		{DW_LNS_set_file 1}
+		{DW_LNS_negate_stmt}
+		{DW_LNS_copy}
+
+		{DW_LNS_set_file 2}
+		{DW_LNE_set_address line_label_5}
+		{DW_LNS_advance_line 5}
+		{DW_LNS_copy}
+
+		{DW_LNS_negate_stmt}
+		{DW_LNS_set_file 1}
+		{DW_LNE_set_address line_label_6}
+		{DW_LNS_advance_line 1}
+		{DW_LNS_copy}
+
+		{DW_LNE_set_address line_label_7}
+		{DW_LNE_end_sequence}
+	    }
+	}
+    }
+
+    if { [prepare_for_testing "failed to prepare" ${testfile}-${tag} \
+	      [list $srcfile $asm_file] $build_options] } {
+	return -1
+    }
+
+    if ![runto_main] {
+	return -1
+    }
+
+    # Delete all breakpoints so that the output of "info breakpoints"
+    # below will only contain a single breakpoint.
+    delete_breakpoints
+
+    # Place a breakpoint within the function in the header file.
+    gdb_breakpoint "${srcfile4}:22"
+
+    # Check that the breakpoint was placed where we expected.  It should
+    # appear at the requested line.  When the bug in GDB was present the
+    # breakpoint would be placed on one of the following lines instead.
+    gdb_test "info breakpoints" \
+	".* in $func_name at \[^\r\n\]+${srcfile4}:22\\y.*" \
+	"info breakpoints, $tag"
+}
+
+do_test line_label_3 "callee" "range-a"
+do_test line_label_5 "main" "range-b"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
new file mode 100644
index 00000000000..9f09f353273
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
@@ -0,0 +1,189 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt | Inline |
+# |------|------|------|------|--------|
+# | 1    | 1    | 16   | Y    |        |
+# | 2    | 1    | 17   | Y    |        |
+# | 3    | 2    | 21   | Y    | X      |
+# | 4    | 2    | 22   | Y    | X      |
+# | 4    | 1    | 18   | N    | X      |
+# | 5    | 1    | 19   | Y    |        |
+# | 6    | 1    | 20   | Y    |        |
+# | 7    | 1    | END  | Y    |        |
+# |------|------|------|------|--------|
+#
+#
+# Place the first brekpoint at file 2, line 22 and a second breakpoint
+# at file 1, line 19.  A third breakpoint is placed at file 1, line
+# 18, but as this line table entry will have been discarded[1] the
+# third breakpoint will actually be placed at the same location as the
+# second breakpoint.
+#
+# This test is designed to test GDB's internal behaviour with respect
+# to discarding particular line table entries.  GCC and DWARF are
+# starting to introduce the idea of line table views.  As the views
+# information becomes better supported within GDB it is likely that
+# this will become out of date.  This is fine, the test will have
+# served its purpose by that point and can be deleted.
+#
+# [1] The entry for file 1, line 18 is discarded because it is at the
+# same address as the previous entry, but the previous entry is-stmt,
+# while line 18 is a non-stmt.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set build_options {nodebug optimize=-O1}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    global build_options
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main $build_options
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_3 addr}
+		    {high_pc line_label_5 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] $build_options] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
+    "check for breakpoint at ${srcfile4}"
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile3}:19"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in main at \[^\r\n\]+${srcfile3}:19\\y.*" \
+    "check for breakpoint at ${srcfile3}"
+
+# Line table entry for line 18 will have been discarded, so this
+# brekpoint will be at the same location as line 19.
+gdb_test "break ${srcfile3}:18" \
+    "Note: breakpoint $decimal also set at pc $hex.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
new file mode 100644
index 00000000000..a3820f16d57
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
@@ -0,0 +1,193 @@
+# Copyright 2020 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/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt | Inline |
+# |------|------|------|------|--------|
+# | 1    | 1    | 16   | Y    |        |
+# | 2    | 1    | 17   | Y    |        |
+# | 3    | 2    | 21   | Y    | X      |
+# | 4    | 2    | 22   | Y    | X      |
+# | 4    | 1    | 18   | N    |        |
+# | 5    | 1    | 19   | N    |        |
+# | 6    | 1    | 20   | Y    |        |
+# | 7    | 1    | END  | Y    |        |
+# |------|------|------|------|--------|
+#
+# Break at file 2, line 22, then single instruction step forward.  We
+# should pass through line 19 and then encounter line 20.
+#
+# Currently we don't expect GDB to see file 1, line 18, as this is a
+# non-stmt line in a different file at the same address as the
+# previous is-stmt line.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+    return -1
+}
+if !$gcc_compiled {
+    return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+    dw2-inline-header.c dw2-inline-header.h
+
+set build_options {nodebug optimize=-O1}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile3 srcfile4
+    global build_options
+    declare_labels lines_label callee_subprog_label
+
+    get_func_info main $build_options
+
+    cu {} {
+	compile_unit {
+	    {producer "gcc" }
+	    {language @DW_LANG_C}
+	    {name ${srcfile3}}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	} {
+	    callee_subprog_label: subprogram {
+		{external 1 flag}
+		{name callee}
+		{inline 3 data1}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name main}
+		{low_pc $main_start addr}
+		{high_pc "$main_start + $main_len" addr}
+	    } {
+		inlined_subroutine {
+		    {abstract_origin %$callee_subprog_label}
+		    {low_pc line_label_3 addr}
+		    {high_pc line_label_5 addr}
+		    {call_file 1 data1}
+		    {call_line 18 data1}
+		}
+	    }
+	}
+    }
+
+    lines {version 2 default_is_stmt 1} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile3" 1
+	file_name "$srcfile4" 1
+
+	program {
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 15}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_set_file 2}
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 4}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNS_advance_line -4}
+	    {DW_LNS_set_file 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_5}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_6}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_negate_stmt}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_7}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] $build_options] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected.  It should
+# appear at the requested line.  When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
+
+gdb_continue_to_breakpoint "${srcfile4}:22" \
+    ".* ${srcfile4} : 22 .*"
+
+# Now single instruction step forward.  Eventually we should hit
+# ${srcfile3}:20, but before we do we should hit the non-statement
+# line ${srcfile3}:19.
+#
+# We don't know how many instructions we'll need to step, but 100
+# should be enough for everyone (surely), and this stops us looping
+# forever if something goes wrong.
+set found_line_19 0
+set found_line_20 0
+set keep_going 1
+for { set i 0 } { $i < 100 && $keep_going } { incr i } {
+    set keep_going 0
+    gdb_test_multiple "stepi" "stepi ${i}" {
+	-re "${srcfile3} : 19 .*${gdb_prompt} " {
+	    set found_line_19 1
+	    set keep_going 1
+	}
+
+	-re "${srcfile3} : 20 .*${gdb_prompt} " {
+	    set found_line_20 1
+	}
+
+	-re "${srcfile4} : 22 .*${gdb_prompt} " {
+	    # Not left line 22 yet.
+	    set keep_going 1
+	}
+    }
+}
+
+gdb_assert { $found_line_19 && $found_line_20 } \
+    "found line 19 and 20"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
new file mode 100644
index 00000000000..a1b7b17cbeb
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
@@ -0,0 +1,46 @@
+/* Copyright 2020 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/>.  */
+
+/* Used to insert labels with which we can build a fake line table.  */
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+volatile int var;
+volatile int bar;
+
+/* Generate some code to take up some space.  */
+#define FILLER do { \
+    var = 99;	    \
+} while (0)
+
+int
+main ()
+{					/* main prologue */
+  asm ("main_label: .globl main_label");
+  LL (1);	// F1, Ln 16
+  FILLER;
+  LL (2);	// F1, Ln 17
+  FILLER;
+  LL (3);	// F2, Ln 21
+  FILLER;
+  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
+  FILLER;
+  LL (5);	// F1, Ln 19 !S
+  FILLER;
+  LL (6);	// F1, Ln 20
+  FILLER;
+  LL (7);
+  FILLER;
+  return 0;				/* main end */
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
new file mode 100644
index 00000000000..a8331268a09
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.c : 16 */
+/* dw2-inline-header.c : 17 */
+/* dw2-inline-header.c : 18 */
+/* dw2-inline-header.c : 19 */
+/* dw2-inline-header.c : 20 */
+/* dw2-inline-header.c : 21 */
+/* dw2-inline-header.c : 22 */
+/* dw2-inline-header.c : 23 */
+/* dw2-inline-header.c : 24 */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
new file mode 100644
index 00000000000..7233acbcd76
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
@@ -0,0 +1,24 @@
+/* Copyright 2020 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/>.  */
+
+/* dw2-inline-header.h : 16 */
+/* dw2-inline-header.h : 17 */
+/* dw2-inline-header.h : 18 */
+/* dw2-inline-header.h : 19 */
+/* dw2-inline-header.h : 20 */
+/* dw2-inline-header.h : 21 */
+/* dw2-inline-header.h : 22 */
+/* dw2-inline-header.h : 23 */
+/* dw2-inline-header.h : 24 */

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files
  2020-05-27 13:10                                     ` Andrew Burgess
@ 2020-06-01  9:05                                       ` Andrew Burgess
  0 siblings, 0 replies; 79+ messages in thread
From: Andrew Burgess @ 2020-06-01  9:05 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey, Bernd Edlinger, Alexandre Oliva

* Andrew Burgess <andrew.burgess@embecosm.com> [2020-05-27 14:10:22 +0100]:

> Below is an updated version of the previous work in progress patch.
> I'm posting it here so there's a record, however, I don't think this
> patch should be merged into upstream.  While working on this solution
> the problems with it became apparent to me.  I've tried to describe my
> issues with this approach in the commit message of the patch.
> 
> It is now my intention to merge the patch I posted here:
> 
>    https://sourceware.org/pipermail/gdb-patches/2020-May/168611.html
> 
> which I'll do in a couple of days unless anyone would like to discuss
> this further.

I have now merged the patch mentioned above.

Tom - this should resolve the regression you are seeing in your MinGW
test.  Do let me know if you continue to see any problems related to
this series.

Thanks,
Andrew



> 
> Thanks,
> Andrew
> 
> ---
> 
> commit 79719063c19b3906fda9d9e7fa0b8c009716b53a
> Author: Andrew Burgess <andrew.burgess@embecosm.com>
> Date:   Wed May 20 19:16:05 2020 +0100
> 
>     gdb: Don't delete empty lines when changing symtabs
>     
>     ***** NOT FOR MERGING INTO UPSTREAM *****
>     
>     This patch is based on the work of Bernd Edlinger here:
>     
>       https://sourceware.org/pipermail/gdb-patches/2020-May/168591.html
>     
>     After the is-stmt support commit:
>     
>         commit 8c95582da858ac981f689a6f599acacb8c5c490f
>         Date:   Mon Dec 30 21:04:51 2019 +0000
>     
>             gdb: Add support for tracking the DWARF line table is-stmt field
>     
>     A regression was observed where a breakpoint could no longer be placed
>     in some cases.
>     
>     Consider a line table like this:
>     
>       File 1: test.c
>       File 2: test.h
>     
>       | Addr | File | Line | Stmt |
>       |------|------|------|------|
>       | 1    | 1    | 16   | Y    |
>       | 2    | 1    | 17   | Y    |
>       | 3    | 2    | 21   | Y    |
>       | 4    | 2    | 22   | Y    |
>       | 4    | 1    | 18   | N    |
>       | 5    | 2    | 23   | N    |
>       | 6    | 1    | 24   | Y    |
>       | 7    | 1    | END  | Y    |
>       |------|------|------|------|
>     
>     Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB
>     built two line table structures:
>     
>       File 1                 File 2
>       ------                 ------
>     
>       | Addr | Line |        | Addr | Line |
>       |------|------|        |------|------|
>       | 1    | 16   |        | 3    | 21   |
>       | 2    | 17   |        | 4    | 22   |
>       | 3    | END  |        | 6    | END  |
>       | 6    | 24   |        |------|------|
>       | 7    | END  |
>       |------|------|
>     
>     After the is-stmt patch GDB now records non-stmt lines, so the
>     generated line table structures look like this:
>     
>       File 1                   File 2
>       ------                   ------
>     
>       | Addr | Line | Stmt |  | Addr | Line | Stmt |
>       |------|------|------|  |------|------|------|
>       | 1    | 16   | Y    |  | 3    | 21   | Y    |
>       | 2    | 17   | Y    |  | 4    | 22   | Y    |
>       | 3    | END  | Y    |  | 4    | END  | Y    |
>       | 4    | 18   | N    |  | 5    | 23   | N    |
>       | 5    | END  | Y    |  | 6    | END  | Y    |
>       | 6    | 24   | Y    |  |------|------|------|
>       | 7    | END  | Y    |
>       |------|------|------|
>     
>     The problem is that in 'File 2', end END marker at address 4 causes
>     the previous line table entry to be discarded, so we actually end up
>     with this:
>     
>       File 2
>       ------
>     
>       | Addr | Line | Stmt |
>       |------|------|------|
>       | 3    | 21   | Y    |
>       | 4    | END  | Y    |
>       | 5    | 23   | N    |
>       | 6    | END  | Y    |
>       |------|------|------|
>     
>     When a user tries to place a breakpoint in file 2 at line 22, this is
>     no longer possible.
>     
>     In order to understand the solution presented here, we must first
>     remind ourselves why we delete line table entries that occur at the
>     same address as an END marker.
>     
>     The problem this (deleting lines) is trying to solve is that if the
>     last real line within a line table is empty (maybe due to
>     optimisation) then the line will be at the same address as the end
>     marker.  If the user tries to place a breakpoint at this address then
>     we actually end up with a breakpoint at whatever is _after_ the
>     location of that empty line, which is likely some other random
>     function.
>     
>     However, when a line table switches subfiles we also insert end
>     markers, this is because the GDB internal line table is split
>     per-subfile, so the end markers allow us to see and "end" for the line
>     just before the switch.  Without this, the line table entry just
>     before the we switch subfiles would appear to extend over the entire
>     region covered by the subfile we switch too.
>     
>     If we further consider the end markers inserted for switching
>     subfiles, there are two common cases when this can occur, these are,
>     first when one file is fully included inside another, and secondly
>     when a function from one (header) file is inlined within a function
>     in a different file.
>     
>     In the first of these situations GDB is exposed to the same risk as
>     for the classic end marker case, if the last line before the subfile
>     switch is empty, then when we switch subfiles we should be deleting
>     the empty line, otherwise a user placing a breakpoint on this line,
>     will end up with a breakpoint in a potentially different function.
>     
>     However, for the case of the inlined function the argument for
>     deleting lines is harder to make, indeed if we consider a function
>     inlined from another file compared to a function inlined from within
>     the same file then it is obvious that in the case where the inlined
>     callee is within the same file there will be no subfile switch, and
>     hence no end of sequence markers inserted, and as a consequence no
>     deleted line entries.
>     
>     It is tempting then to think that, when we switch subfiles as part of
>     an inlined function we would like to preserve line table entries where
>     possible.
>     
>     The problem is how to distinguish between the case of an inlined
>     function subfile switch, and a general inclusion subfile switch?
>     Though I'm sure we can solve this problem, I think it is worth
>     considering if this is even a problem worth solving.
>     
>     First, the act of deleting line table entries only occurs when entries
>     in the original debug information "pile up" and occur at the same
>     address, consider this imagined input line table:
>     
>       | Addr | File | Line | Stmt |
>       |------|------|------|------|
>       | 1    | 1    | 10   | Y    |
>       | 2    | 2    | 100  | Y    |
>       | 3    | 1    | 11   | Y    |
>     
>     In this case no entries will be deleted, as every line table item
>     occurs at a separate address.  Compare it to this:
>     
>       | Addr | File | Line | Stmt |
>       |------|------|------|------|
>       | 1    | 1    | 10   | Y    |
>       | 1    | 2    | 100  | Y    |
>       | 2    | 1    | 11   | Y    |
>     
>     Here, the switch to subfile 2 will cause the line item from subfile
>     1 (also at address 1) to be deleted.  This kind of makes sense, the
>     debug is claiming the address 1 represents both file 1, line 10 and
>     file 2, line 100.  If we stop at this address which line should GDB
>     report?  If the same situation was imagined within a single subfile,
>     like this:
>     
>       | Addr | File | Line | Stmt |
>       |------|------|------|------|
>       | 1    | 1    | 10   | Y    |
>       | 1    | 1    | 100  | Y    |
>       | 2    | 1    | 11   | Y    |
>     
>     GDB would report line 100 at address 1, and line 11 at address 2.  By
>     deleting the line in the two subfile case we get the same behaviour,
>     but we protect against the empty line at the end of a subfile
>     problem.
>     
>     It is for this reason, that I think this commit should not be merged
>     with GDB.
>     
>     One problem that came from this commit was the test
>     gdb.cp/step-and-next-inline.exp, which broke in several places.  After
>     looking at this test again I think that in some cases this test was
>     only ever passing by pure luck.  The debug GCC is producing for this
>     test is pretty broken.  I raised this GCC bug:
>     
>       https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
>     
>     for this and disabled one entire half of the test.  There are still
>     some cases in here that do pass, and if/when GCC is fixed it would be
>     great to enable this test again.  Also the possibility of extending
>     GDB to handle GCC's broken debug information has been discussed.
>     
>     gdb/ChangeLog:
>     
>             * buildsym.c (buildsym_compunit::record_line): Extend header
>             comment.  Add some static assertions.  Handle different end of
>             sequence types.
>             * buildsym.h (struct buildsym_compunit) <end_of_sequence>: New
>             constant.
>             <change_of_subfile>: New constant.
>             * dwarf2/read.c (dwarf_finish_line): Add extra parameter, use this
>             to decide which end of sequence marker to send through as the line
>             number.
>             (lnp_state_machine::record_line): Pass extra parameter to
>             dwarf_finish_line.
>             * symtab.c (find_pc_sect_line): The previous line is better if it
>             was marked is-stmt, and the new possible best is not.
>             * symtab.h (struct linetable_entry) <line>: Extend comment.
>     
>     gdb/testsuite/ChangeLog:
>     
>             * gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
>             use_header case.
>             * gdb.dwarf2/dw2-inline-header-1.exp: New file.
>             * gdb.dwarf2/dw2-inline-header-2.exp: New file.
>             * gdb.dwarf2/dw2-inline-header-3.exp: New file.
>             * gdb.dwarf2/dw2-inline-header-lbls.c: New file.
>             * gdb.dwarf2/dw2-inline-header.c: New file.
>             * gdb.dwarf2/dw2-inline-header.h: New file.
> 
> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> index 33bf6523e90..37fec4f53b6 100644
> --- a/gdb/buildsym.c
> +++ b/gdb/buildsym.c
> @@ -663,7 +663,42 @@ buildsym_compunit::pop_subfile ()
>  }
>  \f
>  /* Add a linetable entry for line number LINE and address PC to the
> -   line vector for SUBFILE.  */
> +   line vector for SUBFILE.
> +
> +   Pass the constant end_of_sequence for LINE when an end of sequence
> +   marker is encountered in the original debug information.  If this case
> +   any previous line table entries already at this address will be deleted,
> +   these lines are by definition empty, and attempting to place a
> +   breakpoint at this address will result in a breakpoint being placed on
> +   whatever is next in the executable, which is never going to be the
> +   correct behaviour.
> +
> +   Alternatively, pass the constant change_of_subfile for LINE to indicate
> +   that the original debug line table switched to a different subfile.
> +   This still results in an end of sequence marker being placed in GDB's
> +   internal line table, but in this case we don't delete previous line
> +   entries at this address.  This case arises from two common situations,
> +   the most common is inlined functions, in this case the switching between
> +   subfiles occurs within a single function.  If a real line entry and a
> +   subfile switch occur at the same address we don't want to delete the
> +   real line entry in this case as we end up loosing too much useful debug
> +   information.
> +
> +   The other situation where a subfile switch is commonly seen is when one
> +   source file is included into another, in this case the original line
> +   table will include information for one subfile before switching to the
> +   second subfile.  If the last line of the last function in the first
> +   subfile is at the same address as the subfile switch then it is possible
> +   that a breakpoint placed on this line would actually be placed in the
> +   subsequent function by mistake.
> +
> +   We could improve on this second situation by entering all end of
> +   sequence markers here without deleting any line table entries, then
> +   cross check the line table with the list of function blocks, we would
> +   then delete any line table entries that occurred at an end of sequence
> +   marker that was also at the end of a function.  Right now we don't do
> +   this, and live with the (small) risk of a breakpoint being misplaced;
> +   the benefit of preserving the extra line table entries is worth it.  */
>  
>  void
>  buildsym_compunit::record_line (struct subfile *subfile, int line,
> @@ -671,6 +706,13 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
>  {
>    struct linetable_entry *e;
>  
> +  /* The END_OF_SEQUENCE must be 0 as the constant 0 is used when
> +     processing the line table as the magic end of sequence marker.  The
> +     CHANGE_OF_SUBFILE marker must be less than 0 as any value greater than
> +     0 is an actual line number.  */
> +  gdb_static_assert (buildsym_compunit::end_of_sequence == 0);
> +  gdb_static_assert (buildsym_compunit::change_of_subfile < 0);
> +
>    /* Make sure line vector exists and is big enough.  */
>    if (!subfile->line_vector)
>      {
> @@ -705,7 +747,7 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
>       end of sequence markers.  All we lose is the ability to set
>       breakpoints at some lines which contain no instructions
>       anyway.  */
> -  if (line == 0)
> +  if (line == buildsym_compunit::end_of_sequence)
>      {
>        while (subfile->line_vector->nitems > 0)
>  	{
> @@ -715,8 +757,11 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
>  	  subfile->line_vector->nitems--;
>  	}
>      }
> +  else if (line == buildsym_compunit::change_of_subfile)
> +    line = buildsym_compunit::end_of_sequence;
>  
>    e = subfile->line_vector->item + subfile->line_vector->nitems++;
> +  gdb_assert (line >= 0);
>    e->line = line;
>    e->is_stmt = is_stmt ? 1 : 0;
>    e->pc = pc;
> diff --git a/gdb/buildsym.h b/gdb/buildsym.h
> index c768a4c2dae..3e22d0b4494 100644
> --- a/gdb/buildsym.h
> +++ b/gdb/buildsym.h
> @@ -190,6 +190,14 @@ struct buildsym_compunit
>    void record_line (struct subfile *subfile, int line, CORE_ADDR pc,
>  		    bool is_stmt);
>  
> +  /* These two constants can be passed to RECORD_LINE as the value of LINE
> +     to indicate special events in the line table.  END_OF_SEQUENCE marks
> +     an actual end of sequence marked in the original debug's line table,
> +     while CHANGE_OF_SUBFILE indicates an end marker due to switching
> +     between subfiles.  */
> +  static const int end_of_sequence = 0;
> +  static const int change_of_subfile = -1;
> +
>    struct compunit_symtab *get_compunit_symtab ()
>    {
>      return m_compunit_symtab;
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index ec3844188ee..ed0f247b6a1 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -20067,7 +20067,7 @@ dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
>  
>  static void
>  dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
> -		   CORE_ADDR address, struct dwarf2_cu *cu)
> +		   CORE_ADDR address, struct dwarf2_cu *cu, bool end_sequence)
>  {
>    if (subfile == NULL)
>      return;
> @@ -20080,7 +20080,10 @@ dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
>  			  paddress (gdbarch, address));
>      }
>  
> -  dwarf_record_line_1 (gdbarch, subfile, 0, address, true, cu);
> +  int lineno = (end_sequence
> +		? buildsym_compunit::end_of_sequence
> +		: buildsym_compunit::change_of_subfile);
> +  dwarf_record_line_1 (gdbarch, subfile, lineno, address, true, cu);
>  }
>  
>  void
> @@ -20113,7 +20116,8 @@ lnp_state_machine::record_line (bool end_sequence)
>  	      || end_sequence)
>  	    {
>  	      dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
> -				 m_currently_recording_lines ? m_cu : nullptr);
> +				 m_currently_recording_lines ? m_cu : nullptr,
> +				 end_sequence);
>  	    }
>  
>  	  if (!end_sequence)
> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index 5c4e282c024..f6ebae45b27 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -3254,7 +3254,9 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
>           save prev if it represents the end of a function (i.e. line number
>           0) instead of a real line.  */
>  
> -      if (prev && prev->line && (!best || prev->pc > best->pc))
> +      if (prev && prev->line && (!best || prev->pc > best->pc
> +				 || (prev->pc == best->pc && !best->is_stmt
> +				     && prev->is_stmt)))
>  	{
>  	  best = prev;
>  	  best_symtab = iter_s;
> diff --git a/gdb/symtab.h b/gdb/symtab.h
> index 9972e8125ba..62bedb45de7 100644
> --- a/gdb/symtab.h
> +++ b/gdb/symtab.h
> @@ -1306,7 +1306,10 @@ struct rust_vtable_symbol : public symbol
>  
>  struct linetable_entry
>  {
> -  /* The line number for this entry.  */
> +  /* The line number for this entry.  This should be a value greater than
> +     0 to indicate an actual line number, or the magic value 0 is used to
> +     indicate the end of a sequence of linetable_entry structures in a
> +     list.  */
>    int line;
>  
>    /* True if this PC is a good location to place a breakpoint for LINE.  */
> diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> index 3733fa75570..a95e21194f9 100644
> --- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> +++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
> @@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
>  proc do_test { use_header } {
>      global srcfile testfile
>  
> +    if { $use_header } {
> +	# This test will not pass due to poor debug information
> +	# generated by GCC (at least upto 10.x).  See
> +	# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
> +	return
> +    }
> +
>      set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
>      if { $use_header } {
>  	lappend options additional_flags=-DUSE_NEXT_INLINE_H
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> new file mode 100644
> index 00000000000..dc7ec929236
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
> @@ -0,0 +1,186 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# |      |      |      |      | Inline | Inline |
> +# | Addr | File | Line | Stmt | Rng A  | Rng B  |
> +# |------|------|------|------|--------|--------|
> +# | 1    | 1    | 16   | Y    |        |        |
> +# | 2    | 1    | 17   | Y    |        |        |
> +# | 3    | 2    | 21   | Y    | X      |        |
> +# | 4    | 2    | 22   | Y    | X      |        |
> +# | 4    | 1    | 18   | N    | X      |        |
> +# | 5    | 2    | 23   | N    | X      | X      |
> +# | 6    | 1    | 24   | Y    |        |        |
> +# | 7    | 1    | END  | Y    |        |        |
> +# |------|------|------|------|--------|--------|
> +#
> +# Places a brekpoint at file 2, line 22.  Previously GDB would discard
> +# the line table entry for this line due to switching files for the
> +# file 1, line 18 non-statement line.  After patching however, GDB now
> +# discards the file 1, line 18 entry instead, and the breakpoint at
> +# line 22 should succeed.
> +#
> +# The two inlined subroutine ranges 'A' and 'B' represent two possible
> +# ways that a compiler might represent this siuatio in the DWARF.
> +#
> +# Range 'B' is something that has been seen in the wild using GCC 8.2.
> +# In this case the compilers range information is clearly wrong, but
> +# this shouldn't impact the main point of the test.
> +#
> +# Range 'A' is a hypothetical case of how the compiler might choose to
> +# represent this range, this has never been seen in the wild, but is
> +# an improved debug experiece over range 'B'.  However, if we ever run
> +# in to the situation where GDB can support the range 'A' test, or
> +# support some real DWARF seen in the wild, then the range 'A' case
> +# should be dropped in favour of supporting real world cases.  This is
> +# included here as it "just worked" once the range 'B' case was
> +# working.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +# Prepare and run the test.
> +proc do_test { start_label func_name tag } {
> +    global srcfile srcfile2 srcfile3 srcfile4 testfile
> +
> +    standard_testfile dw2-inline-header-lbls.c dw2-inline-header-${tag}.S \
> +	dw2-inline-header.c dw2-inline-header.h
> +
> +    set build_options {nodebug optimize=-O1}
> +
> +    set asm_file [standard_output_file $srcfile2]
> +    Dwarf::assemble $asm_file {
> +	global srcdir subdir srcfile srcfile3 srcfile4 testfile
> +	upvar build_options build_options
> +	upvar start_label start_label
> +	declare_labels lines_label callee_subprog_label
> +
> +	get_func_info main $build_options
> +
> +	cu {} {
> +	    compile_unit {
> +		{producer "gcc" }
> +		{language @DW_LANG_C}
> +		{name ${srcfile3}}
> +		{low_pc 0 addr}
> +		{stmt_list ${lines_label} DW_FORM_sec_offset}
> +	    } {
> +		callee_subprog_label: subprogram {
> +		    {external 1 flag}
> +		    {name callee}
> +		    {inline 3 data1}
> +		}
> +		subprogram {
> +		    {external 1 flag}
> +		    {name main}
> +		    {low_pc $main_start addr}
> +		    {high_pc "$main_start + $main_len" addr}
> +		} {
> +		    inlined_subroutine {
> +			{abstract_origin %$callee_subprog_label}
> +			{low_pc $start_label addr}
> +			{high_pc line_label_6 addr}
> +			{call_file 1 data1}
> +			{call_line 18 data1}
> +		    }
> +		}
> +	    }
> +	}
> +
> +	lines {version 2 default_is_stmt 1} lines_label {
> +	    include_dir "${srcdir}/${subdir}"
> +	    file_name "$srcfile3" 1
> +	    file_name "$srcfile4" 1
> +
> +	    program {
> +		{DW_LNE_set_address line_label_1}
> +		{DW_LNS_advance_line 15}
> +		{DW_LNS_copy}
> +
> +		{DW_LNE_set_address line_label_2}
> +		{DW_LNS_advance_line 1}
> +		{DW_LNS_copy}
> +
> +		{DW_LNS_set_file 2}
> +		{DW_LNE_set_address line_label_3}
> +		{DW_LNS_advance_line 4}
> +		{DW_LNS_copy}
> +
> +		{DW_LNE_set_address line_label_4}
> +		{DW_LNS_advance_line 1}
> +		{DW_LNS_copy}
> +
> +		{DW_LNS_advance_line -4}
> +		{DW_LNS_set_file 1}
> +		{DW_LNS_negate_stmt}
> +		{DW_LNS_copy}
> +
> +		{DW_LNS_set_file 2}
> +		{DW_LNE_set_address line_label_5}
> +		{DW_LNS_advance_line 5}
> +		{DW_LNS_copy}
> +
> +		{DW_LNS_negate_stmt}
> +		{DW_LNS_set_file 1}
> +		{DW_LNE_set_address line_label_6}
> +		{DW_LNS_advance_line 1}
> +		{DW_LNS_copy}
> +
> +		{DW_LNE_set_address line_label_7}
> +		{DW_LNE_end_sequence}
> +	    }
> +	}
> +    }
> +
> +    if { [prepare_for_testing "failed to prepare" ${testfile}-${tag} \
> +	      [list $srcfile $asm_file] $build_options] } {
> +	return -1
> +    }
> +
> +    if ![runto_main] {
> +	return -1
> +    }
> +
> +    # Delete all breakpoints so that the output of "info breakpoints"
> +    # below will only contain a single breakpoint.
> +    delete_breakpoints
> +
> +    # Place a breakpoint within the function in the header file.
> +    gdb_breakpoint "${srcfile4}:22"
> +
> +    # Check that the breakpoint was placed where we expected.  It should
> +    # appear at the requested line.  When the bug in GDB was present the
> +    # breakpoint would be placed on one of the following lines instead.
> +    gdb_test "info breakpoints" \
> +	".* in $func_name at \[^\r\n\]+${srcfile4}:22\\y.*" \
> +	"info breakpoints, $tag"
> +}
> +
> +do_test line_label_3 "callee" "range-a"
> +do_test line_label_5 "main" "range-b"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> new file mode 100644
> index 00000000000..9f09f353273
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
> @@ -0,0 +1,189 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt | Inline |
> +# |------|------|------|------|--------|
> +# | 1    | 1    | 16   | Y    |        |
> +# | 2    | 1    | 17   | Y    |        |
> +# | 3    | 2    | 21   | Y    | X      |
> +# | 4    | 2    | 22   | Y    | X      |
> +# | 4    | 1    | 18   | N    | X      |
> +# | 5    | 1    | 19   | Y    |        |
> +# | 6    | 1    | 20   | Y    |        |
> +# | 7    | 1    | END  | Y    |        |
> +# |------|------|------|------|--------|
> +#
> +#
> +# Place the first brekpoint at file 2, line 22 and a second breakpoint
> +# at file 1, line 19.  A third breakpoint is placed at file 1, line
> +# 18, but as this line table entry will have been discarded[1] the
> +# third breakpoint will actually be placed at the same location as the
> +# second breakpoint.
> +#
> +# This test is designed to test GDB's internal behaviour with respect
> +# to discarding particular line table entries.  GCC and DWARF are
> +# starting to introduce the idea of line table views.  As the views
> +# information becomes better supported within GDB it is likely that
> +# this will become out of date.  This is fine, the test will have
> +# served its purpose by that point and can be deleted.
> +#
> +# [1] The entry for file 1, line 18 is discarded because it is at the
> +# same address as the previous entry, but the previous entry is-stmt,
> +# while line 18 is a non-stmt.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set build_options {nodebug optimize=-O1}
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    global build_options
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main $build_options
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_3 addr}
> +		    {high_pc line_label_5 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] $build_options] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
> +    "check for breakpoint at ${srcfile4}"
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile3}:19"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in main at \[^\r\n\]+${srcfile3}:19\\y.*" \
> +    "check for breakpoint at ${srcfile3}"
> +
> +# Line table entry for line 18 will have been discarded, so this
> +# brekpoint will be at the same location as line 19.
> +gdb_test "break ${srcfile3}:18" \
> +    "Note: breakpoint $decimal also set at pc $hex.*"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> new file mode 100644
> index 00000000000..a3820f16d57
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
> @@ -0,0 +1,193 @@
> +# Copyright 2020 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/>.
> +
> +# Setup a line table where:
> +#
> +# | Addr | File | Line | Stmt | Inline |
> +# |------|------|------|------|--------|
> +# | 1    | 1    | 16   | Y    |        |
> +# | 2    | 1    | 17   | Y    |        |
> +# | 3    | 2    | 21   | Y    | X      |
> +# | 4    | 2    | 22   | Y    | X      |
> +# | 4    | 1    | 18   | N    |        |
> +# | 5    | 1    | 19   | N    |        |
> +# | 6    | 1    | 20   | Y    |        |
> +# | 7    | 1    | END  | Y    |        |
> +# |------|------|------|------|--------|
> +#
> +# Break at file 2, line 22, then single instruction step forward.  We
> +# should pass through line 19 and then encounter line 20.
> +#
> +# Currently we don't expect GDB to see file 1, line 18, as this is a
> +# non-stmt line in a different file at the same address as the
> +# previous is-stmt line.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +# The .c files use __attribute__.
> +if [get_compiler_info] {
> +    return -1
> +}
> +if !$gcc_compiled {
> +    return 0
> +}
> +
> +standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
> +    dw2-inline-header.c dw2-inline-header.h
> +
> +set build_options {nodebug optimize=-O1}
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile3 srcfile4
> +    global build_options
> +    declare_labels lines_label callee_subprog_label
> +
> +    get_func_info main $build_options
> +
> +    cu {} {
> +	compile_unit {
> +	    {producer "gcc" }
> +	    {language @DW_LANG_C}
> +	    {name ${srcfile3}}
> +	    {low_pc 0 addr}
> +	    {stmt_list ${lines_label} DW_FORM_sec_offset}
> +	} {
> +	    callee_subprog_label: subprogram {
> +		{external 1 flag}
> +		{name callee}
> +		{inline 3 data1}
> +	    }
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +		{low_pc $main_start addr}
> +		{high_pc "$main_start + $main_len" addr}
> +	    } {
> +		inlined_subroutine {
> +		    {abstract_origin %$callee_subprog_label}
> +		    {low_pc line_label_3 addr}
> +		    {high_pc line_label_5 addr}
> +		    {call_file 1 data1}
> +		    {call_line 18 data1}
> +		}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2 default_is_stmt 1} lines_label {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile3" 1
> +	file_name "$srcfile4" 1
> +
> +	program {
> +	    {DW_LNE_set_address line_label_1}
> +	    {DW_LNS_advance_line 15}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_2}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_set_file 2}
> +	    {DW_LNE_set_address line_label_3}
> +	    {DW_LNS_advance_line 4}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_4}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNS_advance_line -4}
> +	    {DW_LNS_set_file 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_5}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_6}
> +	    {DW_LNS_advance_line 1}
> +	    {DW_LNS_negate_stmt}
> +	    {DW_LNS_copy}
> +
> +	    {DW_LNE_set_address line_label_7}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] $build_options] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# Delete all breakpoints so that the output of "info breakpoints"
> +# below will only contain a single breakpoint.
> +delete_breakpoints
> +
> +# Place a breakpoint within the function in the header file.
> +gdb_breakpoint "${srcfile4}:22"
> +
> +# Check that the breakpoint was placed where we expected.  It should
> +# appear at the requested line.  When the bug in GDB was present the
> +# breakpoint would be placed on one of the following lines instead.
> +gdb_test "info breakpoints" \
> +    ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
> +
> +gdb_continue_to_breakpoint "${srcfile4}:22" \
> +    ".* ${srcfile4} : 22 .*"
> +
> +# Now single instruction step forward.  Eventually we should hit
> +# ${srcfile3}:20, but before we do we should hit the non-statement
> +# line ${srcfile3}:19.
> +#
> +# We don't know how many instructions we'll need to step, but 100
> +# should be enough for everyone (surely), and this stops us looping
> +# forever if something goes wrong.
> +set found_line_19 0
> +set found_line_20 0
> +set keep_going 1
> +for { set i 0 } { $i < 100 && $keep_going } { incr i } {
> +    set keep_going 0
> +    gdb_test_multiple "stepi" "stepi ${i}" {
> +	-re "${srcfile3} : 19 .*${gdb_prompt} " {
> +	    set found_line_19 1
> +	    set keep_going 1
> +	}
> +
> +	-re "${srcfile3} : 20 .*${gdb_prompt} " {
> +	    set found_line_20 1
> +	}
> +
> +	-re "${srcfile4} : 22 .*${gdb_prompt} " {
> +	    # Not left line 22 yet.
> +	    set keep_going 1
> +	}
> +    }
> +}
> +
> +gdb_assert { $found_line_19 && $found_line_20 } \
> +    "found line 19 and 20"
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> new file mode 100644
> index 00000000000..a1b7b17cbeb
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
> @@ -0,0 +1,46 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* Used to insert labels with which we can build a fake line table.  */
> +#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
> +
> +volatile int var;
> +volatile int bar;
> +
> +/* Generate some code to take up some space.  */
> +#define FILLER do { \
> +    var = 99;	    \
> +} while (0)
> +
> +int
> +main ()
> +{					/* main prologue */
> +  asm ("main_label: .globl main_label");
> +  LL (1);	// F1, Ln 16
> +  FILLER;
> +  LL (2);	// F1, Ln 17
> +  FILLER;
> +  LL (3);	// F2, Ln 21
> +  FILLER;
> +  LL (4);	// F2, Ln 22 // F1, Ln 18, !S
> +  FILLER;
> +  LL (5);	// F1, Ln 19 !S
> +  FILLER;
> +  LL (6);	// F1, Ln 20
> +  FILLER;
> +  LL (7);
> +  FILLER;
> +  return 0;				/* main end */
> +}
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> new file mode 100644
> index 00000000000..a8331268a09
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
> @@ -0,0 +1,24 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* dw2-inline-header.c : 16 */
> +/* dw2-inline-header.c : 17 */
> +/* dw2-inline-header.c : 18 */
> +/* dw2-inline-header.c : 19 */
> +/* dw2-inline-header.c : 20 */
> +/* dw2-inline-header.c : 21 */
> +/* dw2-inline-header.c : 22 */
> +/* dw2-inline-header.c : 23 */
> +/* dw2-inline-header.c : 24 */
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> new file mode 100644
> index 00000000000..7233acbcd76
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
> @@ -0,0 +1,24 @@
> +/* Copyright 2020 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/>.  */
> +
> +/* dw2-inline-header.h : 16 */
> +/* dw2-inline-header.h : 17 */
> +/* dw2-inline-header.h : 18 */
> +/* dw2-inline-header.h : 19 */
> +/* dw2-inline-header.h : 20 */
> +/* dw2-inline-header.h : 21 */
> +/* dw2-inline-header.h : 22 */
> +/* dw2-inline-header.h : 23 */
> +/* dw2-inline-header.h : 24 */

^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field
  2020-03-05 18:01         ` Bernd Edlinger
                             ` (2 preceding siblings ...)
  2020-03-08 12:50           ` [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
@ 2020-06-01 13:26           ` Pedro Alves
  3 siblings, 0 replies; 79+ messages in thread
From: Pedro Alves @ 2020-06-01 13:26 UTC (permalink / raw)
  To: Bernd Edlinger, Andrew Burgess; +Cc: gdb-patches

On 3/5/20 6:01 PM, Bernd Edlinger wrote:
> Hmm, I think this got stuck, so I just worked a follow-up patch out for you.
> 
> I also noticed that the test case cannot work with gcc before gcc-8,
> since the is_stmt feature is not implemented earlier, at least with
> gcc-4.8 and gcc-5.4 the test case fails.
> 
> I thought it would be good to detect that by adding the -gstatement-frontiers
> option, so that the gcc driver complains when it is not able to generate
> sufficient debug info for that test.

Won't this prevent the test from running with Clang?  (and likely other compilers?)

Thanks,
Pedro Alves


^ permalink raw reply	[flat|nested] 79+ messages in thread

* Re: [PATCHv3] Fix range end handling of inlined subroutines
  2020-03-23 20:58                         ` Bernd Edlinger
@ 2020-06-01 14:28                           ` Pedro Alves
  0 siblings, 0 replies; 79+ messages in thread
From: Pedro Alves @ 2020-06-01 14:28 UTC (permalink / raw)
  To: Bernd Edlinger, Andrew Burgess; +Cc: gdb-patches

On 3/23/20 8:58 PM, Bernd Edlinger wrote:
> What I did is compare a O(nlogn) vs. O(n^2) implementation
> which mimics the visual studio std::vector:

I don't think I ever heard of anyone building GDB with Visual Studio.
I'm not sure it's even possible, except perhaps with the newer
versions which are much more confirming.

The standard says that push_back must be amortized O(1).  I wonder
whether the slow implementation you observed was with an old
(and thus grossly non-conforming in many aspects) Visual Studio, which
we can safely ignore for GDB.

Thanks,
Pedro Alves


^ permalink raw reply	[flat|nested] 79+ messages in thread

end of thread, other threads:[~2020-06-01 14:28 UTC | newest]

Thread overview: 79+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-05 11:37 [PATCH 0/2] Line table is_stmt support Andrew Burgess
2020-02-05 11:37 ` [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
2020-02-05 17:55   ` Bernd Edlinger
2020-02-10 18:30     ` Bernd Edlinger
2020-02-11 13:57     ` Andrew Burgess
2020-02-14 20:05       ` Bernd Edlinger
2020-03-05 18:01         ` Bernd Edlinger
2020-03-08 12:50           ` [PATCHv2 0/2] Line table is_stmt support Andrew Burgess
2020-03-08 14:39             ` Bernd Edlinger
2020-03-10 23:01               ` Andrew Burgess
2020-03-11  6:50                 ` Simon Marchi
2020-03-11 11:28                   ` Andrew Burgess
2020-03-11 13:27                     ` Simon Marchi
2020-04-03 22:21             ` [PATCH 0/2] More regression fixing from is-stmt patches Andrew Burgess
2020-04-03 22:21             ` [PATCH 1/2] gdb/testsuite: Move helper function into lib/dwarf.exp Andrew Burgess
2020-04-06 20:18               ` Tom Tromey
2020-04-14 11:18                 ` Andrew Burgess
2020-04-03 22:21             ` [PATCH 2/2] gdb: Preserve is-stmt lines when switch between files Andrew Burgess
2020-04-04 18:07               ` Bernd Edlinger
2020-04-04 19:59                 ` Bernd Edlinger
2020-04-04 22:23                 ` Andrew Burgess
2020-04-05  0:04                   ` Bernd Edlinger
2020-04-05  0:47                   ` Bernd Edlinger
2020-04-05  8:55                   ` Bernd Edlinger
2020-04-11  3:52               ` Bernd Edlinger
2020-04-12 17:13                 ` Bernd Edlinger
2020-04-14 11:28                   ` Andrew Burgess
2020-04-14 11:37                     ` Bernd Edlinger
2020-04-14 11:41                       ` Bernd Edlinger
2020-04-14 13:08                       ` Andrew Burgess
2020-04-16 17:18                     ` Andrew Burgess
2020-04-22 21:13                       ` Tom Tromey
2020-04-25  7:06                         ` Bernd Edlinger
2020-04-27 10:34                           ` Andrew Burgess
2020-05-14 20:18                             ` Tom Tromey
2020-05-14 22:39                               ` Andrew Burgess
2020-05-15  3:35                                 ` Bernd Edlinger
2020-05-15 14:46                                   ` Andrew Burgess
2020-05-16  8:12                                     ` Bernd Edlinger
2020-05-17 17:26                                   ` Bernd Edlinger
2020-05-20 18:26                                   ` Andrew Burgess
2020-05-27 13:10                                     ` Andrew Burgess
2020-06-01  9:05                                       ` Andrew Burgess
2020-03-08 12:50           ` [PATCHv2 1/2] gdb/testsuite: Add is-stmt support to the DWARF compiler Andrew Burgess
2020-03-08 12:50           ` [PATCHv2 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Andrew Burgess
2020-03-16 20:57             ` Tom Tromey
2020-03-16 22:37               ` Bernd Edlinger
2020-03-17 12:47               ` Tom Tromey
2020-03-17 18:23                 ` Tom Tromey
2020-03-17 18:51                   ` Bernd Edlinger
2020-03-17 18:56                   ` Andrew Burgess
2020-03-17 20:18                     ` Tom Tromey
2020-03-17 22:21                       ` Andrew Burgess
2020-03-23 17:30             ` [PATCH 0/3] Keep duplicate line table entries Andrew Burgess
2020-03-23 17:30             ` [PATCH 1/3] gdb/testsuite: Add compiler options parameter to function_range helper Andrew Burgess
2020-04-01 18:31               ` Tom Tromey
2020-03-23 17:30             ` [PATCH 2/3] gdb/testsuite: Add support for DW_LNS_set_file to DWARF compiler Andrew Burgess
2020-04-01 18:32               ` Tom Tromey
2020-03-23 17:30             ` [PATCH 3/3] gdb: Don't remove duplicate entries from the line table Andrew Burgess
2020-04-01 18:34               ` Tom Tromey
2020-06-01 13:26           ` [PATCH 2/2] gdb: Add support for tracking the DWARF line table is-stmt field Pedro Alves
2020-02-06  9:01   ` Luis Machado
2020-02-11 15:39     ` Andrew Burgess
2020-02-09 21:07   ` [PATCH] Fix range end handling of inlined subroutines Bernd Edlinger
2020-02-10 21:48     ` Andrew Burgess
2020-02-22  6:39     ` [PATCHv2] " Bernd Edlinger
2020-03-08 14:57       ` [PATCHv3] " Bernd Edlinger
2020-03-11 22:02         ` Andrew Burgess
2020-03-12 18:21           ` Bernd Edlinger
2020-03-12 18:27             ` Christian Biesinger
2020-03-13  8:03               ` Bernd Edlinger
2020-03-17 22:27                 ` Andrew Burgess
2020-03-19  1:33                   ` Bernd Edlinger
2020-03-21 20:31                     ` Bernd Edlinger
2020-03-23 17:53                       ` Andrew Burgess
2020-03-23 20:58                         ` Bernd Edlinger
2020-06-01 14:28                           ` Pedro Alves
2020-03-13 12:47         ` [PATCHv4] " Bernd Edlinger
2020-02-05 11:37 ` [PATCH 1/2] gdb/testsuite: Add is-stmt support to the DWARF compiler Andrew Burgess

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).