aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2014-10-28 15:51:30 +0000
committerPedro Alves <palves@redhat.com>2014-10-28 15:51:30 +0000
commitabbdbd03db7eea82cadbb418da733991cba91b15 (patch)
tree25abe49177e84ef9268c9b35219214225e5c1795 /gdb
parent5a4b0ccc20ba30caef53b01bee2c0aaa5b855339 (diff)
downloadgdb-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/ChangeLog12
-rw-r--r--gdb/testsuite/gdb.base/sigstep.c5
-rw-r--r--gdb/testsuite/gdb.base/sigstep.exp93
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