[gdb/breakpoint] Fix stepping past non-stmt line-table entries

Consider the test-case small.c:
...
$ cat -n small.c
     1  __attribute__ ((noinline, noclone))
     2  int foo (char *c)
     3  {
     4    asm volatile ("" : : "r" (c) : "memory");
     5    return 1;
     6  }
     7
     8  int main ()
     9  {
    10    char tpl1[20] = "/tmp/test.XXX";
    11    char tpl2[20] = "/tmp/test.XXX";
    12    int fd1 = foo (tpl1);
    13    int fd2 = foo (tpl2);
    14    if (fd1 == -1) {
    15      return 1;
    16    }
    17
    18    return 0;
    19  }
...

Compiled with gcc-8 and optimization:
...
$ gcc-8 -O2 -g small.c
...

We step through the calls to foo, but fail to visit line 13:
...
12	  int fd1 = foo (tpl1);
(gdb) step
foo (c=c@entry=0x7fffffffdea0 "/tmp/test.XXX") at small.c:5
5	  return 1;
(gdb) step
foo (c=c@entry=0x7fffffffdec0 "/tmp/test.XXX") at small.c:5
5	  return 1;
(gdb) step
main () at small.c:14
14	  if (fd1 == -1) {
(gdb)
...

This is caused by the following.  The calls to foo are implemented by these
insns:
....
  4003df:       0f 29 04 24             movaps %xmm0,(%rsp)
  4003e3:       0f 29 44 24 20          movaps %xmm0,0x20(%rsp)
  4003e8:       e8 03 01 00 00          callq  4004f0 <foo>
  4003ed:       48 8d 7c 24 20          lea    0x20(%rsp),%rdi
  4003f2:       89 c2                   mov    %eax,%edx
  4003f4:       e8 f7 00 00 00          callq  4004f0 <foo>
  4003f9:       31 c0                   xor    %eax,%eax
...
with corresponding line table entries:
...
INDEX  LINE   ADDRESS            IS-STMT
8      12     0x00000000004003df Y
9      10     0x00000000004003df
10     11     0x00000000004003e3
11     12     0x00000000004003e8
12     13     0x00000000004003ed
13     12     0x00000000004003f2
14     13     0x00000000004003f4 Y
15     13     0x00000000004003f4
16     14     0x00000000004003f9 Y
17     14     0x00000000004003f9
...

Once we step out of the call to foo at 4003e8, we land at 4003ed, and gdb
enters process_event_stop_test to figure out what to do.

That entry has is-stmt=n, so it's not the start of a line, so we don't stop
there.  However, we do update ecs->event_thread->current_line to line 13,
because the frame has changed (because we stepped out of the function).

Next we land at 4003f2.  Again the entry has is-stmt=n, so it's not the start
of a line, so we don't stop there.  However, because the frame hasn't changed,
we don't update update ecs->event_thread->current_line, so it stays 13.

Next we land at 4003f4.  Now is-stmt=y, so it's the start of a line, and we'd
like to stop here.

But we don't stop because this test fails:
...
  if ((ecs->event_thread->suspend.stop_pc == stop_pc_sal.pc)
      && (ecs->event_thread->current_line != stop_pc_sal.line
          || ecs->event_thread->current_symtab != stop_pc_sal.symtab))
    {
...
because ecs->event_thread->current_line == 13 and stop_pc_sal.line == 13.

Fix this by resetting ecs->event_thread->current_line to 0 if is-stmt=n and
the frame has changed, such that we have:
...
12        int fd1 = foo (tpl1);
(gdb) step
foo (c=c@entry=0x7fffffffdbc0 "/tmp/test.XXX") at small.c:5
5         return 1;
(gdb) step
main () at small.c:13
13        int fd2 = foo (tpl2);
(gdb)
...

Tested on x86_64-linux, with gcc-7 and gcc-8.

gdb/ChangeLog:

2021-01-29  Tom de Vries  <tdevries@suse.de>

	PR breakpoints/26063
	* infrun.c (process_event_stop_test): Reset
	ecs->event_thread->current_line to 0 if is-stmt=n and frame has
	changed.

gdb/testsuite/ChangeLog:

2021-01-29  Tom de Vries  <tdevries@suse.de>

	PR breakpoints/26063
	* gdb.dwarf2/dw2-step-out-of-function-no-stmt.c: New test.
	* gdb.dwarf2/dw2-step-out-of-function-no-stmt.exp: New file.
This commit is contained in:
Tom de Vries
2021-01-29 13:36:52 +01:00
parent 620ec3caae
commit ebde6f2ddc
5 changed files with 208 additions and 8 deletions

View File

@@ -7000,27 +7000,44 @@ process_event_stop_test (struct execution_control_state *ecs)
&& (ecs->event_thread->current_line != stop_pc_sal.line
|| ecs->event_thread->current_symtab != stop_pc_sal.symtab))
{
/* We are at a different line. */
if (stop_pc_sal.is_stmt)
{
/* We are at the start of a different line. So stop. Note that
we don't stop if we step into the middle of a different line.
That is said to make things like for (;;) statements work
better. */
/* We are at the start of a statement.
So stop. Note that we don't stop if we step into the middle of a
statement. That is said to make things like for (;;) statements
work better. */
infrun_debug_printf ("stepped to a different line");
end_stepping_range (ecs);
return;
}
else if (frame_id_eq (get_frame_id (get_current_frame ()),
ecs->event_thread->control.step_frame_id))
ecs->event_thread->control.step_frame_id))
{
/* We are at the start of a different line, however, this line is
not marked as a statement, and we have not changed frame. We
ignore this line table entry, and continue stepping forward,
/* We are not at the start of a statement, and we have not changed
frame.
We ignore this line table entry, and continue stepping forward,
looking for a better place to stop. */
refresh_step_info = false;
infrun_debug_printf ("stepped to a different line, but "
"it's not the start of a statement");
}
else
{
/* We are not the start of a statement, and we have changed frame.
We ignore this line table entry, and continue stepping forward,
looking for a better place to stop. Keep refresh_step_info at
true to note that the frame has changed, but ignore the line
number to make sure we don't ignore a subsequent entry with the
same line number. */
stop_pc_sal.line = 0;
infrun_debug_printf ("stepped to a different frame, but "
"it's not the start of a statement");
}
}
/* We aren't done stepping.