mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-02-11 13:02:10 +08:00
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.
This commit is contained in:
parent
5a4b0ccc20
commit
abbdbd03db
@ -1,3 +1,15 @@
|
||||
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
|
||||
|
@ -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;
|
||||
|
@ -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 } {
|
||||
global gdb_prompt inferior_exited_re
|
||||
# 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.
|
||||
|
||||
with_test_prefix "$cmd from handler" {
|
||||
proc advance { enter_cmd in_handler_prefix in_handler exit_cmd } {
|
||||
global gdb_prompt inferior_exited_re
|
||||
global clear_done other_handler_location
|
||||
|
||||
set prefix "$enter_cmd to handler, $in_handler_prefix in handler, $exit_cmd from handler"
|
||||
|
||||
with_test_prefix $prefix {
|
||||
restart
|
||||
|
||||
gdb_test "break handler"
|
||||
|
||||
# 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
|
||||
|
Loading…
Reference in New Issue
Block a user