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.