aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
authorAndrew Burgess <andrew.burgess@embecosm.com>2020-04-03 20:32:38 +0100
committerAndrew Burgess <andrew.burgess@embecosm.com>2020-06-01 10:02:44 +0100
commit1313c56ef9a68b5b753b3148dc7e8b3a4bb2d8ff (patch)
treef15c8066a1abd9532b2841b90d0a6685107a0684 /gdb
parentb7ed9f3d466700d4e766083b1e4f3a132e5fd4b4 (diff)
downloadgdb-1313c56ef9a68b5b753b3148dc7e8b3a4bb2d8ff.zip
gdb-1313c56ef9a68b5b753b3148dc7e8b3a4bb2d8ff.tar.gz
gdb-1313c56ef9a68b5b753b3148dc7e8b3a4bb2d8ff.tar.bz2
gdb: Preserve is-stmt lines when switch between files
After the is-stmt support commit: commit 8c95582da858ac981f689a6f599acacb8c5c490f Date: Mon Dec 30 21:04:51 2019 +0000 gdb: Add support for tracking the DWARF line table is-stmt field A regression was observed where a breakpoint could no longer be placed in some cases. Consider a line table like this: File 1: test.c File 2: test.h | Addr | File | Line | Stmt | |------|------|------|------| | 1 | 1 | 16 | Y | | 2 | 1 | 17 | Y | | 3 | 2 | 21 | Y | | 4 | 2 | 22 | Y | | 4 | 1 | 18 | N | | 5 | 2 | 23 | N | | 6 | 1 | 24 | Y | | 7 | 1 | END | Y | |------|------|------|------| Before the is-stmt patch GDB would ignore any non-stmt lines, so GDB built two line table structures: File 1 File 2 ------ ------ | Addr | Line | | Addr | Line | |------|------| |------|------| | 1 | 16 | | 3 | 21 | | 2 | 17 | | 4 | 22 | | 3 | END | | 6 | END | | 6 | 24 | |------|------| | 7 | END | |------|------| After the is-stmt patch GDB now records non-stmt lines, so the generated line table structures look like this: File 1 File 2 ------ ------ | Addr | Line | Stmt | | Addr | Line | Stmt | |------|------|------| |------|------|------| | 1 | 16 | Y | | 3 | 21 | Y | | 2 | 17 | Y | | 4 | 22 | Y | | 3 | END | Y | | 4 | END | Y | | 4 | 18 | N | | 5 | 23 | N | | 5 | END | Y | | 6 | END | Y | | 6 | 24 | Y | |------|------|------| | 7 | END | Y | |------|------|------| The problem is that in 'File 2', end END marker at address 4 causes the previous line table entry to be discarded, so we actually end up with this: File 2 ------ | Addr | Line | Stmt | |------|------|------| | 3 | 21 | Y | | 4 | END | Y | | 5 | 23 | N | | 6 | END | Y | |------|------|------| When a user tries to place a breakpoint in file 2 at line 22, this is no longer possible. The solution I propose here is that we ignore line table entries that would trigger a change of file if: 1. The new line being added is at the same address as the previous line, and 2. We have previously seen an is-stmt line at the current address. The result of this is that GDB switches file, and knows that some line entry (or entries) are going to be discarded, prefer to keep is-stmt lines and discard non-stmt lines. After this commit the lines tables are now: File 1 File 2 ------ ------ | Addr | Line | Stmt | | Addr | Line | Stmt | |------|------|------| |------|------|------| | 1 | 16 | Y | | 3 | 21 | Y | | 2 | 17 | Y | | 4 | 22 | Y | | 3 | END | Y | | 5 | 23 | N | | 5 | END | Y | | 6 | END | Y | | 6 | 24 | Y | |------|------|------| | 7 | END | Y | |------|------|------| We've lost the non-stmt entry for file 1, line 18, but retained the is-stmt entry for file 2, line 22. The user can now place a breakpoint at that location. One problem that came from this commit was the test gdb.cp/step-and-next-inline.exp, which broke in several places. After looking at this test again I think that in some cases this test was only ever passing by pure luck. The debug GCC is producing for this test is pretty broken. I raised this GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474 for this and disabled one entire half of the test. There are still some cases in here that do pass, and if/when GCC is fixed it would be great to enable this test again. gdb/ChangeLog: * dwarf2/read.c (class lnp_state_machine) <m_last_address>: New member variable. <m_stmt_at_address>: New member variable. (lnp_state_machine::record_line): Don't record some lines, update tracking of is_stmt at the same address. (lnp_state_machine::lnp_state_machine): Initialise new member variables. gdb/testsuite/ChangeLog: * gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the use_header case. * gdb.dwarf2/dw2-inline-header-1.exp: New file. * gdb.dwarf2/dw2-inline-header-2.exp: New file. * gdb.dwarf2/dw2-inline-header-3.exp: New file. * gdb.dwarf2/dw2-inline-header-lbls.c: New file. * gdb.dwarf2/dw2-inline-header.c: New file. * gdb.dwarf2/dw2-inline-header.h: New file.
Diffstat (limited to 'gdb')
-rw-r--r--gdb/ChangeLog10
-rw-r--r--gdb/dwarf2/read.c47
-rw-r--r--gdb/testsuite/ChangeLog11
-rw-r--r--gdb/testsuite/gdb.cp/step-and-next-inline.exp7
-rw-r--r--gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp186
-rw-r--r--gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp189
-rw-r--r--gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp193
-rw-r--r--gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c46
-rw-r--r--gdb/testsuite/gdb.dwarf2/dw2-inline-header.c24
-rw-r--r--gdb/testsuite/gdb.dwarf2/dw2-inline-header.h24
10 files changed, 734 insertions, 3 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index c95c168..b74167e 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,13 @@
+2020-06-01 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * dwarf2/read.c (class lnp_state_machine) <m_last_address>: New
+ member variable.
+ <m_stmt_at_address>: New member variable.
+ (lnp_state_machine::record_line): Don't record some lines, update
+ tracking of is_stmt at the same address.
+ (lnp_state_machine::lnp_state_machine): Initialise new member
+ variables.
+
2020-06-01 Samuel Thibault <samuel.thibault@ens-lyon.org>
* config/i386/i386gnu.mn [%_S.o %_U.o] (COMPILE.post): Add
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 4724738..e6566f9 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -20040,6 +20040,15 @@ private:
/* The last file a line number was recorded for. */
struct subfile *m_last_subfile = NULL;
+ /* The address of the last line entry. */
+ CORE_ADDR m_last_address;
+
+ /* Set to true when a previous line at the same address (using
+ m_last_address) had m_is_stmt true. This is reset to false when a
+ line entry at a new address (m_address different to m_last_address) is
+ processed. */
+ bool m_stmt_at_address = false;
+
/* When true, record the lines we decode. */
bool m_currently_recording_lines = false;
@@ -20233,14 +20242,34 @@ lnp_state_machine::record_line (bool end_sequence)
fe->included_p = 1;
if (m_record_lines_p)
{
- if (m_last_subfile != m_cu->get_builder ()->get_current_subfile ()
- || end_sequence)
+ /* When we switch files we insert an end maker in the first file,
+ switch to the second file and add a new line entry. The
+ problem is that the end marker inserted in the first file will
+ discard any previous line entries at the same address. If the
+ line entries in the first file are marked as is-stmt, while
+ the new line in the second file is non-stmt, then this means
+ the end marker will discard is-stmt lines so we can have a
+ non-stmt line. This means that there are less addresses at
+ which the user can insert a breakpoint.
+
+ To improve this we track the last address in m_last_address,
+ and whether we have seen an is-stmt at this address. Then
+ when switching files, if we have seen a stmt at the current
+ address, and we are switching to create a non-stmt line, then
+ discard the new line. */
+ bool file_changed
+ = m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
+ bool ignore_this_line
+ = (file_changed && !end_sequence && m_last_address == m_address
+ && !m_is_stmt && m_stmt_at_address);
+
+ if ((file_changed && !ignore_this_line) || end_sequence)
{
dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
m_currently_recording_lines ? m_cu : nullptr);
}
- if (!end_sequence)
+ if (!end_sequence && !ignore_this_line)
{
bool is_stmt = producer_is_codewarrior (m_cu) || m_is_stmt;
@@ -20259,6 +20288,15 @@ lnp_state_machine::record_line (bool end_sequence)
}
}
}
+
+ /* Track whether we have seen any m_is_stmt true at m_address in case we
+ have multiple line table entries all at m_address. */
+ if (m_last_address != m_address)
+ {
+ m_stmt_at_address = false;
+ m_last_address = m_address;
+ }
+ m_stmt_at_address |= m_is_stmt;
}
lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
@@ -20278,6 +20316,9 @@ lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
m_address = gdbarch_adjust_dwarf2_line (arch, 0, 0);
m_is_stmt = lh->default_is_stmt;
m_discriminator = 0;
+
+ m_last_address = m_address;
+ m_stmt_at_address = false;
}
void
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 8c5553e..ec6b24a 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,14 @@
+2020-06-01 Andrew Burgess <andrew.burgess@embecosm.com>
+
+ * gdb.cp/step-and-next-inline.exp (do_test): Skip all tests in the
+ use_header case.
+ * gdb.dwarf2/dw2-inline-header-1.exp: New file.
+ * gdb.dwarf2/dw2-inline-header-2.exp: New file.
+ * gdb.dwarf2/dw2-inline-header-3.exp: New file.
+ * gdb.dwarf2/dw2-inline-header-lbls.c: New file.
+ * gdb.dwarf2/dw2-inline-header.c: New file.
+ * gdb.dwarf2/dw2-inline-header.h: New file.
+
2020-05-30 Pedro Alves <palves@redhat.com>
* gdb.linespec/cp-replace-typedefs-ns-template.cc: New.
diff --git a/gdb/testsuite/gdb.cp/step-and-next-inline.exp b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
index 3733fa7..a95e211 100644
--- a/gdb/testsuite/gdb.cp/step-and-next-inline.exp
+++ b/gdb/testsuite/gdb.cp/step-and-next-inline.exp
@@ -24,6 +24,13 @@ if { ![supports_statement_frontiers] } {
proc do_test { use_header } {
global srcfile testfile
+ if { $use_header } {
+ # This test will not pass due to poor debug information
+ # generated by GCC (at least upto 10.x). See
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94474
+ return
+ }
+
set options {c++ debug nowarnings optimize=-O2\ -gstatement-frontiers}
if { $use_header } {
lappend options additional_flags=-DUSE_NEXT_INLINE_H
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
new file mode 100644
index 0000000..dc7ec92
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-1.exp
@@ -0,0 +1,186 @@
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Setup a line table where:
+#
+# | | | | | Inline | Inline |
+# | Addr | File | Line | Stmt | Rng A | Rng B |
+# |------|------|------|------|--------|--------|
+# | 1 | 1 | 16 | Y | | |
+# | 2 | 1 | 17 | Y | | |
+# | 3 | 2 | 21 | Y | X | |
+# | 4 | 2 | 22 | Y | X | |
+# | 4 | 1 | 18 | N | X | |
+# | 5 | 2 | 23 | N | X | X |
+# | 6 | 1 | 24 | Y | | |
+# | 7 | 1 | END | Y | | |
+# |------|------|------|------|--------|--------|
+#
+# Places a brekpoint at file 2, line 22. Previously GDB would discard
+# the line table entry for this line due to switching files for the
+# file 1, line 18 non-statement line. After patching however, GDB now
+# discards the file 1, line 18 entry instead, and the breakpoint at
+# line 22 should succeed.
+#
+# The two inlined subroutine ranges 'A' and 'B' represent two possible
+# ways that a compiler might represent this siuatio in the DWARF.
+#
+# Range 'B' is something that has been seen in the wild using GCC 8.2.
+# In this case the compilers range information is clearly wrong, but
+# this shouldn't impact the main point of the test.
+#
+# Range 'A' is a hypothetical case of how the compiler might choose to
+# represent this range, this has never been seen in the wild, but is
+# an improved debug experiece over range 'B'. However, if we ever run
+# in to the situation where GDB can support the range 'A' test, or
+# support some real DWARF seen in the wild, then the range 'A' case
+# should be dropped in favour of supporting real world cases. This is
+# included here as it "just worked" once the range 'B' case was
+# working.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+ return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+ return -1
+}
+if !$gcc_compiled {
+ return 0
+}
+
+# Prepare and run the test.
+proc do_test { start_label func_name tag } {
+ global srcfile srcfile2 srcfile3 srcfile4 testfile
+
+ standard_testfile dw2-inline-header-lbls.c dw2-inline-header-${tag}.S \
+ dw2-inline-header.c dw2-inline-header.h
+
+ set build_options {nodebug optimize=-O1}
+
+ set asm_file [standard_output_file $srcfile2]
+ Dwarf::assemble $asm_file {
+ global srcdir subdir srcfile srcfile3 srcfile4 testfile
+ upvar build_options build_options
+ upvar start_label start_label
+ declare_labels lines_label callee_subprog_label
+
+ get_func_info main $build_options
+
+ cu {} {
+ compile_unit {
+ {producer "gcc" }
+ {language @DW_LANG_C}
+ {name ${srcfile3}}
+ {low_pc 0 addr}
+ {stmt_list ${lines_label} DW_FORM_sec_offset}
+ } {
+ callee_subprog_label: subprogram {
+ {external 1 flag}
+ {name callee}
+ {inline 3 data1}
+ }
+ subprogram {
+ {external 1 flag}
+ {name main}
+ {low_pc $main_start addr}
+ {high_pc "$main_start + $main_len" addr}
+ } {
+ inlined_subroutine {
+ {abstract_origin %$callee_subprog_label}
+ {low_pc $start_label addr}
+ {high_pc line_label_6 addr}
+ {call_file 1 data1}
+ {call_line 18 data1}
+ }
+ }
+ }
+ }
+
+ lines {version 2 default_is_stmt 1} lines_label {
+ include_dir "${srcdir}/${subdir}"
+ file_name "$srcfile3" 1
+ file_name "$srcfile4" 1
+
+ program {
+ {DW_LNE_set_address line_label_1}
+ {DW_LNS_advance_line 15}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_2}
+ {DW_LNS_advance_line 1}
+ {DW_LNS_copy}
+
+ {DW_LNS_set_file 2}
+ {DW_LNE_set_address line_label_3}
+ {DW_LNS_advance_line 4}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_4}
+ {DW_LNS_advance_line 1}
+ {DW_LNS_copy}
+
+ {DW_LNS_advance_line -4}
+ {DW_LNS_set_file 1}
+ {DW_LNS_negate_stmt}
+ {DW_LNS_copy}
+
+ {DW_LNS_set_file 2}
+ {DW_LNE_set_address line_label_5}
+ {DW_LNS_advance_line 5}
+ {DW_LNS_copy}
+
+ {DW_LNS_negate_stmt}
+ {DW_LNS_set_file 1}
+ {DW_LNE_set_address line_label_6}
+ {DW_LNS_advance_line 1}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_7}
+ {DW_LNE_end_sequence}
+ }
+ }
+ }
+
+ if { [prepare_for_testing "failed to prepare" ${testfile}-${tag} \
+ [list $srcfile $asm_file] $build_options] } {
+ return -1
+ }
+
+ if ![runto_main] {
+ return -1
+ }
+
+ # Delete all breakpoints so that the output of "info breakpoints"
+ # below will only contain a single breakpoint.
+ delete_breakpoints
+
+ # Place a breakpoint within the function in the header file.
+ gdb_breakpoint "${srcfile4}:22"
+
+ # Check that the breakpoint was placed where we expected. It should
+ # appear at the requested line. When the bug in GDB was present the
+ # breakpoint would be placed on one of the following lines instead.
+ gdb_test "info breakpoints" \
+ ".* in $func_name at \[^\r\n\]+${srcfile4}:22\\y.*" \
+ "info breakpoints, $tag"
+}
+
+do_test line_label_3 "callee" "range-a"
+do_test line_label_5 "main" "range-b"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
new file mode 100644
index 0000000..9f09f35
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-2.exp
@@ -0,0 +1,189 @@
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt | Inline |
+# |------|------|------|------|--------|
+# | 1 | 1 | 16 | Y | |
+# | 2 | 1 | 17 | Y | |
+# | 3 | 2 | 21 | Y | X |
+# | 4 | 2 | 22 | Y | X |
+# | 4 | 1 | 18 | N | X |
+# | 5 | 1 | 19 | Y | |
+# | 6 | 1 | 20 | Y | |
+# | 7 | 1 | END | Y | |
+# |------|------|------|------|--------|
+#
+#
+# Place the first brekpoint at file 2, line 22 and a second breakpoint
+# at file 1, line 19. A third breakpoint is placed at file 1, line
+# 18, but as this line table entry will have been discarded[1] the
+# third breakpoint will actually be placed at the same location as the
+# second breakpoint.
+#
+# This test is designed to test GDB's internal behaviour with respect
+# to discarding particular line table entries. GCC and DWARF are
+# starting to introduce the idea of line table views. As the views
+# information becomes better supported within GDB it is likely that
+# this will become out of date. This is fine, the test will have
+# served its purpose by that point and can be deleted.
+#
+# [1] The entry for file 1, line 18 is discarded because it is at the
+# same address as the previous entry, but the previous entry is-stmt,
+# while line 18 is a non-stmt.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+ return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+ return -1
+}
+if !$gcc_compiled {
+ return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+ dw2-inline-header.c dw2-inline-header.h
+
+set build_options {nodebug optimize=-O1}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+ global srcdir subdir srcfile srcfile3 srcfile4
+ global build_options
+ declare_labels lines_label callee_subprog_label
+
+ get_func_info main $build_options
+
+ cu {} {
+ compile_unit {
+ {producer "gcc" }
+ {language @DW_LANG_C}
+ {name ${srcfile3}}
+ {low_pc 0 addr}
+ {stmt_list ${lines_label} DW_FORM_sec_offset}
+ } {
+ callee_subprog_label: subprogram {
+ {external 1 flag}
+ {name callee}
+ {inline 3 data1}
+ }
+ subprogram {
+ {external 1 flag}
+ {name main}
+ {low_pc $main_start addr}
+ {high_pc "$main_start + $main_len" addr}
+ } {
+ inlined_subroutine {
+ {abstract_origin %$callee_subprog_label}
+ {low_pc line_label_3 addr}
+ {high_pc line_label_5 addr}
+ {call_file 1 data1}
+ {call_line 18 data1}
+ }
+ }
+ }
+ }
+
+ lines {version 2 default_is_stmt 1} lines_label {
+ include_dir "${srcdir}/${subdir}"
+ file_name "$srcfile3" 1
+ file_name "$srcfile4" 1
+
+ program {
+ {DW_LNE_set_address line_label_1}
+ {DW_LNS_advance_line 15}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_2}
+ {DW_LNS_advance_line 1}
+ {DW_LNS_copy}
+
+ {DW_LNS_set_file 2}
+ {DW_LNE_set_address line_label_3}
+ {DW_LNS_advance_line 4}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_4}
+ {DW_LNS_advance_line 1}
+ {DW_LNS_copy}
+
+ {DW_LNS_advance_line -4}
+ {DW_LNS_set_file 1}
+ {DW_LNS_negate_stmt}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_5}
+ {DW_LNS_advance_line 1}
+ {DW_LNS_negate_stmt}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_6}
+ {DW_LNS_advance_line 1}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_7}
+ {DW_LNE_end_sequence}
+ }
+ }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+ [list $srcfile $asm_file] $build_options] } {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected. It should
+# appear at the requested line. When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+ ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*" \
+ "check for breakpoint at ${srcfile4}"
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile3}:19"
+
+# Check that the breakpoint was placed where we expected. It should
+# appear at the requested line. When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+ ".* in main at \[^\r\n\]+${srcfile3}:19\\y.*" \
+ "check for breakpoint at ${srcfile3}"
+
+# Line table entry for line 18 will have been discarded, so this
+# brekpoint will be at the same location as line 19.
+gdb_test "break ${srcfile3}:18" \
+ "Note: breakpoint $decimal also set at pc $hex.*"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
new file mode 100644
index 0000000..a3820f1
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-3.exp
@@ -0,0 +1,193 @@
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Setup a line table where:
+#
+# | Addr | File | Line | Stmt | Inline |
+# |------|------|------|------|--------|
+# | 1 | 1 | 16 | Y | |
+# | 2 | 1 | 17 | Y | |
+# | 3 | 2 | 21 | Y | X |
+# | 4 | 2 | 22 | Y | X |
+# | 4 | 1 | 18 | N | |
+# | 5 | 1 | 19 | N | |
+# | 6 | 1 | 20 | Y | |
+# | 7 | 1 | END | Y | |
+# |------|------|------|------|--------|
+#
+# Break at file 2, line 22, then single instruction step forward. We
+# should pass through line 19 and then encounter line 20.
+#
+# Currently we don't expect GDB to see file 1, line 18, as this is a
+# non-stmt line in a different file at the same address as the
+# previous is-stmt line.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+ return 0
+}
+
+# The .c files use __attribute__.
+if [get_compiler_info] {
+ return -1
+}
+if !$gcc_compiled {
+ return 0
+}
+
+standard_testfile dw2-inline-header-lbls.c dw2-inline-header.S \
+ dw2-inline-header.c dw2-inline-header.h
+
+set build_options {nodebug optimize=-O1}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+ global srcdir subdir srcfile srcfile3 srcfile4
+ global build_options
+ declare_labels lines_label callee_subprog_label
+
+ get_func_info main $build_options
+
+ cu {} {
+ compile_unit {
+ {producer "gcc" }
+ {language @DW_LANG_C}
+ {name ${srcfile3}}
+ {low_pc 0 addr}
+ {stmt_list ${lines_label} DW_FORM_sec_offset}
+ } {
+ callee_subprog_label: subprogram {
+ {external 1 flag}
+ {name callee}
+ {inline 3 data1}
+ }
+ subprogram {
+ {external 1 flag}
+ {name main}
+ {low_pc $main_start addr}
+ {high_pc "$main_start + $main_len" addr}
+ } {
+ inlined_subroutine {
+ {abstract_origin %$callee_subprog_label}
+ {low_pc line_label_3 addr}
+ {high_pc line_label_5 addr}
+ {call_file 1 data1}
+ {call_line 18 data1}
+ }
+ }
+ }
+ }
+
+ lines {version 2 default_is_stmt 1} lines_label {
+ include_dir "${srcdir}/${subdir}"
+ file_name "$srcfile3" 1
+ file_name "$srcfile4" 1
+
+ program {
+ {DW_LNE_set_address line_label_1}
+ {DW_LNS_advance_line 15}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_2}
+ {DW_LNS_advance_line 1}
+ {DW_LNS_copy}
+
+ {DW_LNS_set_file 2}
+ {DW_LNE_set_address line_label_3}
+ {DW_LNS_advance_line 4}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_4}
+ {DW_LNS_advance_line 1}
+ {DW_LNS_copy}
+
+ {DW_LNS_advance_line -4}
+ {DW_LNS_set_file 1}
+ {DW_LNS_negate_stmt}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_5}
+ {DW_LNS_advance_line 1}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_6}
+ {DW_LNS_advance_line 1}
+ {DW_LNS_negate_stmt}
+ {DW_LNS_copy}
+
+ {DW_LNE_set_address line_label_7}
+ {DW_LNE_end_sequence}
+ }
+ }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+ [list $srcfile $asm_file] $build_options] } {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+# Delete all breakpoints so that the output of "info breakpoints"
+# below will only contain a single breakpoint.
+delete_breakpoints
+
+# Place a breakpoint within the function in the header file.
+gdb_breakpoint "${srcfile4}:22"
+
+# Check that the breakpoint was placed where we expected. It should
+# appear at the requested line. When the bug in GDB was present the
+# breakpoint would be placed on one of the following lines instead.
+gdb_test "info breakpoints" \
+ ".* in callee at \[^\r\n\]+${srcfile4}:22\\y.*"
+
+gdb_continue_to_breakpoint "${srcfile4}:22" \
+ ".* ${srcfile4} : 22 .*"
+
+# Now single instruction step forward. Eventually we should hit
+# ${srcfile3}:20, but before we do we should hit the non-statement
+# line ${srcfile3}:19.
+#
+# We don't know how many instructions we'll need to step, but 100
+# should be enough for everyone (surely), and this stops us looping
+# forever if something goes wrong.
+set found_line_19 0
+set found_line_20 0
+set keep_going 1
+for { set i 0 } { $i < 100 && $keep_going } { incr i } {
+ set keep_going 0
+ gdb_test_multiple "stepi" "stepi ${i}" {
+ -re "${srcfile3} : 19 .*${gdb_prompt} " {
+ set found_line_19 1
+ set keep_going 1
+ }
+
+ -re "${srcfile3} : 20 .*${gdb_prompt} " {
+ set found_line_20 1
+ }
+
+ -re "${srcfile4} : 22 .*${gdb_prompt} " {
+ # Not left line 22 yet.
+ set keep_going 1
+ }
+ }
+}
+
+gdb_assert { $found_line_19 && $found_line_20 } \
+ "found line 19 and 20"
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
new file mode 100644
index 0000000..a1b7b17
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header-lbls.c
@@ -0,0 +1,46 @@
+/* Copyright 2020 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Used to insert labels with which we can build a fake line table. */
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+volatile int var;
+volatile int bar;
+
+/* Generate some code to take up some space. */
+#define FILLER do { \
+ var = 99; \
+} while (0)
+
+int
+main ()
+{ /* main prologue */
+ asm ("main_label: .globl main_label");
+ LL (1); // F1, Ln 16
+ FILLER;
+ LL (2); // F1, Ln 17
+ FILLER;
+ LL (3); // F2, Ln 21
+ FILLER;
+ LL (4); // F2, Ln 22 // F1, Ln 18, !S
+ FILLER;
+ LL (5); // F1, Ln 19 !S
+ FILLER;
+ LL (6); // F1, Ln 20
+ FILLER;
+ LL (7);
+ FILLER;
+ return 0; /* main end */
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
new file mode 100644
index 0000000..a833126
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.c
@@ -0,0 +1,24 @@
+/* Copyright 2020 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* dw2-inline-header.c : 16 */
+/* dw2-inline-header.c : 17 */
+/* dw2-inline-header.c : 18 */
+/* dw2-inline-header.c : 19 */
+/* dw2-inline-header.c : 20 */
+/* dw2-inline-header.c : 21 */
+/* dw2-inline-header.c : 22 */
+/* dw2-inline-header.c : 23 */
+/* dw2-inline-header.c : 24 */
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
new file mode 100644
index 0000000..7233acb
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-header.h
@@ -0,0 +1,24 @@
+/* Copyright 2020 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* dw2-inline-header.h : 16 */
+/* dw2-inline-header.h : 17 */
+/* dw2-inline-header.h : 18 */
+/* dw2-inline-header.h : 19 */
+/* dw2-inline-header.h : 20 */
+/* dw2-inline-header.h : 21 */
+/* dw2-inline-header.h : 22 */
+/* dw2-inline-header.h : 23 */
+/* dw2-inline-header.h : 24 */