[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

@ -1,3 +1,10 @@
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.
2021-01-28 Andrew Burgess <andrew.burgess@embecosm.com>
* thread.c (thr_try_catch_cmd): Replace swith_to_thread with an

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.

View File

@ -1,3 +1,9 @@
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.
2021-01-29 Tom de Vries <tdevries@suse.de>
* gdb.opt/solib-intra-step.exp: Remove state tracking logic.

View File

@ -0,0 +1,44 @@
/*
Copyright 2021 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/>. */
void
foo (int x)
{
}
void
bar (void)
{
asm ("bar_label: .globl bar_label");
}
int
main (void)
{
asm ("main_label: .globl main_label");
bar ();
asm ("main_label_2: .globl main_label_2");
foo (10);
asm ("main_label_3: .globl main_label_3");
return 0;
}

View File

@ -0,0 +1,126 @@
# Copyright 2021 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Check whether stepping out of a function works, in case:
# - the first insn after the call has an is-stmt=no entry
# - the next insn has an is-stmt=yes entry, for the same line number
#
# This sort of thing can occur in optimized code, f.i. here a slightly more
# elaborate case with another is-stmt=no entry (the one with line number 12)
# inbetween:
# INDEX LINE ADDRESS IS-STMT
# 12 13 0x00000000004003ed
# 13 12 0x00000000004003f2
# 14 13 0x00000000004003f4 Y
load_lib dwarf.exp
# This test can only be run on targets which support DWARF-2 and use gas.
if {![dwarf2_support]} {
verbose "Skipping $gdb_test_file_name."
return 0
}
# The .c files use __attribute__.
if [get_compiler_info] {
return -1
}
if !$gcc_compiled {
verbose "Skipping $gdb_test_file_name."
return 0
}
standard_testfile .c -dw.S
set asm_file [standard_output_file $srcfile2]
Dwarf::assemble $asm_file {
declare_labels Llines
global srcdir subdir srcfile
lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \
main_start main_len
set main_end "$main_start + $main_len"
lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \
bar_start bar_len
set bar_end "$bar_start + $bar_len"
cu {} {
compile_unit {
{language @DW_LANG_C}
{name $srcfile}
{stmt_list $Llines DW_FORM_sec_offset}
} {
subprogram {
{external 1 flag}
{MACRO_AT_func {main}}
}
subprogram {
{external 1 flag}
{MACRO_AT_func {bar}}
}
}
}
lines {version 2} Llines {
include_dir "${srcdir}/${subdir}"
file_name "$srcfile" 1
program {
{DW_LNE_set_address bar_label}
{line 26}
{DW_LNS_copy}
{DW_LNE_set_address $bar_end}
{DW_LNE_end_sequence}
{DW_LNE_set_address main_label}
{line 32}
{DW_LNS_copy}
{DW_LNE_set_address main_label_2}
{line 36}
{DW_LNS_negate_stmt}
{DW_LNS_copy}
{DW_LNS_negate_stmt}
{DW_LNE_set_address main_label_3}
{line 36}
{DW_LNS_copy}
{DW_LNE_set_address $main_end}
{DW_LNE_end_sequence}
}
}
}
if { [prepare_for_testing "failed to prepare" ${testfile} \
[list $srcfile $asm_file] {nodebug}] } {
return -1
}
if ![runto_main] {
return -1
}
# Step into bar.
gdb_breakpoint "bar"
gdb_continue_to_breakpoint "bar"
# Step out of bar.
gdb_test "step" [multi_line \
"main \\(\\) at \[^\r\n\]*$srcfile:36" \
"36\t\[^\r\n\]*"]