From 776f04fafe124218f3602c2d9c598662c0f17aa9 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Fri, 18 Oct 2013 14:28:34 +0000 Subject: [PATCH] [gdb/16062] stepi sometimes doesn't make progress I noticed something odd while doing "stepi" over a fork syscall: ... (gdb) set disassemble-next-line on ... (gdb) si 0x000000323d4ba7c2 131 pid = ARCH_FORK (); 0x000000323d4ba7a4 <__libc_fork+132>: 64 4c 8b 04 25 10 00 00 00 mov %fs:0x10,%r8 0x000000323d4ba7ad <__libc_fork+141>: 31 d2 xor %edx,%edx 0x000000323d4ba7af <__libc_fork+143>: 4d 8d 90 d0 02 00 00 lea 0x2d0(%r8),%r10 0x000000323d4ba7b6 <__libc_fork+150>: 31 f6 xor %esi,%esi 0x000000323d4ba7b8 <__libc_fork+152>: bf 11 00 20 01 mov $0x1200011,%edi 0x000000323d4ba7bd <__libc_fork+157>: b8 38 00 00 00 mov $0x38,%eax => 0x000000323d4ba7c2 <__libc_fork+162>: 0f 05 syscall 0x000000323d4ba7c4 <__libc_fork+164>: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax 0x000000323d4ba7ca <__libc_fork+170>: 0f 87 2b 01 00 00 ja 0x323d4ba8fb <__libc_fork+475> (gdb) si 0x000000323d4ba7c4 131 pid = ARCH_FORK (); 0x000000323d4ba7a4 <__libc_fork+132>: 64 4c 8b 04 25 10 00 00 00 mov %fs:0x10,%r8 0x000000323d4ba7ad <__libc_fork+141>: 31 d2 xor %edx,%edx 0x000000323d4ba7af <__libc_fork+143>: 4d 8d 90 d0 02 00 00 lea 0x2d0(%r8),%r10 0x000000323d4ba7b6 <__libc_fork+150>: 31 f6 xor %esi,%esi 0x000000323d4ba7b8 <__libc_fork+152>: bf 11 00 20 01 mov $0x1200011,%edi 0x000000323d4ba7bd <__libc_fork+157>: b8 38 00 00 00 mov $0x38,%eax 0x000000323d4ba7c2 <__libc_fork+162>: 0f 05 syscall => 0x000000323d4ba7c4 <__libc_fork+164>: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax 0x000000323d4ba7ca <__libc_fork+170>: 0f 87 2b 01 00 00 ja 0x323d4ba8fb <__libc_fork+475> (gdb) si 0x000000323d4ba7c4 131 pid = ARCH_FORK (); 0x000000323d4ba7a4 <__libc_fork+132>: 64 4c 8b 04 25 10 00 00 00 mov %fs:0x10,%r8 0x000000323d4ba7ad <__libc_fork+141>: 31 d2 xor %edx,%edx 0x000000323d4ba7af <__libc_fork+143>: 4d 8d 90 d0 02 00 00 lea 0x2d0(%r8),%r10 0x000000323d4ba7b6 <__libc_fork+150>: 31 f6 xor %esi,%esi 0x000000323d4ba7b8 <__libc_fork+152>: bf 11 00 20 01 mov $0x1200011,%edi 0x000000323d4ba7bd <__libc_fork+157>: b8 38 00 00 00 mov $0x38,%eax 0x000000323d4ba7c2 <__libc_fork+162>: 0f 05 syscall => 0x000000323d4ba7c4 <__libc_fork+164>: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax 0x000000323d4ba7ca <__libc_fork+170>: 0f 87 2b 01 00 00 ja 0x323d4ba8fb <__libc_fork+475> (gdb) si 0x000000323d4ba7ca 131 pid = ARCH_FORK (); 0x000000323d4ba7a4 <__libc_fork+132>: 64 4c 8b 04 25 10 00 00 00 mov %fs:0x10,%r8 0x000000323d4ba7ad <__libc_fork+141>: 31 d2 xor %edx,%edx 0x000000323d4ba7af <__libc_fork+143>: 4d 8d 90 d0 02 00 00 lea 0x2d0(%r8),%r10 0x000000323d4ba7b6 <__libc_fork+150>: 31 f6 xor %esi,%esi 0x000000323d4ba7b8 <__libc_fork+152>: bf 11 00 20 01 mov $0x1200011,%edi 0x000000323d4ba7bd <__libc_fork+157>: b8 38 00 00 00 mov $0x38,%eax 0x000000323d4ba7c2 <__libc_fork+162>: 0f 05 syscall 0x000000323d4ba7c4 <__libc_fork+164>: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax => 0x000000323d4ba7ca <__libc_fork+170>: 0f 87 2b 01 00 00 ja 0x323d4ba8fb <__libc_fork+475> Notice how the third "si" didn't actually make progress. Turning on infrun and lin-lwp debug, we see: (gdb) infrun: clear_proceed_status_thread (process 5252) infrun: proceed (addr=0xffffffffffffffff, signal=144, step=1) infrun: resume (step=1, signal=0), trap_expected=0, current thread [process 5252] at 0x323d4ba7c4 LLR: Preparing to step process 5252, 0, inferior_ptid process 5252 RC: Not resuming sibling process 5252 (not stopped) LLR: PTRACE_SINGLESTEP process 5252, 0 (resume event thread) sigchld infrun: wait_for_inferior () linux_nat_wait: [process -1], [] LLW: enter LNW: waitpid(-1, ...) returned 5252, No child processes LLW: waitpid 5252 received Child exited (stopped) LLW: Candidate event Child exited (stopped) in process 5252. SEL: Select single-step process 5252 LLW: exit infrun: target_wait (-1, status) = infrun: 5252 [process 5252], infrun: status->kind = stopped, signal = SIGCHLD infrun: infwait_normal_state infrun: TARGET_WAITKIND_STOPPED infrun: stop_pc = 0x323d4ba7c4 infrun: random signal 20 infrun: stepi/nexti infrun: stop_stepping So the inferior got a SIGCHLD (because the fork child exited while we're doing 'si'), and since that signal is set to "nostop noprint pass" (by default), it's considered a random signal, so it should not cause a stop. But, it resulted in an immediate a stop_stepping call anyway. So the single-step never really finished. This is a regression caused by: [[PATCH] Do not respawn signals, take 2.] https://sourceware.org/ml/gdb-patches/2012-06/msg00702.html Specifically, caused by this change (as mentioned in the "the lost step issue first" part of that mail): diff --git a/gdb/infrun.c b/gdb/infrun.c index 53db335..3e8dbc8 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -4363,10 +4363,8 @@ process_event_stop_test: (leaving the inferior at the step-resume-breakpoint without actually executing it). Either way continue until the breakpoint is really hit. */ - keep_going (ecs); - return; } - + else /* Handle cases caused by hitting a breakpoint. */ { That made GDB fall through to the > /* In all-stop mode, if we're currently stepping but have stopped in > some other thread, we need to switch back to the stepped thread. */ > if (!non_stop) part. However, if we don't have a stepped thread to get back to, we'll now also fall through to all the "stepping" tests. For line stepping, that'll turn out okay, as we'll just end up realizing the thread is still in the stepping range, and needs to be re-stepped. However, for stepi/nexti, we'll reach: if (ecs->event_thread->control.step_range_end == 1) { /* It is stepi or nexti. We always want to stop stepping after one instruction. */ if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: stepi/nexti\n"); ecs->event_thread->control.stop_step = 1; print_end_stepping_range_reason (); stop_stepping (ecs); return; } and stop, even though the thread actually made no progress. The fix is to restore the keep_going call, but put it after the "switch back to the stepped thread" code, and before the stepping tests. Tested on x86_64 Fedora 17, native and gdbserver. New test included. gdb/ 2013-10-18 Pedro Alves PR gdb/16062 * infrun.c (handle_inferior_event): Keep going if we got a random signal we should not stop for, instead of falling through to the step tests. gdb/testsuite/ 2013-10-18 Pedro Alves PR gdb/16062 * gdb.threads/stepi-random-signal.c: New file. * gdb.threads/stepi-random-signal.exp: New file. --- gdb/ChangeLog | 7 ++ gdb/infrun.c | 11 ++ gdb/testsuite/ChangeLog | 6 + .../gdb.threads/stepi-random-signal.c | 48 ++++++++ .../gdb.threads/stepi-random-signal.exp | 104 ++++++++++++++++++ 5 files changed, 176 insertions(+) create mode 100644 gdb/testsuite/gdb.threads/stepi-random-signal.c create mode 100644 gdb/testsuite/gdb.threads/stepi-random-signal.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 872838b2d1..c3e3d21079 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,10 @@ +2013-10-18 Pedro Alves + + PR gdb/16062 + * infrun.c (handle_inferior_event): Keep going if we got a random + signal we should not stop for, instead of falling through to the + step tests. + 2013-10-18 Yao Qi * c-varobj.c (cplus_number_of_children): Fix indentation. diff --git a/gdb/infrun.c b/gdb/infrun.c index 39c9cf3bb1..b1f961163b 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -4715,6 +4715,17 @@ process_event_stop_test: } } + if (ecs->random_signal) + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: random signal, keep going\n"); + + /* Signal not stepping related. */ + keep_going (ecs); + return; + } + if (ecs->event_thread->control.step_resume_breakpoint) { if (debug_infrun) diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 77d9045e9d..8e057f6d90 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2013-10-18 Pedro Alves + + PR gdb/16062 + * gdb.threads/stepi-random-signal.c: New file. + * gdb.threads/stepi-random-signal.exp: New file. + 2013-10-17 Maciej W. Rozycki * gdb.mi/mi-breakpoint-changed.exp (test_insert_delete_modify): diff --git a/gdb/testsuite/gdb.threads/stepi-random-signal.c b/gdb/testsuite/gdb.threads/stepi-random-signal.c new file mode 100644 index 0000000000..2aec7f1bed --- /dev/null +++ b/gdb/testsuite/gdb.threads/stepi-random-signal.c @@ -0,0 +1,48 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2013 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 . */ + +#include +#include +#include + +static pthread_t main_thread; + +static void * +start (void *arg) +{ + /* A signal whose default action is ignore. */ + pthread_kill (main_thread, SIGCHLD); + + while (1) + sleep (1); /* set break here */ + return NULL; +} + +int +main (void) +{ + unsigned int counter = 1; + pthread_t thread; + + main_thread = pthread_self (); + pthread_create (&thread, NULL, start, NULL); + + while (counter != 0) + counter++; /* set break 2 here */ + + return 0; +} diff --git a/gdb/testsuite/gdb.threads/stepi-random-signal.exp b/gdb/testsuite/gdb.threads/stepi-random-signal.exp new file mode 100644 index 0000000000..5c12c6c457 --- /dev/null +++ b/gdb/testsuite/gdb.threads/stepi-random-signal.exp @@ -0,0 +1,104 @@ +# Copyright 2013 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 . + +standard_testfile +set executable ${testfile} + +if { [gdb_compile_pthreads \ + "${srcdir}/${subdir}/${srcfile}" \ + "${binfile}" \ + executable {debug}] != "" } { + untested "Couldn't compile test program." + return -1 +} + +clean_restart $executable + +# Start the second thread. +if ![runto start] { + return -1 +} + +# Go back to the main thread, and leave it in the loop, where we're +# reasonably sure we don't have 'conditional jmp $pc'-like +# instructions. We wouldn't be able to detect whether a stepi makes +# progress over those. +gdb_test_no_output "set scheduler-locking on" +gdb_test "thread 1" "Switching to .*" +gdb_breakpoint $srcfile:[gdb_get_line_number "set break 2 here"] +gdb_continue_to_breakpoint "loop" ".* set break 2 here .*" + +# Now back to thread 2, and let it queue a signal in thread 1. +gdb_test "thread 2" "Switching to .*" +gdb_breakpoint $srcfile:[gdb_get_line_number "set break here"] +gdb_continue_to_breakpoint "after pthread_kill" ".* set break here .*" + +# We're now ready to stepi thread 1. It should immediately dequeue +# the signal. +gdb_test "thread 1" "Switching to .*" "thread 1 again" + +# No longer need these. +delete_breakpoints + +# Turn on infrun debugging, so we can tell whether the signal is +# really dequeued and that GDB sees it. +gdb_test_no_output "set debug infrun 1" + +# Helper to extract the current PC. PREFIX is used to make each call +# have its own unique test name. + +proc get_pc { prefix } { + with_test_prefix "$prefix" { + return [get_hexadecimal_valueof "\$pc" ""] + } +} + +set prev_addr [get_pc "before stepi"] +if {$prev_addr == ""} { + return +} + +# True if we saw the infrun path we want to test be exercised. +set seen 0 + +set test "stepi" +if {[gdb_test_multiple "stepi" "$test" { + -re "infrun: random signal" { + set seen 1 + exp_continue + } + -re "$gdb_prompt $" { + } +}] != 0} { + return +} + +if {$seen} { + pass "$test" +} else { + fail "$test (no random signal)" +} + +set addr [get_pc "after stepi"] +if {$addr == ""} { + return +} + +set test "stepi interfered by signal makes progress" +if {$addr == $prev_addr} { + fail "$test" +} else { + pass "$test" +}