mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
[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:
parent
620ec3caae
commit
ebde6f2ddc
@ -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
|
||||
|
33
gdb/infrun.c
33
gdb/infrun.c
@ -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.
|
||||
|
@ -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.
|
||||
|
44
gdb/testsuite/gdb.dwarf2/dw2-step-out-of-function-no-stmt.c
Normal file
44
gdb/testsuite/gdb.dwarf2/dw2-step-out-of-function-no-stmt.c
Normal 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;
|
||||
}
|
||||
|
126
gdb/testsuite/gdb.dwarf2/dw2-step-out-of-function-no-stmt.exp
Normal file
126
gdb/testsuite/gdb.dwarf2/dw2-step-out-of-function-no-stmt.exp
Normal 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\]*"]
|
||||
|
Loading…
Reference in New Issue
Block a user