diff options
author | Pedro Alves <palves@redhat.com> | 2014-10-28 15:51:30 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2014-10-28 15:51:30 +0000 |
commit | abbdbd03db7eea82cadbb418da733991cba91b15 (patch) | |
tree | 25abe49177e84ef9268c9b35219214225e5c1795 /gdb | |
parent | 5a4b0ccc20ba30caef53b01bee2c0aaa5b855339 (diff) | |
download | gdb-abbdbd03db7eea82cadbb418da733991cba91b15.zip gdb-abbdbd03db7eea82cadbb418da733991cba91b15.tar.gz gdb-abbdbd03db7eea82cadbb418da733991cba91b15.tar.bz2 |
Test for PR gdb/17511, spurious SIGTRAP after stepping into+in signal handler
I noticed that when I single-step into a signal handler with a
pending/queued signal, the following single-steps while the program is
in the signal handler leave $eflags.TF set. That means subsequent
continues will trap after one instruction, resulting in a spurious
SIGTRAP being reported to the user.
This is a kernel bug; I've reported it to kernel devs (turned out to
be a known bug). I'm seeing it on x86_64 Fedora 20 (Linux
3.16.4-200.fc20.x86_64), and I was told it's still not fixed upstream.
This commit extends gdb.base/sigstep.exp to cover this use case,
xfailed.
Here's what the bug looks like:
(gdb) start
Temporary breakpoint 1, main () at si-handler.c:48
48 setup ();
(gdb) next
50 global = 0; /* set break here */
Let's queue a signal, so we can step into the handler:
(gdb) handle SIGUSR1
Signal Stop Print Pass to program Description
SIGUSR1 Yes Yes Yes User defined signal 1
(gdb) queue-signal SIGUSR1
TF is not set:
(gdb) display $eflags
1: $eflags = [ PF ZF IF ]
Now step into the handler -- "si" does PTRACE_SINGLESTEP+SIGUSR1:
(gdb) si
sigusr1_handler (sig=0) at si-handler.c:31
31 {
1: $eflags = [ PF ZF IF ]
No TF yet. But another single-step...
(gdb) si
0x0000000000400621 31 {
1: $eflags = [ PF ZF TF IF ]
... ends up with TF left set. This results in PTRACE_CONTINUE
trapping after each instruction is executed:
(gdb) c
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
0x0000000000400624 in sigusr1_handler (sig=0) at si-handler.c:31
31 {
1: $eflags = [ PF ZF TF IF ]
(gdb) c
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
sigusr1_handler (sig=10) at si-handler.c:32
32 global = 0;
1: $eflags = [ PF ZF TF IF ]
(gdb)
Note that even another PTRACE_SINGLESTEP does not fix it:
(gdb) si
33 }
1: $eflags = [ PF ZF TF IF ]
(gdb)
Eventually, it gets "fixed" by the rt_sigreturn syscall, when
returning out of the handler:
(gdb) bt
#0 sigusr1_handler (sig=10) at si-handler.c:33
#1 <signal handler called>
#2 main () at si-handler.c:50
(gdb) set disassemble-next-line on
(gdb) si
0x0000000000400632 33 }
0x0000000000400631 <sigusr1_handler+17>: 5d pop %rbp
=> 0x0000000000400632 <sigusr1_handler+18>: c3 retq
1: $eflags = [ PF ZF TF IF ]
(gdb)
<signal handler called>
=> 0x0000003b36a358f0 <__restore_rt+0>: 48 c7 c0 0f 00 00 00 mov $0xf,%rax
1: $eflags = [ PF ZF TF IF ]
(gdb) si
<signal handler called>
=> 0x0000003b36a358f7 <__restore_rt+7>: 0f 05 syscall
1: $eflags = [ PF ZF TF IF ]
(gdb)
main () at si-handler.c:50
50 global = 0; /* set break here */
=> 0x000000000040066b <main+9>: c7 05 cb 09 20 00 00 00 00 00 movl $0x0,0x2009cb(%rip) # 0x601040 <global>
1: $eflags = [ PF ZF IF ]
(gdb)
The bug doesn't happen if we instead PTRACE_CONTINUE into the signal
handler -- e.g., set a breakpoint in the handler, queue a signal, and
"continue".
gdb/testsuite/
2014-10-28 Pedro Alves <palves@redhat.com>
PR gdb/17511
* gdb.base/sigstep.c (handler): Add a few more writes to 'done'.
* gdb.base/sigstep.exp (other_handler_location): New global.
(advance): Support stepping into the signal handler, and running
commands while in the handler.
(in_handler_map): New global.
(top level): In the advance test, add combinations for getting
into the handler with stepping commands, and for running commands
in the handler. Add comment descripting the advancei tests.
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/testsuite/ChangeLog | 12 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/sigstep.c | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/sigstep.exp | 93 |
3 files changed, 97 insertions, 13 deletions
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index a64289e..cada90d 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,5 +1,17 @@ 2014-10-28 Pedro Alves <palves@redhat.com> + PR gdb/17511 + * gdb.base/sigstep.c (handler): Add a few more writes to 'done'. + * gdb.base/sigstep.exp (other_handler_location): New global. + (advance): Support stepping into the signal handler, and running + commands while in the handler. + (in_handler_map): New global. + (top level): In the advance test, add combinations for getting + into the handler with stepping commands, and for running commands + in the handler. Add comment descripting the advancei tests. + +2014-10-28 Pedro Alves <palves@redhat.com> + * gdb.base/sigstep.exp: Use build_executable instead of prepare_for_testing. (top level): Move code that starts GDB, runs to main and creates a diff --git a/gdb/testsuite/gdb.base/sigstep.c b/gdb/testsuite/gdb.base/sigstep.c index cc69184..38dd1fb 100644 --- a/gdb/testsuite/gdb.base/sigstep.c +++ b/gdb/testsuite/gdb.base/sigstep.c @@ -29,7 +29,12 @@ static volatile int dummy; static void handler (int sig) { + /* This is more than one write so that the breakpoint location below + is more than one instruction away. */ done = 1; + done = 1; + done = 1; + done = 1; /* other handler location */ } /* handler */ struct itimerval itime; diff --git a/gdb/testsuite/gdb.base/sigstep.exp b/gdb/testsuite/gdb.base/sigstep.exp index ef98b3d..08ad828 100644 --- a/gdb/testsuite/gdb.base/sigstep.exp +++ b/gdb/testsuite/gdb.base/sigstep.exp @@ -36,6 +36,7 @@ if {[build_executable $testfile.exp $testfile $srcfile debug]} { set clear_done [gdb_get_line_number {done = 0}] set infinite_loop [gdb_get_line_number {while (!done)}] +set other_handler_location [gdb_get_line_number "other handler location"] # Restart GDB, set a display showing $PC, and run to main. @@ -72,29 +73,48 @@ proc validate_backtrace {} { validate_backtrace -proc advance { cmd } { +# Goes to handler using ENTER_CMD, runs IN_HANDLER while in the signal +# hander, and then steps out of the signal handler using EXIT_CMD. + +proc advance { enter_cmd in_handler_prefix in_handler exit_cmd } { global gdb_prompt inferior_exited_re + global clear_done other_handler_location - with_test_prefix "$cmd from handler" { - restart + set prefix "$enter_cmd to handler, $in_handler_prefix in handler, $exit_cmd from handler" - gdb_test "break handler" + with_test_prefix $prefix { + restart # Get us into the handler - gdb_test "continue" ".* handler .*" "continue to handler" + if { $enter_cmd == "continue" } { + gdb_test "break handler" + } else { + gdb_test "handle SIGALRM print pass stop" + gdb_test "handle SIGVTALRM print pass stop" + gdb_test "continue" "Program received signal.*" "continue to signal" + } + gdb_test "$enter_cmd" ".*handler .*" "$enter_cmd to handler" + + delete_breakpoints + + uplevel 1 $in_handler + + if { $exit_cmd == "continue" } { + gdb_test "break $clear_done" ".*" "break clear done" + } set test "leave handler" - gdb_test_multiple "$cmd" "${test}" { + gdb_test_multiple "$exit_cmd" "${test}" { -re "Could not insert single-step breakpoint.*$gdb_prompt $" { setup_kfail gdb/8841 "sparc*-*-openbsd*" fail "$test (could not insert single-step breakpoint)" } -re "done = 1;.*${gdb_prompt} $" { - send_gdb "$cmd\n" + send_gdb "$exit_cmd\n" exp_continue -continue_timer } -re "\} .. handler .*${gdb_prompt} $" { - send_gdb "$cmd\n" + send_gdb "$exit_cmd\n" exp_continue -continue_timer } -re "$inferior_exited_re normally.*${gdb_prompt} $" { @@ -114,6 +134,55 @@ proc advance { cmd } { } } +# Map of PREFIX => "things to do within the signal handler", for the +# advance tests. + +set in_handler_map { + "nothing" { + } + "si+advance" { + # Advance to the second location in handler. + gdb_test "si" "handler.*" "si in handler" + + set test "advance in handler" + gdb_test_multiple "advance $other_handler_location" $test { + -re "Program received signal SIGTRAP.*$gdb_prompt $" { + # On some versions of Linux (observed on + # 3.16.4-200.fc20.x86_64), using PTRACE_SINGLESTEP+sig + # to step into a signal handler, and then issuing + # another PTRACE_SINGLESTEP within the handler ends up + # with $eflags.TF mistakenly set, which results in + # subsequent PTRACE_CONTINUEs trapping after each + # insn. + if {$enter_cmd != "continue"} { + setup_xfail "x86_64-*-linux*" gdb/17511 + } + fail "$test (spurious SIGTRAP)" + return + } + -re "other handler location.*$gdb_prompt $" { + pass $test + } + } + } +} + +# Check that we can step/next/continue, etc. our way in and out of a +# signal handler. Also test that we can step, and run to a breakpoint +# within the handler. + +foreach enter_cmd { "stepi" "nexti" "step" "next" "continue" } { + if { $enter_cmd != "continue" && ![can_single_step_to_signal_handler] } { + continue + } + + foreach exit_cmd { "step" "next" "continue" } { + foreach {in_handler_prefix in_handler} $in_handler_map { + advance $enter_cmd $in_handler_prefix $in_handler $exit_cmd + } + } +} + proc advancei { cmd } { global gdb_prompt inferior_exited_re @@ -199,11 +268,9 @@ proc advancei { cmd } { } } -# Check that we can step/next our way out of a signal handler. - -foreach cmd {"step" "next"} { - advance $cmd -} +# Check that we can step our way out of a signal handler, using +# commands that first step out to the signal trampoline, and then out +# to the mainline code. foreach cmd {"stepi" "nexti" "finish" "return"} { advancei $cmd |