Re: [rfa] New test sigbpt.{c,exp}

Andrew Cagney
2004-06-30T15:25:21Z

[-- Attachment #1: Type: text/plain, Size: 2130 bytes --]

> Mostly okay, but there's too many duplicated test names.
>
> % cat gdb.sum | sort | uniq -c | sort -rn
> 8 PASS: gdb.base/sigbpt.exp: pass SIGSEGV for no breakpoints
> 7 PASS: gdb.base/sigbpt.exp: rerun to main
> 4 PASS: gdb.base/sigbpt.exp: continue to keeper for no breakpoints
> 3 PASS: gdb.base/sigbpt.exp: stepi fault for no breakpoints
> 3 PASS: gdb.base/sigbpt.exp: continue to breakpoint at fault for no breakpoints
> 2 PASS: gdb.base/sigbpt.exp: pass SIGSEGV for bp before segv
> 2 PASS: gdb.base/sigbpt.exp: pass SIGSEGV for bp before and at segv
> 2 PASS: gdb.base/sigbpt.exp: pass SIGSEGV for bp at segv
>
> Can you make more of the test names more unique?

Oops, I thought I'd covered that. Try the attached.

> Last, a typo:
>
> set test "Verify that SIGSEGV occures at the last STEPI insn"

Fixed.

> Also I'm getting four FAIL results (native i686-pc-linux-gnu,
> red hat 8.0, gcc 3.3.3, binutils 2.15, glibc 2.2.93-5-rh,
> kernel 2.4.8-14-rh).
>
> FAIL: gdb.base/sigbpt.exp: stepi out of handler for no breakpoints (executed fault insn)
> FAIL: gdb.base/sigbpt.exp: stepi out of handler for bp before segv (executed fault insn)
> FAIL: gdb.base/sigbpt.exp: stepi out of handler for bp at segv
> FAIL: gdb.base/sigbpt.exp: stepi out of handler for bp before and at segv
>
> Is that a fault of the system under test, or a fault of the test script?
> Your message implies the former (because it passes with a hacked kernel),
> but I just want to check.

"yes", so far I've identified the following:

- h/w single-step #1 - kernel
The kernel is issuing two-for-one instructions when single stepping a
system call.

- h/w single-step #2 - kernel
The kernel is continuing, instead of single-stepping, the sigreturn
system call.

- s/w single-step #1 - gdb
GDB's OSABI software single-step code doesn't know how to single-step
sigreturn.

We, GDB, can work around the kernel bugs by using software single step.
But that means fixing software single-step.

Are there [get_kernel_info] and [get_software_singlestep] testsuite calls?

Andrew

[-- Attachment #2: diffs --]
[-- Type: text/plain, Size: 10835 bytes --]

2004-06-18 Andrew Cagney <cagney@gnu.org>

* gdb.base/sigbpt.exp, gdb.base/sigbpt.c: New test.

Index: gdb.base/sigbpt.c
===================================================================
RCS file: gdb.base/sigbpt.c
diff -N gdb.base/sigbpt.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb.base/sigbpt.c 30 Jun 2004 14:58:02 -0000
@@ -0,0 +1,53 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2004 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern void
+keeper (int sig)
+{
+}
+
+volatile long v1 = 0;
+volatile long v2 = 0;
+volatile long v3 = 0;
+
+extern long
+bowler (void)
+{
+ /* Try to read address zero. Do it in a slightly convoluted way so
+ that more than one instruction is used. */
+ return *(char *) (v1 + v2 + v3);
+}
+
+int
+main ()
+{
+ static volatile int i;
+
+ struct sigaction act;
+ memset (&act, 0, sizeof act);
+ act.sa_handler = keeper;
+ sigaction (SIGSEGV, &act, NULL);
+
+ bowler ();
+ return 0;
+}
Index: gdb.base/sigbpt.exp
===================================================================
RCS file: gdb.base/sigbpt.exp
diff -N gdb.base/sigbpt.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb.base/sigbpt.exp 30 Jun 2004 14:58:02 -0000
@@ -0,0 +1,253 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2004 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 2 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# Check that GDB can and only executes single instructions when
+# stepping through a sequence of breakpoints interleaved by signal.
+
+if $tracelevel {
+ strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+set testfile "sigbpt"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+ gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail."
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+#
+# Run to `main' where we begin our tests.
+#
+
+if ![runto_main] then {
+ gdb_suppress_tests
+}
+
+# If we can examine what's at memory address 0, it is possible that we
+# could also execute it. This could probably make us run away,
+# executing random code, which could have all sorts of ill effects,
+# especially on targets without an MMU. Don't run the tests in that
+# case.
+
+send_gdb "x 0\n"
+gdb_expect {
+ -re "0x0:.*Cannot access memory at address 0x0.*$gdb_prompt $" { }
+ -re "0x0:.*Error accessing memory address 0x0.*$gdb_prompt $" { }
+ -re ".*$gdb_prompt $" {
+ untested "Memory at address 0 is possibly executable"
+ return
+ }
+}
+
+gdb_test "break keeper"
+
+# Run to bowler, and then single step until there's a SIGSEGV. Record
+# the address of each single-step instruction (up to and including the
+# instruction that causes the SIGSEGV) in bowler_addrs, and the address
+# of the actual SIGSEGV in segv_addr.
+
+set bowler_addrs bowler
+gdb_test {display/i $pc}
+gdb_test "advance *bowler" "bowler.*" "advance to the bowler"
+set test "stepping to SIGSEGV"
+gdb_test_multiple "stepi" "$test" {
+ -re "Program received signal SIGSEGV.*pc *(0x\[0-9a-f\]*).*$gdb_prompt $" {
+ set segv_addr $expect_out(1,string)
+ pass "$test"
+ }
+ -re " .*pc *(0x\[0-9a-f\]*).*bowler.*$gdb_prompt $" {
+ set bowler_addrs [concat $expect_out(1,string) $bowler_addrs]
+ send_gdb "stepi\n"
+ exp_continue
+ }
+}
+
+# Now record the address of the instruction following the faulting
+# instruction in bowler_addrs.
+
+set test "get insn after fault"
+gdb_test_multiple {x/2i $pc} "$test" {
+ -re "(0x\[0-9a-f\]*).*bowler.*(0x\[0-9a-f\]*).*bowler.*$gdb_prompt $" {
+ set bowler_addrs [concat $expect_out(2,string) $bowler_addrs]
+ pass "$test"
+ }
+}
+
+# Procedures for returning the address of the instruction before, at
+# and after, the faulting instruction.
+
+proc before_segv { } {
+ global bowler_addrs
+ return [lindex $bowler_addrs 2]
+}
+
+proc at_segv { } {
+ global bowler_addrs
+ return [lindex $bowler_addrs 1]
+}
+
+proc after_segv { } {
+ global bowler_addrs
+ return [lindex $bowler_addrs 0]
+}
+
+# Check that the address table and SIGSEGV correspond.
+
+set test "Verify that SIGSEGV occurs at the last STEPI insn"
+if {[string compare $segv_addr [at_segv]] == 0} {
+ pass "$test"
+} else {
+ fail "$test ($segv_addr [at_segv])"
+}
+
+# Check that the inferior is correctly single stepped all the way back
+# to a faulting instruction.
+
+proc stepi_out { name args } {
+ global gdb_prompt
+
+ # Set SIGSEGV to pass+nostop and then run the inferior all the way
+ # through to the signal handler. With the handler is reached,
+ # disable SIGSEGV, ensuring that further signals stop the
+ # inferior. Stops a SIGSEGV infinite loop when a broke system
+ # keeps re-executing the faulting instruction.
+ rerun_to_main
+ gdb_test "handle SIGSEGV nostop print pass" "" "pass SIGSEGV; $name"
+ gdb_test "continue" "keeper.*" "continue to keeper; $name"
+ gdb_test "handle SIGSEGV stop print nopass" "" "nopass SIGSEGV; $name"
+
+ # Insert all the breakpoints. To avoid the need to step over
+ # these instructions, this is delayed until after the keeper has
+ # been reached.
+ for {set i 0} {$i < [llength $args]} {incr i} {
+ gdb_test "break [lindex $args $i]" "Breakpoint.*" \
+ "set breakpoint $i of [llength $args]; $name"
+ }
+
+ # Single step our way out of the keeper, through the signal
+ # trampoline, and back to the instruction that faulted.
+ set test "stepi out of handler; $name"
+ gdb_test_multiple "stepi" "$test" {
+ -re "keeper.*$gdb_prompt $" {
+ send_gdb "stepi\n"
+ exp_continue
+ }
+ -re "signal handler.*$gdb_prompt $" {
+ send_gdb "stepi\n"
+ exp_continue
+ }
+ -re "Program received signal SIGSEGV.*$gdb_prompt $" {
+ fail "$test (executed fault insn)"
+ }
+ -re "Breakpoint.*pc *[at_segv] .*bowler.*$gdb_prompt $" {
+ pass "$test (at breakpoint)"
+ }
+ -re "Breakpoint.*pc *[after_segv] .*bowler.*$gdb_prompt $" {
+ fail "$test (executed breakpoint)"
+ }
+ -re "pc *[at_segv] .*bowler.*$gdb_prompt $" {
+ pass "$test"
+ }
+ -re "pc *[after_segv] .*bowler.*$gdb_prompt $" {
+ fail "$test (skipped fault insn)"
+ }
+ }
+
+ # Clear any breakpoints
+ for {set i 0} {$i < [llength $args]} {incr i} {
+ gdb_test "clear [lindex $args $i]" "Deleted .*" \
+ "clear breakpoint $i of [llength $args]; $name"
+ }
+}
+
+# Let a signal handler exit, returning to a breakpoint instruction
+# inserted at the original fault instruction. Check that the
+# breakpoint is hit, and that single stepping off that breakpoint
+# executes the underlying fault instruction causing a SIGSEGV.
+
+proc cont_out { name args } {
+ global gdb_prompt
+
+ # Set SIGSEGV to pass+nostop and then run the inferior all the way
+ # through to the signal handler. With the handler is reached,
+ # disable SIGSEGV, ensuring that further signals stop the
+ # inferior. Stops a SIGSEGV infinite loop when a broke system
+ # keeps re-executing the faulting instruction.
+ rerun_to_main
+ gdb_test "handle SIGSEGV nostop print pass" "" "pass SIGSEGV; $name"
+ gdb_test "continue" "keeper.*" "continue to keeper; $name"
+ gdb_test "handle SIGSEGV stop print nopass" "" "nopass SIGSEGV; $name"
+
+ # Insert all the breakpoints. To avoid the need to step over
+ # these instructions, this is delayed until after the keeper has
+ # been reached. Always set a breakpoint at the signal trampoline
+ # instruction.
+ set args [concat $args "*[at_segv]"]
+ for {set i 0} {$i < [llength $args]} {incr i} {
+ gdb_test "break [lindex $args $i]" "Breakpoint.*" \
+ "set breakpoint $i of [llength $args]; $name"
+ }
+
+ # Let the handler return, it should "appear to hit" the breakpoint
+ # inserted at the faulting instruction. Note that the breakpoint
+ # instruction wasn't executed, rather the inferior was SIGTRAPed
+ # with the PC at the breakpoint.
+ gdb_test "continue" "Breakpoint.*pc *[at_segv] .*" \
+ "continue to breakpoint at fault; $name"
+
+ # Now single step the faulted instrction at that breakpoint.
+ gdb_test "stepi" \
+ "Program received signal SIGSEGV.*pc *[at_segv] .*" \
+ "stepi fault; $name"
+
+ # Clear any breakpoints
+ for {set i 0} {$i < [llength $args]} {incr i} {
+ gdb_test "clear [lindex $args $i]" "Deleted .*" \
+ "clear breakpoint $i of [llength $args]; $name"
+ }
+
+}
+
+
+
+# Try to confuse DECR_PC_AFTER_BREAK architectures by scattering
+# breakpoints around the faulting address. In all cases the inferior
+# should single-step out of the signal trampoline halting (but not
+# executing) the fault instruction.
+
+stepi_out "stepi"
+stepi_out "stepi bp before segv" "*[before_segv]"
+stepi_out "stepi bp at segv" "*[at_segv]"
+stepi_out "stepi bp before and at segv" "*[at_segv]" "*[before_segv]"
+
+
+# Try to confuse DECR_PC_AFTER_BREAK architectures by scattering
+# breakpoints around the faulting address. In all cases the inferior
+# should exit the signal trampoline halting at the breakpoint that
+# replaced the fault instruction.
+cont_out "cont"
+cont_out "cont bp after segv" "*[before_segv]"
+cont_out "cont bp before and after segv" "*[before_segv]" "*[after_segv]"