public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting.
  2019-12-23  1:51 [PATCH 0/3] Improve inline frame debug experience Andrew Burgess
  2019-12-23  1:51 ` [PATCH 3/3] gdb: Better frame tracking for inline frames Andrew Burgess
  2019-12-23  1:51 ` [PATCH 1/3] gdb: Include end of sequence markers in the line table Andrew Burgess
@ 2019-12-23  1:51 ` Andrew Burgess
  2020-01-24 17:40   ` Tom Tromey
  2020-06-05 14:49   ` Tom de Vries
  2020-01-06 22:14 ` [PATCH 0/3] Improve inline frame debug experience Andrew Burgess
  3 siblings, 2 replies; 25+ messages in thread
From: Andrew Burgess @ 2019-12-23  1:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Don't reorder line table entries for the same address when sorting the
line table, maintain the compiler given line order.  Usually this will
reflect the order in which lines are conceptually encountered at a
given address.

Consider this example:

/* 1  */    volatile int global_var;
/* 2  */    int  __attribute__ ((noinline))
/* 3  */    bar ()
/* 4  */    {
/* 5  */      return global_var;
/* 6  */    }
/* 7  */    static inline int __attribute__ ((always_inline))
/* 8  */    foo ()
/* 9  */    {
/* 10 */      return bar ();
/* 11 */    }
/* 12 */    int
/* 13 */    main ()
/* 14 */    {
/* 15 */      global_var = 0;
/* 16 */      return foo ();
/* 17 */    }

GCC 10 currently generates a line table like this (as shown by
objdump):

  CU: ./test.c:
  File name          Line number    Starting address
  test.c                       4            0x4004b0
  test.c                       5            0x4004b0
  test.c                       6            0x4004b6
  test.c                       6            0x4004b7

  test.c                      14            0x4003b0
  test.c                      15            0x4003b0
  test.c                      16            0x4003ba
  test.c                      10            0x4003ba
  test.c                      10            0x4003c1

The interesting entries are those for lines 16 and 10 at address
0x4003ba, these represent the call to foo and the inlined body of
foo.

With the current line table sorting GDB builds the line table like
this (as shown by 'maintenance info line-table'):

  INDEX    LINE ADDRESS
  0          14 0x00000000004003b0
  1          15 0x00000000004003b0
  2          10 0x00000000004003ba
  3          16 0x00000000004003ba
  4         END 0x00000000004003c1
  5           4 0x00000000004004b0
  6           5 0x00000000004004b0
  7         END 0x00000000004004b7

Notice that entries 2 and 3 for lines 10 and 16 are now in a different
order to the line table as given by the compiler.  With this patch
applied the order is now:

  INDEX    LINE ADDRESS
  0          14 0x00000000004003b0
  1          15 0x00000000004003b0
  2          16 0x00000000004003ba
  3          10 0x00000000004003ba
  4         END 0x00000000004003c1
  5           4 0x00000000004004b0
  6           5 0x00000000004004b0
  7         END 0x00000000004004b7

Notice that entries 2 and 3 are now in their original order again.

The consequence of the incorrect ordering is that when stepping
through inlined functions GDB will display the wrong line for the
inner most frame.  Here's a GDB session before this patch is applied:

  Starting program: /home/andrew/tmp/inline/test

  Temporary breakpoint 1, main () at test.c:15
  15	/* 15 */      global_var = 0;
  (gdb) step
  16	/* 16 */      return foo ();
  (gdb) step
  foo () at test.c:16
  16	/* 16 */      return foo ();
  (gdb) step
  bar () at test.c:5
  5	/* 5  */      return global_var;

The step from line 15 to 16 was fine, but the next step should have
taken us to line 10, instead we are left at line 16.  The final step
to line 5 is as expected.

With this patch applied the session goes better:

  Starting program: /home/andrew/tmp/inline/test

  Temporary breakpoint 1, main () at test.c:15
  15	/* 15 */      global_var = 0;
  (gdb) step
  16	/* 16 */      return foo ();
  (gdb) step
  foo () at test.c:10
  10	/* 10 */      return bar ();
  (gdb) step
  bar () at test.c:5
  5	/* 5  */      return global_var;

We now visit the lines as 15, 16, 10, 5 as we would like.

The reason for this issue is that the inline frame unwinder is
detecting that foo is inlined in main.  When we stop at the shared
address 0x4003ba the inline frame unwinder first shows us the outer
frame, this information is extracted from the DWARF's
DW_TAG_inlined_subroutine entries and passed via GDB's block data.

When we step again the inlined frame unwinder moves us up the call
stack to the inner most frame at which point the frame is displayed as
normal, with the location for the address being looked up in the line
table.

As GDB uses the last line table entry for an address as "the" line to
report for that address it is critical that GDB maintain the order of
the line table entries.  In the first case, by reordering the line
table we report the wrong location.

I had to make a small adjustment in find_pc_sect_line in order to
correctly find the previous line in the line table.  In some line
tables I was seeing an actual line entry and an end of sequence marker
at the same address, before this commit these would reorder to move
the end of sequence marker before the line entry (end of sequence has
line number 0).  Now the end of sequence marker remains in its correct
location, and in order to find a previous line we should step backward
over any end of sequence markers.

As an example, the binary:
  gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold

Has this line table before the patch:

  INDEX    LINE ADDRESS
  0          48 0x0000000000400487
  1         END 0x000000000040048e
  2          52 0x000000000040048e
  3          54 0x0000000000400492
  4          56 0x0000000000400497
  5         END 0x000000000040049a
  6          62 0x000000000040049a
  7         END 0x00000000004004a1
  8          66 0x00000000004004a1
  9          68 0x00000000004004a5
  10         70 0x00000000004004aa
  11         72 0x00000000004004b9
  12        END 0x00000000004004bc
  13         76 0x00000000004004bc
  14         78 0x00000000004004c0
  15         80 0x00000000004004c5
  16        END 0x00000000004004cc

And after this patch:

  INDEX    LINE ADDRESS
  0          48 0x0000000000400487
  1          52 0x000000000040048e
  2         END 0x000000000040048e
  3          54 0x0000000000400492
  4          56 0x0000000000400497
  5         END 0x000000000040049a
  6          62 0x000000000040049a
  7          66 0x00000000004004a1
  8         END 0x00000000004004a1
  9          68 0x00000000004004a5
  10         70 0x00000000004004aa
  11         72 0x00000000004004b9
  12        END 0x00000000004004bc
  13         76 0x00000000004004bc
  14         78 0x00000000004004c0
  15         80 0x00000000004004c5
  16        END 0x00000000004004cc

When calling find_pc_sect_line with the address 0x000000000040048e, in
both cases we find entry #3, we then try to find the previous entry,
which originally found this entry '2         52 0x000000000040048e',
after the patch it finds '2         END 0x000000000040048e', which
cases the lookup to fail.

By skipping the END marker after this patch we get back to the correct
entry, which is now #1: '1          52 0x000000000040048e', and
everything works again.

gdb/ChangeLog:

	* buildsym.c (lte_is_less_than): Delete.
	(buildsym_compunit::end_symtab_with_blockvector): Create local
	lambda function to sort line table entries, and use
	std::stable_sort instead of std::sort.
	* symtab.c (find_pc_sect_line): Skip backward over end of sequence
	markers when looking for a previous line.

gdb/testsuite/ChangeLog:

	* gdb.dwarf2/dw2-inline-stepping.c: New file.
	* gdb.dwarf2/dw2-inline-stepping.exp: New file.

Change-Id: Ia0309494be4cfd9dcc554f30209477f5f040b21b
---
 gdb/ChangeLog                                    |   9 ++
 gdb/buildsym.c                                   |  42 +++----
 gdb/symtab.c                                     |   7 +-
 gdb/testsuite/ChangeLog                          |   5 +
 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.c   |  45 +++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.exp | 147 +++++++++++++++++++++++
 6 files changed, 230 insertions(+), 25 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.exp

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 79f83057634..2d131d07a4d 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -725,23 +725,6 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
   e->pc = pc;
 }
 
-/* Needed in order to sort line tables from IBM xcoff files.  Sigh!  */
-
-static bool
-lte_is_less_than (const linetable_entry &ln1, const linetable_entry &ln2)
-{
-  /* Note: this code does not assume that CORE_ADDRs can fit in ints.
-     Please keep it that way.  */
-  if (ln1.pc < ln2.pc)
-    return true;
-
-  if (ln1.pc > ln2.pc)
-    return false;
-
-  /* If pc equal, sort by line.  I'm not sure whether this is optimum
-     behavior (see comment at struct linetable in symtab.h).  */
-  return ln1.line < ln2.line;
-}
 \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
@@ -959,14 +942,25 @@ buildsym_compunit::end_symtab_with_blockvector (struct block *static_block,
 	  linetablesize = sizeof (struct linetable) +
 	    subfile->line_vector->nitems * sizeof (struct linetable_entry);
 
-	  /* Like the pending blocks, the line table may be
-	     scrambled in reordered executables.  Sort it if
-	     OBJF_REORDERED is true.  */
+	  const auto lte_is_less_than
+	    = [] (const linetable_entry &ln1,
+		  const linetable_entry &ln2)->bool
+	      {
+		/* Note: this code does not assume that CORE_ADDRs can fit
+		   in ints.  Please keep it that way.  */
+		return (ln1.pc < ln2.pc);
+	      };
+
+	  /* Like the pending blocks, the line table may be scrambled in
+	     reordered executables.  Sort it if OBJF_REORDERED is true.  It
+	     is important to preserve the order of lines at the same
+	     address, as this maintains the inline function caller/callee
+	     relationships, this is why std::stable_sort is used.  */
 	  if (m_objfile->flags & OBJF_REORDERED)
-	    std::sort (subfile->line_vector->item,
-		       subfile->line_vector->item
-			 + subfile->line_vector->nitems,
-		       lte_is_less_than);
+	    std::stable_sort (subfile->line_vector->item,
+			      subfile->line_vector->item
+			      + subfile->line_vector->nitems,
+			      lte_is_less_than);
 	}
 
       /* Allocate a symbol table if necessary.  */
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 26551372cbb..ee149b7642a 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3219,7 +3219,12 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
       struct linetable_entry *last = item + len;
       item = std::upper_bound (first, last, pc, pc_compare);
       if (item != first)
-	prev = item - 1;		/* Found a matching item.  */
+	{
+	  /* Found a matching item.  Skip backwards over any end of
+	     sequence markers.  */
+	  for (prev = item - 1; prev->line == 0 && prev != first; prev--)
+	    /* Nothing.  */;
+	}
 
       /* At this point, prev points at the line whose start addr is <= pc, and
          item points at the next line.  If we ran off the end of the linetable
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.c
new file mode 100644
index 00000000000..41a89370c33
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.c
@@ -0,0 +1,45 @@
+/* Copyright 2019 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 relies on foo being inlined into main and bar not being
+   inlined.  The test is checking GDB's behaviour as we single step from
+   main through foo and into bar.  */
+
+volatile int global_var;
+
+int  __attribute__ ((noinline))
+bar ()
+{						/* bar prologue */
+  asm ("bar_label: .globl bar_label");
+  return global_var;				/* bar return global_var */
+}						/* bar end */
+
+static inline int __attribute__ ((always_inline))
+foo ()
+{						/* foo prologue */
+  return bar ();				/* foo call bar */
+}						/* foo end */
+
+int
+main ()
+{						/* main prologue */
+  int ans;
+  asm ("main_label: .globl main_label");
+  global_var = 0;				/* main set global_var */
+  asm ("main_label2: .globl main_label2");
+  ans = foo ();					/* main call foo */
+  asm ("main_label3: .globl main_label3");
+  return ans;
+}						/* main end */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.exp
new file mode 100644
index 00000000000..64da28acec5
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.exp
@@ -0,0 +1,147 @@
+# Copyright 2019 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-inline-stepping.c dw2-inline-stepping.S
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile2
+    declare_labels ranges_label lines_label foo_prog
+
+    lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \
+	main_start main_len
+    set main_end "$main_start + $main_len"
+    lassign [function_range bar [list ${srcdir}/${subdir}/$srcfile]] \
+	bar_start bar_len
+    set bar_end "$bar_start + $bar_len"
+
+    set call_line [gdb_get_line_number "main call foo"]
+
+    cu {} {
+	compile_unit {
+	    {language @DW_LANG_C}
+	    {name dw2-inline-stepping.c}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	    {ranges ${ranges_label} DW_FORM_sec_offset}
+	} {
+	    subprogram {
+		{external 1 flag}
+		{name bar}
+		{low_pc $bar_start addr}
+		{high_pc "$bar_start + $bar_len" addr}
+	    }
+	    foo_prog: subprogram {
+		{name foo}
+		{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 %$foo_prog}
+		    {low_pc main_label2 addr}
+		    {high_pc main_label3 addr}
+		    {call_file 1 data1}
+		    {call_line $call_line data1}
+               }
+	    }
+	}
+    }
+
+    lines {version 2} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile" 1
+
+	program {
+	    {DW_LNE_set_address $main_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "main prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "main set global_var"] - [gdb_get_line_number "main prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "main call foo"] - [gdb_get_line_number "main set global_var"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "foo call bar"] - [gdb_get_line_number "main call foo"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $main_end}
+	    {DW_LNE_end_sequence}
+
+	    {DW_LNE_set_address $bar_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "bar prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address bar_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "bar return global_var"] - [gdb_get_line_number "bar prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $bar_end}
+	    {DW_LNE_end_sequence}
+	}
+    }
+
+    ranges {is_64 [is_64_target]} {
+	ranges_label: sequence {
+	    {range {${main_start}} ${main_end}}
+	    {range {${bar_start}} ${bar_end}}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+set patterns [list "main call foo" \
+		  "foo call bar" \
+		  "bar return global_var"]
+foreach p $patterns {
+    gdb_test "step" "/\\* $p \\*/" \
+	"step to '$p'"
+}
+
-- 
2.14.5

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

* [PATCH 3/3] gdb: Better frame tracking for inline frames
  2019-12-23  1:51 [PATCH 0/3] Improve inline frame debug experience Andrew Burgess
@ 2019-12-23  1:51 ` Andrew Burgess
  2019-12-26  7:25   ` Christian Biesinger via gdb-patches
  2019-12-23  1:51 ` [PATCH 1/3] gdb: Include end of sequence markers in the line table Andrew Burgess
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 25+ messages in thread
From: Andrew Burgess @ 2019-12-23  1:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit improves GDB's handling of inline functions when there are
more than one inline function in a stack, so for example if we have a
stack like:

   main -> aaa -> bbb -> ccc -> ddd

And aaa, bbb, and ccc are all inline within main GDB should (when
given sufficient debug information) be able to step from main through
aaa, bbb, and ccc.  Unfortunately, this currently doesn't work, here's
an example session:

  (gdb) start
  Temporary breakpoint 1 at 0x4003b0: file test.c, line 38.
  Starting program: /project/gdb/tests/inline/test

  Temporary breakpoint 1, main () at test.c:38
  38	  global_var = 0;
  (gdb) step
  39	  return aaa () + 1;
  (gdb) step
  aaa () at test.c:39
  39	  return aaa () + 1;
  (gdb) step
  bbb () at test.c:39
  39	  return aaa () + 1;
  (gdb) step
  ccc () at test.c:39
  39	  return aaa () + 1;
  (gdb) step
  ddd () at test.c:32
  32	  return global_var;
  (gdb) bt
  #0  ddd () at test.c:32
  #1  0x00000000004003c1 in ccc () at test.c:39
  #2  bbb () at test.c:26
  #3  aaa () at test.c:14
  #4  main () at test.c:39

Notice that once we get to line 39 in main, GDB keeps reporting line
39 in main as the location despite understanding that the inferior is
stepping through the nested inline functions with each use of step.

The problem is that as soon as the inferior stops we call
skip_inline_frames (from inline-frame.c) which calculates the
inferiors current state in relation to inline functions - it figures
out if we're in an inline function, and if we are counts how many
inline frames there are at the current location.

So, in our example above, when we step from line 38 in main to line 39
we stop at a location that is simultaneously in all of main, aaa, bbb,
and ccc.  The block structure reflects the order in which the
functions would be called, with ccc being the most inner block and
main being the most outer block.  When we stop GDB naturally finds the
block for ccc, however within skip_inline_frames we spot that bbb,
aaa, and main are super-blocks of the current location and that each
layer represents an inline function.  The skip_inline_frames then
records the depth of inline functions (3 in this case for aaa, bbb,
and ccc) and also the symbol of the outermost inline function (in this
case 'aaa' as main isn't an inline function, it just has things inline
within it).

Now GDB understands the stack to be main -> aaa -> bbb -> ccc,
however, the state initialised in skip_inline_frames starts off
indicating that we should hide 3 frames from the user, so we report
that we're in main at line 39.  The location of main, line 39 is
derived by asking the inline function state for the last symbol in the
stack (aaa in this case), and then asking for it's location - the
location of an inlined function symbol is its call site, so main, line
39 in this case.

If the user then asks GDB to step we don't actually move the inferior
at all, instead we spot that we are in an inline function stack,
lookup the inline state data, and reduce the skip depth by 1.  We then
report to the user that GDB has stopped.  GDB now understands that we
are in 'aaa'.  In order to get the precise location we again ask GDB
for the last symbol from the inline data structure, and we are again
told 'aaa', we then get the location from 'aaa', and report that we
are in main, line 39.

Hopefully it's clear what the mistake here is, once we've reduced the
inline skip depth we should not be using 'aaa' to compute the precise
location, instead we should be using 'bbb'.  That is what this patch
does.

Now, when we call skip_inline_frames instead of just recording the
last skipped symbol we now record all symbols in the inline frame
stack.  When we ask GDB for the last skipped symbol we return a symbol
based on how many frames we are skipping, not just the last know
symbol.

With this fix in place, the same session as above now looks much
better:

  (gdb) start
  Temporary breakpoint 1 at 0x4003b0: file test.c, line 38.
  Starting program: /project/gdb/tests/inline/test

  Temporary breakpoint 1, main () at test.c:38
  38	  global_var = 0;
  (gdb) s
  39	  return aaa () + 1;
  (gdb) s
  aaa () at test.c:14
  14	  return bbb () + 1;
  (gdb) s
  bbb () at test.c:26
  26	  return ccc () + 1;
  (gdb) s
  ccc () at test.c:20
  20	  return ddd () + 1;
  (gdb) s
  ddd () at test.c:32
  32	  return global_var;
  (gdb) bt
  #0  ddd () at test.c:32
  #1  0x00000000004003c1 in ccc () at test.c:20
  #2  bbb () at test.c:26
  #3  aaa () at test.c:14
  #4  main () at test.c:39

gdb/ChangeLog:

	* frame.c (find_frame_sal): Move call to get_next_frame into more
	inner scope.
	* inline-frame.c (inilne_state) <inline_state>: Update argument
	types.
	(inilne_state) <skipped_symbol>: Rename to...
	(inilne_state) <skipped_symbols>: ...this, and change to a vector.
	(skip_inline_frames): Build vector of skipped symbols and use this
	to reate the inline_state.
	(inline_skipped_symbol): Add a comment and some assertions, fetch
	skipped symbol from the list.

gdb/testsuite/ChangeLog:

	* gdb.dwarf2/dw2-inline-many-frames.c: New file.
	* gdb.dwarf2/dw2-inline-many-frames.exp: New file.

Change-Id: I99def5ffb44eb9e58cda4b449bf3d91ab0386c62
---
 gdb/ChangeLog                                      |  13 +
 gdb/frame.c                                        |   9 +-
 gdb/inline-frame.c                                 |  30 +-
 gdb/testsuite/ChangeLog                            |   5 +
 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c  | 158 +++++++++
 .../gdb.dwarf2/dw2-inline-many-frames.exp          | 379 +++++++++++++++++++++
 6 files changed, 579 insertions(+), 15 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp

diff --git a/gdb/frame.c b/gdb/frame.c
index 660f8cfa00e..0d2a9a3d25e 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2508,14 +2508,15 @@ find_frame_sal (frame_info *frame)
   int notcurrent;
   CORE_ADDR pc;
 
-  /* If the next frame represents an inlined function call, this frame's
-     sal is the "call site" of that inlined function, which can not
-     be inferred from get_frame_pc.  */
-  next_frame = get_next_frame (frame);
   if (frame_inlined_callees (frame) > 0)
     {
       struct symbol *sym;
 
+      /* If the current frame has some inlined callees, and we have a next
+	 frame, then that frame must be an inlined frame.  In this case
+	 this frame's sal is the "call site" of the next frame's inlined
+	 function, which can not be inferred from get_frame_pc.  */
+      next_frame = get_next_frame (frame);
       if (next_frame)
 	sym = get_frame_function (next_frame);
       else
diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c
index 5840e3ee319..db8d703adab 100644
--- a/gdb/inline-frame.c
+++ b/gdb/inline-frame.c
@@ -37,9 +37,9 @@
 struct inline_state
 {
   inline_state (thread_info *thread_, int skipped_frames_, CORE_ADDR saved_pc_,
-		symbol *skipped_symbol_)
+		std::vector<symbol *> &&skipped_symbols_)
     : thread (thread_), skipped_frames (skipped_frames_), saved_pc (saved_pc_),
-      skipped_symbol (skipped_symbol_)
+      skipped_symbols (std::move (skipped_symbols_))
   {}
 
   /* The thread this data relates to.  It should be a currently
@@ -56,10 +56,10 @@ struct inline_state
      any skipped frames.  */
   CORE_ADDR saved_pc;
 
-  /* Only valid if SKIPPED_FRAMES is non-zero.  This is the symbol
-     of the outermost skipped inline function.  It's used to find the
-     call site of the current frame.  */
-  struct symbol *skipped_symbol;
+  /* Only valid if SKIPPED_FRAMES is non-zero.  This is the list of all
+     function symbols that have been skipped, from inner most to outer
+     most.  It is used to find the call site of the current frame.  */
+  std::vector<struct symbol *> skipped_symbols;
 };
 
 static std::vector<inline_state> inline_states;
@@ -324,7 +324,7 @@ void
 skip_inline_frames (thread_info *thread, bpstat stop_chain)
 {
   const struct block *frame_block, *cur_block;
-  struct symbol *last_sym = NULL;
+  std::vector<struct symbol *> skipped_syms;
   int skip_count = 0;
 
   /* This function is called right after reinitializing the frame
@@ -352,7 +352,7 @@ skip_inline_frames (thread_info *thread, bpstat stop_chain)
 		    break;
 
 		  skip_count++;
-		  last_sym = BLOCK_FUNCTION (cur_block);
+		  skipped_syms.push_back (BLOCK_FUNCTION (cur_block));
 		}
 	      else
 		break;
@@ -365,7 +365,8 @@ skip_inline_frames (thread_info *thread, bpstat stop_chain)
     }
 
   gdb_assert (find_inline_frame_state (thread) == NULL);
-  inline_states.emplace_back (thread, skip_count, this_pc, last_sym);
+  inline_states.emplace_back (thread, skip_count, this_pc,
+			      std::move (skipped_syms));
 
   if (skip_count != 0)
     reinit_frame_cache ();
@@ -404,9 +405,16 @@ struct symbol *
 inline_skipped_symbol (thread_info *thread)
 {
   inline_state *state = find_inline_frame_state (thread);
-
   gdb_assert (state != NULL);
-  return state->skipped_symbol;
+
+  /* This should only be called when we are skipping at least one frame,
+     hence SKIPPED_FRAMES will be greater than zero when we get here.
+     We initialise SKIPPED_FRAMES at the same time as we build
+     SKIPPED_SYMBOLS, hence it should be true that SKIPPED_FRAMES never
+     indexes outside of the SKIPPED_SYMBOLS vector.  */
+  gdb_assert (state->skipped_frames > 0);
+  gdb_assert (state->skipped_frames <= state->skipped_symbols.size ());
+  return state->skipped_symbols[state->skipped_frames - 1];
 }
 
 /* Return the number of functions inlined into THIS_FRAME.  Some of
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
new file mode 100644
index 00000000000..16493e1c360
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
@@ -0,0 +1,158 @@
+/* Copyright 2019 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 sets up a call stack that looks like this:
+
+   #11     #10    #9     #8     #7     #6     #5     #4     #3     #2     #1     #0
+   main -> aaa -> bbb -> ccc -> ddd -> eee -> fff -> ggg -> hhh -> iii -> jjj -> kkk
+   \_______________________/    \________/    \______________________/    \________/
+      Inline sequence #1          Normal          Inline sequence #2        Normal
+
+   We use the 'start' command to move into main, after that we 'step'
+   through each function until we are in kkk.  We then use the 'up' command
+   to look back at each from to main.
+
+   The test checks that we can handle and step through sequences of more
+   than one inline frame (so 'main .... ccc', and 'fff .... iii'), and also
+   that we can move around in a stack that contains more than one disjoint
+   sequence of inline frames.
+
+   The order of the functions in this file is deliberately mixed up so that
+   the line numbers are not "all ascending" or "all descending" in the line
+   table.  */
+
+#define INLINE_FUNCTION __attribute__ ((always_inline)) static inline
+#define NON_INLINE_FUNCTION __attribute__ ((noinline))
+
+volatile int global_var = 0;
+
+INLINE_FUNCTION int aaa ();
+INLINE_FUNCTION int bbb ();
+INLINE_FUNCTION int ccc ();
+
+NON_INLINE_FUNCTION int ddd ();
+NON_INLINE_FUNCTION int eee ();
+NON_INLINE_FUNCTION int fff ();
+
+INLINE_FUNCTION int ggg ();
+INLINE_FUNCTION int hhh ();
+INLINE_FUNCTION int iii ();
+
+NON_INLINE_FUNCTION int jjj ();
+NON_INLINE_FUNCTION int kkk ();
+
+INLINE_FUNCTION int
+aaa ()
+{						/* aaa prologue */
+  asm ("aaa_label: .globl aaa_label");
+  return bbb () + 1;				/* aaa return */
+}						/* aaa end */
+
+NON_INLINE_FUNCTION int
+jjj ()
+{						/* jjj prologue */
+  int ans;
+  asm ("jjj_label: .globl jjj_label");
+  ans = kkk () + 1;				/* jjj return */
+  asm ("jjj_label2: .globl jjj_label2");
+  return ans;
+}						/* jjj end */
+
+INLINE_FUNCTION int
+ggg ()
+{						/* ggg prologue */
+  asm ("ggg_label: .globl ggg_label");
+  return hhh () + 1;				/* ggg return */
+}						/* ggg end */
+
+INLINE_FUNCTION int
+ccc ()
+{						/* ccc prologue */
+  asm ("ccc_label: .globl ccc_label");
+  return ddd () + 1;				/* ccc return */
+}						/* ccc end */
+
+NON_INLINE_FUNCTION int
+fff ()
+{						/* fff prologue */
+  int ans;
+  asm ("fff_label: .globl fff_label");
+  ans = ggg () + 1;				/* fff return */
+  asm ("fff_label2: .globl fff_label2");
+  return ans;
+}						/* fff end */
+
+NON_INLINE_FUNCTION int
+kkk ()
+{						/* kkk prologue */
+  asm ("kkk_label: .globl kkk_label");
+  return global_var;				/* kkk return */
+}						/* kkk end */
+
+INLINE_FUNCTION int
+bbb ()
+{						/* bbb prologue */
+  asm ("bbb_label: .globl bbb_label");
+  return ccc () + 1;				/* bbb return */
+}						/* bbb end */
+
+INLINE_FUNCTION int
+hhh ()
+{						/* hhh prologue */
+  asm ("hh_label: .globl hhh_label");
+  return iii () + 1;				/* hhh return */
+}						/* hhh end */
+
+int
+main ()
+{						/* main prologue */
+  int ans;
+  asm ("main_label: .globl main_label");
+  global_var = 0;				/* main set global_var */
+  asm ("main_label2: .globl main_label2");
+  ans = aaa () + 1;				/* main call aaa */
+  asm ("main_label3: .globl main_label3");
+  return ans;
+}						/* main end */
+
+NON_INLINE_FUNCTION int
+ddd ()
+{						/* ddd prologue */
+  int ans;
+  asm ("ddd_label: .globl ddd_label");
+  ans =  eee () + 1;				/* ddd return */
+  asm ("ddd_label2: .globl ddd_label2");
+  return ans;
+}						/* ddd end */
+
+INLINE_FUNCTION int
+iii ()
+{						/* iii prologue */
+  int ans;
+  asm ("iii_label: .globl iii_label");
+  ans = jjj () + 1;				/* iii return */
+  asm ("iii_label2: .globl iii_label2");
+  return ans;
+}						/* iii end */
+
+NON_INLINE_FUNCTION int
+eee ()
+{						/* eee prologue */
+  int ans;
+  asm ("eee_label: .globl eee_label");
+  ans = fff () + 1;				/* eee return */
+  asm ("eee_label2: .globl eee_label2");
+  return ans;
+}						/* eee end */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
new file mode 100644
index 00000000000..bc93b3b1cf8
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
@@ -0,0 +1,379 @@
+# Copyright 2019 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-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
+    declare_labels ranges_label lines_label
+    declare_labels aaa_label bbb_label ccc_label
+    declare_labels ggg_label hhh_label iii_label
+
+    get_func_info main
+    get_func_info ddd
+    get_func_info eee
+    get_func_info fff
+    get_func_info jjj
+    get_func_info kkk
+
+    set call_in_main [gdb_get_line_number "main call aaa"]
+    set call_in_aaa [gdb_get_line_number "aaa return"]
+    set call_in_bbb [gdb_get_line_number "bbb return"]
+    set call_in_ccc [gdb_get_line_number "ccc return"]
+    set call_in_fff [gdb_get_line_number "fff return"]
+    set call_in_ggg [gdb_get_line_number "ggg return"]
+    set call_in_hhh [gdb_get_line_number "hhh return"]
+    set call_in_iii [gdb_get_line_number "iii return"]
+
+    cu {} {
+	compile_unit {
+	    {language @DW_LANG_C}
+	    {name dw2-inline-stepping.c}
+	    {low_pc 0 addr}
+	    {stmt_list ${lines_label} DW_FORM_sec_offset}
+	    {ranges ${ranges_label} DW_FORM_sec_offset}
+	} {
+	    subprogram {
+		{external 1 flag}
+		{name ddd}
+		{low_pc $ddd_start addr}
+		{high_pc "$ddd_start + $ddd_len" addr}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name eee}
+		{low_pc $eee_start addr}
+		{high_pc "$eee_start + $eee_len" addr}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name jjj}
+		{low_pc $jjj_start addr}
+		{high_pc "$jjj_start + $jjj_len" addr}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name kkk}
+		{low_pc $kkk_start addr}
+		{high_pc "$kkk_start + $kkk_len" addr}
+	    }
+	    aaa_label: subprogram {
+		{name aaa}
+		{inline 3 data1}
+	    }
+	    bbb_label: subprogram {
+		{name bbb}
+		{inline 3 data1}
+	    }
+	    ccc_label: subprogram {
+		{name ccc}
+		{inline 3 data1}
+	    }
+	    ggg_label: subprogram {
+		{name ggg}
+		{inline 3 data1}
+	    }
+	    hhh_label: subprogram {
+		{name hhh}
+		{inline 3 data1}
+	    }
+	    iii_label: subprogram {
+		{name iii}
+		{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 %$aaa_label}
+		    {low_pc main_label2 addr}
+		    {high_pc main_label3 addr}
+		    {call_file 1 data1}
+		    {call_line $call_in_main data1}
+		} {
+		    inlined_subroutine {
+			{abstract_origin %$bbb_label}
+			{low_pc main_label2 addr}
+			{high_pc main_label3 addr}
+			{call_file 1 data1}
+			{call_line $call_in_aaa data1}
+		    }  {
+			inlined_subroutine {
+			    {abstract_origin %$ccc_label}
+			    {low_pc main_label2 addr}
+			    {high_pc main_label3 addr}
+			    {call_file 1 data1}
+			    {call_line $call_in_bbb data1}
+			}
+		    }
+		}
+	    }
+	    subprogram {
+		{external 1 flag}
+		{name fff}
+		{low_pc $fff_start addr}
+		{high_pc "$fff_start + $fff_len" addr}
+	    }  {
+		inlined_subroutine {
+		    {abstract_origin %$ggg_label}
+		    {low_pc fff_label addr}
+		    {high_pc main_label2 addr}
+		    {call_file 1 data1}
+		    {call_line $call_in_fff data1}
+		} {
+		    inlined_subroutine {
+			{abstract_origin %$hhh_label}
+			{low_pc fff_label addr}
+			{high_pc fff_label2 addr}
+			{call_file 1 data1}
+			{call_line $call_in_ggg data1}
+		    }  {
+			inlined_subroutine {
+			    {abstract_origin %$iii_label}
+			    {low_pc fff_label addr}
+			    {high_pc fff_label2 addr}
+			    {call_file 1 data1}
+			    {call_line $call_in_hhh data1}
+			}
+		    }
+		}
+	    }
+	}
+    }
+
+    lines {version 2} lines_label {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile" 1
+
+	program {
+	    {DW_LNE_set_address $main_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "main prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "main set global_var"] - [gdb_get_line_number "main prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "main call aaa"] - [gdb_get_line_number "main set global_var"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "aaa return"] - [gdb_get_line_number "main call aaa"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "bbb return"] - [gdb_get_line_number "aaa return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "ccc return"] - [gdb_get_line_number "bbb return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address main_label3}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "main end"] - [gdb_get_line_number "ccc return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $main_end}
+	    {DW_LNE_end_sequence}
+
+	    {DW_LNE_set_address $ddd_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "ddd prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address ddd_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "ddd return"] - [gdb_get_line_number "ddd prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address ddd_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "ddd end"] - [gdb_get_line_number "ddd return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $ddd_end}
+	    {DW_LNE_end_sequence}
+
+	    {DW_LNE_set_address $eee_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "eee prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address eee_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "eee return"] - [gdb_get_line_number "eee prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address eee_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "eee end"] - [gdb_get_line_number "eee return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $eee_end}
+	    {DW_LNE_end_sequence}
+
+	    {DW_LNE_set_address $fff_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "fff prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address fff_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "fff return"] - [gdb_get_line_number "fff prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address fff_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "ggg return"] - [gdb_get_line_number "fff return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address fff_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "hhh return"] - [gdb_get_line_number "ggg return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address fff_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "iii return"] - [gdb_get_line_number "hhh return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address fff_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "fff end"] - [gdb_get_line_number "fff return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $fff_end}
+	    {DW_LNE_end_sequence}
+
+	    {DW_LNE_set_address $jjj_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "jjj prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address jjj_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "jjj return"] - [gdb_get_line_number "jjj prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address jjj_label2}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "jjj end"] - [gdb_get_line_number "jjj return"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $jjj_end}
+	    {DW_LNE_end_sequence}
+
+	    {DW_LNE_set_address $kkk_start}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "kkk prologue"] - 1]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address kkk_label}
+	    {DW_LNS_advance_line [expr [gdb_get_line_number "kkk return"] - [gdb_get_line_number "kkk prologue"]]}
+	    {DW_LNS_copy}
+	    {DW_LNE_set_address $kkk_end}
+	    {DW_LNE_end_sequence}
+	}
+    }
+
+    ranges {is_64 [is_64_target]} {
+	ranges_label: sequence {
+	    {range {${main_start}} ${main_end}}
+	    {range {${ddd_start}} ${ddd_end}}
+	    {range {${eee_start}} ${eee_end}}
+	    {range {${fff_start}} ${fff_end}}
+	    {range {${jjj_start}} ${jjj_end}}
+	    {range {${kkk_start}} ${kkk_end}}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# First we step through all of the functions until we get the 'kkk'.
+set patterns [list "main call aaa" \
+		  "aaa return" \
+		  "bbb return" \
+		  "ccc return" \
+		  "ddd return" \
+		  "eee return" \
+		  "fff return" \
+		  "ggg return" \
+		  "hhh return" \
+		  "iii return" \
+		  "jjj return" \
+		  "kkk return" ]
+foreach p $patterns {
+    gdb_test "step" "/\\* $p \\*/" \
+	"step to '$p'"
+}
+
+# Now check the backtrace.
+set line_in_main [gdb_get_line_number "main call aaa"]
+set line_in_aaa [gdb_get_line_number "aaa return"]
+set line_in_bbb [gdb_get_line_number "bbb return"]
+set line_in_ccc [gdb_get_line_number "ccc return"]
+set line_in_ddd [gdb_get_line_number "ddd return"]
+set line_in_eee [gdb_get_line_number "eee return"]
+set line_in_fff [gdb_get_line_number "fff return"]
+set line_in_ggg [gdb_get_line_number "ggg return"]
+set line_in_hhh [gdb_get_line_number "hhh return"]
+set line_in_iii [gdb_get_line_number "iii return"]
+set line_in_jjj [gdb_get_line_number "jjj return"]
+set line_in_kkk [gdb_get_line_number "kkk return"]
+
+gdb_test "bt" [multi_line \
+		   "#0  kkk \\(\\) at \[^\r\n\]+${srcfile}:${line_in_kkk}" \
+		   "#1  $hex in jjj \\(\\) at \[^\r\n\]+${srcfile}:${line_in_jjj}" \
+		   "#2  $hex in iii \\(\\) at \[^\r\n\]+${srcfile}:${line_in_iii}" \
+		   "#3  hhh \\(\\) at \[^\r\n\]+${srcfile}:${line_in_hhh}" \
+		   "#4  ggg \\(\\) at \[^\r\n\]+${srcfile}:${line_in_ggg}" \
+		   "#5  fff \\(\\) at \[^\r\n\]+${srcfile}:${line_in_fff}" \
+		   "#6  $hex in eee \\(\\) at \[^\r\n\]+${srcfile}:${line_in_eee}" \
+		   "#7  $hex in ddd \\(\\) at \[^\r\n\]+${srcfile}:${line_in_ddd}" \
+		   "#8  $hex in ccc \\(\\) at \[^\r\n\]+${srcfile}:${line_in_ccc}" \
+		   "#9  bbb \\(\\) at \[^\r\n\]+${srcfile}:${line_in_bbb}" \
+		   "#10 aaa \\(\\) at \[^\r\n\]+${srcfile}:${line_in_aaa}" \
+		   "#11 main \\(\\) at \[^\r\n\]+${srcfile}:${line_in_main}" ]
+
+# Now check we can use 'up' to inspect each frame correctly.
+set patterns [list  \
+		  "jjj return" \
+		  "iii return" \
+		  "hhh return" \
+		  "ggg return" \
+		  "fff return" \
+		  "eee return" \
+		  "ddd return" \
+		  "ccc return" \
+		  "bbb return" \
+		  "aaa return" \
+		  "main call aaa" ]
+foreach p $patterns {
+    gdb_test "up" "/\\* $p \\*/" \
+	"up to '$p'"
+}
-- 
2.14.5

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

* [PATCH 1/3] gdb: Include end of sequence markers in the line table
  2019-12-23  1:51 [PATCH 0/3] Improve inline frame debug experience Andrew Burgess
  2019-12-23  1:51 ` [PATCH 3/3] gdb: Better frame tracking for inline frames Andrew Burgess
@ 2019-12-23  1:51 ` Andrew Burgess
  2019-12-23  1:51 ` [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting Andrew Burgess
  2020-01-06 22:14 ` [PATCH 0/3] Improve inline frame debug experience Andrew Burgess
  3 siblings, 0 replies; 25+ messages in thread
From: Andrew Burgess @ 2019-12-23  1:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

In this commit:

  commit d9b3de22f33e400f7f409cce3acf6c7dab07dd79
  Date:   Wed May 27 14:44:29 2015 -0700

      Add struct to record dwarf line number state machine.

I believe an unintended change was made to how we store the DWARF line
table, the end of sequence markers between sequences of lines were
lost from the line table.

This commit fixes this small oversight and restores the end of
sequence markers.

Given that we've survived this long without noticing is clearly an
indication that this isn't that serious, however, a later patch that I
am developing would benefit from having the markers in place, so I'd
like to restore them.

Having the markers also means that the output of 'maintenance info
line-table' now more closely reflects the DWARF line table.

I've taken this opportunity to improve how 'maintenance info
line-table' displays the end of sequence markers - it now uses the END
keyword, rather than just printing an entry with line number 0.  So we
see this:

  INDEX    LINE ADDRESS
  0          12 0x00000000004003b0
  1          17 0x00000000004003b0
  2          18 0x00000000004003b0
  3         END 0x00000000004003b7
  4           5 0x00000000004004a0
  5           6 0x00000000004004a0
  6         END 0x00000000004004a7

Instead of what we would have seen, which was this:

  INDEX    LINE ADDRESS
  0          12 0x00000000004003b0
  1          17 0x00000000004003b0
  2          18 0x00000000004003b0
  3           0 0x00000000004003b7
  4           5 0x00000000004004a0
  5           6 0x00000000004004a0
  6           0 0x00000000004004a7

I've added a small test that uses 'maintenance info line-table' to
ensure that we don't regress this again.

gdb/ChangeLog:

	* dwarf2read.c (lnp_state_machine::record_line): Include
	end_sequence parameter in debug print out.  Record the line if we
	are at an end_sequence marker even if it's not the start of a
	statement.
	* symmisc.c (maintenance_print_one_line_table): Print end of
	sequence markers with 'END' not '0'.

gdb/testsuite/ChangeLog:

	* gdb.base/maint.exp: Update line table parsing test.
	* gdb.dwarf2/dw2-ranges-base.exp: Add new line table parsing test.

Change-Id: I002f872248db82a1d4fefdc6b51ff5dbf932d8a8
---
 gdb/ChangeLog                                |  9 +++++++++
 gdb/dwarf2read.c                             |  8 +++++---
 gdb/symmisc.c                                |  8 ++++++--
 gdb/testsuite/ChangeLog                      |  5 +++++
 gdb/testsuite/gdb.base/maint.exp             | 20 ++++++++++++++------
 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp | 22 ++++++++++++++++++++++
 6 files changed, 61 insertions(+), 11 deletions(-)

diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 685d9962978..c37df3ea9d0 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -21314,10 +21314,11 @@ lnp_state_machine::record_line (bool end_sequence)
     {
       fprintf_unfiltered (gdb_stdlog,
 			  "Processing actual line %u: file %u,"
-			  " address %s, is_stmt %u, discrim %u\n",
+			  " address %s, is_stmt %u, discrim %u%s\n",
 			  m_line, m_file,
 			  paddress (m_gdbarch, m_address),
-			  m_is_stmt, m_discriminator);
+			  m_is_stmt, m_discriminator,
+			  (end_sequence ? "\t(end sequence)" : ""));
     }
 
   file_entry *fe = current_file ();
@@ -21330,7 +21331,8 @@ 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))
+      if (m_record_lines_p
+	  && (producer_is_codewarrior (m_cu) || m_is_stmt || end_sequence))
 	{
 	  if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
 	      || end_sequence)
diff --git a/gdb/symmisc.c b/gdb/symmisc.c
index 69d035dc843..9e510432f35 100644
--- a/gdb/symmisc.c
+++ b/gdb/symmisc.c
@@ -1000,8 +1000,12 @@ maintenance_print_one_line_table (struct symtab *symtab, void *data)
 	  struct linetable_entry *item;
 
 	  item = &linetable->item [i];
-	  printf_filtered (_("%-6d %6d %s\n"), i, item->line,
-			   core_addr_to_string (item->pc));
+	  printf_filtered ("%-6d ", i);
+	  if (item->line > 0)
+	    printf_filtered ("%6d ", item->line);
+	  else
+	    printf_filtered ("%6s ", _("END"));
+	  printf_filtered ("%s\n", core_addr_to_string (item->pc));
 	}
     }
 
diff --git a/gdb/testsuite/gdb.base/maint.exp b/gdb/testsuite/gdb.base/maint.exp
index 15988c79386..90220632076 100644
--- a/gdb/testsuite/gdb.base/maint.exp
+++ b/gdb/testsuite/gdb.base/maint.exp
@@ -508,8 +508,8 @@ gdb_test "maint" \
 # Test that "main info line-table" w/o a file name shows the symtab for
 # $srcfile.
 set saw_srcfile 0
-set test "maint info line-table w/o a file name"
-gdb_test_multiple "maint info line-table" $test {
+gdb_test_multiple "maint info line-table" \
+    "maint info line-table w/o a file name" {
     -re "symtab: \[^\n\r\]+${srcfile} \\(\\(struct symtab \\*\\) $hex\\)\r\nlinetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+ADDRESS" {
 	set saw_srcfile 1
 	exp_continue
@@ -518,7 +518,11 @@ gdb_test_multiple "maint info line-table" $test {
 	# Match each symtab to avoid overflowing expect's buffer.
 	exp_continue
     }
-    -re "$decimal\[ \t\]+$decimal\[ \t\]+$hex\r\n" {
+    -re "symtab: \[^\n\r\]+ \\(\\(struct symtab \\*\\) $hex\\)\r\nlinetable: \\(\\(struct linetable \\*\\) 0x0\\):\r\nNo line table.\r\n" {
+	# For symtabs with no linetable.
+	exp_continue
+    }
+    -re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\r\n" {
 	# Line table entries can be long too:
 	#
 	#  INDEX    LINE ADDRESS
@@ -531,13 +535,17 @@ gdb_test_multiple "maint info line-table" $test {
 	#  6          45 0x0000000000400740
 	#  (...)
 	#  454       129 0x00007ffff7df1d28
-	#  455         0 0x00007ffff7df1d3f
+	#  455       END 0x00007ffff7df1d3f
 	#
 	# Match each line to avoid overflowing expect's buffer.
 	exp_continue
     }
-    -re "$gdb_prompt $" {
-	gdb_assert $saw_srcfile $test
+    -re "^$decimal\[ \t\]+END\[ \t\]+$hex\r\n" {
+	# Matches an end marker in the above.
+	exp_continue
+    }
+    -re "^$gdb_prompt $" {
+	gdb_assert $saw_srcfile $gdb_test_name
     }
 }
 
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
index 5405d7dc9bb..cff9055cc91 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
@@ -141,3 +141,25 @@ gdb_test "info line frame2" \
     "Line 21 of .* starts at address .* and ends at .*"
 gdb_test "info line frame3" \
     "Line 31 of .* starts at address .* and ends at .*"
+
+# Ensure that the line table correctly tracks the end of sequence markers.
+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" {
+	    exp_continue
+	}
+	-re "^$decimal\[ \t\]+END\[ \t\]+$hex\r\n" {
+	    incr end_seq_count
+	    exp_continue
+	}
+	-re "^$gdb_prompt $" {
+	    gdb_assert [expr $end_seq_count == 3] $gdb_test_name
+	}
+	-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" {
+	    exp_continue
+	}
+    }
-- 
2.14.5

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

* [PATCH 0/3] Improve inline frame debug experience
@ 2019-12-23  1:51 Andrew Burgess
  2019-12-23  1:51 ` [PATCH 3/3] gdb: Better frame tracking for inline frames Andrew Burgess
                   ` (3 more replies)
  0 siblings, 4 replies; 25+ messages in thread
From: Andrew Burgess @ 2019-12-23  1:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This series replaces 3 patches from gerrit:

  https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/525
  https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/526
  https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/616

I've done nothing but rebase and retest.

All feedback welcome.

Thanks,
Andrew

---

Andrew Burgess (3):
  gdb: Include end of sequence markers in the line table
  gdb: Don't reorder line table entries too much when sorting.
  gdb: Better frame tracking for inline frames

 gdb/ChangeLog                                      |  31 ++
 gdb/buildsym.c                                     |  42 +--
 gdb/dwarf2read.c                                   |   8 +-
 gdb/frame.c                                        |   9 +-
 gdb/inline-frame.c                                 |  30 +-
 gdb/symmisc.c                                      |   8 +-
 gdb/symtab.c                                       |   7 +-
 gdb/testsuite/ChangeLog                            |  15 +
 gdb/testsuite/gdb.base/maint.exp                   |  20 +-
 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c  | 158 +++++++++
 .../gdb.dwarf2/dw2-inline-many-frames.exp          | 379 +++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.c     |  45 +++
 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.exp   | 147 ++++++++
 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp       |  22 ++
 14 files changed, 870 insertions(+), 51 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.exp

-- 
2.14.5

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

* Re: [PATCH 3/3] gdb: Better frame tracking for inline frames
  2019-12-23  1:51 ` [PATCH 3/3] gdb: Better frame tracking for inline frames Andrew Burgess
@ 2019-12-26  7:25   ` Christian Biesinger via gdb-patches
  2019-12-26 23:33     ` Andrew Burgess
  0 siblings, 1 reply; 25+ messages in thread
From: Christian Biesinger via gdb-patches @ 2019-12-26  7:25 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

On Mon, Dec 23, 2019, 02:51 Andrew Burgess <andrew.burgess@embecosm.com>
wrote:

> This commit improves GDB's handling of inline functions when there are
> more than one inline function in a stack, so for example if we have a
> stack like:
>
>    main -> aaa -> bbb -> ccc -> ddd
>
> And aaa, bbb, and ccc are all inline within main GDB should (when
> given sufficient debug information) be able to step from main through
> aaa, bbb, and ccc.  Unfortunately, this currently doesn't work, here's
> an example session:
>
>   (gdb) start
>   Temporary breakpoint 1 at 0x4003b0: file test.c, line 38.
>   Starting program: /project/gdb/tests/inline/test
>
>   Temporary breakpoint 1, main () at test.c:38
>   38      global_var = 0;
>   (gdb) step
>   39      return aaa () + 1;
>   (gdb) step
>   aaa () at test.c:39
>   39      return aaa () + 1;
>   (gdb) step
>   bbb () at test.c:39
>   39      return aaa () + 1;
>   (gdb) step
>   ccc () at test.c:39
>   39      return aaa () + 1;
>   (gdb) step
>   ddd () at test.c:32
>   32      return global_var;
>   (gdb) bt
>   #0  ddd () at test.c:32
>   #1  0x00000000004003c1 in ccc () at test.c:39
>   #2  bbb () at test.c:26
>   #3  aaa () at test.c:14
>   #4  main () at test.c:39
>

How come only #1 is printing the address?


> Notice that once we get to line 39 in main, GDB keeps reporting line
> 39 in main as the location despite understanding that the inferior is
> stepping through the nested inline functions with each use of step.
>
> The problem is that as soon as the inferior stops we call
> skip_inline_frames (from inline-frame.c) which calculates the
> inferiors current state in relation to inline functions - it figures
> out if we're in an inline function, and if we are counts how many
> inline frames there are at the current location.
>
> So, in our example above, when we step from line 38 in main to line 39
> we stop at a location that is simultaneously in all of main, aaa, bbb,
> and ccc.  The block structure reflects the order in which the
> functions would be called, with ccc being the most inner block and
> main being the most outer block.  When we stop GDB naturally finds the
> block for ccc, however within skip_inline_frames we spot that bbb,
> aaa, and main are super-blocks of the current location and that each
> layer represents an inline function.  The skip_inline_frames then
> records the depth of inline functions (3 in this case for aaa, bbb,
> and ccc) and also the symbol of the outermost inline function (in this
> case 'aaa' as main isn't an inline function, it just has things inline
> within it).
>
> Now GDB understands the stack to be main -> aaa -> bbb -> ccc,
> however, the state initialised in skip_inline_frames starts off
> indicating that we should hide 3 frames from the user, so we report
> that we're in main at line 39.  The location of main, line 39 is
> derived by asking the inline function state for the last symbol in the
> stack (aaa in this case), and then asking for it's location - the
> location of an inlined function symbol is its call site, so main, line
> 39 in this case.
>
> If the user then asks GDB to step we don't actually move the inferior
> at all, instead we spot that we are in an inline function stack,
> lookup the inline state data, and reduce the skip depth by 1.  We then
> report to the user that GDB has stopped.  GDB now understands that we
> are in 'aaa'.  In order to get the precise location we again ask GDB
> for the last symbol from the inline data structure, and we are again
> told 'aaa', we then get the location from 'aaa', and report that we
> are in main, line 39.
>
> Hopefully it's clear what the mistake here is, once we've reduced the
> inline skip depth we should not be using 'aaa' to compute the precise
> location, instead we should be using 'bbb'.  That is what this patch
> does.
>
> Now, when we call skip_inline_frames instead of just recording the
> last skipped symbol we now record all symbols in the inline frame
> stack.  When we ask GDB for the last skipped symbol we return a symbol
> based on how many frames we are skipping, not just the last know
> symbol.
>
> With this fix in place, the same session as above now looks much
> better:
>
>   (gdb) start
>   Temporary breakpoint 1 at 0x4003b0: file test.c, line 38.
>   Starting program: /project/gdb/tests/inline/test
>
>   Temporary breakpoint 1, main () at test.c:38
>   38      global_var = 0;
>   (gdb) s
>   39      return aaa () + 1;
>   (gdb) s
>   aaa () at test.c:14
>   14      return bbb () + 1;
>   (gdb) s
>   bbb () at test.c:26
>   26      return ccc () + 1;
>   (gdb) s
>   ccc () at test.c:20
>   20      return ddd () + 1;
>   (gdb) s
>   ddd () at test.c:32
>   32      return global_var;
>   (gdb) bt
>   #0  ddd () at test.c:32
>   #1  0x00000000004003c1 in ccc () at test.c:20
>   #2  bbb () at test.c:26
>   #3  aaa () at test.c:14
>   #4  main () at test.c:39
>
> gdb/ChangeLog:
>
>         * frame.c (find_frame_sal): Move call to get_next_frame into more
>         inner scope.
>         * inline-frame.c (inilne_state) <inline_state>: Update argument
>         types.
>         (inilne_state) <skipped_symbol>: Rename to...
>         (inilne_state) <skipped_symbols>: ...this, and change to a vector.
>         (skip_inline_frames): Build vector of skipped symbols and use this
>         to reate the inline_state.
>         (inline_skipped_symbol): Add a comment and some assertions, fetch
>         skipped symbol from the list.
>
> gdb/testsuite/ChangeLog:
>
>         * gdb.dwarf2/dw2-inline-many-frames.c: New file.
>         * gdb.dwarf2/dw2-inline-many-frames.exp: New file.
>
> Change-Id: I99def5ffb44eb9e58cda4b449bf3d91ab0386c62
> ---
>  gdb/ChangeLog                                      |  13 +
>  gdb/frame.c                                        |   9 +-
>  gdb/inline-frame.c                                 |  30 +-
>  gdb/testsuite/ChangeLog                            |   5 +
>  gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c  | 158 +++++++++
>  .../gdb.dwarf2/dw2-inline-many-frames.exp          | 379
> +++++++++++++++++++++
>  6 files changed, 579 insertions(+), 15 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
>
> diff --git a/gdb/frame.c b/gdb/frame.c
> index 660f8cfa00e..0d2a9a3d25e 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
> @@ -2508,14 +2508,15 @@ find_frame_sal (frame_info *frame)
>    int notcurrent;
>    CORE_ADDR pc;
>
> -  /* If the next frame represents an inlined function call, this frame's
> -     sal is the "call site" of that inlined function, which can not
> -     be inferred from get_frame_pc.  */
> -  next_frame = get_next_frame (frame);
>    if (frame_inlined_callees (frame) > 0)
>      {
>        struct symbol *sym;
>
> +      /* If the current frame has some inlined callees, and we have a next
> +        frame, then that frame must be an inlined frame.  In this case
> +        this frame's sal is the "call site" of the next frame's inlined
> +        function, which can not be inferred from get_frame_pc.  */
> +      next_frame = get_next_frame (frame);
>        if (next_frame)
>         sym = get_frame_function (next_frame);
>        else
> diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c
> index 5840e3ee319..db8d703adab 100644
> --- a/gdb/inline-frame.c
> +++ b/gdb/inline-frame.c
> @@ -37,9 +37,9 @@
>  struct inline_state
>  {
>    inline_state (thread_info *thread_, int skipped_frames_, CORE_ADDR
> saved_pc_,
> -               symbol *skipped_symbol_)
> +               std::vector<symbol *> &&skipped_symbols_)
>      : thread (thread_), skipped_frames (skipped_frames_), saved_pc
> (saved_pc_),
> -      skipped_symbol (skipped_symbol_)
> +      skipped_symbols (std::move (skipped_symbols_))
>    {}
>
>    /* The thread this data relates to.  It should be a currently
> @@ -56,10 +56,10 @@ struct inline_state
>       any skipped frames.  */
>    CORE_ADDR saved_pc;
>
> -  /* Only valid if SKIPPED_FRAMES is non-zero.  This is the symbol
> -     of the outermost skipped inline function.  It's used to find the
> -     call site of the current frame.  */
> -  struct symbol *skipped_symbol;
> +  /* Only valid if SKIPPED_FRAMES is non-zero.  This is the list of all
> +     function symbols that have been skipped, from inner most to outer
> +     most.  It is used to find the call site of the current frame.  */
> +  std::vector<struct symbol *> skipped_symbols;
>  };
>
>  static std::vector<inline_state> inline_states;
> @@ -324,7 +324,7 @@ void
>  skip_inline_frames (thread_info *thread, bpstat stop_chain)
>  {
>    const struct block *frame_block, *cur_block;
> -  struct symbol *last_sym = NULL;
> +  std::vector<struct symbol *> skipped_syms;
>    int skip_count = 0;
>
>    /* This function is called right after reinitializing the frame
> @@ -352,7 +352,7 @@ skip_inline_frames (thread_info *thread, bpstat
> stop_chain)
>                     break;
>
>                   skip_count++;
> -                 last_sym = BLOCK_FUNCTION (cur_block);
> +                 skipped_syms.push_back (BLOCK_FUNCTION (cur_block));
>                 }
>               else
>                 break;
> @@ -365,7 +365,8 @@ skip_inline_frames (thread_info *thread, bpstat
> stop_chain)
>      }
>
>    gdb_assert (find_inline_frame_state (thread) == NULL);
> -  inline_states.emplace_back (thread, skip_count, this_pc, last_sym);
> +  inline_states.emplace_back (thread, skip_count, this_pc,
> +                             std::move (skipped_syms));
>
>    if (skip_count != 0)
>      reinit_frame_cache ();
> @@ -404,9 +405,16 @@ struct symbol *
>  inline_skipped_symbol (thread_info *thread)
>  {
>    inline_state *state = find_inline_frame_state (thread);
> -
>    gdb_assert (state != NULL);
> -  return state->skipped_symbol;
> +
> +  /* This should only be called when we are skipping at least one frame,
> +     hence SKIPPED_FRAMES will be greater than zero when we get here.
> +     We initialise SKIPPED_FRAMES at the same time as we build
> +     SKIPPED_SYMBOLS, hence it should be true that SKIPPED_FRAMES never
> +     indexes outside of the SKIPPED_SYMBOLS vector.  */
> +  gdb_assert (state->skipped_frames > 0);
> +  gdb_assert (state->skipped_frames <= state->skipped_symbols.size ());
> +  return state->skipped_symbols[state->skipped_frames - 1];
>  }
>
>  /* Return the number of functions inlined into THIS_FRAME.  Some of
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
> b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
> new file mode 100644
> index 00000000000..16493e1c360
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
> @@ -0,0 +1,158 @@
> +/* Copyright 2019 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 sets up a call stack that looks like this:
> +
> +   #11     #10    #9     #8     #7     #6     #5     #4     #3     #2
>  #1     #0
> +   main -> aaa -> bbb -> ccc -> ddd -> eee -> fff -> ggg -> hhh -> iii ->
> jjj -> kkk
> +   \_______________________/    \________/    \______________________/
> \________/
> +      Inline sequence #1          Normal          Inline sequence #2
>   Normal
> +
> +   We use the 'start' command to move into main, after that we 'step'
> +   through each function until we are in kkk.  We then use the 'up'
> command
> +   to look back at each from to main.
> +
> +   The test checks that we can handle and step through sequences of more
> +   than one inline frame (so 'main .... ccc', and 'fff .... iii'), and
> also
> +   that we can move around in a stack that contains more than one disjoint
> +   sequence of inline frames.
> +
> +   The order of the functions in this file is deliberately mixed up so
> that
> +   the line numbers are not "all ascending" or "all descending" in the
> line
> +   table.  */
> +
> +#define INLINE_FUNCTION __attribute__ ((always_inline)) static inline
> +#define NON_INLINE_FUNCTION __attribute__ ((noinline))
> +
> +volatile int global_var = 0;
> +
> +INLINE_FUNCTION int aaa ();
> +INLINE_FUNCTION int bbb ();
> +INLINE_FUNCTION int ccc ();
> +
> +NON_INLINE_FUNCTION int ddd ();
> +NON_INLINE_FUNCTION int eee ();
> +NON_INLINE_FUNCTION int fff ();
> +
> +INLINE_FUNCTION int ggg ();
> +INLINE_FUNCTION int hhh ();
> +INLINE_FUNCTION int iii ();
> +
> +NON_INLINE_FUNCTION int jjj ();
> +NON_INLINE_FUNCTION int kkk ();
> +
> +INLINE_FUNCTION int
> +aaa ()
> +{                                              /* aaa prologue */
> +  asm ("aaa_label: .globl aaa_label");
> +  return bbb () + 1;                           /* aaa return */
> +}                                              /* aaa end */
> +
> +NON_INLINE_FUNCTION int
> +jjj ()
> +{                                              /* jjj prologue */
> +  int ans;
> +  asm ("jjj_label: .globl jjj_label");
> +  ans = kkk () + 1;                            /* jjj return */
> +  asm ("jjj_label2: .globl jjj_label2");
> +  return ans;
> +}                                              /* jjj end */
> +
> +INLINE_FUNCTION int
> +ggg ()
> +{                                              /* ggg prologue */
> +  asm ("ggg_label: .globl ggg_label");
> +  return hhh () + 1;                           /* ggg return */
> +}                                              /* ggg end */
> +
> +INLINE_FUNCTION int
> +ccc ()
> +{                                              /* ccc prologue */
> +  asm ("ccc_label: .globl ccc_label");
> +  return ddd () + 1;                           /* ccc return */
> +}                                              /* ccc end */
> +
> +NON_INLINE_FUNCTION int
> +fff ()
> +{                                              /* fff prologue */
> +  int ans;
> +  asm ("fff_label: .globl fff_label");
> +  ans = ggg () + 1;                            /* fff return */
> +  asm ("fff_label2: .globl fff_label2");
> +  return ans;
> +}                                              /* fff end */
> +
> +NON_INLINE_FUNCTION int
> +kkk ()
> +{                                              /* kkk prologue */
> +  asm ("kkk_label: .globl kkk_label");
> +  return global_var;                           /* kkk return */
> +}                                              /* kkk end */
> +
> +INLINE_FUNCTION int
> +bbb ()
> +{                                              /* bbb prologue */
> +  asm ("bbb_label: .globl bbb_label");
> +  return ccc () + 1;                           /* bbb return */
> +}                                              /* bbb end */
> +
> +INLINE_FUNCTION int
> +hhh ()
> +{                                              /* hhh prologue */
> +  asm ("hh_label: .globl hhh_label");
> +  return iii () + 1;                           /* hhh return */
> +}                                              /* hhh end */
> +
> +int
> +main ()
> +{                                              /* main prologue */
> +  int ans;
> +  asm ("main_label: .globl main_label");
> +  global_var = 0;                              /* main set global_var */
> +  asm ("main_label2: .globl main_label2");
> +  ans = aaa () + 1;                            /* main call aaa */
> +  asm ("main_label3: .globl main_label3");
> +  return ans;
> +}                                              /* main end */
> +
> +NON_INLINE_FUNCTION int
> +ddd ()
> +{                                              /* ddd prologue */
> +  int ans;
> +  asm ("ddd_label: .globl ddd_label");
> +  ans =  eee () + 1;                           /* ddd return */
> +  asm ("ddd_label2: .globl ddd_label2");
> +  return ans;
> +}                                              /* ddd end */
> +
> +INLINE_FUNCTION int
> +iii ()
> +{                                              /* iii prologue */
> +  int ans;
> +  asm ("iii_label: .globl iii_label");
> +  ans = jjj () + 1;                            /* iii return */
> +  asm ("iii_label2: .globl iii_label2");
> +  return ans;
> +}                                              /* iii end */
> +
> +NON_INLINE_FUNCTION int
> +eee ()
> +{                                              /* eee prologue */
> +  int ans;
> +  asm ("eee_label: .globl eee_label");
> +  ans = fff () + 1;                            /* eee return */
> +  asm ("eee_label2: .globl eee_label2");
> +  return ans;
> +}                                              /* eee end */
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
> b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
> new file mode 100644
> index 00000000000..bc93b3b1cf8
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
> @@ -0,0 +1,379 @@
> +# Copyright 2019 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-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
> +    declare_labels ranges_label lines_label
> +    declare_labels aaa_label bbb_label ccc_label
> +    declare_labels ggg_label hhh_label iii_label
> +
> +    get_func_info main
> +    get_func_info ddd
> +    get_func_info eee
> +    get_func_info fff
> +    get_func_info jjj
> +    get_func_info kkk
> +
> +    set call_in_main [gdb_get_line_number "main call aaa"]
> +    set call_in_aaa [gdb_get_line_number "aaa return"]
> +    set call_in_bbb [gdb_get_line_number "bbb return"]
> +    set call_in_ccc [gdb_get_line_number "ccc return"]
> +    set call_in_fff [gdb_get_line_number "fff return"]
> +    set call_in_ggg [gdb_get_line_number "ggg return"]
> +    set call_in_hhh [gdb_get_line_number "hhh return"]
> +    set call_in_iii [gdb_get_line_number "iii return"]
> +
> +    cu {} {
> +       compile_unit {
> +           {language @DW_LANG_C}
> +           {name dw2-inline-stepping.c}
> +           {low_pc 0 addr}
> +           {stmt_list ${lines_label} DW_FORM_sec_offset}
> +           {ranges ${ranges_label} DW_FORM_sec_offset}
> +       } {
> +           subprogram {
> +               {external 1 flag}
> +               {name ddd}
> +               {low_pc $ddd_start addr}
> +               {high_pc "$ddd_start + $ddd_len" addr}
> +           }
> +           subprogram {
> +               {external 1 flag}
> +               {name eee}
> +               {low_pc $eee_start addr}
> +               {high_pc "$eee_start + $eee_len" addr}
> +           }
> +           subprogram {
> +               {external 1 flag}
> +               {name jjj}
> +               {low_pc $jjj_start addr}
> +               {high_pc "$jjj_start + $jjj_len" addr}
> +           }
> +           subprogram {
> +               {external 1 flag}
> +               {name kkk}
> +               {low_pc $kkk_start addr}
> +               {high_pc "$kkk_start + $kkk_len" addr}
> +           }
> +           aaa_label: subprogram {
> +               {name aaa}
> +               {inline 3 data1}
> +           }
> +           bbb_label: subprogram {
> +               {name bbb}
> +               {inline 3 data1}
> +           }
> +           ccc_label: subprogram {
> +               {name ccc}
> +               {inline 3 data1}
> +           }
> +           ggg_label: subprogram {
> +               {name ggg}
> +               {inline 3 data1}
> +           }
> +           hhh_label: subprogram {
> +               {name hhh}
> +               {inline 3 data1}
> +           }
> +           iii_label: subprogram {
> +               {name iii}
> +               {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 %$aaa_label}
> +                   {low_pc main_label2 addr}
> +                   {high_pc main_label3 addr}
> +                   {call_file 1 data1}
> +                   {call_line $call_in_main data1}
> +               } {
> +                   inlined_subroutine {
> +                       {abstract_origin %$bbb_label}
> +                       {low_pc main_label2 addr}
> +                       {high_pc main_label3 addr}
> +                       {call_file 1 data1}
> +                       {call_line $call_in_aaa data1}
> +                   }  {
> +                       inlined_subroutine {
> +                           {abstract_origin %$ccc_label}
> +                           {low_pc main_label2 addr}
> +                           {high_pc main_label3 addr}
> +                           {call_file 1 data1}
> +                           {call_line $call_in_bbb data1}
> +                       }
> +                   }
> +               }
> +           }
> +           subprogram {
> +               {external 1 flag}
> +               {name fff}
> +               {low_pc $fff_start addr}
> +               {high_pc "$fff_start + $fff_len" addr}
> +           }  {
> +               inlined_subroutine {
> +                   {abstract_origin %$ggg_label}
> +                   {low_pc fff_label addr}
> +                   {high_pc main_label2 addr}
> +                   {call_file 1 data1}
> +                   {call_line $call_in_fff data1}
> +               } {
> +                   inlined_subroutine {
> +                       {abstract_origin %$hhh_label}
> +                       {low_pc fff_label addr}
> +                       {high_pc fff_label2 addr}
> +                       {call_file 1 data1}
> +                       {call_line $call_in_ggg data1}
> +                   }  {
> +                       inlined_subroutine {
> +                           {abstract_origin %$iii_label}
> +                           {low_pc fff_label addr}
> +                           {high_pc fff_label2 addr}
> +                           {call_file 1 data1}
> +                           {call_line $call_in_hhh data1}
> +                       }
> +                   }
> +               }
> +           }
> +       }
> +    }
> +
> +    lines {version 2} lines_label {
> +       include_dir "${srcdir}/${subdir}"
> +       file_name "$srcfile" 1
> +
> +       program {
> +           {DW_LNE_set_address $main_start}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "main
> prologue"] - 1]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address main_label}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "main set
> global_var"] - [gdb_get_line_number "main prologue"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address main_label2}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "main call
> aaa"] - [gdb_get_line_number "main set global_var"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address main_label2}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "aaa return"]
> - [gdb_get_line_number "main call aaa"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address main_label2}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "bbb return"]
> - [gdb_get_line_number "aaa return"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address main_label2}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "ccc return"]
> - [gdb_get_line_number "bbb return"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address main_label3}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "main end"] -
> [gdb_get_line_number "ccc return"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address $main_end}
> +           {DW_LNE_end_sequence}
> +
> +           {DW_LNE_set_address $ddd_start}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "ddd
> prologue"] - 1]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address ddd_label}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "ddd return"]
> - [gdb_get_line_number "ddd prologue"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address ddd_label2}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "ddd end"] -
> [gdb_get_line_number "ddd return"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address $ddd_end}
> +           {DW_LNE_end_sequence}
> +
> +           {DW_LNE_set_address $eee_start}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "eee
> prologue"] - 1]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address eee_label}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "eee return"]
> - [gdb_get_line_number "eee prologue"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address eee_label2}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "eee end"] -
> [gdb_get_line_number "eee return"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address $eee_end}
> +           {DW_LNE_end_sequence}
> +
> +           {DW_LNE_set_address $fff_start}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "fff
> prologue"] - 1]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address fff_label}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "fff return"]
> - [gdb_get_line_number "fff prologue"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address fff_label}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "ggg return"]
> - [gdb_get_line_number "fff return"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address fff_label}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "hhh return"]
> - [gdb_get_line_number "ggg return"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address fff_label}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "iii return"]
> - [gdb_get_line_number "hhh return"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address fff_label2}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "fff end"] -
> [gdb_get_line_number "fff return"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address $fff_end}
> +           {DW_LNE_end_sequence}
> +
> +           {DW_LNE_set_address $jjj_start}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "jjj
> prologue"] - 1]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address jjj_label}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "jjj return"]
> - [gdb_get_line_number "jjj prologue"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address jjj_label2}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "jjj end"] -
> [gdb_get_line_number "jjj return"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address $jjj_end}
> +           {DW_LNE_end_sequence}
> +
> +           {DW_LNE_set_address $kkk_start}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "kkk
> prologue"] - 1]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address kkk_label}
> +           {DW_LNS_advance_line [expr [gdb_get_line_number "kkk return"]
> - [gdb_get_line_number "kkk prologue"]]}
> +           {DW_LNS_copy}
> +           {DW_LNE_set_address $kkk_end}
> +           {DW_LNE_end_sequence}
> +       }
> +    }
> +
> +    ranges {is_64 [is_64_target]} {
> +       ranges_label: sequence {
> +           {range {${main_start}} ${main_end}}
> +           {range {${ddd_start}} ${ddd_end}}
> +           {range {${eee_start}} ${eee_end}}
> +           {range {${fff_start}} ${fff_end}}
> +           {range {${jjj_start}} ${jjj_end}}
> +           {range {${kkk_start}} ${kkk_end}}
> +       }
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +         [list $srcfile $asm_file] {nodebug}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +# First we step through all of the functions until we get the 'kkk'.
> +set patterns [list "main call aaa" \
> +                 "aaa return" \
> +                 "bbb return" \
> +                 "ccc return" \
> +                 "ddd return" \
> +                 "eee return" \
> +                 "fff return" \
> +                 "ggg return" \
> +                 "hhh return" \
> +                 "iii return" \
> +                 "jjj return" \
> +                 "kkk return" ]
> +foreach p $patterns {
> +    gdb_test "step" "/\\* $p \\*/" \
> +       "step to '$p'"
> +}
> +
> +# Now check the backtrace.
> +set line_in_main [gdb_get_line_number "main call aaa"]
> +set line_in_aaa [gdb_get_line_number "aaa return"]
> +set line_in_bbb [gdb_get_line_number "bbb return"]
> +set line_in_ccc [gdb_get_line_number "ccc return"]
> +set line_in_ddd [gdb_get_line_number "ddd return"]
> +set line_in_eee [gdb_get_line_number "eee return"]
> +set line_in_fff [gdb_get_line_number "fff return"]
> +set line_in_ggg [gdb_get_line_number "ggg return"]
> +set line_in_hhh [gdb_get_line_number "hhh return"]
> +set line_in_iii [gdb_get_line_number "iii return"]
> +set line_in_jjj [gdb_get_line_number "jjj return"]
> +set line_in_kkk [gdb_get_line_number "kkk return"]
> +
> +gdb_test "bt" [multi_line \
> +                  "#0  kkk \\(\\) at \[^\r\n\]+${srcfile}:${line_in_kkk}"
> \
> +                  "#1  $hex in jjj \\(\\) at
> \[^\r\n\]+${srcfile}:${line_in_jjj}" \
> +                  "#2  $hex in iii \\(\\) at
> \[^\r\n\]+${srcfile}:${line_in_iii}" \
> +                  "#3  hhh \\(\\) at \[^\r\n\]+${srcfile}:${line_in_hhh}"
> \
> +                  "#4  ggg \\(\\) at \[^\r\n\]+${srcfile}:${line_in_ggg}"
> \
> +                  "#5  fff \\(\\) at \[^\r\n\]+${srcfile}:${line_in_fff}"
> \
> +                  "#6  $hex in eee \\(\\) at
> \[^\r\n\]+${srcfile}:${line_in_eee}" \
> +                  "#7  $hex in ddd \\(\\) at
> \[^\r\n\]+${srcfile}:${line_in_ddd}" \
> +                  "#8  $hex in ccc \\(\\) at
> \[^\r\n\]+${srcfile}:${line_in_ccc}" \
> +                  "#9  bbb \\(\\) at \[^\r\n\]+${srcfile}:${line_in_bbb}"
> \
> +                  "#10 aaa \\(\\) at \[^\r\n\]+${srcfile}:${line_in_aaa}"
> \
> +                  "#11 main \\(\\) at
> \[^\r\n\]+${srcfile}:${line_in_main}" ]
> +
> +# Now check we can use 'up' to inspect each frame correctly.
> +set patterns [list  \
> +                 "jjj return" \
> +                 "iii return" \
> +                 "hhh return" \
> +                 "ggg return" \
> +                 "fff return" \
> +                 "eee return" \
> +                 "ddd return" \
> +                 "ccc return" \
> +                 "bbb return" \
> +                 "aaa return" \
> +                 "main call aaa" ]
> +foreach p $patterns {
> +    gdb_test "up" "/\\* $p \\*/" \
> +       "up to '$p'"
> +}
> --
> 2.14.5
>
>

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

* Re: [PATCH 3/3] gdb: Better frame tracking for inline frames
  2019-12-26  7:25   ` Christian Biesinger via gdb-patches
@ 2019-12-26 23:33     ` Andrew Burgess
  0 siblings, 0 replies; 25+ messages in thread
From: Andrew Burgess @ 2019-12-26 23:33 UTC (permalink / raw)
  To: Christian Biesinger; +Cc: gdb-patches

* Christian Biesinger <cbiesinger@google.com> [2019-12-26 07:24:39 +0100]:

> On Mon, Dec 23, 2019, 02:51 Andrew Burgess <andrew.burgess@embecosm.com>
> wrote:
> 
> > This commit improves GDB's handling of inline functions when there are
> > more than one inline function in a stack, so for example if we have a
> > stack like:
> >
> >    main -> aaa -> bbb -> ccc -> ddd
> >
> > And aaa, bbb, and ccc are all inline within main GDB should (when
> > given sufficient debug information) be able to step from main through
> > aaa, bbb, and ccc.  Unfortunately, this currently doesn't work, here's
> > an example session:
> >
> >   (gdb) start
> >   Temporary breakpoint 1 at 0x4003b0: file test.c, line 38.
> >   Starting program: /project/gdb/tests/inline/test
> >
> >   Temporary breakpoint 1, main () at test.c:38
> >   38      global_var = 0;
> >   (gdb) step
> >   39      return aaa () + 1;
> >   (gdb) step
> >   aaa () at test.c:39
> >   39      return aaa () + 1;
> >   (gdb) step
> >   bbb () at test.c:39
> >   39      return aaa () + 1;
> >   (gdb) step
> >   ccc () at test.c:39
> >   39      return aaa () + 1;
> >   (gdb) step
> >   ddd () at test.c:32
> >   32      return global_var;
> >   (gdb) bt
> >   #0  ddd () at test.c:32
> >   #1  0x00000000004003c1 in ccc () at test.c:39
> >   #2  bbb () at test.c:26
> >   #3  aaa () at test.c:14
> >   #4  main () at test.c:39
> >
> 
> How come only #1 is printing the address?

Well.....

For inline frames the sal returned by find_frame_sal has a symtab and
line set, but doesn't have the pc or end fields set.

If we then look at frame_show_address it seems specifically designed
to return false for precisely the case we're discussing.

I agree with you that it seems odd that we only get the address for #1
in this case.  I considered this patch:

## START ##

diff --git a/gdb/stack.c b/gdb/stack.c
index 22820524871..ce85a1183f0 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -327,7 +327,7 @@ frame_show_address (struct frame_info *frame,
        gdb_assert (inline_skipped_frames (inferior_thread ()) > 0);
       else
        gdb_assert (get_frame_type (get_next_frame (frame)) == INLINE_FRAME);
-      return false;
+      return frame_relative_level (frame) > 0;
     }
 
   return get_frame_pc (frame) != sal.pc;

## END ##

The addresses are printed more now, there's a few test failures that
would need to be checked first.

Ideally I would like to keep changing this behaviour separate from
this patch series, but I do think this might be something we should
consider changing.

Thanks,
Andrew



> 
> 
> > Notice that once we get to line 39 in main, GDB keeps reporting line
> > 39 in main as the location despite understanding that the inferior is
> > stepping through the nested inline functions with each use of step.
> >
> > The problem is that as soon as the inferior stops we call
> > skip_inline_frames (from inline-frame.c) which calculates the
> > inferiors current state in relation to inline functions - it figures
> > out if we're in an inline function, and if we are counts how many
> > inline frames there are at the current location.
> >
> > So, in our example above, when we step from line 38 in main to line 39
> > we stop at a location that is simultaneously in all of main, aaa, bbb,
> > and ccc.  The block structure reflects the order in which the
> > functions would be called, with ccc being the most inner block and
> > main being the most outer block.  When we stop GDB naturally finds the
> > block for ccc, however within skip_inline_frames we spot that bbb,
> > aaa, and main are super-blocks of the current location and that each
> > layer represents an inline function.  The skip_inline_frames then
> > records the depth of inline functions (3 in this case for aaa, bbb,
> > and ccc) and also the symbol of the outermost inline function (in this
> > case 'aaa' as main isn't an inline function, it just has things inline
> > within it).
> >
> > Now GDB understands the stack to be main -> aaa -> bbb -> ccc,
> > however, the state initialised in skip_inline_frames starts off
> > indicating that we should hide 3 frames from the user, so we report
> > that we're in main at line 39.  The location of main, line 39 is
> > derived by asking the inline function state for the last symbol in the
> > stack (aaa in this case), and then asking for it's location - the
> > location of an inlined function symbol is its call site, so main, line
> > 39 in this case.
> >
> > If the user then asks GDB to step we don't actually move the inferior
> > at all, instead we spot that we are in an inline function stack,
> > lookup the inline state data, and reduce the skip depth by 1.  We then
> > report to the user that GDB has stopped.  GDB now understands that we
> > are in 'aaa'.  In order to get the precise location we again ask GDB
> > for the last symbol from the inline data structure, and we are again
> > told 'aaa', we then get the location from 'aaa', and report that we
> > are in main, line 39.
> >
> > Hopefully it's clear what the mistake here is, once we've reduced the
> > inline skip depth we should not be using 'aaa' to compute the precise
> > location, instead we should be using 'bbb'.  That is what this patch
> > does.
> >
> > Now, when we call skip_inline_frames instead of just recording the
> > last skipped symbol we now record all symbols in the inline frame
> > stack.  When we ask GDB for the last skipped symbol we return a symbol
> > based on how many frames we are skipping, not just the last know
> > symbol.
> >
> > With this fix in place, the same session as above now looks much
> > better:
> >
> >   (gdb) start
> >   Temporary breakpoint 1 at 0x4003b0: file test.c, line 38.
> >   Starting program: /project/gdb/tests/inline/test
> >
> >   Temporary breakpoint 1, main () at test.c:38
> >   38      global_var = 0;
> >   (gdb) s
> >   39      return aaa () + 1;
> >   (gdb) s
> >   aaa () at test.c:14
> >   14      return bbb () + 1;
> >   (gdb) s
> >   bbb () at test.c:26
> >   26      return ccc () + 1;
> >   (gdb) s
> >   ccc () at test.c:20
> >   20      return ddd () + 1;
> >   (gdb) s
> >   ddd () at test.c:32
> >   32      return global_var;
> >   (gdb) bt
> >   #0  ddd () at test.c:32
> >   #1  0x00000000004003c1 in ccc () at test.c:20
> >   #2  bbb () at test.c:26
> >   #3  aaa () at test.c:14
> >   #4  main () at test.c:39
> >
> > gdb/ChangeLog:
> >
> >         * frame.c (find_frame_sal): Move call to get_next_frame into more
> >         inner scope.
> >         * inline-frame.c (inilne_state) <inline_state>: Update argument
> >         types.
> >         (inilne_state) <skipped_symbol>: Rename to...
> >         (inilne_state) <skipped_symbols>: ...this, and change to a vector.
> >         (skip_inline_frames): Build vector of skipped symbols and use this
> >         to reate the inline_state.
> >         (inline_skipped_symbol): Add a comment and some assertions, fetch
> >         skipped symbol from the list.
> >
> > gdb/testsuite/ChangeLog:
> >
> >         * gdb.dwarf2/dw2-inline-many-frames.c: New file.
> >         * gdb.dwarf2/dw2-inline-many-frames.exp: New file.
> >
> > Change-Id: I99def5ffb44eb9e58cda4b449bf3d91ab0386c62
> > ---
> >  gdb/ChangeLog                                      |  13 +
> >  gdb/frame.c                                        |   9 +-
> >  gdb/inline-frame.c                                 |  30 +-
> >  gdb/testsuite/ChangeLog                            |   5 +
> >  gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c  | 158 +++++++++
> >  .../gdb.dwarf2/dw2-inline-many-frames.exp          | 379
> > +++++++++++++++++++++
> >  6 files changed, 579 insertions(+), 15 deletions(-)
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
> >
> > diff --git a/gdb/frame.c b/gdb/frame.c
> > index 660f8cfa00e..0d2a9a3d25e 100644
> > --- a/gdb/frame.c
> > +++ b/gdb/frame.c
> > @@ -2508,14 +2508,15 @@ find_frame_sal (frame_info *frame)
> >    int notcurrent;
> >    CORE_ADDR pc;
> >
> > -  /* If the next frame represents an inlined function call, this frame's
> > -     sal is the "call site" of that inlined function, which can not
> > -     be inferred from get_frame_pc.  */
> > -  next_frame = get_next_frame (frame);
> >    if (frame_inlined_callees (frame) > 0)
> >      {
> >        struct symbol *sym;
> >
> > +      /* If the current frame has some inlined callees, and we have a next
> > +        frame, then that frame must be an inlined frame.  In this case
> > +        this frame's sal is the "call site" of the next frame's inlined
> > +        function, which can not be inferred from get_frame_pc.  */
> > +      next_frame = get_next_frame (frame);
> >        if (next_frame)
> >         sym = get_frame_function (next_frame);
> >        else
> > diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c
> > index 5840e3ee319..db8d703adab 100644
> > --- a/gdb/inline-frame.c
> > +++ b/gdb/inline-frame.c
> > @@ -37,9 +37,9 @@
> >  struct inline_state
> >  {
> >    inline_state (thread_info *thread_, int skipped_frames_, CORE_ADDR
> > saved_pc_,
> > -               symbol *skipped_symbol_)
> > +               std::vector<symbol *> &&skipped_symbols_)
> >      : thread (thread_), skipped_frames (skipped_frames_), saved_pc
> > (saved_pc_),
> > -      skipped_symbol (skipped_symbol_)
> > +      skipped_symbols (std::move (skipped_symbols_))
> >    {}
> >
> >    /* The thread this data relates to.  It should be a currently
> > @@ -56,10 +56,10 @@ struct inline_state
> >       any skipped frames.  */
> >    CORE_ADDR saved_pc;
> >
> > -  /* Only valid if SKIPPED_FRAMES is non-zero.  This is the symbol
> > -     of the outermost skipped inline function.  It's used to find the
> > -     call site of the current frame.  */
> > -  struct symbol *skipped_symbol;
> > +  /* Only valid if SKIPPED_FRAMES is non-zero.  This is the list of all
> > +     function symbols that have been skipped, from inner most to outer
> > +     most.  It is used to find the call site of the current frame.  */
> > +  std::vector<struct symbol *> skipped_symbols;
> >  };
> >
> >  static std::vector<inline_state> inline_states;
> > @@ -324,7 +324,7 @@ void
> >  skip_inline_frames (thread_info *thread, bpstat stop_chain)
> >  {
> >    const struct block *frame_block, *cur_block;
> > -  struct symbol *last_sym = NULL;
> > +  std::vector<struct symbol *> skipped_syms;
> >    int skip_count = 0;
> >
> >    /* This function is called right after reinitializing the frame
> > @@ -352,7 +352,7 @@ skip_inline_frames (thread_info *thread, bpstat
> > stop_chain)
> >                     break;
> >
> >                   skip_count++;
> > -                 last_sym = BLOCK_FUNCTION (cur_block);
> > +                 skipped_syms.push_back (BLOCK_FUNCTION (cur_block));
> >                 }
> >               else
> >                 break;
> > @@ -365,7 +365,8 @@ skip_inline_frames (thread_info *thread, bpstat
> > stop_chain)
> >      }
> >
> >    gdb_assert (find_inline_frame_state (thread) == NULL);
> > -  inline_states.emplace_back (thread, skip_count, this_pc, last_sym);
> > +  inline_states.emplace_back (thread, skip_count, this_pc,
> > +                             std::move (skipped_syms));
> >
> >    if (skip_count != 0)
> >      reinit_frame_cache ();
> > @@ -404,9 +405,16 @@ struct symbol *
> >  inline_skipped_symbol (thread_info *thread)
> >  {
> >    inline_state *state = find_inline_frame_state (thread);
> > -
> >    gdb_assert (state != NULL);
> > -  return state->skipped_symbol;
> > +
> > +  /* This should only be called when we are skipping at least one frame,
> > +     hence SKIPPED_FRAMES will be greater than zero when we get here.
> > +     We initialise SKIPPED_FRAMES at the same time as we build
> > +     SKIPPED_SYMBOLS, hence it should be true that SKIPPED_FRAMES never
> > +     indexes outside of the SKIPPED_SYMBOLS vector.  */
> > +  gdb_assert (state->skipped_frames > 0);
> > +  gdb_assert (state->skipped_frames <= state->skipped_symbols.size ());
> > +  return state->skipped_symbols[state->skipped_frames - 1];
> >  }
> >
> >  /* Return the number of functions inlined into THIS_FRAME.  Some of
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
> > b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
> > new file mode 100644
> > index 00000000000..16493e1c360
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
> > @@ -0,0 +1,158 @@
> > +/* Copyright 2019 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 sets up a call stack that looks like this:
> > +
> > +   #11     #10    #9     #8     #7     #6     #5     #4     #3     #2
> >  #1     #0
> > +   main -> aaa -> bbb -> ccc -> ddd -> eee -> fff -> ggg -> hhh -> iii ->
> > jjj -> kkk
> > +   \_______________________/    \________/    \______________________/
> > \________/
> > +      Inline sequence #1          Normal          Inline sequence #2
> >   Normal
> > +
> > +   We use the 'start' command to move into main, after that we 'step'
> > +   through each function until we are in kkk.  We then use the 'up'
> > command
> > +   to look back at each from to main.
> > +
> > +   The test checks that we can handle and step through sequences of more
> > +   than one inline frame (so 'main .... ccc', and 'fff .... iii'), and
> > also
> > +   that we can move around in a stack that contains more than one disjoint
> > +   sequence of inline frames.
> > +
> > +   The order of the functions in this file is deliberately mixed up so
> > that
> > +   the line numbers are not "all ascending" or "all descending" in the
> > line
> > +   table.  */
> > +
> > +#define INLINE_FUNCTION __attribute__ ((always_inline)) static inline
> > +#define NON_INLINE_FUNCTION __attribute__ ((noinline))
> > +
> > +volatile int global_var = 0;
> > +
> > +INLINE_FUNCTION int aaa ();
> > +INLINE_FUNCTION int bbb ();
> > +INLINE_FUNCTION int ccc ();
> > +
> > +NON_INLINE_FUNCTION int ddd ();
> > +NON_INLINE_FUNCTION int eee ();
> > +NON_INLINE_FUNCTION int fff ();
> > +
> > +INLINE_FUNCTION int ggg ();
> > +INLINE_FUNCTION int hhh ();
> > +INLINE_FUNCTION int iii ();
> > +
> > +NON_INLINE_FUNCTION int jjj ();
> > +NON_INLINE_FUNCTION int kkk ();
> > +
> > +INLINE_FUNCTION int
> > +aaa ()
> > +{                                              /* aaa prologue */
> > +  asm ("aaa_label: .globl aaa_label");
> > +  return bbb () + 1;                           /* aaa return */
> > +}                                              /* aaa end */
> > +
> > +NON_INLINE_FUNCTION int
> > +jjj ()
> > +{                                              /* jjj prologue */
> > +  int ans;
> > +  asm ("jjj_label: .globl jjj_label");
> > +  ans = kkk () + 1;                            /* jjj return */
> > +  asm ("jjj_label2: .globl jjj_label2");
> > +  return ans;
> > +}                                              /* jjj end */
> > +
> > +INLINE_FUNCTION int
> > +ggg ()
> > +{                                              /* ggg prologue */
> > +  asm ("ggg_label: .globl ggg_label");
> > +  return hhh () + 1;                           /* ggg return */
> > +}                                              /* ggg end */
> > +
> > +INLINE_FUNCTION int
> > +ccc ()
> > +{                                              /* ccc prologue */
> > +  asm ("ccc_label: .globl ccc_label");
> > +  return ddd () + 1;                           /* ccc return */
> > +}                                              /* ccc end */
> > +
> > +NON_INLINE_FUNCTION int
> > +fff ()
> > +{                                              /* fff prologue */
> > +  int ans;
> > +  asm ("fff_label: .globl fff_label");
> > +  ans = ggg () + 1;                            /* fff return */
> > +  asm ("fff_label2: .globl fff_label2");
> > +  return ans;
> > +}                                              /* fff end */
> > +
> > +NON_INLINE_FUNCTION int
> > +kkk ()
> > +{                                              /* kkk prologue */
> > +  asm ("kkk_label: .globl kkk_label");
> > +  return global_var;                           /* kkk return */
> > +}                                              /* kkk end */
> > +
> > +INLINE_FUNCTION int
> > +bbb ()
> > +{                                              /* bbb prologue */
> > +  asm ("bbb_label: .globl bbb_label");
> > +  return ccc () + 1;                           /* bbb return */
> > +}                                              /* bbb end */
> > +
> > +INLINE_FUNCTION int
> > +hhh ()
> > +{                                              /* hhh prologue */
> > +  asm ("hh_label: .globl hhh_label");
> > +  return iii () + 1;                           /* hhh return */
> > +}                                              /* hhh end */
> > +
> > +int
> > +main ()
> > +{                                              /* main prologue */
> > +  int ans;
> > +  asm ("main_label: .globl main_label");
> > +  global_var = 0;                              /* main set global_var */
> > +  asm ("main_label2: .globl main_label2");
> > +  ans = aaa () + 1;                            /* main call aaa */
> > +  asm ("main_label3: .globl main_label3");
> > +  return ans;
> > +}                                              /* main end */
> > +
> > +NON_INLINE_FUNCTION int
> > +ddd ()
> > +{                                              /* ddd prologue */
> > +  int ans;
> > +  asm ("ddd_label: .globl ddd_label");
> > +  ans =  eee () + 1;                           /* ddd return */
> > +  asm ("ddd_label2: .globl ddd_label2");
> > +  return ans;
> > +}                                              /* ddd end */
> > +
> > +INLINE_FUNCTION int
> > +iii ()
> > +{                                              /* iii prologue */
> > +  int ans;
> > +  asm ("iii_label: .globl iii_label");
> > +  ans = jjj () + 1;                            /* iii return */
> > +  asm ("iii_label2: .globl iii_label2");
> > +  return ans;
> > +}                                              /* iii end */
> > +
> > +NON_INLINE_FUNCTION int
> > +eee ()
> > +{                                              /* eee prologue */
> > +  int ans;
> > +  asm ("eee_label: .globl eee_label");
> > +  ans = fff () + 1;                            /* eee return */
> > +  asm ("eee_label2: .globl eee_label2");
> > +  return ans;
> > +}                                              /* eee end */
> > diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
> > b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
> > new file mode 100644
> > index 00000000000..bc93b3b1cf8
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
> > @@ -0,0 +1,379 @@
> > +# Copyright 2019 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-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
> > +    declare_labels ranges_label lines_label
> > +    declare_labels aaa_label bbb_label ccc_label
> > +    declare_labels ggg_label hhh_label iii_label
> > +
> > +    get_func_info main
> > +    get_func_info ddd
> > +    get_func_info eee
> > +    get_func_info fff
> > +    get_func_info jjj
> > +    get_func_info kkk
> > +
> > +    set call_in_main [gdb_get_line_number "main call aaa"]
> > +    set call_in_aaa [gdb_get_line_number "aaa return"]
> > +    set call_in_bbb [gdb_get_line_number "bbb return"]
> > +    set call_in_ccc [gdb_get_line_number "ccc return"]
> > +    set call_in_fff [gdb_get_line_number "fff return"]
> > +    set call_in_ggg [gdb_get_line_number "ggg return"]
> > +    set call_in_hhh [gdb_get_line_number "hhh return"]
> > +    set call_in_iii [gdb_get_line_number "iii return"]
> > +
> > +    cu {} {
> > +       compile_unit {
> > +           {language @DW_LANG_C}
> > +           {name dw2-inline-stepping.c}
> > +           {low_pc 0 addr}
> > +           {stmt_list ${lines_label} DW_FORM_sec_offset}
> > +           {ranges ${ranges_label} DW_FORM_sec_offset}
> > +       } {
> > +           subprogram {
> > +               {external 1 flag}
> > +               {name ddd}
> > +               {low_pc $ddd_start addr}
> > +               {high_pc "$ddd_start + $ddd_len" addr}
> > +           }
> > +           subprogram {
> > +               {external 1 flag}
> > +               {name eee}
> > +               {low_pc $eee_start addr}
> > +               {high_pc "$eee_start + $eee_len" addr}
> > +           }
> > +           subprogram {
> > +               {external 1 flag}
> > +               {name jjj}
> > +               {low_pc $jjj_start addr}
> > +               {high_pc "$jjj_start + $jjj_len" addr}
> > +           }
> > +           subprogram {
> > +               {external 1 flag}
> > +               {name kkk}
> > +               {low_pc $kkk_start addr}
> > +               {high_pc "$kkk_start + $kkk_len" addr}
> > +           }
> > +           aaa_label: subprogram {
> > +               {name aaa}
> > +               {inline 3 data1}
> > +           }
> > +           bbb_label: subprogram {
> > +               {name bbb}
> > +               {inline 3 data1}
> > +           }
> > +           ccc_label: subprogram {
> > +               {name ccc}
> > +               {inline 3 data1}
> > +           }
> > +           ggg_label: subprogram {
> > +               {name ggg}
> > +               {inline 3 data1}
> > +           }
> > +           hhh_label: subprogram {
> > +               {name hhh}
> > +               {inline 3 data1}
> > +           }
> > +           iii_label: subprogram {
> > +               {name iii}
> > +               {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 %$aaa_label}
> > +                   {low_pc main_label2 addr}
> > +                   {high_pc main_label3 addr}
> > +                   {call_file 1 data1}
> > +                   {call_line $call_in_main data1}
> > +               } {
> > +                   inlined_subroutine {
> > +                       {abstract_origin %$bbb_label}
> > +                       {low_pc main_label2 addr}
> > +                       {high_pc main_label3 addr}
> > +                       {call_file 1 data1}
> > +                       {call_line $call_in_aaa data1}
> > +                   }  {
> > +                       inlined_subroutine {
> > +                           {abstract_origin %$ccc_label}
> > +                           {low_pc main_label2 addr}
> > +                           {high_pc main_label3 addr}
> > +                           {call_file 1 data1}
> > +                           {call_line $call_in_bbb data1}
> > +                       }
> > +                   }
> > +               }
> > +           }
> > +           subprogram {
> > +               {external 1 flag}
> > +               {name fff}
> > +               {low_pc $fff_start addr}
> > +               {high_pc "$fff_start + $fff_len" addr}
> > +           }  {
> > +               inlined_subroutine {
> > +                   {abstract_origin %$ggg_label}
> > +                   {low_pc fff_label addr}
> > +                   {high_pc main_label2 addr}
> > +                   {call_file 1 data1}
> > +                   {call_line $call_in_fff data1}
> > +               } {
> > +                   inlined_subroutine {
> > +                       {abstract_origin %$hhh_label}
> > +                       {low_pc fff_label addr}
> > +                       {high_pc fff_label2 addr}
> > +                       {call_file 1 data1}
> > +                       {call_line $call_in_ggg data1}
> > +                   }  {
> > +                       inlined_subroutine {
> > +                           {abstract_origin %$iii_label}
> > +                           {low_pc fff_label addr}
> > +                           {high_pc fff_label2 addr}
> > +                           {call_file 1 data1}
> > +                           {call_line $call_in_hhh data1}
> > +                       }
> > +                   }
> > +               }
> > +           }
> > +       }
> > +    }
> > +
> > +    lines {version 2} lines_label {
> > +       include_dir "${srcdir}/${subdir}"
> > +       file_name "$srcfile" 1
> > +
> > +       program {
> > +           {DW_LNE_set_address $main_start}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "main
> > prologue"] - 1]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address main_label}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "main set
> > global_var"] - [gdb_get_line_number "main prologue"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address main_label2}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "main call
> > aaa"] - [gdb_get_line_number "main set global_var"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address main_label2}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "aaa return"]
> > - [gdb_get_line_number "main call aaa"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address main_label2}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "bbb return"]
> > - [gdb_get_line_number "aaa return"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address main_label2}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "ccc return"]
> > - [gdb_get_line_number "bbb return"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address main_label3}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "main end"] -
> > [gdb_get_line_number "ccc return"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address $main_end}
> > +           {DW_LNE_end_sequence}
> > +
> > +           {DW_LNE_set_address $ddd_start}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "ddd
> > prologue"] - 1]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address ddd_label}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "ddd return"]
> > - [gdb_get_line_number "ddd prologue"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address ddd_label2}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "ddd end"] -
> > [gdb_get_line_number "ddd return"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address $ddd_end}
> > +           {DW_LNE_end_sequence}
> > +
> > +           {DW_LNE_set_address $eee_start}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "eee
> > prologue"] - 1]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address eee_label}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "eee return"]
> > - [gdb_get_line_number "eee prologue"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address eee_label2}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "eee end"] -
> > [gdb_get_line_number "eee return"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address $eee_end}
> > +           {DW_LNE_end_sequence}
> > +
> > +           {DW_LNE_set_address $fff_start}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "fff
> > prologue"] - 1]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address fff_label}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "fff return"]
> > - [gdb_get_line_number "fff prologue"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address fff_label}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "ggg return"]
> > - [gdb_get_line_number "fff return"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address fff_label}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "hhh return"]
> > - [gdb_get_line_number "ggg return"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address fff_label}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "iii return"]
> > - [gdb_get_line_number "hhh return"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address fff_label2}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "fff end"] -
> > [gdb_get_line_number "fff return"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address $fff_end}
> > +           {DW_LNE_end_sequence}
> > +
> > +           {DW_LNE_set_address $jjj_start}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "jjj
> > prologue"] - 1]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address jjj_label}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "jjj return"]
> > - [gdb_get_line_number "jjj prologue"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address jjj_label2}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "jjj end"] -
> > [gdb_get_line_number "jjj return"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address $jjj_end}
> > +           {DW_LNE_end_sequence}
> > +
> > +           {DW_LNE_set_address $kkk_start}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "kkk
> > prologue"] - 1]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address kkk_label}
> > +           {DW_LNS_advance_line [expr [gdb_get_line_number "kkk return"]
> > - [gdb_get_line_number "kkk prologue"]]}
> > +           {DW_LNS_copy}
> > +           {DW_LNE_set_address $kkk_end}
> > +           {DW_LNE_end_sequence}
> > +       }
> > +    }
> > +
> > +    ranges {is_64 [is_64_target]} {
> > +       ranges_label: sequence {
> > +           {range {${main_start}} ${main_end}}
> > +           {range {${ddd_start}} ${ddd_end}}
> > +           {range {${eee_start}} ${eee_end}}
> > +           {range {${fff_start}} ${fff_end}}
> > +           {range {${jjj_start}} ${jjj_end}}
> > +           {range {${kkk_start}} ${kkk_end}}
> > +       }
> > +    }
> > +}
> > +
> > +if { [prepare_for_testing "failed to prepare" ${testfile} \
> > +         [list $srcfile $asm_file] {nodebug}] } {
> > +    return -1
> > +}
> > +
> > +if ![runto_main] {
> > +    return -1
> > +}
> > +
> > +# First we step through all of the functions until we get the 'kkk'.
> > +set patterns [list "main call aaa" \
> > +                 "aaa return" \
> > +                 "bbb return" \
> > +                 "ccc return" \
> > +                 "ddd return" \
> > +                 "eee return" \
> > +                 "fff return" \
> > +                 "ggg return" \
> > +                 "hhh return" \
> > +                 "iii return" \
> > +                 "jjj return" \
> > +                 "kkk return" ]
> > +foreach p $patterns {
> > +    gdb_test "step" "/\\* $p \\*/" \
> > +       "step to '$p'"
> > +}
> > +
> > +# Now check the backtrace.
> > +set line_in_main [gdb_get_line_number "main call aaa"]
> > +set line_in_aaa [gdb_get_line_number "aaa return"]
> > +set line_in_bbb [gdb_get_line_number "bbb return"]
> > +set line_in_ccc [gdb_get_line_number "ccc return"]
> > +set line_in_ddd [gdb_get_line_number "ddd return"]
> > +set line_in_eee [gdb_get_line_number "eee return"]
> > +set line_in_fff [gdb_get_line_number "fff return"]
> > +set line_in_ggg [gdb_get_line_number "ggg return"]
> > +set line_in_hhh [gdb_get_line_number "hhh return"]
> > +set line_in_iii [gdb_get_line_number "iii return"]
> > +set line_in_jjj [gdb_get_line_number "jjj return"]
> > +set line_in_kkk [gdb_get_line_number "kkk return"]
> > +
> > +gdb_test "bt" [multi_line \
> > +                  "#0  kkk \\(\\) at \[^\r\n\]+${srcfile}:${line_in_kkk}"
> > \
> > +                  "#1  $hex in jjj \\(\\) at
> > \[^\r\n\]+${srcfile}:${line_in_jjj}" \
> > +                  "#2  $hex in iii \\(\\) at
> > \[^\r\n\]+${srcfile}:${line_in_iii}" \
> > +                  "#3  hhh \\(\\) at \[^\r\n\]+${srcfile}:${line_in_hhh}"
> > \
> > +                  "#4  ggg \\(\\) at \[^\r\n\]+${srcfile}:${line_in_ggg}"
> > \
> > +                  "#5  fff \\(\\) at \[^\r\n\]+${srcfile}:${line_in_fff}"
> > \
> > +                  "#6  $hex in eee \\(\\) at
> > \[^\r\n\]+${srcfile}:${line_in_eee}" \
> > +                  "#7  $hex in ddd \\(\\) at
> > \[^\r\n\]+${srcfile}:${line_in_ddd}" \
> > +                  "#8  $hex in ccc \\(\\) at
> > \[^\r\n\]+${srcfile}:${line_in_ccc}" \
> > +                  "#9  bbb \\(\\) at \[^\r\n\]+${srcfile}:${line_in_bbb}"
> > \
> > +                  "#10 aaa \\(\\) at \[^\r\n\]+${srcfile}:${line_in_aaa}"
> > \
> > +                  "#11 main \\(\\) at
> > \[^\r\n\]+${srcfile}:${line_in_main}" ]
> > +
> > +# Now check we can use 'up' to inspect each frame correctly.
> > +set patterns [list  \
> > +                 "jjj return" \
> > +                 "iii return" \
> > +                 "hhh return" \
> > +                 "ggg return" \
> > +                 "fff return" \
> > +                 "eee return" \
> > +                 "ddd return" \
> > +                 "ccc return" \
> > +                 "bbb return" \
> > +                 "aaa return" \
> > +                 "main call aaa" ]
> > +foreach p $patterns {
> > +    gdb_test "up" "/\\* $p \\*/" \
> > +       "up to '$p'"
> > +}
> > --
> > 2.14.5
> >
> >

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

* Re: [PATCH 0/3] Improve inline frame debug experience
  2019-12-23  1:51 [PATCH 0/3] Improve inline frame debug experience Andrew Burgess
                   ` (2 preceding siblings ...)
  2019-12-23  1:51 ` [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting Andrew Burgess
@ 2020-01-06 22:14 ` Andrew Burgess
  2020-01-17 17:56   ` Andrew Burgess
  3 siblings, 1 reply; 25+ messages in thread
From: Andrew Burgess @ 2020-01-06 22:14 UTC (permalink / raw)
  To: gdb-patches

Ping!

Thanks,
Andrew


* Andrew Burgess <andrew.burgess@embecosm.com> [2019-12-23 01:51:04 +0000]:

> This series replaces 3 patches from gerrit:
> 
>   https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/525
>   https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/526
>   https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/616
> 
> I've done nothing but rebase and retest.
> 
> All feedback welcome.
> 
> Thanks,
> Andrew
> 
> ---
> 
> Andrew Burgess (3):
>   gdb: Include end of sequence markers in the line table
>   gdb: Don't reorder line table entries too much when sorting.
>   gdb: Better frame tracking for inline frames
> 
>  gdb/ChangeLog                                      |  31 ++
>  gdb/buildsym.c                                     |  42 +--
>  gdb/dwarf2read.c                                   |   8 +-
>  gdb/frame.c                                        |   9 +-
>  gdb/inline-frame.c                                 |  30 +-
>  gdb/symmisc.c                                      |   8 +-
>  gdb/symtab.c                                       |   7 +-
>  gdb/testsuite/ChangeLog                            |  15 +
>  gdb/testsuite/gdb.base/maint.exp                   |  20 +-
>  gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c  | 158 +++++++++
>  .../gdb.dwarf2/dw2-inline-many-frames.exp          | 379 +++++++++++++++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.c     |  45 +++
>  gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.exp   | 147 ++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp       |  22 ++
>  14 files changed, 870 insertions(+), 51 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.c
>  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.exp
> 
> -- 
> 2.14.5
> 

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

* Re: [PATCH 0/3] Improve inline frame debug experience
  2020-01-06 22:14 ` [PATCH 0/3] Improve inline frame debug experience Andrew Burgess
@ 2020-01-17 17:56   ` Andrew Burgess
  2020-01-24 18:12     ` Tom Tromey
  0 siblings, 1 reply; 25+ messages in thread
From: Andrew Burgess @ 2020-01-17 17:56 UTC (permalink / raw)
  To: gdb-patches

I'd like to get this series merged as I would like to get back to
reviewing this series:

  https://sourceware.org/ml/gdb-patches/2019-11/msg00792.html

I plan to update, retest, and merge this in the next few days unless
anyone objects.

Thanks,
Andrew


* Andrew Burgess <andrew.burgess@embecosm.com> [2020-01-06 22:14:27 +0000]:

> Ping!
> 
> Thanks,
> Andrew
> 
> 
> * Andrew Burgess <andrew.burgess@embecosm.com> [2019-12-23 01:51:04 +0000]:
> 
> > This series replaces 3 patches from gerrit:
> > 
> >   https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/525
> >   https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/526
> >   https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/616
> > 
> > I've done nothing but rebase and retest.
> > 
> > All feedback welcome.
> > 
> > Thanks,
> > Andrew
> > 
> > ---
> > 
> > Andrew Burgess (3):
> >   gdb: Include end of sequence markers in the line table
> >   gdb: Don't reorder line table entries too much when sorting.
> >   gdb: Better frame tracking for inline frames
> > 
> >  gdb/ChangeLog                                      |  31 ++
> >  gdb/buildsym.c                                     |  42 +--
> >  gdb/dwarf2read.c                                   |   8 +-
> >  gdb/frame.c                                        |   9 +-
> >  gdb/inline-frame.c                                 |  30 +-
> >  gdb/symmisc.c                                      |   8 +-
> >  gdb/symtab.c                                       |   7 +-
> >  gdb/testsuite/ChangeLog                            |  15 +
> >  gdb/testsuite/gdb.base/maint.exp                   |  20 +-
> >  gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c  | 158 +++++++++
> >  .../gdb.dwarf2/dw2-inline-many-frames.exp          | 379 +++++++++++++++++++++
> >  gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.c     |  45 +++
> >  gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.exp   | 147 ++++++++
> >  gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp       |  22 ++
> >  14 files changed, 870 insertions(+), 51 deletions(-)
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.c
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-many-frames.exp
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.c
> >  create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-inline-stepping.exp
> > 
> > -- 
> > 2.14.5
> > 

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

* Re: [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting.
  2019-12-23  1:51 ` [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting Andrew Burgess
@ 2020-01-24 17:40   ` Tom Tromey
  2020-06-05  6:10     ` Tom de Vries
  2020-06-05 14:49   ` Tom de Vries
  1 sibling, 1 reply; 25+ messages in thread
From: Tom Tromey @ 2020-01-24 17:40 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

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

Andrew> Don't reorder line table entries for the same address when sorting the
Andrew> line table, maintain the compiler given line order.  Usually this will
Andrew> reflect the order in which lines are conceptually encountered at a
Andrew> given address.

Thanks for the long explanation and the patch.

I had a couple minor nits.

Andrew> -	  /* Like the pending blocks, the line table may be
Andrew> -	     scrambled in reordered executables.  Sort it if
Andrew> -	     OBJF_REORDERED is true.  */
Andrew> +	  const auto lte_is_less_than
Andrew> +	    = [] (const linetable_entry &ln1,
Andrew> +		  const linetable_entry &ln2)->bool

I'd put spaces around the "->".


Andrew> +	      {
Andrew> +		/* Note: this code does not assume that CORE_ADDRs can fit
Andrew> +		   in ints.  Please keep it that way.  */
Andrew> +		return (ln1.pc < ln2.pc);

I don't think this comment adds anything any more.  IMO it can just be
dropped.

Tom

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

* Re: [PATCH 0/3] Improve inline frame debug experience
  2020-01-17 17:56   ` Andrew Burgess
@ 2020-01-24 18:12     ` Tom Tromey
  2020-01-25  5:08       ` Andrew Burgess
  0 siblings, 1 reply; 25+ messages in thread
From: Tom Tromey @ 2020-01-24 18:12 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

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

Andrew> I'd like to get this series merged as I would like to get back to
Andrew> reviewing this series:

Andrew>   https://sourceware.org/ml/gdb-patches/2019-11/msg00792.html

Andrew> I plan to update, retest, and merge this in the next few days unless
Andrew> anyone objects.

I found a couple of nits, but otherwise it seems ok to me; though I
don't know this area well, which is why I didn't comment until now.

In a sub-thread, Christian pointed out an oddity which you said would be
good to handle in another patch.  If that's not something you're
planning to do, would you mind filing a bug that refers to the
discussion?

thanks,
Tom

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

* Re: [PATCH 0/3] Improve inline frame debug experience
  2020-01-24 18:12     ` Tom Tromey
@ 2020-01-25  5:08       ` Andrew Burgess
  0 siblings, 0 replies; 25+ messages in thread
From: Andrew Burgess @ 2020-01-25  5:08 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

* Tom Tromey <tom@tromey.com> [2020-01-24 10:40:41 -0700]:

> >>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
> 
> Andrew> I'd like to get this series merged as I would like to get back to
> Andrew> reviewing this series:
> 
> Andrew>   https://sourceware.org/ml/gdb-patches/2019-11/msg00792.html
> 
> Andrew> I plan to update, retest, and merge this in the next few days unless
> Andrew> anyone objects.
> 
> I found a couple of nits, but otherwise it seems ok to me; though I
> don't know this area well, which is why I didn't comment until now.
> 
> In a sub-thread, Christian pointed out an oddity which you said would be
> good to handle in another patch.  If that's not something you're
> planning to do, would you mind filing a bug that refers to the
> discussion?

Thanks for taking a look at the patches.  I've pushed this series now,
and I'll take a look to see if I can resolve the issue Christian
identified.  If I can't then I'll file a bug report.

Thanks,
Andrew

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

* Re: [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting.
  2020-01-24 17:40   ` Tom Tromey
@ 2020-06-05  6:10     ` Tom de Vries
  0 siblings, 0 replies; 25+ messages in thread
From: Tom de Vries @ 2020-06-05  6:10 UTC (permalink / raw)
  To: Tom Tromey, Andrew Burgess; +Cc: gdb-patches

On 24-01-2020 18:35, Tom Tromey wrote:
>>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
> 
> Andrew> Don't reorder line table entries for the same address when sorting the
> Andrew> line table, maintain the compiler given line order.  Usually this will
> Andrew> reflect the order in which lines are conceptually encountered at a
> Andrew> given address.
> 
> Thanks for the long explanation and the patch.
> 
> I had a couple minor nits.
> 
> Andrew> -	  /* Like the pending blocks, the line table may be
> Andrew> -	     scrambled in reordered executables.  Sort it if
> Andrew> -	     OBJF_REORDERED is true.  */
> Andrew> +	  const auto lte_is_less_than
> Andrew> +	    = [] (const linetable_entry &ln1,
> Andrew> +		  const linetable_entry &ln2)->bool
> 
> I'd put spaces around the "->".
> 
> 
> Andrew> +	      {
> Andrew> +		/* Note: this code does not assume that CORE_ADDRs can fit
> Andrew> +		   in ints.  Please keep it that way.  */
> Andrew> +		return (ln1.pc < ln2.pc);
> 
> I don't think this comment adds anything any more.  IMO it can just be
> dropped.

This commit caused the following regressions with target board readnow:
...
+FAIL: gdb.ada/bp_c_mixed_case.exp: break <NoDebugMixedCaseFunc>
+FAIL: gdb.arch/amd64-invalid-stack-top.exp: first backtrace, with error
message
+FAIL: gdb.arch/amd64-invalid-stack-top.exp: second backtrace, with
error message
+FAIL: gdb.arch/amd64-invalid-stack-top.exp: check mi -stack-list-frames
command, first time
+FAIL: gdb.arch/amd64-invalid-stack-top.exp: check mi -stack-list-frames
command, second time
+FAIL: gdb.arch/i386-bp_permanent.exp: single-step past permanent breakpoint
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0:
final_debug=0: step
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0:
final_debug=0: set-break: after resolving: break final
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0:
final_debug=0: set-break: after resolving: break gnu_ifunc
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=0:
final_debug=0: set-break: after resolving: info breakpoints
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1:
final_debug=0: step
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1:
final_debug=0: set-break: after resolving: break final
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1:
final_debug=0: set-break: after resolving: break gnu_ifunc
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1:
final_debug=0: set-break: after resolving: info breakpoints
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=1: resolver_debug=0:
final_debug=0: step
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=1: resolver_debug=0:
final_debug=0: set-break: after resolving: break final
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=1: resolver_debug=0:
final_debug=0: set-break: after resolving: break gnu_ifunc
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=1: resolver_debug=0:
final_debug=0: set-break: after resolving: info breakpoints
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=1: resolver_debug=1:
final_debug=0: step
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=1: resolver_debug=1:
final_debug=0: set-break: after resolving: break final
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=1: resolver_debug=1:
final_debug=0: set-break: after resolving: break gnu_ifunc
+FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=1: resolver_debug=1:
final_debug=0: set-break: after resolving: info breakpoints
+FAIL: gdb.base/solib-weak.exp: run to breakpoint - lib1 nodebug, lib2
nodebug, lib1 first
+FAIL: gdb.base/solib-weak.exp: run to breakpoint - lib1 nodebug, lib2
nodebug, lib2 first
+FAIL: gdb.base/solib-weak.exp: run to breakpoint - lib1 nodebug, lib2
debug, lib1 first
+FAIL: gdb.base/solib-weak.exp: run to breakpoint - lib1 debug, lib2
nodebug, lib2 first
+FAIL: gdb.base/step-symless.exp: step
+FAIL: gdb.base/until-nodebug.exp: until 1
+FAIL: gdb.base/until-nodebug.exp: until 2 (the program exited)
+FAIL: gdb.btrace/unknown_functions.exp: flat
+FAIL: gdb.btrace/unknown_functions.exp: indented
+FAIL: gdb.cp/minsym-fallback.exp: break C::f()
+FAIL: gdb.cp/minsym-fallback.exp: break C::operator()()
+FAIL: gdb.reverse/singlejmp-reverse.exp: reverse-step
+FAIL: gdb.reverse/singlejmp-reverse.exp: reverse-next
...

As well as the following progressions:
...
+PASS: gdb.gdb/complaints.exp: breakpoint in captured_command_loop
+PASS: gdb.gdb/python-interrupts.exp: breakpoint in captured_command_loop
+PASS: gdb.gdb/python-selftest.exp: breakpoint in captured_command_loop
+PASS: gdb.gdb/selftest.exp: breakpoint in captured_main
...

The first regression is noted in PR25858 - "[readnow] FAIL:
gdb.ada/bp_c_mixed_case.exp: break <NoDebugMixedCaseFunc>" (
https://sourceware.org/bugzilla/show_bug.cgi?id=25858 ).

Thanks,
- Tom

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

* Re: [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting.
  2019-12-23  1:51 ` [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting Andrew Burgess
  2020-01-24 17:40   ` Tom Tromey
@ 2020-06-05 14:49   ` Tom de Vries
  2020-06-05 16:00     ` Tom de Vries
  1 sibling, 1 reply; 25+ messages in thread
From: Tom de Vries @ 2020-06-05 14:49 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 23-12-2019 02:51, Andrew Burgess wrote:
> I had to make a small adjustment in find_pc_sect_line in order to
> correctly find the previous line in the line table.  In some line
> tables I was seeing an actual line entry and an end of sequence marker
> at the same address, before this commit these would reorder to move
> the end of sequence marker before the line entry (end of sequence has
> line number 0).  Now the end of sequence marker remains in its correct
> location, and in order to find a previous line we should step backward
> over any end of sequence markers.
> 
> As an example, the binary:
>   gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold
> 
> Has this line table before the patch:
> 
>   INDEX    LINE ADDRESS
>   0          48 0x0000000000400487
>   1         END 0x000000000040048e
>   2          52 0x000000000040048e
>   3          54 0x0000000000400492
>   4          56 0x0000000000400497
>   5         END 0x000000000040049a
>   6          62 0x000000000040049a
>   7         END 0x00000000004004a1
>   8          66 0x00000000004004a1
>   9          68 0x00000000004004a5
>   10         70 0x00000000004004aa
>   11         72 0x00000000004004b9
>   12        END 0x00000000004004bc
>   13         76 0x00000000004004bc
>   14         78 0x00000000004004c0
>   15         80 0x00000000004004c5
>   16        END 0x00000000004004cc
> 
> And after this patch:
> 
>   INDEX    LINE ADDRESS
>   0          48 0x0000000000400487
>   1          52 0x000000000040048e
>   2         END 0x000000000040048e
>   3          54 0x0000000000400492
>   4          56 0x0000000000400497
>   5         END 0x000000000040049a
>   6          62 0x000000000040049a
>   7          66 0x00000000004004a1
>   8         END 0x00000000004004a1
>   9          68 0x00000000004004a5
>   10         70 0x00000000004004aa
>   11         72 0x00000000004004b9
>   12        END 0x00000000004004bc
>   13         76 0x00000000004004bc
>   14         78 0x00000000004004c0
>   15         80 0x00000000004004c5
>   16        END 0x00000000004004cc
> 
> When calling find_pc_sect_line with the address 0x000000000040048e, in
> both cases we find entry #3, we then try to find the previous entry,
> which originally found this entry '2         52 0x000000000040048e',
> after the patch it finds '2         END 0x000000000040048e', which
> cases the lookup to fail.
> 
> By skipping the END marker after this patch we get back to the correct
> entry, which is now #1: '1          52 0x000000000040048e', and
> everything works again.

I start to suspect that you have been working around an incorrect line
table.

Consider this bit:
...
   0          48 0x0000000000400487
   1          52 0x000000000040048e
   2         END 0x000000000040048e
...

The end marker marks the address one past the end of the sequence.
Therefore, it makes no sense to have an entry in the sequence with the
same address as the end marker.

[ dwarf doc:

end_sequence:

A boolean indicating that the current address is that of the first byte
after the end of a sequence of target machine instructions. end_sequence
terminates a sequence of lines; therefore other information in the same
row is not meaningful.

DW_LNE_end_sequence:

The DW_LNE_end_sequence opcode takes no operands. It sets the
end_sequence register of the state machine to “true” and appends a row
to the matrix using the current values of the state-machine registers.
Then it resets the registers to the initial values specified above (see
Section 6.2.2). Every line number program sequence must end with a
DW_LNE_end_sequence instruction which creates a row whose address is
that of the byte after the last target machine instruction of the sequence.

]

The incorrect entry is generated by this dwarf assembler sequence:
...
                {DW_LNS_copy}
                {DW_LNE_end_sequence}
...

I think we should probably fix the dwarf assembly test-cases.

If we want to handle this in gdb, the thing that seems most logical to
me is to ignore this kind of entries.

Note btw that the opposite does make sense: and end marker can have the
same address as a following start-of-sequence entry.

Thanks,
- Tom

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

* Re: [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting.
  2020-06-05 14:49   ` Tom de Vries
@ 2020-06-05 16:00     ` Tom de Vries
  2020-06-05 23:44       ` [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting Tom de Vries
  0 siblings, 1 reply; 25+ messages in thread
From: Tom de Vries @ 2020-06-05 16:00 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 05-06-2020 16:49, Tom de Vries wrote:
> On 23-12-2019 02:51, Andrew Burgess wrote:
>> I had to make a small adjustment in find_pc_sect_line in order to
>> correctly find the previous line in the line table.  In some line
>> tables I was seeing an actual line entry and an end of sequence marker
>> at the same address, before this commit these would reorder to move
>> the end of sequence marker before the line entry (end of sequence has
>> line number 0).  Now the end of sequence marker remains in its correct
>> location, and in order to find a previous line we should step backward
>> over any end of sequence markers.
>>
>> As an example, the binary:
>>   gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold
>>
>> Has this line table before the patch:
>>
>>   INDEX    LINE ADDRESS
>>   0          48 0x0000000000400487
>>   1         END 0x000000000040048e
>>   2          52 0x000000000040048e
>>   3          54 0x0000000000400492
>>   4          56 0x0000000000400497
>>   5         END 0x000000000040049a
>>   6          62 0x000000000040049a
>>   7         END 0x00000000004004a1
>>   8          66 0x00000000004004a1
>>   9          68 0x00000000004004a5
>>   10         70 0x00000000004004aa
>>   11         72 0x00000000004004b9
>>   12        END 0x00000000004004bc
>>   13         76 0x00000000004004bc
>>   14         78 0x00000000004004c0
>>   15         80 0x00000000004004c5
>>   16        END 0x00000000004004cc
>>
>> And after this patch:
>>
>>   INDEX    LINE ADDRESS
>>   0          48 0x0000000000400487
>>   1          52 0x000000000040048e
>>   2         END 0x000000000040048e
>>   3          54 0x0000000000400492
>>   4          56 0x0000000000400497
>>   5         END 0x000000000040049a
>>   6          62 0x000000000040049a
>>   7          66 0x00000000004004a1
>>   8         END 0x00000000004004a1
>>   9          68 0x00000000004004a5
>>   10         70 0x00000000004004aa
>>   11         72 0x00000000004004b9
>>   12        END 0x00000000004004bc
>>   13         76 0x00000000004004bc
>>   14         78 0x00000000004004c0
>>   15         80 0x00000000004004c5
>>   16        END 0x00000000004004cc
>>
>> When calling find_pc_sect_line with the address 0x000000000040048e, in
>> both cases we find entry #3, we then try to find the previous entry,
>> which originally found this entry '2         52 0x000000000040048e',
>> after the patch it finds '2         END 0x000000000040048e', which
>> cases the lookup to fail.
>>
>> By skipping the END marker after this patch we get back to the correct
>> entry, which is now #1: '1          52 0x000000000040048e', and
>> everything works again.
> 
> I start to suspect that you have been working around an incorrect line
> table.
> 
> Consider this bit:
> ...
>    0          48 0x0000000000400487
>    1          52 0x000000000040048e
>    2         END 0x000000000040048e
> ...
> 
> The end marker marks the address one past the end of the sequence.
> Therefore, it makes no sense to have an entry in the sequence with the
> same address as the end marker.
> 
> [ dwarf doc:
> 
> end_sequence:
> 
> A boolean indicating that the current address is that of the first byte
> after the end of a sequence of target machine instructions. end_sequence
> terminates a sequence of lines; therefore other information in the same
> row is not meaningful.
> 
> DW_LNE_end_sequence:
> 
> The DW_LNE_end_sequence opcode takes no operands. It sets the
> end_sequence register of the state machine to “true” and appends a row
> to the matrix using the current values of the state-machine registers.
> Then it resets the registers to the initial values specified above (see
> Section 6.2.2). Every line number program sequence must end with a
> DW_LNE_end_sequence instruction which creates a row whose address is
> that of the byte after the last target machine instruction of the sequence.
> 
> ]
> 
> The incorrect entry is generated by this dwarf assembler sequence:
> ...
>                 {DW_LNS_copy}
>                 {DW_LNE_end_sequence}
> ...
> 
> I think we should probably fix the dwarf assembly test-cases.
> 
> If we want to handle this in gdb, the thing that seems most logical to
> me is to ignore this kind of entries.

Hmm, that seems to be done already, in buildsym_compunit::record_line.

Anyway, I was looking at the line table for
gdb.dwarf2/dw2-ranges-base.exp, and got a line table with subsequent end
markers:
...
INDEX  LINE   ADDRESS            IS-STMT
0      31     0x00000000004004a7 Y
1      21     0x00000000004004ae Y
2      END    0x00000000004004ae Y
3      11     0x00000000004004ba Y
4      END    0x00000000004004ba Y
5      END    0x00000000004004c6 Y
...

By using this patch:
...
diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 33bf6523e9..76f0b54ff6 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector
(struct block *static_block,
            = [] (const linetable_entry &ln1,
                  const linetable_entry &ln2) -> bool
              {
+               if (ln1.pc == ln2.pc
+                   && ((ln1.line == 0) != (ln2.line == 0)))
+                 return ln1.line == 0 ? true : false;
+
                return (ln1.pc < ln2.pc);
              };

...
I get the expected:
...
INDEX  LINE   ADDRESS            IS-STMT
0      31     0x00000000004004a7 Y
1      END    0x00000000004004ae Y
2      21     0x00000000004004ae Y
3      END    0x00000000004004ba Y
4      11     0x00000000004004ba Y
5      END    0x00000000004004c6 Y
...

Thanks,
- Tom

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

* [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting
  2020-06-05 16:00     ` Tom de Vries
@ 2020-06-05 23:44       ` Tom de Vries
  2020-06-06  6:51         ` Andrew Burgess
  2020-06-06  9:25         ` Andrew Burgess
  0 siblings, 2 replies; 25+ messages in thread
From: Tom de Vries @ 2020-06-05 23:44 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

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

[ was: Re: [PATCH 2/3] gdb: Don't reorder line table entries too much
when sorting. ]

On 05-06-2020 18:00, Tom de Vries wrote:
> On 05-06-2020 16:49, Tom de Vries wrote:
>> On 23-12-2019 02:51, Andrew Burgess wrote:
>>> I had to make a small adjustment in find_pc_sect_line in order to
>>> correctly find the previous line in the line table.  In some line
>>> tables I was seeing an actual line entry and an end of sequence marker
>>> at the same address, before this commit these would reorder to move
>>> the end of sequence marker before the line entry (end of sequence has
>>> line number 0).  Now the end of sequence marker remains in its correct
>>> location, and in order to find a previous line we should step backward
>>> over any end of sequence markers.
>>>
>>> As an example, the binary:
>>>   gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold
>>>
>>> Has this line table before the patch:
>>>
>>>   INDEX    LINE ADDRESS
>>>   0          48 0x0000000000400487
>>>   1         END 0x000000000040048e
>>>   2          52 0x000000000040048e
>>>   3          54 0x0000000000400492
>>>   4          56 0x0000000000400497
>>>   5         END 0x000000000040049a
>>>   6          62 0x000000000040049a
>>>   7         END 0x00000000004004a1
>>>   8          66 0x00000000004004a1
>>>   9          68 0x00000000004004a5
>>>   10         70 0x00000000004004aa
>>>   11         72 0x00000000004004b9
>>>   12        END 0x00000000004004bc
>>>   13         76 0x00000000004004bc
>>>   14         78 0x00000000004004c0
>>>   15         80 0x00000000004004c5
>>>   16        END 0x00000000004004cc
>>>
>>> And after this patch:
>>>
>>>   INDEX    LINE ADDRESS
>>>   0          48 0x0000000000400487
>>>   1          52 0x000000000040048e
>>>   2         END 0x000000000040048e
>>>   3          54 0x0000000000400492
>>>   4          56 0x0000000000400497
>>>   5         END 0x000000000040049a
>>>   6          62 0x000000000040049a
>>>   7          66 0x00000000004004a1
>>>   8         END 0x00000000004004a1
>>>   9          68 0x00000000004004a5
>>>   10         70 0x00000000004004aa
>>>   11         72 0x00000000004004b9
>>>   12        END 0x00000000004004bc
>>>   13         76 0x00000000004004bc
>>>   14         78 0x00000000004004c0
>>>   15         80 0x00000000004004c5
>>>   16        END 0x00000000004004cc
>>>
>>> When calling find_pc_sect_line with the address 0x000000000040048e, in
>>> both cases we find entry #3, we then try to find the previous entry,
>>> which originally found this entry '2         52 0x000000000040048e',
>>> after the patch it finds '2         END 0x000000000040048e', which
>>> cases the lookup to fail.
>>>
>>> By skipping the END marker after this patch we get back to the correct
>>> entry, which is now #1: '1          52 0x000000000040048e', and
>>> everything works again.
>>
>> I start to suspect that you have been working around an incorrect line
>> table.
>>
>> Consider this bit:
>> ...
>>    0          48 0x0000000000400487
>>    1          52 0x000000000040048e
>>    2         END 0x000000000040048e
>> ...
>>
>> The end marker marks the address one past the end of the sequence.
>> Therefore, it makes no sense to have an entry in the sequence with the
>> same address as the end marker.
>>
>> [ dwarf doc:
>>
>> end_sequence:
>>
>> A boolean indicating that the current address is that of the first byte
>> after the end of a sequence of target machine instructions. end_sequence
>> terminates a sequence of lines; therefore other information in the same
>> row is not meaningful.
>>
>> DW_LNE_end_sequence:
>>
>> The DW_LNE_end_sequence opcode takes no operands. It sets the
>> end_sequence register of the state machine to “true” and appends a row
>> to the matrix using the current values of the state-machine registers.
>> Then it resets the registers to the initial values specified above (see
>> Section 6.2.2). Every line number program sequence must end with a
>> DW_LNE_end_sequence instruction which creates a row whose address is
>> that of the byte after the last target machine instruction of the sequence.
>>
>> ]
>>
>> The incorrect entry is generated by this dwarf assembler sequence:
>> ...
>>                 {DW_LNS_copy}
>>                 {DW_LNE_end_sequence}
>> ...
>>
>> I think we should probably fix the dwarf assembly test-cases.
>>
>> If we want to handle this in gdb, the thing that seems most logical to
>> me is to ignore this kind of entries.
> 
> Hmm, that seems to be done already, in buildsym_compunit::record_line.
> 
> Anyway, I was looking at the line table for
> gdb.dwarf2/dw2-ranges-base.exp, and got a line table with subsequent end
> markers:
> ...
> INDEX  LINE   ADDRESS            IS-STMT
> 0      31     0x00000000004004a7 Y
> 1      21     0x00000000004004ae Y
> 2      END    0x00000000004004ae Y
> 3      11     0x00000000004004ba Y
> 4      END    0x00000000004004ba Y
> 5      END    0x00000000004004c6 Y
> ...
> 
> By using this patch:
> ...
> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> index 33bf6523e9..76f0b54ff6 100644
> --- a/gdb/buildsym.c
> +++ b/gdb/buildsym.c
> @@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector
> (struct block *static_block,
>             = [] (const linetable_entry &ln1,
>                   const linetable_entry &ln2) -> bool
>               {
> +               if (ln1.pc == ln2.pc
> +                   && ((ln1.line == 0) != (ln2.line == 0)))
> +                 return ln1.line == 0 ? true : false;
> +
>                 return (ln1.pc < ln2.pc);
>               };
> 
> ...
> I get the expected:
> ...
> INDEX  LINE   ADDRESS            IS-STMT
> 0      31     0x00000000004004a7 Y
> 1      END    0x00000000004004ae Y
> 2      21     0x00000000004004ae Y
> 3      END    0x00000000004004ba Y
> 4      11     0x00000000004004ba Y
> 5      END    0x00000000004004c6 Y
> ...

Any comments on patch below?

Thanks,
- Tom


[-- Attachment #2: 0001-gdb-symtab-Fix-line-table-end-of-sequence-sorting.patch --]
[-- Type: text/x-patch, Size: 5217 bytes --]

[gdb/symtab] Fix line-table end-of-sequence sorting

Consider test-case gdb.dwarf2/dw2-ranges-base.exp.  It has a line-table for
dw2-ranges-base.c like this:
...
 Line Number Statements:
  [0x0000014e]  Extended opcode 2: set Address to 0x4004ba
  [0x00000159]  Advance Line by 10 to 11
  [0x0000015b]  Copy
  [0x0000015c]  Advance PC by 12 to 0x4004c6
  [0x0000015e]  Advance Line by 19 to 30
  [0x00000160]  Copy
  [0x00000161]  Extended opcode 1: End of Sequence

  [0x00000164]  Extended opcode 2: set Address to 0x4004ae
  [0x0000016f]  Advance Line by 20 to 21
  [0x00000171]  Copy
  [0x00000172]  Advance PC by 12 to 0x4004ba
  [0x00000174]  Advance Line by 29 to 50
  [0x00000176]  Copy
  [0x00000177]  Extended opcode 1: End of Sequence

  [0x0000017a]  Extended opcode 2: set Address to 0x4004a7
  [0x00000185]  Advance Line by 30 to 31
  [0x00000187]  Copy
  [0x00000188]  Advance PC by 7 to 0x4004ae
  [0x0000018a]  Advance Line by 39 to 70
  [0x0000018c]  Copy
  [0x0000018d]  Extended opcode 1: End of Sequence
...

The Copy followed by End-of-Sequence is as specified in the dwarf assembly,
but incorrect.  F.i., consider:
...
  [0x0000015c]  Advance PC by 12 to 0x4004c6
  [0x0000015e]  Advance Line by 19 to 30
  [0x00000160]  Copy
  [0x00000161]  Extended opcode 1: End of Sequence
...

Both the Copy and the End-of-Sequence append a row to the matrix using the
same addres: 0x4004c6.  The Copy declares a target instruction at that
address.  The End-of-Sequence declares that the sequence ends before that
address.  It's a contradiction that the target instruction is both part of the
sequence (according to Copy) and not part of the sequence (according to
End-of-Sequence).

The offending Copy is skipped though by buildsym_compunit::record_line for
unrelated reasons.  So, if we disable the sorting in
buildsym_compunit::end_symtab_with_blockvector, we have:
...
INDEX  LINE   ADDRESS            IS-STMT
0      11     0x00000000004004ba Y
1      END    0x00000000004004c6 Y
2      21     0x00000000004004ae Y
3      END    0x00000000004004ba Y
4      31     0x00000000004004a7 Y
5      END    0x00000000004004ae Y
...
but if we re-enable the sorting, we have:
...
INDEX  LINE   ADDRESS            IS-STMT
0      31     0x00000000004004a7 Y
1      21     0x00000000004004ae Y
2      END    0x00000000004004ae Y
3      11     0x00000000004004ba Y
4      END    0x00000000004004ba Y
5      END    0x00000000004004c6 Y
...
which has both:
- the contradictory order for the same-address pairs 1/2 and 3/4, as well as
- a non-sensical pair of ENDs,
while we'd like:
...
INDEX  LINE   ADDRESS            IS-STMT
0      31     0x00000000004004a7 Y
1      END    0x00000000004004ae Y
2      21     0x00000000004004ae Y
3      END    0x00000000004004ba Y
4      11     0x00000000004004ba Y
5      END    0x00000000004004c6 Y
...

This is a regression since commit 3d92a3e313 "gdb: Don't reorder line table
entries too much when sorting", that introduced sorting on address while
keeping entries with the same address in pre-sort order, which leads to
incorrect results if one of the entries is an End-Of-Sequence.

Fix this by handling End-Of-Sequence entries in the sorting function.

Tested on x86_64-linux.

gdb/ChangeLog:

2020-06-06  Tom de Vries  <tdevries@suse.de>

	* buildsym.c (buildsym_compunit::end_symtab_with_blockvector): Handle
	End-Of-Sequence in lte_is_less_than.

gdb/testsuite/ChangeLog:

2020-06-06  Tom de Vries  <tdevries@suse.de>

	* gdb.dwarf2/dw2-ranges-base.exp: Test line-table order.

---
 gdb/buildsym.c                               |  4 ++++
 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp | 14 ++++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 33bf6523e9..76f0b54ff6 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector (struct block *static_block,
 	    = [] (const linetable_entry &ln1,
 		  const linetable_entry &ln2) -> bool
 	      {
+		if (ln1.pc == ln2.pc
+		    && ((ln1.line == 0) != (ln2.line == 0)))
+		  return ln1.line == 0 ? true : false;
+
 		return (ln1.pc < ln2.pc);
 	      };
 
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
index 92f8f6cecb..39281a8857 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
@@ -144,12 +144,26 @@ gdb_test "info line frame3" \
 
 # Ensure that the line table correctly tracks the end of sequence markers.
 set end_seq_count 0
+set prev -1
+set seq_count 0
 gdb_test_multiple "maint info line-table gdb.dwarf2/dw2-ranges-base.c" \
     "count END markers in line table" {
 	-re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
+	    if { $prev != -1 } {
+		gdb_assert "$prev == 1" \
+		    "prev of normal entry at $seq_count is end marker"
+	    }
+	    set prev 0
+	    incr seq_count
 	    exp_continue
 	}
 	-re "^$decimal\[ \t\]+END\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
+	    if { $prev != -1 } {
+		gdb_assert "$prev == 0" \
+		    "prev of end marker at $seq_count is normal entry"
+	    }
+	    set prev 1
+	    incr seq_count
 	    incr end_seq_count
 	    exp_continue
 	}

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

* Re: [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting
  2020-06-05 23:44       ` [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting Tom de Vries
@ 2020-06-06  6:51         ` Andrew Burgess
  2020-06-06  8:18           ` Tom de Vries
  2020-06-06  9:25         ` Andrew Burgess
  1 sibling, 1 reply; 25+ messages in thread
From: Andrew Burgess @ 2020-06-06  6:51 UTC (permalink / raw)
  To: Tom de Vries; +Cc: gdb-patches

* Tom de Vries <tdevries@suse.de> [2020-06-06 01:44:42 +0200]:

> [ was: Re: [PATCH 2/3] gdb: Don't reorder line table entries too much
> when sorting. ]
> 
> On 05-06-2020 18:00, Tom de Vries wrote:
> > On 05-06-2020 16:49, Tom de Vries wrote:
> >> On 23-12-2019 02:51, Andrew Burgess wrote:
> >>> I had to make a small adjustment in find_pc_sect_line in order to
> >>> correctly find the previous line in the line table.  In some line
> >>> tables I was seeing an actual line entry and an end of sequence marker
> >>> at the same address, before this commit these would reorder to move
> >>> the end of sequence marker before the line entry (end of sequence has
> >>> line number 0).  Now the end of sequence marker remains in its correct
> >>> location, and in order to find a previous line we should step backward
> >>> over any end of sequence markers.
> >>>
> >>> As an example, the binary:
> >>>   gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold
> >>>
> >>> Has this line table before the patch:
> >>>
> >>>   INDEX    LINE ADDRESS
> >>>   0          48 0x0000000000400487
> >>>   1         END 0x000000000040048e
> >>>   2          52 0x000000000040048e
> >>>   3          54 0x0000000000400492
> >>>   4          56 0x0000000000400497
> >>>   5         END 0x000000000040049a
> >>>   6          62 0x000000000040049a
> >>>   7         END 0x00000000004004a1
> >>>   8          66 0x00000000004004a1
> >>>   9          68 0x00000000004004a5
> >>>   10         70 0x00000000004004aa
> >>>   11         72 0x00000000004004b9
> >>>   12        END 0x00000000004004bc
> >>>   13         76 0x00000000004004bc
> >>>   14         78 0x00000000004004c0
> >>>   15         80 0x00000000004004c5
> >>>   16        END 0x00000000004004cc
> >>>
> >>> And after this patch:
> >>>
> >>>   INDEX    LINE ADDRESS
> >>>   0          48 0x0000000000400487
> >>>   1          52 0x000000000040048e
> >>>   2         END 0x000000000040048e
> >>>   3          54 0x0000000000400492
> >>>   4          56 0x0000000000400497
> >>>   5         END 0x000000000040049a
> >>>   6          62 0x000000000040049a
> >>>   7          66 0x00000000004004a1
> >>>   8         END 0x00000000004004a1
> >>>   9          68 0x00000000004004a5
> >>>   10         70 0x00000000004004aa
> >>>   11         72 0x00000000004004b9
> >>>   12        END 0x00000000004004bc
> >>>   13         76 0x00000000004004bc
> >>>   14         78 0x00000000004004c0
> >>>   15         80 0x00000000004004c5
> >>>   16        END 0x00000000004004cc
> >>>
> >>> When calling find_pc_sect_line with the address 0x000000000040048e, in
> >>> both cases we find entry #3, we then try to find the previous entry,
> >>> which originally found this entry '2         52 0x000000000040048e',
> >>> after the patch it finds '2         END 0x000000000040048e', which
> >>> cases the lookup to fail.
> >>>
> >>> By skipping the END marker after this patch we get back to the correct
> >>> entry, which is now #1: '1          52 0x000000000040048e', and
> >>> everything works again.
> >>
> >> I start to suspect that you have been working around an incorrect line
> >> table.
> >>
> >> Consider this bit:
> >> ...
> >>    0          48 0x0000000000400487
> >>    1          52 0x000000000040048e
> >>    2         END 0x000000000040048e
> >> ...
> >>
> >> The end marker marks the address one past the end of the sequence.
> >> Therefore, it makes no sense to have an entry in the sequence with the
> >> same address as the end marker.
> >>
> >> [ dwarf doc:
> >>
> >> end_sequence:
> >>
> >> A boolean indicating that the current address is that of the first byte
> >> after the end of a sequence of target machine instructions. end_sequence
> >> terminates a sequence of lines; therefore other information in the same
> >> row is not meaningful.
> >>
> >> DW_LNE_end_sequence:
> >>
> >> The DW_LNE_end_sequence opcode takes no operands. It sets the
> >> end_sequence register of the state machine to “true” and appends a row
> >> to the matrix using the current values of the state-machine registers.
> >> Then it resets the registers to the initial values specified above (see
> >> Section 6.2.2). Every line number program sequence must end with a
> >> DW_LNE_end_sequence instruction which creates a row whose address is
> >> that of the byte after the last target machine instruction of the sequence.
> >>
> >> ]
> >>
> >> The incorrect entry is generated by this dwarf assembler sequence:
> >> ...
> >>                 {DW_LNS_copy}
> >>                 {DW_LNE_end_sequence}
> >> ...
> >>
> >> I think we should probably fix the dwarf assembly test-cases.
> >>
> >> If we want to handle this in gdb, the thing that seems most logical to
> >> me is to ignore this kind of entries.
> > 
> > Hmm, that seems to be done already, in buildsym_compunit::record_line.
> > 
> > Anyway, I was looking at the line table for
> > gdb.dwarf2/dw2-ranges-base.exp, and got a line table with subsequent end
> > markers:
> > ...
> > INDEX  LINE   ADDRESS            IS-STMT
> > 0      31     0x00000000004004a7 Y
> > 1      21     0x00000000004004ae Y
> > 2      END    0x00000000004004ae Y
> > 3      11     0x00000000004004ba Y
> > 4      END    0x00000000004004ba Y
> > 5      END    0x00000000004004c6 Y
> > ...
> > 
> > By using this patch:
> > ...
> > diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> > index 33bf6523e9..76f0b54ff6 100644
> > --- a/gdb/buildsym.c
> > +++ b/gdb/buildsym.c
> > @@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector
> > (struct block *static_block,
> >             = [] (const linetable_entry &ln1,
> >                   const linetable_entry &ln2) -> bool
> >               {
> > +               if (ln1.pc == ln2.pc
> > +                   && ((ln1.line == 0) != (ln2.line == 0)))
> > +                 return ln1.line == 0 ? true : false;

I will take a look at this patch properly as soon as I can, but just
spotted this pet peeve of mine, please just write:

  return ln1.line == 0;

Thanks,
Andrew


> > +
> >                 return (ln1.pc < ln2.pc);
> >               };
> > 
> > ...
> > I get the expected:
> > ...
> > INDEX  LINE   ADDRESS            IS-STMT
> > 0      31     0x00000000004004a7 Y
> > 1      END    0x00000000004004ae Y
> > 2      21     0x00000000004004ae Y
> > 3      END    0x00000000004004ba Y
> > 4      11     0x00000000004004ba Y
> > 5      END    0x00000000004004c6 Y
> > ...
> 
> Any comments on patch below?
> 
> Thanks,
> - Tom
> 

> [gdb/symtab] Fix line-table end-of-sequence sorting
> 
> Consider test-case gdb.dwarf2/dw2-ranges-base.exp.  It has a line-table for
> dw2-ranges-base.c like this:
> ...
>  Line Number Statements:
>   [0x0000014e]  Extended opcode 2: set Address to 0x4004ba
>   [0x00000159]  Advance Line by 10 to 11
>   [0x0000015b]  Copy
>   [0x0000015c]  Advance PC by 12 to 0x4004c6
>   [0x0000015e]  Advance Line by 19 to 30
>   [0x00000160]  Copy
>   [0x00000161]  Extended opcode 1: End of Sequence
> 
>   [0x00000164]  Extended opcode 2: set Address to 0x4004ae
>   [0x0000016f]  Advance Line by 20 to 21
>   [0x00000171]  Copy
>   [0x00000172]  Advance PC by 12 to 0x4004ba
>   [0x00000174]  Advance Line by 29 to 50
>   [0x00000176]  Copy
>   [0x00000177]  Extended opcode 1: End of Sequence
> 
>   [0x0000017a]  Extended opcode 2: set Address to 0x4004a7
>   [0x00000185]  Advance Line by 30 to 31
>   [0x00000187]  Copy
>   [0x00000188]  Advance PC by 7 to 0x4004ae
>   [0x0000018a]  Advance Line by 39 to 70
>   [0x0000018c]  Copy
>   [0x0000018d]  Extended opcode 1: End of Sequence
> ...
> 
> The Copy followed by End-of-Sequence is as specified in the dwarf assembly,
> but incorrect.  F.i., consider:
> ...
>   [0x0000015c]  Advance PC by 12 to 0x4004c6
>   [0x0000015e]  Advance Line by 19 to 30
>   [0x00000160]  Copy
>   [0x00000161]  Extended opcode 1: End of Sequence
> ...
> 
> Both the Copy and the End-of-Sequence append a row to the matrix using the
> same addres: 0x4004c6.  The Copy declares a target instruction at that
> address.  The End-of-Sequence declares that the sequence ends before that
> address.  It's a contradiction that the target instruction is both part of the
> sequence (according to Copy) and not part of the sequence (according to
> End-of-Sequence).
> 
> The offending Copy is skipped though by buildsym_compunit::record_line for
> unrelated reasons.  So, if we disable the sorting in
> buildsym_compunit::end_symtab_with_blockvector, we have:
> ...
> INDEX  LINE   ADDRESS            IS-STMT
> 0      11     0x00000000004004ba Y
> 1      END    0x00000000004004c6 Y
> 2      21     0x00000000004004ae Y
> 3      END    0x00000000004004ba Y
> 4      31     0x00000000004004a7 Y
> 5      END    0x00000000004004ae Y
> ...
> but if we re-enable the sorting, we have:
> ...
> INDEX  LINE   ADDRESS            IS-STMT
> 0      31     0x00000000004004a7 Y
> 1      21     0x00000000004004ae Y
> 2      END    0x00000000004004ae Y
> 3      11     0x00000000004004ba Y
> 4      END    0x00000000004004ba Y
> 5      END    0x00000000004004c6 Y
> ...
> which has both:
> - the contradictory order for the same-address pairs 1/2 and 3/4, as well as
> - a non-sensical pair of ENDs,
> while we'd like:
> ...
> INDEX  LINE   ADDRESS            IS-STMT
> 0      31     0x00000000004004a7 Y
> 1      END    0x00000000004004ae Y
> 2      21     0x00000000004004ae Y
> 3      END    0x00000000004004ba Y
> 4      11     0x00000000004004ba Y
> 5      END    0x00000000004004c6 Y
> ...
> 
> This is a regression since commit 3d92a3e313 "gdb: Don't reorder line table
> entries too much when sorting", that introduced sorting on address while
> keeping entries with the same address in pre-sort order, which leads to
> incorrect results if one of the entries is an End-Of-Sequence.
> 
> Fix this by handling End-Of-Sequence entries in the sorting function.
> 
> Tested on x86_64-linux.
> 
> gdb/ChangeLog:
> 
> 2020-06-06  Tom de Vries  <tdevries@suse.de>
> 
> 	* buildsym.c (buildsym_compunit::end_symtab_with_blockvector): Handle
> 	End-Of-Sequence in lte_is_less_than.
> 
> gdb/testsuite/ChangeLog:
> 
> 2020-06-06  Tom de Vries  <tdevries@suse.de>
> 
> 	* gdb.dwarf2/dw2-ranges-base.exp: Test line-table order.
> 
> ---
>  gdb/buildsym.c                               |  4 ++++
>  gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp | 14 ++++++++++++++
>  2 files changed, 18 insertions(+)
> 
> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> index 33bf6523e9..76f0b54ff6 100644
> --- a/gdb/buildsym.c
> +++ b/gdb/buildsym.c
> @@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector (struct block *static_block,
>  	    = [] (const linetable_entry &ln1,
>  		  const linetable_entry &ln2) -> bool
>  	      {
> +		if (ln1.pc == ln2.pc
> +		    && ((ln1.line == 0) != (ln2.line == 0)))
> +		  return ln1.line == 0 ? true : false;
> +
>  		return (ln1.pc < ln2.pc);
>  	      };
>  
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> index 92f8f6cecb..39281a8857 100644
> --- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> @@ -144,12 +144,26 @@ gdb_test "info line frame3" \
>  
>  # Ensure that the line table correctly tracks the end of sequence markers.
>  set end_seq_count 0
> +set prev -1
> +set seq_count 0
>  gdb_test_multiple "maint info line-table gdb.dwarf2/dw2-ranges-base.c" \
>      "count END markers in line table" {
>  	-re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
> +	    if { $prev != -1 } {
> +		gdb_assert "$prev == 1" \
> +		    "prev of normal entry at $seq_count is end marker"
> +	    }
> +	    set prev 0
> +	    incr seq_count
>  	    exp_continue
>  	}
>  	-re "^$decimal\[ \t\]+END\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
> +	    if { $prev != -1 } {
> +		gdb_assert "$prev == 0" \
> +		    "prev of end marker at $seq_count is normal entry"
> +	    }
> +	    set prev 1
> +	    incr seq_count
>  	    incr end_seq_count
>  	    exp_continue
>  	}


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

* Re: [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting
  2020-06-06  6:51         ` Andrew Burgess
@ 2020-06-06  8:18           ` Tom de Vries
  0 siblings, 0 replies; 25+ messages in thread
From: Tom de Vries @ 2020-06-06  8:18 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

On 06-06-2020 08:51, Andrew Burgess wrote:
> * Tom de Vries <tdevries@suse.de> [2020-06-06 01:44:42 +0200]:
> 
>> [ was: Re: [PATCH 2/3] gdb: Don't reorder line table entries too much
>> when sorting. ]
>>
>> On 05-06-2020 18:00, Tom de Vries wrote:
>>> On 05-06-2020 16:49, Tom de Vries wrote:
>>>> On 23-12-2019 02:51, Andrew Burgess wrote:
>>>>> I had to make a small adjustment in find_pc_sect_line in order to
>>>>> correctly find the previous line in the line table.  In some line
>>>>> tables I was seeing an actual line entry and an end of sequence marker
>>>>> at the same address, before this commit these would reorder to move
>>>>> the end of sequence marker before the line entry (end of sequence has
>>>>> line number 0).  Now the end of sequence marker remains in its correct
>>>>> location, and in order to find a previous line we should step backward
>>>>> over any end of sequence markers.
>>>>>
>>>>> As an example, the binary:
>>>>>   gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold
>>>>>
>>>>> Has this line table before the patch:
>>>>>
>>>>>   INDEX    LINE ADDRESS
>>>>>   0          48 0x0000000000400487
>>>>>   1         END 0x000000000040048e
>>>>>   2          52 0x000000000040048e
>>>>>   3          54 0x0000000000400492
>>>>>   4          56 0x0000000000400497
>>>>>   5         END 0x000000000040049a
>>>>>   6          62 0x000000000040049a
>>>>>   7         END 0x00000000004004a1
>>>>>   8          66 0x00000000004004a1
>>>>>   9          68 0x00000000004004a5
>>>>>   10         70 0x00000000004004aa
>>>>>   11         72 0x00000000004004b9
>>>>>   12        END 0x00000000004004bc
>>>>>   13         76 0x00000000004004bc
>>>>>   14         78 0x00000000004004c0
>>>>>   15         80 0x00000000004004c5
>>>>>   16        END 0x00000000004004cc
>>>>>
>>>>> And after this patch:
>>>>>
>>>>>   INDEX    LINE ADDRESS
>>>>>   0          48 0x0000000000400487
>>>>>   1          52 0x000000000040048e
>>>>>   2         END 0x000000000040048e
>>>>>   3          54 0x0000000000400492
>>>>>   4          56 0x0000000000400497
>>>>>   5         END 0x000000000040049a
>>>>>   6          62 0x000000000040049a
>>>>>   7          66 0x00000000004004a1
>>>>>   8         END 0x00000000004004a1
>>>>>   9          68 0x00000000004004a5
>>>>>   10         70 0x00000000004004aa
>>>>>   11         72 0x00000000004004b9
>>>>>   12        END 0x00000000004004bc
>>>>>   13         76 0x00000000004004bc
>>>>>   14         78 0x00000000004004c0
>>>>>   15         80 0x00000000004004c5
>>>>>   16        END 0x00000000004004cc
>>>>>
>>>>> When calling find_pc_sect_line with the address 0x000000000040048e, in
>>>>> both cases we find entry #3, we then try to find the previous entry,
>>>>> which originally found this entry '2         52 0x000000000040048e',
>>>>> after the patch it finds '2         END 0x000000000040048e', which
>>>>> cases the lookup to fail.
>>>>>
>>>>> By skipping the END marker after this patch we get back to the correct
>>>>> entry, which is now #1: '1          52 0x000000000040048e', and
>>>>> everything works again.
>>>>
>>>> I start to suspect that you have been working around an incorrect line
>>>> table.
>>>>
>>>> Consider this bit:
>>>> ...
>>>>    0          48 0x0000000000400487
>>>>    1          52 0x000000000040048e
>>>>    2         END 0x000000000040048e
>>>> ...
>>>>
>>>> The end marker marks the address one past the end of the sequence.
>>>> Therefore, it makes no sense to have an entry in the sequence with the
>>>> same address as the end marker.
>>>>
>>>> [ dwarf doc:
>>>>
>>>> end_sequence:
>>>>
>>>> A boolean indicating that the current address is that of the first byte
>>>> after the end of a sequence of target machine instructions. end_sequence
>>>> terminates a sequence of lines; therefore other information in the same
>>>> row is not meaningful.
>>>>
>>>> DW_LNE_end_sequence:
>>>>
>>>> The DW_LNE_end_sequence opcode takes no operands. It sets the
>>>> end_sequence register of the state machine to “true” and appends a row
>>>> to the matrix using the current values of the state-machine registers.
>>>> Then it resets the registers to the initial values specified above (see
>>>> Section 6.2.2). Every line number program sequence must end with a
>>>> DW_LNE_end_sequence instruction which creates a row whose address is
>>>> that of the byte after the last target machine instruction of the sequence.
>>>>
>>>> ]
>>>>
>>>> The incorrect entry is generated by this dwarf assembler sequence:
>>>> ...
>>>>                 {DW_LNS_copy}
>>>>                 {DW_LNE_end_sequence}
>>>> ...
>>>>
>>>> I think we should probably fix the dwarf assembly test-cases.
>>>>
>>>> If we want to handle this in gdb, the thing that seems most logical to
>>>> me is to ignore this kind of entries.
>>>
>>> Hmm, that seems to be done already, in buildsym_compunit::record_line.
>>>
>>> Anyway, I was looking at the line table for
>>> gdb.dwarf2/dw2-ranges-base.exp, and got a line table with subsequent end
>>> markers:
>>> ...
>>> INDEX  LINE   ADDRESS            IS-STMT
>>> 0      31     0x00000000004004a7 Y
>>> 1      21     0x00000000004004ae Y
>>> 2      END    0x00000000004004ae Y
>>> 3      11     0x00000000004004ba Y
>>> 4      END    0x00000000004004ba Y
>>> 5      END    0x00000000004004c6 Y
>>> ...
>>>
>>> By using this patch:
>>> ...
>>> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
>>> index 33bf6523e9..76f0b54ff6 100644
>>> --- a/gdb/buildsym.c
>>> +++ b/gdb/buildsym.c
>>> @@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector
>>> (struct block *static_block,
>>>             = [] (const linetable_entry &ln1,
>>>                   const linetable_entry &ln2) -> bool
>>>               {
>>> +               if (ln1.pc == ln2.pc
>>> +                   && ((ln1.line == 0) != (ln2.line == 0)))
>>> +                 return ln1.line == 0 ? true : false;
> 
> I will take a look at this patch properly as soon as I can, but just
> spotted this pet peeve of mine, please just write:
> 
>   return ln1.line == 0;

Ack, thanks, will update that.

Btw, I've retested this patch in combination with reverting the
find_pc_sect_line part of "gdb: Don't reorder line table entries too
much when sorting" and did not run into any test fails.

Thanks,
- Tom

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

* Re: [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting
  2020-06-05 23:44       ` [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting Tom de Vries
  2020-06-06  6:51         ` Andrew Burgess
@ 2020-06-06  9:25         ` Andrew Burgess
  2020-06-08 14:40           ` [gdb/testsuite] Fix bad line table entry sequence Tom de Vries
  2020-06-08 15:50           ` [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting Tom de Vries
  1 sibling, 2 replies; 25+ messages in thread
From: Andrew Burgess @ 2020-06-06  9:25 UTC (permalink / raw)
  To: Tom de Vries; +Cc: gdb-patches

* Tom de Vries <tdevries@suse.de> [2020-06-06 01:44:42 +0200]:

> [ was: Re: [PATCH 2/3] gdb: Don't reorder line table entries too much
> when sorting. ]
> 
> On 05-06-2020 18:00, Tom de Vries wrote:
> > On 05-06-2020 16:49, Tom de Vries wrote:
> >> On 23-12-2019 02:51, Andrew Burgess wrote:
> >>> I had to make a small adjustment in find_pc_sect_line in order to
> >>> correctly find the previous line in the line table.  In some line
> >>> tables I was seeing an actual line entry and an end of sequence marker
> >>> at the same address, before this commit these would reorder to move
> >>> the end of sequence marker before the line entry (end of sequence has
> >>> line number 0).  Now the end of sequence marker remains in its correct
> >>> location, and in order to find a previous line we should step backward
> >>> over any end of sequence markers.
> >>>
> >>> As an example, the binary:
> >>>   gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold
> >>>
> >>> Has this line table before the patch:
> >>>
> >>>   INDEX    LINE ADDRESS
> >>>   0          48 0x0000000000400487
> >>>   1         END 0x000000000040048e
> >>>   2          52 0x000000000040048e
> >>>   3          54 0x0000000000400492
> >>>   4          56 0x0000000000400497
> >>>   5         END 0x000000000040049a
> >>>   6          62 0x000000000040049a
> >>>   7         END 0x00000000004004a1
> >>>   8          66 0x00000000004004a1
> >>>   9          68 0x00000000004004a5
> >>>   10         70 0x00000000004004aa
> >>>   11         72 0x00000000004004b9
> >>>   12        END 0x00000000004004bc
> >>>   13         76 0x00000000004004bc
> >>>   14         78 0x00000000004004c0
> >>>   15         80 0x00000000004004c5
> >>>   16        END 0x00000000004004cc
> >>>
> >>> And after this patch:
> >>>
> >>>   INDEX    LINE ADDRESS
> >>>   0          48 0x0000000000400487
> >>>   1          52 0x000000000040048e
> >>>   2         END 0x000000000040048e
> >>>   3          54 0x0000000000400492
> >>>   4          56 0x0000000000400497
> >>>   5         END 0x000000000040049a
> >>>   6          62 0x000000000040049a
> >>>   7          66 0x00000000004004a1
> >>>   8         END 0x00000000004004a1
> >>>   9          68 0x00000000004004a5
> >>>   10         70 0x00000000004004aa
> >>>   11         72 0x00000000004004b9
> >>>   12        END 0x00000000004004bc
> >>>   13         76 0x00000000004004bc
> >>>   14         78 0x00000000004004c0
> >>>   15         80 0x00000000004004c5
> >>>   16        END 0x00000000004004cc
> >>>
> >>> When calling find_pc_sect_line with the address 0x000000000040048e, in
> >>> both cases we find entry #3, we then try to find the previous entry,
> >>> which originally found this entry '2         52 0x000000000040048e',
> >>> after the patch it finds '2         END 0x000000000040048e', which
> >>> cases the lookup to fail.
> >>>
> >>> By skipping the END marker after this patch we get back to the correct
> >>> entry, which is now #1: '1          52 0x000000000040048e', and
> >>> everything works again.
> >>
> >> I start to suspect that you have been working around an incorrect line
> >> table.
> >>
> >> Consider this bit:
> >> ...
> >>    0          48 0x0000000000400487
> >>    1          52 0x000000000040048e
> >>    2         END 0x000000000040048e
> >> ...
> >>
> >> The end marker marks the address one past the end of the sequence.
> >> Therefore, it makes no sense to have an entry in the sequence with the
> >> same address as the end marker.
> >>
> >> [ dwarf doc:
> >>
> >> end_sequence:
> >>
> >> A boolean indicating that the current address is that of the first byte
> >> after the end of a sequence of target machine instructions. end_sequence
> >> terminates a sequence of lines; therefore other information in the same
> >> row is not meaningful.
> >>
> >> DW_LNE_end_sequence:
> >>
> >> The DW_LNE_end_sequence opcode takes no operands. It sets the
> >> end_sequence register of the state machine to “true” and appends a row
> >> to the matrix using the current values of the state-machine registers.
> >> Then it resets the registers to the initial values specified above (see
> >> Section 6.2.2). Every line number program sequence must end with a
> >> DW_LNE_end_sequence instruction which creates a row whose address is
> >> that of the byte after the last target machine instruction of the sequence.
> >>
> >> ]
> >>
> >> The incorrect entry is generated by this dwarf assembler sequence:
> >> ...
> >>                 {DW_LNS_copy}
> >>                 {DW_LNE_end_sequence}
> >> ...
> >>
> >> I think we should probably fix the dwarf assembly test-cases.
> >>
> >> If we want to handle this in gdb, the thing that seems most logical to
> >> me is to ignore this kind of entries.
> > 
> > Hmm, that seems to be done already, in buildsym_compunit::record_line.
> > 
> > Anyway, I was looking at the line table for
> > gdb.dwarf2/dw2-ranges-base.exp, and got a line table with subsequent end
> > markers:
> > ...
> > INDEX  LINE   ADDRESS            IS-STMT
> > 0      31     0x00000000004004a7 Y
> > 1      21     0x00000000004004ae Y
> > 2      END    0x00000000004004ae Y
> > 3      11     0x00000000004004ba Y
> > 4      END    0x00000000004004ba Y
> > 5      END    0x00000000004004c6 Y
> > ...
> > 
> > By using this patch:
> > ...
> > diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> > index 33bf6523e9..76f0b54ff6 100644
> > --- a/gdb/buildsym.c
> > +++ b/gdb/buildsym.c
> > @@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector
> > (struct block *static_block,
> >             = [] (const linetable_entry &ln1,
> >                   const linetable_entry &ln2) -> bool
> >               {
> > +               if (ln1.pc == ln2.pc
> > +                   && ((ln1.line == 0) != (ln2.line == 0)))
> > +                 return ln1.line == 0 ? true : false;
> > +
> >                 return (ln1.pc < ln2.pc);
> >               };
> > 
> > ...
> > I get the expected:
> > ...
> > INDEX  LINE   ADDRESS            IS-STMT
> > 0      31     0x00000000004004a7 Y
> > 1      END    0x00000000004004ae Y
> > 2      21     0x00000000004004ae Y
> > 3      END    0x00000000004004ba Y
> > 4      11     0x00000000004004ba Y
> > 5      END    0x00000000004004c6 Y
> > ...
> 
> Any comments on patch below?

Yes! Thank you for working on this.

This should go in, however, I'd like to tweak the commit message a bit
please (see below).

Also, do you plan to include the revert of find_pc_sect_line from
3d92a3e313 in this patch - I think you should.

Thanks,
Andrew

> 
> Thanks,
> - Tom
> 

> [gdb/symtab] Fix line-table end-of-sequence sorting
> 
> Consider test-case gdb.dwarf2/dw2-ranges-base.exp.  It has a line-table for
> dw2-ranges-base.c like this:
> ...
>  Line Number Statements:
>   [0x0000014e]  Extended opcode 2: set Address to 0x4004ba
>   [0x00000159]  Advance Line by 10 to 11
>   [0x0000015b]  Copy
>   [0x0000015c]  Advance PC by 12 to 0x4004c6
>   [0x0000015e]  Advance Line by 19 to 30
>   [0x00000160]  Copy
>   [0x00000161]  Extended opcode 1: End of Sequence
> 
>   [0x00000164]  Extended opcode 2: set Address to 0x4004ae
>   [0x0000016f]  Advance Line by 20 to 21
>   [0x00000171]  Copy
>   [0x00000172]  Advance PC by 12 to 0x4004ba
>   [0x00000174]  Advance Line by 29 to 50
>   [0x00000176]  Copy
>   [0x00000177]  Extended opcode 1: End of Sequence
> 
>   [0x0000017a]  Extended opcode 2: set Address to 0x4004a7
>   [0x00000185]  Advance Line by 30 to 31
>   [0x00000187]  Copy
>   [0x00000188]  Advance PC by 7 to 0x4004ae
>   [0x0000018a]  Advance Line by 39 to 70
>   [0x0000018c]  Copy
>   [0x0000018d]  Extended opcode 1: End of Sequence
> ...
> 
> The Copy followed by End-of-Sequence is as specified in the dwarf assembly,
> but incorrect.  F.i., consider:
> ...
>   [0x0000015c]  Advance PC by 12 to 0x4004c6
>   [0x0000015e]  Advance Line by 19 to 30
>   [0x00000160]  Copy
>   [0x00000161]  Extended opcode 1: End of Sequence
> ...
> 
> Both the Copy and the End-of-Sequence append a row to the matrix using the
> same addres: 0x4004c6.  The Copy declares a target instruction at that
> address.  The End-of-Sequence declares that the sequence ends before that
> address.  It's a contradiction that the target instruction is both part of the
> sequence (according to Copy) and not part of the sequence (according to
> End-of-Sequence).

I don't want to argue about this specific test, but about the idea of
an empty line occurring at the same address as an end of sequence
marker.  This can happen due to compiler optimisation, though it is
perfectly reasonable to suggest that in this case the compiler should
be removing the line marker, this doesn't always happen.

I guess what I'm saying is that I think the case for this being
obviously wrong is not quite as clear cut as you seem to imply, but
also, I don't think this is really relevant to this bug - so maybe we
could just drop this part?

> The offending Copy is skipped though by buildsym_compunit::record_line for
> unrelated reasons.

Not really unrelated - specifically to catch this case (assuming we're
thinking about the same code).

>                     So, if we disable the sorting in
> buildsym_compunit::end_symtab_with_blockvector, we have:
> ...
> INDEX  LINE   ADDRESS            IS-STMT
> 0      11     0x00000000004004ba Y
> 1      END    0x00000000004004c6 Y
> 2      21     0x00000000004004ae Y
> 3      END    0x00000000004004ba Y
> 4      31     0x00000000004004a7 Y
> 5      END    0x00000000004004ae Y
> ...
> but if we re-enable the sorting, we have:
> ...
> INDEX  LINE   ADDRESS            IS-STMT
> 0      31     0x00000000004004a7 Y
> 1      21     0x00000000004004ae Y
> 2      END    0x00000000004004ae Y
> 3      11     0x00000000004004ba Y
> 4      END    0x00000000004004ba Y
> 5      END    0x00000000004004c6 Y
> ...
> which has both:
> - the contradictory order for the same-address pairs 1/2 and 3/4, as well as
> - a non-sensical pair of ENDs,
> while we'd like:
> ...
> INDEX  LINE   ADDRESS            IS-STMT
> 0      31     0x00000000004004a7 Y
> 1      END    0x00000000004004ae Y
> 2      21     0x00000000004004ae Y
> 3      END    0x00000000004004ba Y
> 4      11     0x00000000004004ba Y
> 5      END    0x00000000004004c6 Y
> ...

You're description here is absolutely correct, but I don't feel it
doesn't draw enough attention to what the real mistake inside GDB is.

Consider this line table, it shows two sequences, each delimited with
an END marker.  The table is extracted from the DWARF with the entries
in this exact order:

  | Address | Line |
  |---------+------|
  |   0x100 |   10 |
  |   0x102 |   11 |
  |   0x104 |  END |
  |---------+------|
  |    0x98 |  100 |
  |    0x9a |  101 |
  |    0x9c |  102 |
  |    0x9e |  103 |
  |   0x100 |  END |
  |---------+------|

What we want is to reorder the two sequences relative to each other.
The current sorting does this by sorting on 'Address', while leaving
entries at the same address in their original order, this gives:

  | Address | Line |
  |---------+------|
  |    0x98 |  100 |
  |    0x9a |  101 |
  |    0x9c |  102 |
  |    0x9e |  103 |
  |   0x100 |   10 |
  |   0x100 |  END |
  |---------+------|
  |   0x102 |   11 |
  |   0x104 |  END |
  |---------+------|

Here we see that line 10 has jumped from being the start of one
sequence to appear at the end of the other sequence's END marker, this
is maintained the table order, but is clearly incorrect.

A better sort would order by 'Address', but always move END markers to
be the first entry at a given 'Address', this would give us:

  | Address | Line |
  |---------+------|
  |    0x98 |  100 |
  |    0x9a |  101 |
  |    0x9c |  102 |
  |    0x9e |  103 |
  |   0x100 |  END |
  |---------+------|
  |   0x100 |   10 |
  |   0x102 |   11 |
  |   0x104 |  END |
  |---------+------|

> 
> This is a regression since commit 3d92a3e313 "gdb: Don't reorder line table
> entries too much when sorting", that introduced sorting on address while
> keeping entries with the same address in pre-sort order, which leads to
> incorrect results if one of the entries is an End-Of-Sequence.

Before commit 3d92a3e313, entries at the same address were sorted by
line number.  The correctly pushes END markers (line number 0) to be
the first entry at that address, but also corrupts the ordering of non
END markers.

This commit corrects the mistake of 3d92a3e313, by finding a middle
ground; keep entries at the same address in order _except_ for end
markers, which are always sorted to be earlier in the table.

> 
> Fix this by handling End-Of-Sequence entries in the sorting function.
> 
> Tested on x86_64-linux.
> 
> gdb/ChangeLog:
> 
> 2020-06-06  Tom de Vries  <tdevries@suse.de>
> 
> 	* buildsym.c (buildsym_compunit::end_symtab_with_blockvector): Handle
> 	End-Of-Sequence in lte_is_less_than.
> 
> gdb/testsuite/ChangeLog:
> 
> 2020-06-06  Tom de Vries  <tdevries@suse.de>
> 
> 	* gdb.dwarf2/dw2-ranges-base.exp: Test line-table order.
> 
> ---
>  gdb/buildsym.c                               |  4 ++++
>  gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp | 14 ++++++++++++++
>  2 files changed, 18 insertions(+)
> 
> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> index 33bf6523e9..76f0b54ff6 100644
> --- a/gdb/buildsym.c
> +++ b/gdb/buildsym.c
> @@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector (struct block *static_block,
>  	    = [] (const linetable_entry &ln1,
>  		  const linetable_entry &ln2) -> bool
>  	      {
> +		if (ln1.pc == ln2.pc
> +		    && ((ln1.line == 0) != (ln2.line == 0)))
> +		  return ln1.line == 0 ? true : false;
> +
>  		return (ln1.pc < ln2.pc);
>  	      };
>  
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> index 92f8f6cecb..39281a8857 100644
> --- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> @@ -144,12 +144,26 @@ gdb_test "info line frame3" \
>  
>  # Ensure that the line table correctly tracks the end of sequence markers.
>  set end_seq_count 0
> +set prev -1
> +set seq_count 0
>  gdb_test_multiple "maint info line-table gdb.dwarf2/dw2-ranges-base.c" \
>      "count END markers in line table" {
>  	-re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
> +	    if { $prev != -1 } {
> +		gdb_assert "$prev == 1" \
> +		    "prev of normal entry at $seq_count is end marker"
> +	    }
> +	    set prev 0
> +	    incr seq_count
>  	    exp_continue
>  	}
>  	-re "^$decimal\[ \t\]+END\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
> +	    if { $prev != -1 } {
> +		gdb_assert "$prev == 0" \
> +		    "prev of end marker at $seq_count is normal entry"
> +	    }
> +	    set prev 1
> +	    incr seq_count
>  	    incr end_seq_count
>  	    exp_continue
>  	}


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

* [gdb/testsuite] Fix bad line table entry sequence
  2020-06-06  9:25         ` Andrew Burgess
@ 2020-06-08 14:40           ` Tom de Vries
  2020-06-15 10:31             ` Andrew Burgess
  2020-06-08 15:50           ` [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting Tom de Vries
  1 sibling, 1 reply; 25+ messages in thread
From: Tom de Vries @ 2020-06-08 14:40 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

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

[ was: Re: [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting ]

On 06-06-2020 11:25, Andrew Burgess wrote:
> * Tom de Vries <tdevries@suse.de> [2020-06-06 01:44:42 +0200]:
> 
>> [ was: Re: [PATCH 2/3] gdb: Don't reorder line table entries too much
>> when sorting. ]
>>
>> On 05-06-2020 18:00, Tom de Vries wrote:
>>> On 05-06-2020 16:49, Tom de Vries wrote:
>>>> On 23-12-2019 02:51, Andrew Burgess wrote:
>>>>> I had to make a small adjustment in find_pc_sect_line in order to
>>>>> correctly find the previous line in the line table.  In some line
>>>>> tables I was seeing an actual line entry and an end of sequence marker
>>>>> at the same address, before this commit these would reorder to move
>>>>> the end of sequence marker before the line entry (end of sequence has
>>>>> line number 0).  Now the end of sequence marker remains in its correct
>>>>> location, and in order to find a previous line we should step backward
>>>>> over any end of sequence markers.
>>>>>
>>>>> As an example, the binary:
>>>>>   gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold
>>>>>
>>>>> Has this line table before the patch:
>>>>>
>>>>>   INDEX    LINE ADDRESS
>>>>>   0          48 0x0000000000400487
>>>>>   1         END 0x000000000040048e
>>>>>   2          52 0x000000000040048e
>>>>>   3          54 0x0000000000400492
>>>>>   4          56 0x0000000000400497
>>>>>   5         END 0x000000000040049a
>>>>>   6          62 0x000000000040049a
>>>>>   7         END 0x00000000004004a1
>>>>>   8          66 0x00000000004004a1
>>>>>   9          68 0x00000000004004a5
>>>>>   10         70 0x00000000004004aa
>>>>>   11         72 0x00000000004004b9
>>>>>   12        END 0x00000000004004bc
>>>>>   13         76 0x00000000004004bc
>>>>>   14         78 0x00000000004004c0
>>>>>   15         80 0x00000000004004c5
>>>>>   16        END 0x00000000004004cc
>>>>>
>>>>> And after this patch:
>>>>>
>>>>>   INDEX    LINE ADDRESS
>>>>>   0          48 0x0000000000400487
>>>>>   1          52 0x000000000040048e
>>>>>   2         END 0x000000000040048e
>>>>>   3          54 0x0000000000400492
>>>>>   4          56 0x0000000000400497
>>>>>   5         END 0x000000000040049a
>>>>>   6          62 0x000000000040049a
>>>>>   7          66 0x00000000004004a1
>>>>>   8         END 0x00000000004004a1
>>>>>   9          68 0x00000000004004a5
>>>>>   10         70 0x00000000004004aa
>>>>>   11         72 0x00000000004004b9
>>>>>   12        END 0x00000000004004bc
>>>>>   13         76 0x00000000004004bc
>>>>>   14         78 0x00000000004004c0
>>>>>   15         80 0x00000000004004c5
>>>>>   16        END 0x00000000004004cc
>>>>>
>>>>> When calling find_pc_sect_line with the address 0x000000000040048e, in
>>>>> both cases we find entry #3, we then try to find the previous entry,
>>>>> which originally found this entry '2         52 0x000000000040048e',
>>>>> after the patch it finds '2         END 0x000000000040048e', which
>>>>> cases the lookup to fail.
>>>>>
>>>>> By skipping the END marker after this patch we get back to the correct
>>>>> entry, which is now #1: '1          52 0x000000000040048e', and
>>>>> everything works again.
>>>>
>>>> I start to suspect that you have been working around an incorrect line
>>>> table.
>>>>
>>>> Consider this bit:
>>>> ...
>>>>    0          48 0x0000000000400487
>>>>    1          52 0x000000000040048e
>>>>    2         END 0x000000000040048e
>>>> ...
>>>>
>>>> The end marker marks the address one past the end of the sequence.
>>>> Therefore, it makes no sense to have an entry in the sequence with the
>>>> same address as the end marker.
>>>>
>>>> [ dwarf doc:
>>>>
>>>> end_sequence:
>>>>
>>>> A boolean indicating that the current address is that of the first byte
>>>> after the end of a sequence of target machine instructions. end_sequence
>>>> terminates a sequence of lines; therefore other information in the same
>>>> row is not meaningful.
>>>>
>>>> DW_LNE_end_sequence:
>>>>
>>>> The DW_LNE_end_sequence opcode takes no operands. It sets the
>>>> end_sequence register of the state machine to “true” and appends a row
>>>> to the matrix using the current values of the state-machine registers.
>>>> Then it resets the registers to the initial values specified above (see
>>>> Section 6.2.2). Every line number program sequence must end with a
>>>> DW_LNE_end_sequence instruction which creates a row whose address is
>>>> that of the byte after the last target machine instruction of the sequence.
>>>>
>>>> ]
>>>>
>>>> The incorrect entry is generated by this dwarf assembler sequence:
>>>> ...
>>>>                 {DW_LNS_copy}
>>>>                 {DW_LNE_end_sequence}
>>>> ...
>>>>
>>>> I think we should probably fix the dwarf assembly test-cases.
>>>>
>>>> If we want to handle this in gdb, the thing that seems most logical to
>>>> me is to ignore this kind of entries.
>>>
>>> Hmm, that seems to be done already, in buildsym_compunit::record_line.
>>>
>>> Anyway, I was looking at the line table for
>>> gdb.dwarf2/dw2-ranges-base.exp, and got a line table with subsequent end
>>> markers:
>>> ...
>>> INDEX  LINE   ADDRESS            IS-STMT
>>> 0      31     0x00000000004004a7 Y
>>> 1      21     0x00000000004004ae Y
>>> 2      END    0x00000000004004ae Y
>>> 3      11     0x00000000004004ba Y
>>> 4      END    0x00000000004004ba Y
>>> 5      END    0x00000000004004c6 Y
>>> ...
>>>
>>> By using this patch:
>>> ...
>>> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
>>> index 33bf6523e9..76f0b54ff6 100644
>>> --- a/gdb/buildsym.c
>>> +++ b/gdb/buildsym.c
>>> @@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector
>>> (struct block *static_block,
>>>             = [] (const linetable_entry &ln1,
>>>                   const linetable_entry &ln2) -> bool
>>>               {
>>> +               if (ln1.pc == ln2.pc
>>> +                   && ((ln1.line == 0) != (ln2.line == 0)))
>>> +                 return ln1.line == 0 ? true : false;
>>> +
>>>                 return (ln1.pc < ln2.pc);
>>>               };
>>>
>>> ...
>>> I get the expected:
>>> ...
>>> INDEX  LINE   ADDRESS            IS-STMT
>>> 0      31     0x00000000004004a7 Y
>>> 1      END    0x00000000004004ae Y
>>> 2      21     0x00000000004004ae Y
>>> 3      END    0x00000000004004ba Y
>>> 4      11     0x00000000004004ba Y
>>> 5      END    0x00000000004004c6 Y
>>> ...
>>
>> Any comments on patch below?
> 
> Yes! Thank you for working on this.
> 
> This should go in, however, I'd like to tweak the commit message a bit
> please (see below).
> 
> Also, do you plan to include the revert of find_pc_sect_line from
> 3d92a3e313 in this patch - I think you should.
> 
> Thanks,
> Andrew
> 
>>
>> Thanks,
>> - Tom
>>
> 
>> [gdb/symtab] Fix line-table end-of-sequence sorting
>>
>> Consider test-case gdb.dwarf2/dw2-ranges-base.exp.  It has a line-table for
>> dw2-ranges-base.c like this:
>> ...
>>  Line Number Statements:
>>   [0x0000014e]  Extended opcode 2: set Address to 0x4004ba
>>   [0x00000159]  Advance Line by 10 to 11
>>   [0x0000015b]  Copy
>>   [0x0000015c]  Advance PC by 12 to 0x4004c6
>>   [0x0000015e]  Advance Line by 19 to 30
>>   [0x00000160]  Copy
>>   [0x00000161]  Extended opcode 1: End of Sequence
>>
>>   [0x00000164]  Extended opcode 2: set Address to 0x4004ae
>>   [0x0000016f]  Advance Line by 20 to 21
>>   [0x00000171]  Copy
>>   [0x00000172]  Advance PC by 12 to 0x4004ba
>>   [0x00000174]  Advance Line by 29 to 50
>>   [0x00000176]  Copy
>>   [0x00000177]  Extended opcode 1: End of Sequence
>>
>>   [0x0000017a]  Extended opcode 2: set Address to 0x4004a7
>>   [0x00000185]  Advance Line by 30 to 31
>>   [0x00000187]  Copy
>>   [0x00000188]  Advance PC by 7 to 0x4004ae
>>   [0x0000018a]  Advance Line by 39 to 70
>>   [0x0000018c]  Copy
>>   [0x0000018d]  Extended opcode 1: End of Sequence
>> ...
>>
>> The Copy followed by End-of-Sequence is as specified in the dwarf assembly,
>> but incorrect.  F.i., consider:
>> ...
>>   [0x0000015c]  Advance PC by 12 to 0x4004c6
>>   [0x0000015e]  Advance Line by 19 to 30
>>   [0x00000160]  Copy
>>   [0x00000161]  Extended opcode 1: End of Sequence
>> ...
>>
>> Both the Copy and the End-of-Sequence append a row to the matrix using the
>> same addres: 0x4004c6.  The Copy declares a target instruction at that
>> address.  The End-of-Sequence declares that the sequence ends before that
>> address.  It's a contradiction that the target instruction is both part of the
>> sequence (according to Copy) and not part of the sequence (according to
>> End-of-Sequence).
> 
> I don't want to argue about this specific test, but about the idea of
> an empty line occurring at the same address as an end of sequence
> marker.  This can happen due to compiler optimisation, though it is
> perfectly reasonable to suggest that in this case the compiler should
> be removing the line marker, this doesn't always happen.
> 
> I guess what I'm saying is that I think the case for this being
> obviously wrong is not quite as clear cut as you seem to imply>, but
> also, I don't think this is really relevant to this bug - so maybe we
> could just drop this part?
> 

To me it's obviously wrong, so it would help me if you explain your
interpretation of the dwarf standard on this topic.

I think though I may have indeed conflated two issues in my original
patch, so I've now got one patch that deals with the incorrect dwarf in
the dwarf test-cases (attached below), and I'll submit another dealing
with the regression.  I hope that will make the conversation easier.

Thanks,
- Tom

[-- Attachment #2: 0001-gdb-testsuite-Fix-bad-line-table-entry-sequence.patch --]
[-- Type: text/x-patch, Size: 10220 bytes --]

[gdb/testsuite] Fix bad line table entry sequence

Consider test-case gdb.dwarf2/dw2-ranges-base.exp.  It has a line-table for
dw2-ranges-base.c like this:
...
 Line Number Statements:
  [0x0000014e]  Extended opcode 2: set Address to 0x4004ba
  [0x00000159]  Advance Line by 10 to 11
  [0x0000015b]  Copy
  [0x0000015c]  Advance PC by 12 to 0x4004c6
  [0x0000015e]  Advance Line by 19 to 30
  [0x00000160]  Copy
  [0x00000161]  Extended opcode 1: End of Sequence
...

The Copy followed by End-of-Sequence is as specified in the dwarf assembly,
but incorrect.

Both the Copy and the End-of-Sequence append a row to the matrix, each using
the same addres: 0x4004c6.  The Copy declares a target instruction at that
address.  The End-of-Sequence declares that the sequence ends before that
address.

It's a contradiction that the target instruction is both part of the sequence
(according to Copy) and not part of the sequence (according to End-of-Sequence).

The offending Copy is silently skipped by buildsym_compunit::record_line.  A
gdb PR has been filed to warn about this (PR26092).

Fix the dwarf assembly test-cases that contain this contradictory construct,
to prevent people from:
- coming across this while testing patches, and mistakenly getting the
  impression that this is valid dwarf, or even
- copying it to new test-cases.

Also, add a dwarf assembly test-case called dw2-bad-dw-lne-end-sequence.exp
containing this construct, to verify that it's ignored.

Tested on x86_64-linux.

Also tested using a detection patch with assert, such that the only test-case
triggering that assert is dw2-bad-dw-lne-end-sequence.exp.  The corresponding
build had to be done without --with-separate-debug-dir=/usr/lib/debug",
otherwise we'd trigger the assert in debug info generated with recent GCC (PR
gcc/95574).

gdb/testsuite/ChangeLog:

2020-06-08  Tom de Vries  <tdevries@suse.de>

	* gdb.dwarf2/dw2-bad-elf.exp: Fix bad line table entry sequence.
	* gdb.dwarf2/dw2-dir-file-name.exp: Same.
	* gdb.dwarf2/dw2-inline-small-func.exp: Same.
	* gdb.dwarf2/dw2-ranges-base.exp: Same.
	* gdb.dwarf2/dw2-ranges-func.exp: Same.
	* gdb.dwarf2/dw2-bad-dw-lne-end-sequence.exp: New file.

---
 .../gdb.dwarf2/dw2-bad-dw-lne-end-sequence.exp     | 116 +++++++++++++++++++++
 gdb/testsuite/gdb.dwarf2/dw2-bad-elf.exp           |   4 -
 gdb/testsuite/gdb.dwarf2/dw2-dir-file-name.exp     |   1 -
 gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp |   1 -
 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp       |   6 --
 gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp       |  10 --
 6 files changed, 116 insertions(+), 22 deletions(-)

diff --git a/gdb/testsuite/gdb.dwarf2/dw2-bad-dw-lne-end-sequence.exp b/gdb/testsuite/gdb.dwarf2/dw2-bad-dw-lne-end-sequence.exp
new file mode 100644
index 0000000000..b01f82ce85
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-bad-dw-lne-end-sequence.exp
@@ -0,0 +1,116 @@
+# 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/>.
+
+# Test that line table entries with the same address as an DW_LNE_end_sequence
+# are ignored.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    verbose "Skipping bad DW_LNE_end_sequence test."
+    return 0
+}
+
+standard_testfile main.c .dwarf.S
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile2
+    declare_labels L
+
+    # Find start address and length for our functions.
+    set main_func \
+	[function_range main [list ${srcdir}/${subdir}/$srcfile]]
+
+    cu {} {
+	compile_unit {
+	    {language @DW_LANG_C}
+	    {name main.c}
+	    {stmt_list $L DW_FORM_sec_offset}
+	} {
+	    subprogram {
+		{external 1 flag}
+		{name main}
+	    }
+	}
+    }
+
+    lines {version 2} L {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile" 1
+
+	# Generate simple line table program.  The last DW_LNS_copy is
+	# problematic because it has the same address as the
+	# DW_LNE_end_sequence.  We'll test that it's ignored.
+	program {
+	    {DW_LNE_set_address [lindex $main_func 0]}
+	    {DW_LNS_advance_line 10}
+	    {DW_LNS_copy}
+	    {DW_LNS_advance_pc [lindex $main_func 1]}
+	    {DW_LNS_advance_line 19}
+	    {DW_LNS_copy}
+	    {DW_LNE_end_sequence}
+	}
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	  [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+gdb_test_no_output "set auto-solib-add off"
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_test_no_output "maint expand-symtabs gdb.dwarf2/main.c" \
+    "maint expand-symtabs"
+
+set header_re \
+    [multi_line \
+	 ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):" \
+	 "INDEX\[ \t\]+LINE\[ \t\]+ADDRESS\[ \t\]+IS-STMT *" \
+	 ""]
+
+set end_of_sequence_re \
+    "^$decimal\[ \t\]+END\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n"
+
+set ok 1
+set cmd "maint info line-table gdb.dwarf2/main.c"
+gdb_test_multiple $cmd "bad ops ignored" {
+    -re "^$decimal\[ \t\]+($decimal)\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
+	set line $expect_out(1,string)
+	if { $line != 11 } {
+	    set ok 0
+	}
+	exp_continue
+    }
+    -re "$end_of_sequence_re" {
+	exp_continue
+    }
+    -re "^$gdb_prompt $" {
+	if { $ok } {
+	    pass $gdb_test_name
+	} else {
+	    fail $gdb_test_name
+	}
+    }
+    -re $header_re {
+	exp_continue
+    }
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-bad-elf.exp b/gdb/testsuite/gdb.dwarf2/dw2-bad-elf.exp
index f6fe54a634..88a522ad63 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-bad-elf.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-bad-elf.exp
@@ -126,8 +126,6 @@ Dwarf::assemble $asm_file {
 	    {DW_LNS_advance_line 10}
 	    {DW_LNS_copy}
 	    {DW_LNS_advance_pc [lindex $main_result 1]}
-	    {DW_LNS_advance_line 19}
-	    {DW_LNS_copy}
 	    {DW_LNE_end_sequence}
 	}
     }
@@ -142,8 +140,6 @@ Dwarf::assemble $asm_file {
 	    {DW_LNS_advance_line 5}
 	    {DW_LNS_copy}
 	    {DW_LNS_advance_pc 64}
-	    {DW_LNS_advance_line 8}
-	    {DW_LNS_copy}
 	    {DW_LNE_end_sequence}
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-dir-file-name.exp b/gdb/testsuite/gdb.dwarf2/dw2-dir-file-name.exp
index 0de71f29d5..3fc142661b 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-dir-file-name.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-dir-file-name.exp
@@ -146,7 +146,6 @@ proc out_line { name cu_dir cu_name line_dir line_name } {
 	.uleb128	${addr_len}+1
 	.byte		2
 	.${addr_len}byte ${name}_end
-	.byte		1	/* DW_LNS_copy */
 	.byte		0	/* DW_LNE_end_of_sequence */
 	.uleb128	1
 	.byte		1
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
index 4fcc3cfeac..1efe49d009 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
@@ -114,7 +114,6 @@ Dwarf::assemble $asm_file {
 	    {DW_LNS_copy}
 
 	    {DW_LNE_set_address line_label_3}
-	    {DW_LNS_copy}
 	    {DW_LNE_end_sequence}
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
index 92f8f6cecb..9e4ebf7f3c 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
@@ -88,24 +88,18 @@ Dwarf::assemble $asm_file {
 	    {DW_LNS_advance_line 10}
 	    {DW_LNS_copy}
 	    {DW_LNS_advance_pc [lindex $main_func 1]}
-	    {DW_LNS_advance_line 19}
-	    {DW_LNS_copy}
 	    {DW_LNE_end_sequence}
 
 	    {DW_LNE_set_address [lindex $frame2_func 0]}
 	    {DW_LNS_advance_line 20}
 	    {DW_LNS_copy}
 	    {DW_LNS_advance_pc [lindex $frame2_func 1]}
-	    {DW_LNS_advance_line 29}
-	    {DW_LNS_copy}
 	    {DW_LNE_end_sequence}
 
 	    {DW_LNE_set_address [lindex $frame3_func 0]}
 	    {DW_LNS_advance_line 30}
 	    {DW_LNS_copy}
 	    {DW_LNS_advance_pc [lindex $frame3_func 1]}
-	    {DW_LNS_advance_line 39}
-	    {DW_LNS_copy}
 	    {DW_LNE_end_sequence}
 	}
     }
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp
index 0a95adc5bc..eafd9f65fb 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp
@@ -143,8 +143,6 @@ proc do_test {suffix} {
 		{DW_LNS_advance_line [expr [gdb_get_line_number "main return"] - [gdb_get_line_number "main foo call"]]}
 		{DW_LNS_copy}
 		{DW_LNE_set_address $main_end}
-		{DW_LNS_advance_line [expr [gdb_get_line_number "main end"] - [gdb_get_line_number "main return"] + 1]}
-		{DW_LNS_copy}
 		{DW_LNE_end_sequence}
 
 		{DW_LNE_set_address $foo_start}
@@ -160,24 +158,18 @@ proc do_test {suffix} {
 		{DW_LNS_advance_line [expr [gdb_get_line_number "foo end"] - [gdb_get_line_number "foo foo_cold call"]]}
 		{DW_LNS_copy}
 		{DW_LNE_set_address $foo_end}
-		{DW_LNS_advance_line 1}
-		{DW_LNS_copy}
 		{DW_LNE_end_sequence}
 
 		{DW_LNE_set_address $bar_start}
 		{DW_LNS_advance_line [expr [gdb_get_line_number "bar end"] - 1]}
 		{DW_LNS_copy}
 		{DW_LNS_advance_pc $bar_len}
-		{DW_LNS_advance_line 1}
-		{DW_LNS_copy}
 		{DW_LNE_end_sequence}
 
 		{DW_LNE_set_address $baz_start}
 		{DW_LNS_advance_line [expr [gdb_get_line_number "baz end"] - 1]}
 		{DW_LNS_copy}
 		{DW_LNS_advance_pc $baz_len}
-		{DW_LNS_advance_line 1}
-		{DW_LNS_copy}
 		{DW_LNE_end_sequence}
 
 		{DW_LNE_set_address $foo_cold_start}
@@ -190,8 +182,6 @@ proc do_test {suffix} {
 		{DW_LNS_advance_line [expr [gdb_get_line_number "foo_cold end"] - [gdb_get_line_number "foo_cold baz call"]]}
 		{DW_LNS_copy}
 		{DW_LNE_set_address $foo_cold_end}
-		{DW_LNS_advance_line 1}
-		{DW_LNS_copy}
 		{DW_LNE_end_sequence}
 	    }
 	}

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

* Re: [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting
  2020-06-06  9:25         ` Andrew Burgess
  2020-06-08 14:40           ` [gdb/testsuite] Fix bad line table entry sequence Tom de Vries
@ 2020-06-08 15:50           ` Tom de Vries
  2020-06-15 10:42             ` Andrew Burgess
  1 sibling, 1 reply; 25+ messages in thread
From: Tom de Vries @ 2020-06-08 15:50 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

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

On 06-06-2020 11:25, Andrew Burgess wrote:
> * Tom de Vries <tdevries@suse.de> [2020-06-06 01:44:42 +0200]:
> 
>> [ was: Re: [PATCH 2/3] gdb: Don't reorder line table entries too much
>> when sorting. ]
>>
>> On 05-06-2020 18:00, Tom de Vries wrote:
>>> On 05-06-2020 16:49, Tom de Vries wrote:
>>>> On 23-12-2019 02:51, Andrew Burgess wrote:
>>>>> I had to make a small adjustment in find_pc_sect_line in order to
>>>>> correctly find the previous line in the line table.  In some line
>>>>> tables I was seeing an actual line entry and an end of sequence marker
>>>>> at the same address, before this commit these would reorder to move
>>>>> the end of sequence marker before the line entry (end of sequence has
>>>>> line number 0).  Now the end of sequence marker remains in its correct
>>>>> location, and in order to find a previous line we should step backward
>>>>> over any end of sequence markers.
>>>>>
>>>>> As an example, the binary:
>>>>>   gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold
>>>>>
>>>>> Has this line table before the patch:
>>>>>
>>>>>   INDEX    LINE ADDRESS
>>>>>   0          48 0x0000000000400487
>>>>>   1         END 0x000000000040048e
>>>>>   2          52 0x000000000040048e
>>>>>   3          54 0x0000000000400492
>>>>>   4          56 0x0000000000400497
>>>>>   5         END 0x000000000040049a
>>>>>   6          62 0x000000000040049a
>>>>>   7         END 0x00000000004004a1
>>>>>   8          66 0x00000000004004a1
>>>>>   9          68 0x00000000004004a5
>>>>>   10         70 0x00000000004004aa
>>>>>   11         72 0x00000000004004b9
>>>>>   12        END 0x00000000004004bc
>>>>>   13         76 0x00000000004004bc
>>>>>   14         78 0x00000000004004c0
>>>>>   15         80 0x00000000004004c5
>>>>>   16        END 0x00000000004004cc
>>>>>
>>>>> And after this patch:
>>>>>
>>>>>   INDEX    LINE ADDRESS
>>>>>   0          48 0x0000000000400487
>>>>>   1          52 0x000000000040048e
>>>>>   2         END 0x000000000040048e
>>>>>   3          54 0x0000000000400492
>>>>>   4          56 0x0000000000400497
>>>>>   5         END 0x000000000040049a
>>>>>   6          62 0x000000000040049a
>>>>>   7          66 0x00000000004004a1
>>>>>   8         END 0x00000000004004a1
>>>>>   9          68 0x00000000004004a5
>>>>>   10         70 0x00000000004004aa
>>>>>   11         72 0x00000000004004b9
>>>>>   12        END 0x00000000004004bc
>>>>>   13         76 0x00000000004004bc
>>>>>   14         78 0x00000000004004c0
>>>>>   15         80 0x00000000004004c5
>>>>>   16        END 0x00000000004004cc
>>>>>
>>>>> When calling find_pc_sect_line with the address 0x000000000040048e, in
>>>>> both cases we find entry #3, we then try to find the previous entry,
>>>>> which originally found this entry '2         52 0x000000000040048e',
>>>>> after the patch it finds '2         END 0x000000000040048e', which
>>>>> cases the lookup to fail.
>>>>>
>>>>> By skipping the END marker after this patch we get back to the correct
>>>>> entry, which is now #1: '1          52 0x000000000040048e', and
>>>>> everything works again.
>>>>
>>>> I start to suspect that you have been working around an incorrect line
>>>> table.
>>>>
>>>> Consider this bit:
>>>> ...
>>>>    0          48 0x0000000000400487
>>>>    1          52 0x000000000040048e
>>>>    2         END 0x000000000040048e
>>>> ...
>>>>
>>>> The end marker marks the address one past the end of the sequence.
>>>> Therefore, it makes no sense to have an entry in the sequence with the
>>>> same address as the end marker.
>>>>
>>>> [ dwarf doc:
>>>>
>>>> end_sequence:
>>>>
>>>> A boolean indicating that the current address is that of the first byte
>>>> after the end of a sequence of target machine instructions. end_sequence
>>>> terminates a sequence of lines; therefore other information in the same
>>>> row is not meaningful.
>>>>
>>>> DW_LNE_end_sequence:
>>>>
>>>> The DW_LNE_end_sequence opcode takes no operands. It sets the
>>>> end_sequence register of the state machine to “true” and appends a row
>>>> to the matrix using the current values of the state-machine registers.
>>>> Then it resets the registers to the initial values specified above (see
>>>> Section 6.2.2). Every line number program sequence must end with a
>>>> DW_LNE_end_sequence instruction which creates a row whose address is
>>>> that of the byte after the last target machine instruction of the sequence.
>>>>
>>>> ]
>>>>
>>>> The incorrect entry is generated by this dwarf assembler sequence:
>>>> ...
>>>>                 {DW_LNS_copy}
>>>>                 {DW_LNE_end_sequence}
>>>> ...
>>>>
>>>> I think we should probably fix the dwarf assembly test-cases.
>>>>
>>>> If we want to handle this in gdb, the thing that seems most logical to
>>>> me is to ignore this kind of entries.
>>>
>>> Hmm, that seems to be done already, in buildsym_compunit::record_line.
>>>
>>> Anyway, I was looking at the line table for
>>> gdb.dwarf2/dw2-ranges-base.exp, and got a line table with subsequent end
>>> markers:
>>> ...
>>> INDEX  LINE   ADDRESS            IS-STMT
>>> 0      31     0x00000000004004a7 Y
>>> 1      21     0x00000000004004ae Y
>>> 2      END    0x00000000004004ae Y
>>> 3      11     0x00000000004004ba Y
>>> 4      END    0x00000000004004ba Y
>>> 5      END    0x00000000004004c6 Y
>>> ...
>>>
>>> By using this patch:
>>> ...
>>> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
>>> index 33bf6523e9..76f0b54ff6 100644
>>> --- a/gdb/buildsym.c
>>> +++ b/gdb/buildsym.c
>>> @@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector
>>> (struct block *static_block,
>>>             = [] (const linetable_entry &ln1,
>>>                   const linetable_entry &ln2) -> bool
>>>               {
>>> +               if (ln1.pc == ln2.pc
>>> +                   && ((ln1.line == 0) != (ln2.line == 0)))
>>> +                 return ln1.line == 0 ? true : false;
>>> +
>>>                 return (ln1.pc < ln2.pc);
>>>               };
>>>
>>> ...
>>> I get the expected:
>>> ...
>>> INDEX  LINE   ADDRESS            IS-STMT
>>> 0      31     0x00000000004004a7 Y
>>> 1      END    0x00000000004004ae Y
>>> 2      21     0x00000000004004ae Y
>>> 3      END    0x00000000004004ba Y
>>> 4      11     0x00000000004004ba Y
>>> 5      END    0x00000000004004c6 Y
>>> ...
>>
>> Any comments on patch below?
> 
> Yes! Thank you for working on this.
> 
> This should go in, however, I'd like to tweak the commit message a bit
> please (see below).
> 
> Also, do you plan to include the revert of find_pc_sect_line from
> 3d92a3e313 in this patch - I think you should.
> 

Included in this version of the patch.

> Thanks,
> Andrew
> 
>>
>> Thanks,
>> - Tom
>>
> 
>> [gdb/symtab] Fix line-table end-of-sequence sorting
>>
>> Consider test-case gdb.dwarf2/dw2-ranges-base.exp.  It has a line-table for
>> dw2-ranges-base.c like this:
>> ...
>>  Line Number Statements:
>>   [0x0000014e]  Extended opcode 2: set Address to 0x4004ba
>>   [0x00000159]  Advance Line by 10 to 11
>>   [0x0000015b]  Copy
>>   [0x0000015c]  Advance PC by 12 to 0x4004c6
>>   [0x0000015e]  Advance Line by 19 to 30
>>   [0x00000160]  Copy
>>   [0x00000161]  Extended opcode 1: End of Sequence
>>
>>   [0x00000164]  Extended opcode 2: set Address to 0x4004ae
>>   [0x0000016f]  Advance Line by 20 to 21
>>   [0x00000171]  Copy
>>   [0x00000172]  Advance PC by 12 to 0x4004ba
>>   [0x00000174]  Advance Line by 29 to 50
>>   [0x00000176]  Copy
>>   [0x00000177]  Extended opcode 1: End of Sequence
>>
>>   [0x0000017a]  Extended opcode 2: set Address to 0x4004a7
>>   [0x00000185]  Advance Line by 30 to 31
>>   [0x00000187]  Copy
>>   [0x00000188]  Advance PC by 7 to 0x4004ae
>>   [0x0000018a]  Advance Line by 39 to 70
>>   [0x0000018c]  Copy
>>   [0x0000018d]  Extended opcode 1: End of Sequence
>> ...
>>
>> The Copy followed by End-of-Sequence is as specified in the dwarf assembly,
>> but incorrect.  F.i., consider:
>> ...
>>   [0x0000015c]  Advance PC by 12 to 0x4004c6
>>   [0x0000015e]  Advance Line by 19 to 30
>>   [0x00000160]  Copy
>>   [0x00000161]  Extended opcode 1: End of Sequence
>> ...
>>
>> Both the Copy and the End-of-Sequence append a row to the matrix using the
>> same addres: 0x4004c6.  The Copy declares a target instruction at that
>> address.  The End-of-Sequence declares that the sequence ends before that
>> address.  It's a contradiction that the target instruction is both part of the
>> sequence (according to Copy) and not part of the sequence (according to
>> End-of-Sequence).
> 
> I don't want to argue about this specific test, but about the idea of
> an empty line occurring at the same address as an end of sequence
> marker.  This can happen due to compiler optimisation, though it is
> perfectly reasonable to suggest that in this case the compiler should
> be removing the line marker, this doesn't always happen.
> 
> I guess what I'm saying is that I think the case for this being
> obviously wrong is not quite as clear cut as you seem to imply, but
> also, I don't think this is really relevant to this bug - so maybe we
> could just drop this part?
> 
>> The offending Copy is skipped though by buildsym_compunit::record_line for
>> unrelated reasons.
> 
> Not really unrelated - specifically to catch this case (assuming we're
> thinking about the same code).
>

I read this comment in 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.  */
...

So, my impression is that this was added to deal with problems related
to the sorting based on line numbers, and no to remove invalid dwarf
ops. Hence my "unrelated" classification.

>>                     So, if we disable the sorting in
>> buildsym_compunit::end_symtab_with_blockvector, we have:
>> ...
>> INDEX  LINE   ADDRESS            IS-STMT
>> 0      11     0x00000000004004ba Y
>> 1      END    0x00000000004004c6 Y
>> 2      21     0x00000000004004ae Y
>> 3      END    0x00000000004004ba Y
>> 4      31     0x00000000004004a7 Y
>> 5      END    0x00000000004004ae Y
>> ...
>> but if we re-enable the sorting, we have:
>> ...
>> INDEX  LINE   ADDRESS            IS-STMT
>> 0      31     0x00000000004004a7 Y
>> 1      21     0x00000000004004ae Y
>> 2      END    0x00000000004004ae Y
>> 3      11     0x00000000004004ba Y
>> 4      END    0x00000000004004ba Y
>> 5      END    0x00000000004004c6 Y
>> ...
>> which has both:
>> - the contradictory order for the same-address pairs 1/2 and 3/4, as well as
>> - a non-sensical pair of ENDs,
>> while we'd like:
>> ...
>> INDEX  LINE   ADDRESS            IS-STMT
>> 0      31     0x00000000004004a7 Y
>> 1      END    0x00000000004004ae Y
>> 2      21     0x00000000004004ae Y
>> 3      END    0x00000000004004ba Y
>> 4      11     0x00000000004004ba Y
>> 5      END    0x00000000004004c6 Y
>> ...
> 
> You're description here is absolutely correct, but I don't feel it
> doesn't draw enough attention to what the real mistake inside GDB is.
> 
> Consider this line table, it shows two sequences, each delimited with
> an END marker.  The table is extracted from the DWARF with the entries
> in this exact order:
> 
>   | Address | Line |
>   |---------+------|
>   |   0x100 |   10 |
>   |   0x102 |   11 |
>   |   0x104 |  END |
>   |---------+------|
>   |    0x98 |  100 |
>   |    0x9a |  101 |
>   |    0x9c |  102 |
>   |    0x9e |  103 |
>   |   0x100 |  END |
>   |---------+------|
> 
> What we want is to reorder the two sequences relative to each other.
> The current sorting does this by sorting on 'Address', while leaving
> entries at the same address in their original order, this gives:
> 
>   | Address | Line |
>   |---------+------|
>   |    0x98 |  100 |
>   |    0x9a |  101 |
>   |    0x9c |  102 |
>   |    0x9e |  103 |
>   |   0x100 |   10 |
>   |   0x100 |  END |
>   |---------+------|
>   |   0x102 |   11 |
>   |   0x104 |  END |
>   |---------+------|
> 
> Here we see that line 10 has jumped from being the start of one
> sequence to appear at the end of the other sequence's END marker, this
> is maintained the table order, but is clearly incorrect.
> 
> A better sort would order by 'Address', but always move END markers to
> be the first entry at a given 'Address', this would give us:
> 
>   | Address | Line |
>   |---------+------|
>   |    0x98 |  100 |
>   |    0x9a |  101 |
>   |    0x9c |  102 |
>   |    0x9e |  103 |
>   |   0x100 |  END |
>   |---------+------|
>   |   0x100 |   10 |
>   |   0x102 |   11 |
>   |   0x104 |  END |
>   |---------+------|
> 
>>
>> This is a regression since commit 3d92a3e313 "gdb: Don't reorder line table
>> entries too much when sorting", that introduced sorting on address while
>> keeping entries with the same address in pre-sort order, which leads to
>> incorrect results if one of the entries is an End-Of-Sequence.
> 
> Before commit 3d92a3e313, entries at the same address were sorted by
> line number.  The correctly pushes END markers (line number 0) to be
> the first entry at that address, but also corrupts the ordering of non
> END markers.
> 
> This commit corrects the mistake of 3d92a3e313, by finding a middle
> ground; keep entries at the same address in order _except_ for end
> markers, which are always sorted to be earlier in the table.
> 

Here's the updated patch, I've tried to make the commit message more
detailed based on your feedback above, I hope this addresses your concerns.

Thanks,
- Tom




[-- Attachment #2: 0002-gdb-symtab-Fix-line-table-end-of-sequence-sorting.patch --]
[-- Type: text/x-patch, Size: 5592 bytes --]

[gdb/symtab] Fix line-table end-of-sequence sorting

Consider test-case gdb.dwarf2/dw2-ranges-base.exp.  It has (after
"[gdb/testsuite] Fix bad line table entry sequence") a line-table for
dw2-ranges-base.c like this:
...
 Line Number Statements:
  [0x0000014e]  Extended opcode 2: set Address to 0x4004ba
  [0x00000159]  Advance Line by 10 to 11
  [0x0000015b]  Copy
  [0x0000015c]  Advance PC by 12 to 0x4004c6
  [0x0000015e]  Extended opcode 1: End of Sequence

  [0x00000161]  Extended opcode 2: set Address to 0x4004ae
  [0x0000016c]  Advance Line by 20 to 21
  [0x0000016e]  Copy
  [0x0000016f]  Advance PC by 12 to 0x4004ba
  [0x00000171]  Extended opcode 1: End of Sequence

  [0x00000174]  Extended opcode 2: set Address to 0x4004a7
  [0x0000017f]  Advance Line by 30 to 31
  [0x00000181]  Copy
  [0x00000182]  Advance PC by 7 to 0x4004ae
  [0x00000184]  Extended opcode 1: End of Sequence
...

If we disable the sorting in buildsym_compunit::end_symtab_with_blockvector,
we have the unsorted line table:
...
INDEX  LINE   ADDRESS            IS-STMT
0      11     0x00000000004004ba Y
1      END    0x00000000004004c6 Y
2      21     0x00000000004004ae Y
3      END    0x00000000004004ba Y
4      31     0x00000000004004a7 Y
5      END    0x00000000004004ae Y
...
It contains 3 sequences, 11/END, 21/END and 31/END.

We want to sort the 3 sequences relative to each other, while sorting on
address, to get:
...
INDEX  LINE   ADDRESS            IS-STMT
0      31     0x00000000004004a7 Y
1      END    0x00000000004004ae Y
2      21     0x00000000004004ae Y
3      END    0x00000000004004ba Y
4      11     0x00000000004004ba Y
5      END    0x00000000004004c6 Y
...

However, if we re-enable the sorting, we have instead:
...
INDEX  LINE   ADDRESS            IS-STMT
0      31     0x00000000004004a7 Y
1      21     0x00000000004004ae Y
2      END    0x00000000004004ae Y
3      11     0x00000000004004ba Y
4      END    0x00000000004004ba Y
5      END    0x00000000004004c6 Y
...

This is a regression since commit 3d92a3e313 "gdb: Don't reorder line table
entries too much when sorting", that introduced sorting on address while
keeping entries with the same address in pre-sort order.

Indeed the entries 1 and 2 are in pre-sort order (they map to entries 2 and 5
in the unsorted line table), but entry 1 does not belong in the sequence
terminated by 2.

Fix this by handling End-Of-Sequence entries in the sorting function, such
that they are sorted before other entries with the same address.

Also, revert the find_pc_sect_line workaround introduced in commit 3d92a3e313,
since that's no longer necessary.

Tested on x86_64-linux.

gdb/ChangeLog:

2020-06-06  Tom de Vries  <tdevries@suse.de>

	* buildsym.c (buildsym_compunit::end_symtab_with_blockvector): Handle
	End-Of-Sequence in lte_is_less_than.
	* symtab.c (find_pc_sect_line): Revert change from commit 3d92a3e313
	"gdb: Don't reorder line table entries too much when sorting".

gdb/testsuite/ChangeLog:

2020-06-06  Tom de Vries  <tdevries@suse.de>

	* gdb.dwarf2/dw2-ranges-base.exp: Test line-table order.

---
 gdb/buildsym.c                               |  4 ++++
 gdb/symtab.c                                 |  7 +------
 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp | 14 ++++++++++++++
 3 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index 33bf6523e9..aa26b3a97b 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector (struct block *static_block,
 	    = [] (const linetable_entry &ln1,
 		  const linetable_entry &ln2) -> bool
 	      {
+		if (ln1.pc == ln2.pc
+		    && ((ln1.line == 0) != (ln2.line == 0)))
+		  return ln1.line == 0;
+
 		return (ln1.pc < ln2.pc);
 	      };
 
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 791ce11a73..8344904652 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3237,12 +3237,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
       struct linetable_entry *last = item + len;
       item = std::upper_bound (first, last, pc, pc_compare);
       if (item != first)
-	{
-	  /* Found a matching item.  Skip backwards over any end of
-	     sequence markers.  */
-	  for (prev = item - 1; prev->line == 0 && prev != first; prev--)
-	    /* Nothing.  */;
-	}
+	prev = item - 1;		/* Found a matching item.  */
 
       /* At this point, prev points at the line whose start addr is <= pc, and
          item points at the next line.  If we ran off the end of the linetable
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
index 9e4ebf7f3c..430a45c53b 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
@@ -138,12 +138,26 @@ gdb_test "info line frame3" \
 
 # Ensure that the line table correctly tracks the end of sequence markers.
 set end_seq_count 0
+set prev -1
+set seq_count 0
 gdb_test_multiple "maint info line-table gdb.dwarf2/dw2-ranges-base.c" \
     "count END markers in line table" {
 	-re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
+	    if { $prev != -1 } {
+		gdb_assert "$prev == 1" \
+		    "prev of normal entry at $seq_count is end marker"
+	    }
+	    set prev 0
+	    incr seq_count
 	    exp_continue
 	}
 	-re "^$decimal\[ \t\]+END\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
+	    if { $prev != -1 } {
+		gdb_assert "$prev == 0" \
+		    "prev of end marker at $seq_count is normal entry"
+	    }
+	    set prev 1
+	    incr seq_count
 	    incr end_seq_count
 	    exp_continue
 	}

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

* Re: [gdb/testsuite] Fix bad line table entry sequence
  2020-06-08 14:40           ` [gdb/testsuite] Fix bad line table entry sequence Tom de Vries
@ 2020-06-15 10:31             ` Andrew Burgess
  0 siblings, 0 replies; 25+ messages in thread
From: Andrew Burgess @ 2020-06-15 10:31 UTC (permalink / raw)
  To: Tom de Vries; +Cc: gdb-patches

* Tom de Vries <tdevries@suse.de> [2020-06-08 16:40:40 +0200]:

> [ was: Re: [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting ]
> 
> On 06-06-2020 11:25, Andrew Burgess wrote:
> > * Tom de Vries <tdevries@suse.de> [2020-06-06 01:44:42 +0200]:
> > 
> >> [ was: Re: [PATCH 2/3] gdb: Don't reorder line table entries too much
> >> when sorting. ]
> >>
> >> On 05-06-2020 18:00, Tom de Vries wrote:
> >>> On 05-06-2020 16:49, Tom de Vries wrote:
> >>>> On 23-12-2019 02:51, Andrew Burgess wrote:
> >>>>> I had to make a small adjustment in find_pc_sect_line in order to
> >>>>> correctly find the previous line in the line table.  In some line
> >>>>> tables I was seeing an actual line entry and an end of sequence marker
> >>>>> at the same address, before this commit these would reorder to move
> >>>>> the end of sequence marker before the line entry (end of sequence has
> >>>>> line number 0).  Now the end of sequence marker remains in its correct
> >>>>> location, and in order to find a previous line we should step backward
> >>>>> over any end of sequence markers.
> >>>>>
> >>>>> As an example, the binary:
> >>>>>   gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold
> >>>>>
> >>>>> Has this line table before the patch:
> >>>>>
> >>>>>   INDEX    LINE ADDRESS
> >>>>>   0          48 0x0000000000400487
> >>>>>   1         END 0x000000000040048e
> >>>>>   2          52 0x000000000040048e
> >>>>>   3          54 0x0000000000400492
> >>>>>   4          56 0x0000000000400497
> >>>>>   5         END 0x000000000040049a
> >>>>>   6          62 0x000000000040049a
> >>>>>   7         END 0x00000000004004a1
> >>>>>   8          66 0x00000000004004a1
> >>>>>   9          68 0x00000000004004a5
> >>>>>   10         70 0x00000000004004aa
> >>>>>   11         72 0x00000000004004b9
> >>>>>   12        END 0x00000000004004bc
> >>>>>   13         76 0x00000000004004bc
> >>>>>   14         78 0x00000000004004c0
> >>>>>   15         80 0x00000000004004c5
> >>>>>   16        END 0x00000000004004cc
> >>>>>
> >>>>> And after this patch:
> >>>>>
> >>>>>   INDEX    LINE ADDRESS
> >>>>>   0          48 0x0000000000400487
> >>>>>   1          52 0x000000000040048e
> >>>>>   2         END 0x000000000040048e
> >>>>>   3          54 0x0000000000400492
> >>>>>   4          56 0x0000000000400497
> >>>>>   5         END 0x000000000040049a
> >>>>>   6          62 0x000000000040049a
> >>>>>   7          66 0x00000000004004a1
> >>>>>   8         END 0x00000000004004a1
> >>>>>   9          68 0x00000000004004a5
> >>>>>   10         70 0x00000000004004aa
> >>>>>   11         72 0x00000000004004b9
> >>>>>   12        END 0x00000000004004bc
> >>>>>   13         76 0x00000000004004bc
> >>>>>   14         78 0x00000000004004c0
> >>>>>   15         80 0x00000000004004c5
> >>>>>   16        END 0x00000000004004cc
> >>>>>
> >>>>> When calling find_pc_sect_line with the address 0x000000000040048e, in
> >>>>> both cases we find entry #3, we then try to find the previous entry,
> >>>>> which originally found this entry '2         52 0x000000000040048e',
> >>>>> after the patch it finds '2         END 0x000000000040048e', which
> >>>>> cases the lookup to fail.
> >>>>>
> >>>>> By skipping the END marker after this patch we get back to the correct
> >>>>> entry, which is now #1: '1          52 0x000000000040048e', and
> >>>>> everything works again.
> >>>>
> >>>> I start to suspect that you have been working around an incorrect line
> >>>> table.
> >>>>
> >>>> Consider this bit:
> >>>> ...
> >>>>    0          48 0x0000000000400487
> >>>>    1          52 0x000000000040048e
> >>>>    2         END 0x000000000040048e
> >>>> ...
> >>>>
> >>>> The end marker marks the address one past the end of the sequence.
> >>>> Therefore, it makes no sense to have an entry in the sequence with the
> >>>> same address as the end marker.
> >>>>
> >>>> [ dwarf doc:
> >>>>
> >>>> end_sequence:
> >>>>
> >>>> A boolean indicating that the current address is that of the first byte
> >>>> after the end of a sequence of target machine instructions. end_sequence
> >>>> terminates a sequence of lines; therefore other information in the same
> >>>> row is not meaningful.
> >>>>
> >>>> DW_LNE_end_sequence:
> >>>>
> >>>> The DW_LNE_end_sequence opcode takes no operands. It sets the
> >>>> end_sequence register of the state machine to “true” and appends a row
> >>>> to the matrix using the current values of the state-machine registers.
> >>>> Then it resets the registers to the initial values specified above (see
> >>>> Section 6.2.2). Every line number program sequence must end with a
> >>>> DW_LNE_end_sequence instruction which creates a row whose address is
> >>>> that of the byte after the last target machine instruction of the sequence.
> >>>>
> >>>> ]
> >>>>
> >>>> The incorrect entry is generated by this dwarf assembler sequence:
> >>>> ...
> >>>>                 {DW_LNS_copy}
> >>>>                 {DW_LNE_end_sequence}
> >>>> ...
> >>>>
> >>>> I think we should probably fix the dwarf assembly test-cases.
> >>>>
> >>>> If we want to handle this in gdb, the thing that seems most logical to
> >>>> me is to ignore this kind of entries.
> >>>
> >>> Hmm, that seems to be done already, in buildsym_compunit::record_line.
> >>>
> >>> Anyway, I was looking at the line table for
> >>> gdb.dwarf2/dw2-ranges-base.exp, and got a line table with subsequent end
> >>> markers:
> >>> ...
> >>> INDEX  LINE   ADDRESS            IS-STMT
> >>> 0      31     0x00000000004004a7 Y
> >>> 1      21     0x00000000004004ae Y
> >>> 2      END    0x00000000004004ae Y
> >>> 3      11     0x00000000004004ba Y
> >>> 4      END    0x00000000004004ba Y
> >>> 5      END    0x00000000004004c6 Y
> >>> ...
> >>>
> >>> By using this patch:
> >>> ...
> >>> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> >>> index 33bf6523e9..76f0b54ff6 100644
> >>> --- a/gdb/buildsym.c
> >>> +++ b/gdb/buildsym.c
> >>> @@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector
> >>> (struct block *static_block,
> >>>             = [] (const linetable_entry &ln1,
> >>>                   const linetable_entry &ln2) -> bool
> >>>               {
> >>> +               if (ln1.pc == ln2.pc
> >>> +                   && ((ln1.line == 0) != (ln2.line == 0)))
> >>> +                 return ln1.line == 0 ? true : false;
> >>> +
> >>>                 return (ln1.pc < ln2.pc);
> >>>               };
> >>>
> >>> ...
> >>> I get the expected:
> >>> ...
> >>> INDEX  LINE   ADDRESS            IS-STMT
> >>> 0      31     0x00000000004004a7 Y
> >>> 1      END    0x00000000004004ae Y
> >>> 2      21     0x00000000004004ae Y
> >>> 3      END    0x00000000004004ba Y
> >>> 4      11     0x00000000004004ba Y
> >>> 5      END    0x00000000004004c6 Y
> >>> ...
> >>
> >> Any comments on patch below?
> > 
> > Yes! Thank you for working on this.
> > 
> > This should go in, however, I'd like to tweak the commit message a bit
> > please (see below).
> > 
> > Also, do you plan to include the revert of find_pc_sect_line from
> > 3d92a3e313 in this patch - I think you should.
> > 
> > Thanks,
> > Andrew
> > 
> >>
> >> Thanks,
> >> - Tom
> >>
> > 
> >> [gdb/symtab] Fix line-table end-of-sequence sorting
> >>
> >> Consider test-case gdb.dwarf2/dw2-ranges-base.exp.  It has a line-table for
> >> dw2-ranges-base.c like this:
> >> ...
> >>  Line Number Statements:
> >>   [0x0000014e]  Extended opcode 2: set Address to 0x4004ba
> >>   [0x00000159]  Advance Line by 10 to 11
> >>   [0x0000015b]  Copy
> >>   [0x0000015c]  Advance PC by 12 to 0x4004c6
> >>   [0x0000015e]  Advance Line by 19 to 30
> >>   [0x00000160]  Copy
> >>   [0x00000161]  Extended opcode 1: End of Sequence
> >>
> >>   [0x00000164]  Extended opcode 2: set Address to 0x4004ae
> >>   [0x0000016f]  Advance Line by 20 to 21
> >>   [0x00000171]  Copy
> >>   [0x00000172]  Advance PC by 12 to 0x4004ba
> >>   [0x00000174]  Advance Line by 29 to 50
> >>   [0x00000176]  Copy
> >>   [0x00000177]  Extended opcode 1: End of Sequence
> >>
> >>   [0x0000017a]  Extended opcode 2: set Address to 0x4004a7
> >>   [0x00000185]  Advance Line by 30 to 31
> >>   [0x00000187]  Copy
> >>   [0x00000188]  Advance PC by 7 to 0x4004ae
> >>   [0x0000018a]  Advance Line by 39 to 70
> >>   [0x0000018c]  Copy
> >>   [0x0000018d]  Extended opcode 1: End of Sequence
> >> ...
> >>
> >> The Copy followed by End-of-Sequence is as specified in the dwarf assembly,
> >> but incorrect.  F.i., consider:
> >> ...
> >>   [0x0000015c]  Advance PC by 12 to 0x4004c6
> >>   [0x0000015e]  Advance Line by 19 to 30
> >>   [0x00000160]  Copy
> >>   [0x00000161]  Extended opcode 1: End of Sequence
> >> ...
> >>
> >> Both the Copy and the End-of-Sequence append a row to the matrix using the
> >> same addres: 0x4004c6.  The Copy declares a target instruction at that
> >> address.  The End-of-Sequence declares that the sequence ends before that
> >> address.  It's a contradiction that the target instruction is both part of the
> >> sequence (according to Copy) and not part of the sequence (according to
> >> End-of-Sequence).
> > 
> > I don't want to argue about this specific test, but about the idea of
> > an empty line occurring at the same address as an end of sequence
> > marker.  This can happen due to compiler optimisation, though it is
> > perfectly reasonable to suggest that in this case the compiler should
> > be removing the line marker, this doesn't always happen.
> > 
> > I guess what I'm saying is that I think the case for this being
> > obviously wrong is not quite as clear cut as you seem to imply>, but
> > also, I don't think this is really relevant to this bug - so maybe we
> > could just drop this part?
> > 
> 
> To me it's obviously wrong, so it would help me if you explain your
> interpretation of the dwarf standard on this topic.

I'm not aware of any statement in DWARF that specifically mentions
this case, but not do I believe that this case is specifically pointed
out as being forbidden.

Then there's the existing code in GDB to deal with this case, in
buildsym_compunit::record_line, this code:

  /* 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)
    {
      while (subfile->line_vector->nitems > 0)
	{
	  e = subfile->line_vector->item + subfile->line_vector->nitems - 1;
	  if (e->pc != pc)
	    break;
	  subfile->line_vector->nitems--;
	}
    }

Is specifically to deal with this case.

Finally, I feel that a compiler could easily get into the situation
where optimising out a line might result in an empty line.

So, I certainly agree with you that an empty line is pretty
meaningless (though maybe it does still tell us something), but I
don't think we can say its wrong.

> 
> I think though I may have indeed conflated two issues in my original
> patch, so I've now got one patch that deals with the incorrect dwarf in
> the dwarf test-cases (attached below), and I'll submit another dealing
> with the regression.  I hope that will make the conversation easier.

Sounds like a good plan.

> 
> Thanks,
> - Tom

> [gdb/testsuite] Fix bad line table entry sequence
> 
> Consider test-case gdb.dwarf2/dw2-ranges-base.exp.  It has a line-table for
> dw2-ranges-base.c like this:
> ...
>  Line Number Statements:
>   [0x0000014e]  Extended opcode 2: set Address to 0x4004ba
>   [0x00000159]  Advance Line by 10 to 11
>   [0x0000015b]  Copy
>   [0x0000015c]  Advance PC by 12 to 0x4004c6
>   [0x0000015e]  Advance Line by 19 to 30
>   [0x00000160]  Copy
>   [0x00000161]  Extended opcode 1: End of Sequence
> ...
> 
> The Copy followed by End-of-Sequence is as specified in the dwarf assembly,
> but incorrect.
> 
> Both the Copy and the End-of-Sequence append a row to the matrix, each using
> the same addres: 0x4004c6.  The Copy declares a target instruction at that
> address.  The End-of-Sequence declares that the sequence ends before that
> address.
> 
> It's a contradiction that the target instruction is both part of the sequence
> (according to Copy) and not part of the sequence (according to End-of-Sequence).
> 
> The offending Copy is silently skipped by buildsym_compunit::record_line.  A
> gdb PR has been filed to warn about this (PR26092).
> 
> Fix the dwarf assembly test-cases that contain this contradictory construct,
> to prevent people from:
> - coming across this while testing patches, and mistakenly getting the
>   impression that this is valid dwarf, or even
> - copying it to new test-cases.
> 
> Also, add a dwarf assembly test-case called dw2-bad-dw-lne-end-sequence.exp
> containing this construct, to verify that it's ignored.
> 
> Tested on x86_64-linux.
> 
> Also tested using a detection patch with assert, such that the only test-case
> triggering that assert is dw2-bad-dw-lne-end-sequence.exp.  The corresponding
> build had to be done without --with-separate-debug-dir=/usr/lib/debug",
> otherwise we'd trigger the assert in debug info generated with recent GCC (PR
> gcc/95574).
> 
> gdb/testsuite/ChangeLog:
> 
> 2020-06-08  Tom de Vries  <tdevries@suse.de>
> 
> 	* gdb.dwarf2/dw2-bad-elf.exp: Fix bad line table entry sequence.
> 	* gdb.dwarf2/dw2-dir-file-name.exp: Same.
> 	* gdb.dwarf2/dw2-inline-small-func.exp: Same.
> 	* gdb.dwarf2/dw2-ranges-base.exp: Same.
> 	* gdb.dwarf2/dw2-ranges-func.exp: Same.
> 	* gdb.dwarf2/dw2-bad-dw-lne-end-sequence.exp: New file.
> 
> ---
>  .../gdb.dwarf2/dw2-bad-dw-lne-end-sequence.exp     | 116 +++++++++++++++++++++
>  gdb/testsuite/gdb.dwarf2/dw2-bad-elf.exp           |   4 -
>  gdb/testsuite/gdb.dwarf2/dw2-dir-file-name.exp     |   1 -
>  gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp |   1 -
>  gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp       |   6 --
>  gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp       |  10 --
>  6 files changed, 116 insertions(+), 22 deletions(-)

I have no problem with this patch going in.  I 100% agree we need to
make sure we do have a test specifically to cover the functionality in
buildsym_compunit::record_line (which your new test does), so that's
good.

My only slight complaint would be that this patch is still phrased
around an empty line being somehow prohibited, and I'm still not
convinced that's the case (unless there is part of DWARF I'm
missing that says "a line shall not be empty".

I'd name the test something like "dw2-empty-line-table-entry" or
similar, then make the following change:

> 
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-bad-dw-lne-end-sequence.exp b/gdb/testsuite/gdb.dwarf2/dw2-bad-dw-lne-end-sequence.exp
> new file mode 100644
> index 0000000000..b01f82ce85
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-bad-dw-lne-end-sequence.exp
> @@ -0,0 +1,116 @@
> +# 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/>.
> +
> +# Test that line table entries with the same address as an DW_LNE_end_sequence
> +# are ignored.
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> +    verbose "Skipping bad DW_LNE_end_sequence test."
> +    return 0
> +}
> +
> +standard_testfile main.c .dwarf.S
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    global srcdir subdir srcfile srcfile2
> +    declare_labels L
> +
> +    # Find start address and length for our functions.
> +    set main_func \
> +	[function_range main [list ${srcdir}/${subdir}/$srcfile]]
> +
> +    cu {} {
> +	compile_unit {
> +	    {language @DW_LANG_C}
> +	    {name main.c}
> +	    {stmt_list $L DW_FORM_sec_offset}
> +	} {
> +	    subprogram {
> +		{external 1 flag}
> +		{name main}
> +	    }
> +	}
> +    }
> +
> +    lines {version 2} L {
> +	include_dir "${srcdir}/${subdir}"
> +	file_name "$srcfile" 1
> +
> +	# Generate simple line table program.  The last DW_LNS_copy is
> +	# problematic because it has the same address as the
> +	# DW_LNE_end_sequence.  We'll test that it's ignored.

    # Generate simple line table program.  The last DW_LNS_copy
    # results in an empty line as the following DW_LNE_end_sequence is
    # at the same address.  We'll test that the empty line is ignored.

Thanks,
Andrew

> +	program {
> +	    {DW_LNE_set_address [lindex $main_func 0]}
> +	    {DW_LNS_advance_line 10}
> +	    {DW_LNS_copy}
> +	    {DW_LNS_advance_pc [lindex $main_func 1]}
> +	    {DW_LNS_advance_line 19}
> +	    {DW_LNS_copy}
> +	    {DW_LNE_end_sequence}
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	  [list $srcfile $asm_file] {nodebug}] } {
> +    return -1
> +}
> +
> +gdb_test_no_output "set auto-solib-add off"
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +gdb_test_no_output "maint expand-symtabs gdb.dwarf2/main.c" \
> +    "maint expand-symtabs"
> +
> +set header_re \
> +    [multi_line \
> +	 ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):" \
> +	 "INDEX\[ \t\]+LINE\[ \t\]+ADDRESS\[ \t\]+IS-STMT *" \
> +	 ""]
> +
> +set end_of_sequence_re \
> +    "^$decimal\[ \t\]+END\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n"
> +
> +set ok 1
> +set cmd "maint info line-table gdb.dwarf2/main.c"
> +gdb_test_multiple $cmd "bad ops ignored" {
> +    -re "^$decimal\[ \t\]+($decimal)\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
> +	set line $expect_out(1,string)
> +	if { $line != 11 } {
> +	    set ok 0
> +	}
> +	exp_continue
> +    }
> +    -re "$end_of_sequence_re" {
> +	exp_continue
> +    }
> +    -re "^$gdb_prompt $" {
> +	if { $ok } {
> +	    pass $gdb_test_name
> +	} else {
> +	    fail $gdb_test_name
> +	}
> +    }
> +    -re $header_re {
> +	exp_continue
> +    }
> +}
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-bad-elf.exp b/gdb/testsuite/gdb.dwarf2/dw2-bad-elf.exp
> index f6fe54a634..88a522ad63 100644
> --- a/gdb/testsuite/gdb.dwarf2/dw2-bad-elf.exp
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-bad-elf.exp
> @@ -126,8 +126,6 @@ Dwarf::assemble $asm_file {
>  	    {DW_LNS_advance_line 10}
>  	    {DW_LNS_copy}
>  	    {DW_LNS_advance_pc [lindex $main_result 1]}
> -	    {DW_LNS_advance_line 19}
> -	    {DW_LNS_copy}
>  	    {DW_LNE_end_sequence}
>  	}
>      }
> @@ -142,8 +140,6 @@ Dwarf::assemble $asm_file {
>  	    {DW_LNS_advance_line 5}
>  	    {DW_LNS_copy}
>  	    {DW_LNS_advance_pc 64}
> -	    {DW_LNS_advance_line 8}
> -	    {DW_LNS_copy}
>  	    {DW_LNE_end_sequence}
>  	}
>      }
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-dir-file-name.exp b/gdb/testsuite/gdb.dwarf2/dw2-dir-file-name.exp
> index 0de71f29d5..3fc142661b 100644
> --- a/gdb/testsuite/gdb.dwarf2/dw2-dir-file-name.exp
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-dir-file-name.exp
> @@ -146,7 +146,6 @@ proc out_line { name cu_dir cu_name line_dir line_name } {
>  	.uleb128	${addr_len}+1
>  	.byte		2
>  	.${addr_len}byte ${name}_end
> -	.byte		1	/* DW_LNS_copy */
>  	.byte		0	/* DW_LNE_end_of_sequence */
>  	.uleb128	1
>  	.byte		1
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
> index 4fcc3cfeac..1efe49d009 100644
> --- a/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-small-func.exp
> @@ -114,7 +114,6 @@ Dwarf::assemble $asm_file {
>  	    {DW_LNS_copy}
>  
>  	    {DW_LNE_set_address line_label_3}
> -	    {DW_LNS_copy}
>  	    {DW_LNE_end_sequence}
>  	}
>      }
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> index 92f8f6cecb..9e4ebf7f3c 100644
> --- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> @@ -88,24 +88,18 @@ Dwarf::assemble $asm_file {
>  	    {DW_LNS_advance_line 10}
>  	    {DW_LNS_copy}
>  	    {DW_LNS_advance_pc [lindex $main_func 1]}
> -	    {DW_LNS_advance_line 19}
> -	    {DW_LNS_copy}
>  	    {DW_LNE_end_sequence}
>  
>  	    {DW_LNE_set_address [lindex $frame2_func 0]}
>  	    {DW_LNS_advance_line 20}
>  	    {DW_LNS_copy}
>  	    {DW_LNS_advance_pc [lindex $frame2_func 1]}
> -	    {DW_LNS_advance_line 29}
> -	    {DW_LNS_copy}
>  	    {DW_LNE_end_sequence}
>  
>  	    {DW_LNE_set_address [lindex $frame3_func 0]}
>  	    {DW_LNS_advance_line 30}
>  	    {DW_LNS_copy}
>  	    {DW_LNS_advance_pc [lindex $frame3_func 1]}
> -	    {DW_LNS_advance_line 39}
> -	    {DW_LNS_copy}
>  	    {DW_LNE_end_sequence}
>  	}
>      }
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp
> index 0a95adc5bc..eafd9f65fb 100644
> --- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-func.exp
> @@ -143,8 +143,6 @@ proc do_test {suffix} {
>  		{DW_LNS_advance_line [expr [gdb_get_line_number "main return"] - [gdb_get_line_number "main foo call"]]}
>  		{DW_LNS_copy}
>  		{DW_LNE_set_address $main_end}
> -		{DW_LNS_advance_line [expr [gdb_get_line_number "main end"] - [gdb_get_line_number "main return"] + 1]}
> -		{DW_LNS_copy}
>  		{DW_LNE_end_sequence}
>  
>  		{DW_LNE_set_address $foo_start}
> @@ -160,24 +158,18 @@ proc do_test {suffix} {
>  		{DW_LNS_advance_line [expr [gdb_get_line_number "foo end"] - [gdb_get_line_number "foo foo_cold call"]]}
>  		{DW_LNS_copy}
>  		{DW_LNE_set_address $foo_end}
> -		{DW_LNS_advance_line 1}
> -		{DW_LNS_copy}
>  		{DW_LNE_end_sequence}
>  
>  		{DW_LNE_set_address $bar_start}
>  		{DW_LNS_advance_line [expr [gdb_get_line_number "bar end"] - 1]}
>  		{DW_LNS_copy}
>  		{DW_LNS_advance_pc $bar_len}
> -		{DW_LNS_advance_line 1}
> -		{DW_LNS_copy}
>  		{DW_LNE_end_sequence}
>  
>  		{DW_LNE_set_address $baz_start}
>  		{DW_LNS_advance_line [expr [gdb_get_line_number "baz end"] - 1]}
>  		{DW_LNS_copy}
>  		{DW_LNS_advance_pc $baz_len}
> -		{DW_LNS_advance_line 1}
> -		{DW_LNS_copy}
>  		{DW_LNE_end_sequence}
>  
>  		{DW_LNE_set_address $foo_cold_start}
> @@ -190,8 +182,6 @@ proc do_test {suffix} {
>  		{DW_LNS_advance_line [expr [gdb_get_line_number "foo_cold end"] - [gdb_get_line_number "foo_cold baz call"]]}
>  		{DW_LNS_copy}
>  		{DW_LNE_set_address $foo_cold_end}
> -		{DW_LNS_advance_line 1}
> -		{DW_LNS_copy}
>  		{DW_LNE_end_sequence}
>  	    }
>  	}


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

* Re: [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting
  2020-06-08 15:50           ` [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting Tom de Vries
@ 2020-06-15 10:42             ` Andrew Burgess
  0 siblings, 0 replies; 25+ messages in thread
From: Andrew Burgess @ 2020-06-15 10:42 UTC (permalink / raw)
  To: Tom de Vries; +Cc: gdb-patches

* Tom de Vries <tdevries@suse.de> [2020-06-08 17:50:33 +0200]:

> On 06-06-2020 11:25, Andrew Burgess wrote:
> > * Tom de Vries <tdevries@suse.de> [2020-06-06 01:44:42 +0200]:
> > 
> >> [ was: Re: [PATCH 2/3] gdb: Don't reorder line table entries too much
> >> when sorting. ]
> >>
> >> On 05-06-2020 18:00, Tom de Vries wrote:
> >>> On 05-06-2020 16:49, Tom de Vries wrote:
> >>>> On 23-12-2019 02:51, Andrew Burgess wrote:
> >>>>> I had to make a small adjustment in find_pc_sect_line in order to
> >>>>> correctly find the previous line in the line table.  In some line
> >>>>> tables I was seeing an actual line entry and an end of sequence marker
> >>>>> at the same address, before this commit these would reorder to move
> >>>>> the end of sequence marker before the line entry (end of sequence has
> >>>>> line number 0).  Now the end of sequence marker remains in its correct
> >>>>> location, and in order to find a previous line we should step backward
> >>>>> over any end of sequence markers.
> >>>>>
> >>>>> As an example, the binary:
> >>>>>   gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold
> >>>>>
> >>>>> Has this line table before the patch:
> >>>>>
> >>>>>   INDEX    LINE ADDRESS
> >>>>>   0          48 0x0000000000400487
> >>>>>   1         END 0x000000000040048e
> >>>>>   2          52 0x000000000040048e
> >>>>>   3          54 0x0000000000400492
> >>>>>   4          56 0x0000000000400497
> >>>>>   5         END 0x000000000040049a
> >>>>>   6          62 0x000000000040049a
> >>>>>   7         END 0x00000000004004a1
> >>>>>   8          66 0x00000000004004a1
> >>>>>   9          68 0x00000000004004a5
> >>>>>   10         70 0x00000000004004aa
> >>>>>   11         72 0x00000000004004b9
> >>>>>   12        END 0x00000000004004bc
> >>>>>   13         76 0x00000000004004bc
> >>>>>   14         78 0x00000000004004c0
> >>>>>   15         80 0x00000000004004c5
> >>>>>   16        END 0x00000000004004cc
> >>>>>
> >>>>> And after this patch:
> >>>>>
> >>>>>   INDEX    LINE ADDRESS
> >>>>>   0          48 0x0000000000400487
> >>>>>   1          52 0x000000000040048e
> >>>>>   2         END 0x000000000040048e
> >>>>>   3          54 0x0000000000400492
> >>>>>   4          56 0x0000000000400497
> >>>>>   5         END 0x000000000040049a
> >>>>>   6          62 0x000000000040049a
> >>>>>   7          66 0x00000000004004a1
> >>>>>   8         END 0x00000000004004a1
> >>>>>   9          68 0x00000000004004a5
> >>>>>   10         70 0x00000000004004aa
> >>>>>   11         72 0x00000000004004b9
> >>>>>   12        END 0x00000000004004bc
> >>>>>   13         76 0x00000000004004bc
> >>>>>   14         78 0x00000000004004c0
> >>>>>   15         80 0x00000000004004c5
> >>>>>   16        END 0x00000000004004cc
> >>>>>
> >>>>> When calling find_pc_sect_line with the address 0x000000000040048e, in
> >>>>> both cases we find entry #3, we then try to find the previous entry,
> >>>>> which originally found this entry '2         52 0x000000000040048e',
> >>>>> after the patch it finds '2         END 0x000000000040048e', which
> >>>>> cases the lookup to fail.
> >>>>>
> >>>>> By skipping the END marker after this patch we get back to the correct
> >>>>> entry, which is now #1: '1          52 0x000000000040048e', and
> >>>>> everything works again.
> >>>>
> >>>> I start to suspect that you have been working around an incorrect line
> >>>> table.
> >>>>
> >>>> Consider this bit:
> >>>> ...
> >>>>    0          48 0x0000000000400487
> >>>>    1          52 0x000000000040048e
> >>>>    2         END 0x000000000040048e
> >>>> ...
> >>>>
> >>>> The end marker marks the address one past the end of the sequence.
> >>>> Therefore, it makes no sense to have an entry in the sequence with the
> >>>> same address as the end marker.
> >>>>
> >>>> [ dwarf doc:
> >>>>
> >>>> end_sequence:
> >>>>
> >>>> A boolean indicating that the current address is that of the first byte
> >>>> after the end of a sequence of target machine instructions. end_sequence
> >>>> terminates a sequence of lines; therefore other information in the same
> >>>> row is not meaningful.
> >>>>
> >>>> DW_LNE_end_sequence:
> >>>>
> >>>> The DW_LNE_end_sequence opcode takes no operands. It sets the
> >>>> end_sequence register of the state machine to “true” and appends a row
> >>>> to the matrix using the current values of the state-machine registers.
> >>>> Then it resets the registers to the initial values specified above (see
> >>>> Section 6.2.2). Every line number program sequence must end with a
> >>>> DW_LNE_end_sequence instruction which creates a row whose address is
> >>>> that of the byte after the last target machine instruction of the sequence.
> >>>>
> >>>> ]
> >>>>
> >>>> The incorrect entry is generated by this dwarf assembler sequence:
> >>>> ...
> >>>>                 {DW_LNS_copy}
> >>>>                 {DW_LNE_end_sequence}
> >>>> ...
> >>>>
> >>>> I think we should probably fix the dwarf assembly test-cases.
> >>>>
> >>>> If we want to handle this in gdb, the thing that seems most logical to
> >>>> me is to ignore this kind of entries.
> >>>
> >>> Hmm, that seems to be done already, in buildsym_compunit::record_line.
> >>>
> >>> Anyway, I was looking at the line table for
> >>> gdb.dwarf2/dw2-ranges-base.exp, and got a line table with subsequent end
> >>> markers:
> >>> ...
> >>> INDEX  LINE   ADDRESS            IS-STMT
> >>> 0      31     0x00000000004004a7 Y
> >>> 1      21     0x00000000004004ae Y
> >>> 2      END    0x00000000004004ae Y
> >>> 3      11     0x00000000004004ba Y
> >>> 4      END    0x00000000004004ba Y
> >>> 5      END    0x00000000004004c6 Y
> >>> ...
> >>>
> >>> By using this patch:
> >>> ...
> >>> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> >>> index 33bf6523e9..76f0b54ff6 100644
> >>> --- a/gdb/buildsym.c
> >>> +++ b/gdb/buildsym.c
> >>> @@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector
> >>> (struct block *static_block,
> >>>             = [] (const linetable_entry &ln1,
> >>>                   const linetable_entry &ln2) -> bool
> >>>               {
> >>> +               if (ln1.pc == ln2.pc
> >>> +                   && ((ln1.line == 0) != (ln2.line == 0)))
> >>> +                 return ln1.line == 0 ? true : false;
> >>> +
> >>>                 return (ln1.pc < ln2.pc);
> >>>               };
> >>>
> >>> ...
> >>> I get the expected:
> >>> ...
> >>> INDEX  LINE   ADDRESS            IS-STMT
> >>> 0      31     0x00000000004004a7 Y
> >>> 1      END    0x00000000004004ae Y
> >>> 2      21     0x00000000004004ae Y
> >>> 3      END    0x00000000004004ba Y
> >>> 4      11     0x00000000004004ba Y
> >>> 5      END    0x00000000004004c6 Y
> >>> ...
> >>
> >> Any comments on patch below?
> > 
> > Yes! Thank you for working on this.
> > 
> > This should go in, however, I'd like to tweak the commit message a bit
> > please (see below).
> > 
> > Also, do you plan to include the revert of find_pc_sect_line from
> > 3d92a3e313 in this patch - I think you should.
> > 
> 
> Included in this version of the patch.
> 
> > Thanks,
> > Andrew
> > 
> >>
> >> Thanks,
> >> - Tom
> >>
> > 
> >> [gdb/symtab] Fix line-table end-of-sequence sorting
> >>
> >> Consider test-case gdb.dwarf2/dw2-ranges-base.exp.  It has a line-table for
> >> dw2-ranges-base.c like this:
> >> ...
> >>  Line Number Statements:
> >>   [0x0000014e]  Extended opcode 2: set Address to 0x4004ba
> >>   [0x00000159]  Advance Line by 10 to 11
> >>   [0x0000015b]  Copy
> >>   [0x0000015c]  Advance PC by 12 to 0x4004c6
> >>   [0x0000015e]  Advance Line by 19 to 30
> >>   [0x00000160]  Copy
> >>   [0x00000161]  Extended opcode 1: End of Sequence
> >>
> >>   [0x00000164]  Extended opcode 2: set Address to 0x4004ae
> >>   [0x0000016f]  Advance Line by 20 to 21
> >>   [0x00000171]  Copy
> >>   [0x00000172]  Advance PC by 12 to 0x4004ba
> >>   [0x00000174]  Advance Line by 29 to 50
> >>   [0x00000176]  Copy
> >>   [0x00000177]  Extended opcode 1: End of Sequence
> >>
> >>   [0x0000017a]  Extended opcode 2: set Address to 0x4004a7
> >>   [0x00000185]  Advance Line by 30 to 31
> >>   [0x00000187]  Copy
> >>   [0x00000188]  Advance PC by 7 to 0x4004ae
> >>   [0x0000018a]  Advance Line by 39 to 70
> >>   [0x0000018c]  Copy
> >>   [0x0000018d]  Extended opcode 1: End of Sequence
> >> ...
> >>
> >> The Copy followed by End-of-Sequence is as specified in the dwarf assembly,
> >> but incorrect.  F.i., consider:
> >> ...
> >>   [0x0000015c]  Advance PC by 12 to 0x4004c6
> >>   [0x0000015e]  Advance Line by 19 to 30
> >>   [0x00000160]  Copy
> >>   [0x00000161]  Extended opcode 1: End of Sequence
> >> ...
> >>
> >> Both the Copy and the End-of-Sequence append a row to the matrix using the
> >> same addres: 0x4004c6.  The Copy declares a target instruction at that
> >> address.  The End-of-Sequence declares that the sequence ends before that
> >> address.  It's a contradiction that the target instruction is both part of the
> >> sequence (according to Copy) and not part of the sequence (according to
> >> End-of-Sequence).
> > 
> > I don't want to argue about this specific test, but about the idea of
> > an empty line occurring at the same address as an end of sequence
> > marker.  This can happen due to compiler optimisation, though it is
> > perfectly reasonable to suggest that in this case the compiler should
> > be removing the line marker, this doesn't always happen.
> > 
> > I guess what I'm saying is that I think the case for this being
> > obviously wrong is not quite as clear cut as you seem to imply, but
> > also, I don't think this is really relevant to this bug - so maybe we
> > could just drop this part?
> > 
> >> The offending Copy is skipped though by buildsym_compunit::record_line for
> >> unrelated reasons.
> > 
> > Not really unrelated - specifically to catch this case (assuming we're
> > thinking about the same code).
> >
> 
> I read this comment in 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.  */
> ...
> 
> So, my impression is that this was added to deal with problems related
> to the sorting based on line numbers, and no to remove invalid dwarf
> ops. Hence my "unrelated" classification.

Well, sorting _and_ empty lines, so I think it is extremely relevant.

For reference, the thread where this was added, which specifically
talks about encountering empty lines in the wild, and how this
interacts with sorting:

  https://sourceware.org/pipermail/gdb-patches/2007-October/052736.html

And the commit where this code landed:

  commit 607ae575a784d7d02956031883ae407faf06fd89
  Date:   Thu Oct 11 17:38:59 2007 +0000

              * buildsym.c (record_line): Remove empty lines followed by
              end-of-sequence markers.

> 
> >>                     So, if we disable the sorting in
> >> buildsym_compunit::end_symtab_with_blockvector, we have:
> >> ...
> >> INDEX  LINE   ADDRESS            IS-STMT
> >> 0      11     0x00000000004004ba Y
> >> 1      END    0x00000000004004c6 Y
> >> 2      21     0x00000000004004ae Y
> >> 3      END    0x00000000004004ba Y
> >> 4      31     0x00000000004004a7 Y
> >> 5      END    0x00000000004004ae Y
> >> ...
> >> but if we re-enable the sorting, we have:
> >> ...
> >> INDEX  LINE   ADDRESS            IS-STMT
> >> 0      31     0x00000000004004a7 Y
> >> 1      21     0x00000000004004ae Y
> >> 2      END    0x00000000004004ae Y
> >> 3      11     0x00000000004004ba Y
> >> 4      END    0x00000000004004ba Y
> >> 5      END    0x00000000004004c6 Y
> >> ...
> >> which has both:
> >> - the contradictory order for the same-address pairs 1/2 and 3/4, as well as
> >> - a non-sensical pair of ENDs,
> >> while we'd like:
> >> ...
> >> INDEX  LINE   ADDRESS            IS-STMT
> >> 0      31     0x00000000004004a7 Y
> >> 1      END    0x00000000004004ae Y
> >> 2      21     0x00000000004004ae Y
> >> 3      END    0x00000000004004ba Y
> >> 4      11     0x00000000004004ba Y
> >> 5      END    0x00000000004004c6 Y
> >> ...
> > 
> > You're description here is absolutely correct, but I don't feel it
> > doesn't draw enough attention to what the real mistake inside GDB is.
> > 
> > Consider this line table, it shows two sequences, each delimited with
> > an END marker.  The table is extracted from the DWARF with the entries
> > in this exact order:
> > 
> >   | Address | Line |
> >   |---------+------|
> >   |   0x100 |   10 |
> >   |   0x102 |   11 |
> >   |   0x104 |  END |
> >   |---------+------|
> >   |    0x98 |  100 |
> >   |    0x9a |  101 |
> >   |    0x9c |  102 |
> >   |    0x9e |  103 |
> >   |   0x100 |  END |
> >   |---------+------|
> > 
> > What we want is to reorder the two sequences relative to each other.
> > The current sorting does this by sorting on 'Address', while leaving
> > entries at the same address in their original order, this gives:
> > 
> >   | Address | Line |
> >   |---------+------|
> >   |    0x98 |  100 |
> >   |    0x9a |  101 |
> >   |    0x9c |  102 |
> >   |    0x9e |  103 |
> >   |   0x100 |   10 |
> >   |   0x100 |  END |
> >   |---------+------|
> >   |   0x102 |   11 |
> >   |   0x104 |  END |
> >   |---------+------|
> > 
> > Here we see that line 10 has jumped from being the start of one
> > sequence to appear at the end of the other sequence's END marker, this
> > is maintained the table order, but is clearly incorrect.
> > 
> > A better sort would order by 'Address', but always move END markers to
> > be the first entry at a given 'Address', this would give us:
> > 
> >   | Address | Line |
> >   |---------+------|
> >   |    0x98 |  100 |
> >   |    0x9a |  101 |
> >   |    0x9c |  102 |
> >   |    0x9e |  103 |
> >   |   0x100 |  END |
> >   |---------+------|
> >   |   0x100 |   10 |
> >   |   0x102 |   11 |
> >   |   0x104 |  END |
> >   |---------+------|
> > 
> >>
> >> This is a regression since commit 3d92a3e313 "gdb: Don't reorder line table
> >> entries too much when sorting", that introduced sorting on address while
> >> keeping entries with the same address in pre-sort order, which leads to
> >> incorrect results if one of the entries is an End-Of-Sequence.
> > 
> > Before commit 3d92a3e313, entries at the same address were sorted by
> > line number.  The correctly pushes END markers (line number 0) to be
> > the first entry at that address, but also corrupts the ordering of non
> > END markers.
> > 
> > This commit corrects the mistake of 3d92a3e313, by finding a middle
> > ground; keep entries at the same address in order _except_ for end
> > markers, which are always sorted to be earlier in the table.
> > 
> 
> Here's the updated patch, I've tried to make the commit message more
> detailed based on your feedback above, I hope this addresses your concerns.
> 
> Thanks,
> - Tom
> 
> 
> 

> [gdb/symtab] Fix line-table end-of-sequence sorting
> 
> Consider test-case gdb.dwarf2/dw2-ranges-base.exp.  It has (after
> "[gdb/testsuite] Fix bad line table entry sequence") a line-table for
> dw2-ranges-base.c like this:
> ...
>  Line Number Statements:
>   [0x0000014e]  Extended opcode 2: set Address to 0x4004ba
>   [0x00000159]  Advance Line by 10 to 11
>   [0x0000015b]  Copy
>   [0x0000015c]  Advance PC by 12 to 0x4004c6
>   [0x0000015e]  Extended opcode 1: End of Sequence
> 
>   [0x00000161]  Extended opcode 2: set Address to 0x4004ae
>   [0x0000016c]  Advance Line by 20 to 21
>   [0x0000016e]  Copy
>   [0x0000016f]  Advance PC by 12 to 0x4004ba
>   [0x00000171]  Extended opcode 1: End of Sequence
> 
>   [0x00000174]  Extended opcode 2: set Address to 0x4004a7
>   [0x0000017f]  Advance Line by 30 to 31
>   [0x00000181]  Copy
>   [0x00000182]  Advance PC by 7 to 0x4004ae
>   [0x00000184]  Extended opcode 1: End of Sequence
> ...
> 
> If we disable the sorting in buildsym_compunit::end_symtab_with_blockvector,
> we have the unsorted line table:
> ...
> INDEX  LINE   ADDRESS            IS-STMT
> 0      11     0x00000000004004ba Y
> 1      END    0x00000000004004c6 Y
> 2      21     0x00000000004004ae Y
> 3      END    0x00000000004004ba Y
> 4      31     0x00000000004004a7 Y
> 5      END    0x00000000004004ae Y
> ...
> It contains 3 sequences, 11/END, 21/END and 31/END.
> 
> We want to sort the 3 sequences relative to each other, while sorting on
> address, to get:
> ...
> INDEX  LINE   ADDRESS            IS-STMT
> 0      31     0x00000000004004a7 Y
> 1      END    0x00000000004004ae Y
> 2      21     0x00000000004004ae Y
> 3      END    0x00000000004004ba Y
> 4      11     0x00000000004004ba Y
> 5      END    0x00000000004004c6 Y
> ...
> 
> However, if we re-enable the sorting, we have instead:
> ...
> INDEX  LINE   ADDRESS            IS-STMT
> 0      31     0x00000000004004a7 Y
> 1      21     0x00000000004004ae Y
> 2      END    0x00000000004004ae Y
> 3      11     0x00000000004004ba Y
> 4      END    0x00000000004004ba Y
> 5      END    0x00000000004004c6 Y
> ...
> 
> This is a regression since commit 3d92a3e313 "gdb: Don't reorder line table
> entries too much when sorting", that introduced sorting on address while
> keeping entries with the same address in pre-sort order.
> 
> Indeed the entries 1 and 2 are in pre-sort order (they map to entries 2 and 5
> in the unsorted line table), but entry 1 does not belong in the sequence
> terminated by 2.
> 
> Fix this by handling End-Of-Sequence entries in the sorting function, such
> that they are sorted before other entries with the same address.
> 
> Also, revert the find_pc_sect_line workaround introduced in commit 3d92a3e313,
> since that's no longer necessary.
> 
> Tested on x86_64-linux.
> 
> gdb/ChangeLog:
> 
> 2020-06-06  Tom de Vries  <tdevries@suse.de>
> 
> 	* buildsym.c (buildsym_compunit::end_symtab_with_blockvector): Handle
> 	End-Of-Sequence in lte_is_less_than.
> 	* symtab.c (find_pc_sect_line): Revert change from commit 3d92a3e313
> 	"gdb: Don't reorder line table entries too much when sorting".

LGTM.

Thanks for doing this.

Andrew


> 
> gdb/testsuite/ChangeLog:
> 
> 2020-06-06  Tom de Vries  <tdevries@suse.de>
> 
> 	* gdb.dwarf2/dw2-ranges-base.exp: Test line-table order.
> 
> ---
>  gdb/buildsym.c                               |  4 ++++
>  gdb/symtab.c                                 |  7 +------
>  gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp | 14 ++++++++++++++
>  3 files changed, 19 insertions(+), 6 deletions(-)
> 
> diff --git a/gdb/buildsym.c b/gdb/buildsym.c
> index 33bf6523e9..aa26b3a97b 100644
> --- a/gdb/buildsym.c
> +++ b/gdb/buildsym.c
> @@ -943,6 +943,10 @@ buildsym_compunit::end_symtab_with_blockvector (struct block *static_block,
>  	    = [] (const linetable_entry &ln1,
>  		  const linetable_entry &ln2) -> bool
>  	      {
> +		if (ln1.pc == ln2.pc
> +		    && ((ln1.line == 0) != (ln2.line == 0)))
> +		  return ln1.line == 0;
> +
>  		return (ln1.pc < ln2.pc);
>  	      };
>  
> diff --git a/gdb/symtab.c b/gdb/symtab.c
> index 791ce11a73..8344904652 100644
> --- a/gdb/symtab.c
> +++ b/gdb/symtab.c
> @@ -3237,12 +3237,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
>        struct linetable_entry *last = item + len;
>        item = std::upper_bound (first, last, pc, pc_compare);
>        if (item != first)
> -	{
> -	  /* Found a matching item.  Skip backwards over any end of
> -	     sequence markers.  */
> -	  for (prev = item - 1; prev->line == 0 && prev != first; prev--)
> -	    /* Nothing.  */;
> -	}
> +	prev = item - 1;		/* Found a matching item.  */
>  
>        /* At this point, prev points at the line whose start addr is <= pc, and
>           item points at the next line.  If we ran off the end of the linetable
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> index 9e4ebf7f3c..430a45c53b 100644
> --- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
> @@ -138,12 +138,26 @@ gdb_test "info line frame3" \
>  
>  # Ensure that the line table correctly tracks the end of sequence markers.
>  set end_seq_count 0
> +set prev -1
> +set seq_count 0
>  gdb_test_multiple "maint info line-table gdb.dwarf2/dw2-ranges-base.c" \
>      "count END markers in line table" {
>  	-re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
> +	    if { $prev != -1 } {
> +		gdb_assert "$prev == 1" \
> +		    "prev of normal entry at $seq_count is end marker"
> +	    }
> +	    set prev 0
> +	    incr seq_count
>  	    exp_continue
>  	}
>  	-re "^$decimal\[ \t\]+END\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
> +	    if { $prev != -1 } {
> +		gdb_assert "$prev == 0" \
> +		    "prev of end marker at $seq_count is normal entry"
> +	    }
> +	    set prev 1
> +	    incr seq_count
>  	    incr end_seq_count
>  	    exp_continue
>  	}


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

* Re: [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting.
  2019-12-26 22:17 ` Andrew Burgess
@ 2019-12-28 11:09   ` Bernd Edlinger
  0 siblings, 0 replies; 25+ messages in thread
From: Bernd Edlinger @ 2019-12-28 11:09 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

On 12/26/19 11:17 PM, Andrew Burgess wrote:
> * Bernd Edlinger <bernd.edlinger@hotmail.de> [2019-12-25 11:19:50 +0000]:
> 
>> Hi,
>>
>> when I tried this patch, git am says this:
>>
>> +foreach p $patterns {
>> +    gdb_test "step" "/\\* $p \\*/" \
>> +	"step to '$p'"
>> +}
>> +
>> -- 
>> 2.14.5
>>
>> Applying: gdb: Don't reorder line table entries too much when sorting.
>> /home/ed/gnu/binutils-gdb/.git/rebase-apply/patch:294: new blank line at EOF.
>> +
>> warning: 1 line adds whitespace errors.
> 
> Thanks, I fixed two whitespace errors in this patch.
> 
>>
>>
>> I just was curious to try this patch series,
>> since this has some overlap with a patch I posted here:
>> https://sourceware.org/ml/gdb-patches/2019-11/msg00792.html
>>
>> we both want to add end_sequence here:
>>> @@ -21330,7 +21331,8 @@ 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))
>>> +      if (m_record_lines_p
>>> +         && (producer_is_codewarrior (m_cu) || m_is_stmt || end_sequence))
>>>         {
>>>           if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
>>>               || end_sequence)
>>
>> I was not sure, if m_is_stmt is ever false when end_sequence is true,
>> but considered that to be safer this way too.
>> Does that actually happen?
> 
> Honestly, I didn't check.  Like you it seemed to make more sense to
> leave things as they are unless I have a good reason to change them.
> 

Neither did I, but when I add an assertion there it is obvious that
it happens rarely with gcc-4.8 but very often with gcc-10.

> Does how I wrote the test for this help you create a test for you
> patch at all?  If I can help in any way I'd be happy to try.
> 

Wow, that's a really impressive work...

I was able to write a test for the step over inline issue,
but it is dependent on the gcc version, gcc-8 or newer generate
debug info where the problem exists, but test itself passes with all
gcc versions as far as I can tell, it does just not prove much
for old gcc versions.

I posted everyting again here:
https://sourceware.org/ml/gdb-patches/2019-12/msg01052.html

In case you want to see.


Thanks
Bernd.

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

* Re: [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting.
  2019-12-25 11:19 [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting Bernd Edlinger
@ 2019-12-26 22:17 ` Andrew Burgess
  2019-12-28 11:09   ` Bernd Edlinger
  0 siblings, 1 reply; 25+ messages in thread
From: Andrew Burgess @ 2019-12-26 22:17 UTC (permalink / raw)
  To: Bernd Edlinger; +Cc: gdb-patches

* Bernd Edlinger <bernd.edlinger@hotmail.de> [2019-12-25 11:19:50 +0000]:

> Hi,
> 
> when I tried this patch, git am says this:
> 
> +foreach p $patterns {
> +    gdb_test "step" "/\\* $p \\*/" \
> +	"step to '$p'"
> +}
> +
> -- 
> 2.14.5
> 
> Applying: gdb: Don't reorder line table entries too much when sorting.
> /home/ed/gnu/binutils-gdb/.git/rebase-apply/patch:294: new blank line at EOF.
> +
> warning: 1 line adds whitespace errors.

Thanks, I fixed two whitespace errors in this patch.

> 
> 
> I just was curious to try this patch series,
> since this has some overlap with a patch I posted here:
> https://sourceware.org/ml/gdb-patches/2019-11/msg00792.html
> 
> we both want to add end_sequence here:
> > @@ -21330,7 +21331,8 @@ 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))
> > +      if (m_record_lines_p
> > +         && (producer_is_codewarrior (m_cu) || m_is_stmt || end_sequence))
> >         {
> >           if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
> >               || end_sequence)
> 
> I was not sure, if m_is_stmt is ever false when end_sequence is true,
> but considered that to be safer this way too.
> Does that actually happen?

Honestly, I didn't check.  Like you it seemed to make more sense to
leave things as they are unless I have a good reason to change them.

Does how I wrote the test for this help you create a test for you
patch at all?  If I can help in any way I'd be happy to try.

Thanks,
Andrew

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

* Re: [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting.
@ 2019-12-25 11:19 Bernd Edlinger
  2019-12-26 22:17 ` Andrew Burgess
  0 siblings, 1 reply; 25+ messages in thread
From: Bernd Edlinger @ 2019-12-25 11:19 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

Hi,

when I tried this patch, git am says this:

+foreach p $patterns {
+    gdb_test "step" "/\\* $p \\*/" \
+	"step to '$p'"
+}
+
-- 
2.14.5

Applying: gdb: Don't reorder line table entries too much when sorting.
/home/ed/gnu/binutils-gdb/.git/rebase-apply/patch:294: new blank line at EOF.
+
warning: 1 line adds whitespace errors.


I just was curious to try this patch series,
since this has some overlap with a patch I posted here:
https://sourceware.org/ml/gdb-patches/2019-11/msg00792.html

we both want to add end_sequence here:
> @@ -21330,7 +21331,8 @@ 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))
> +      if (m_record_lines_p
> +         && (producer_is_codewarrior (m_cu) || m_is_stmt || end_sequence))
>         {
>           if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
>               || end_sequence)

I was not sure, if m_is_stmt is ever false when end_sequence is true,
but considered that to be safer this way too.
Does that actually happen?


Bernd.

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

end of thread, other threads:[~2020-06-15 10:42 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-23  1:51 [PATCH 0/3] Improve inline frame debug experience Andrew Burgess
2019-12-23  1:51 ` [PATCH 3/3] gdb: Better frame tracking for inline frames Andrew Burgess
2019-12-26  7:25   ` Christian Biesinger via gdb-patches
2019-12-26 23:33     ` Andrew Burgess
2019-12-23  1:51 ` [PATCH 1/3] gdb: Include end of sequence markers in the line table Andrew Burgess
2019-12-23  1:51 ` [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting Andrew Burgess
2020-01-24 17:40   ` Tom Tromey
2020-06-05  6:10     ` Tom de Vries
2020-06-05 14:49   ` Tom de Vries
2020-06-05 16:00     ` Tom de Vries
2020-06-05 23:44       ` [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting Tom de Vries
2020-06-06  6:51         ` Andrew Burgess
2020-06-06  8:18           ` Tom de Vries
2020-06-06  9:25         ` Andrew Burgess
2020-06-08 14:40           ` [gdb/testsuite] Fix bad line table entry sequence Tom de Vries
2020-06-15 10:31             ` Andrew Burgess
2020-06-08 15:50           ` [PATCH][gdb/symtab] Fix line-table end-of-sequence sorting Tom de Vries
2020-06-15 10:42             ` Andrew Burgess
2020-01-06 22:14 ` [PATCH 0/3] Improve inline frame debug experience Andrew Burgess
2020-01-17 17:56   ` Andrew Burgess
2020-01-24 18:12     ` Tom Tromey
2020-01-25  5:08       ` Andrew Burgess
2019-12-25 11:19 [PATCH 2/3] gdb: Don't reorder line table entries too much when sorting Bernd Edlinger
2019-12-26 22:17 ` Andrew Burgess
2019-12-28 11:09   ` Bernd Edlinger

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