[PATCHv2,6/8] gdb/testsuite: expand gdb.base/foll-vfork.exp

Message ID 20cb4bbc67707268a75d32b8eaa539758ac4b31b.1688484032.git.aburgess@redhat.com
State New
Headers
Series Some vfork related fixes |

Commit Message

Andrew Burgess July 4, 2023, 3:22 p.m. UTC
  This commit provides tests for all of the bugs fixed in the previous
four commits, this is achieved by expanding gdb.base/foll-vfork.exp to
run with different configurations:

  * target-non-stop on/off
  * non-stop on/off
  * schedule-multiple on/off

We don't test with schedule-multiple on if we are using a remote
target, this is due to bug gdb/30574.  I've added a link to that bug
in this commit, but all this commit does is expose that bug, there's
no fixes here.

Some of the bugs fixed in the previous commits are very timing
dependent, as such, they don't always show up.  I've had more success
when running this test on a very loaded machine -- I usually run ~8
copies of the test in parallel, then the bugs would normally show up
pretty quickly.

Other than running the test in more configurations, I've not made any
changes to what is actually being tested, other than in one place
where, when testing with non-stop mode, GDB stops in a different
inferior, as such I had to add a new 'inferior 2' call, this can be
found in vfork_relations_in_info_inferiors.

I have cleaned things up a little, for example, making use of
proc_with_prefix to remove some with_test_prefix calls.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30574
---
 gdb/testsuite/gdb.base/foll-vfork.exp | 628 ++++++++++++--------------
 1 file changed, 300 insertions(+), 328 deletions(-)
  

Comments

Terekhov, Mikhail via Gdb-patches July 5, 2023, 11:22 a.m. UTC | #1
On Tuesday, July 4, 2023 5:23 PM, Andrew Burgess wrote:
> This commit provides tests for all of the bugs fixed in the previous
> four commits, this is achieved by expanding gdb.base/foll-vfork.exp to
> run with different configurations:
> 
>   * target-non-stop on/off
>   * non-stop on/off
>   * schedule-multiple on/off
> 
> We don't test with schedule-multiple on if we are using a remote
> target, this is due to bug gdb/30574.  I've added a link to that bug
> in this commit, but all this commit does is expose that bug, there's
> no fixes here.
> 
> Some of the bugs fixed in the previous commits are very timing
> dependent, as such, they don't always show up.  I've had more success
> when running this test on a very loaded machine -- I usually run ~8
> copies of the test in parallel, then the bugs would normally show up
> pretty quickly.
> 
> Other than running the test in more configurations, I've not made any
> changes to what is actually being tested, other than in one place
> where, when testing with non-stop mode, GDB stops in a different
> inferior, as such I had to add a new 'inferior 2' call, this can be
> found in vfork_relations_in_info_inferiors.
> 
> I have cleaned things up a little, for example, making use of
> proc_with_prefix to remove some with_test_prefix calls.
> 
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30574
> ---
>  gdb/testsuite/gdb.base/foll-vfork.exp | 628 ++++++++++++--------------
>  1 file changed, 300 insertions(+), 328 deletions(-)
> 
> diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp b/gdb/testsuite/gdb.base/foll-
> vfork.exp
> index bdceff0f5de..be0715b05c0 100644
> --- a/gdb/testsuite/gdb.base/foll-vfork.exp
> +++ b/gdb/testsuite/gdb.base/foll-vfork.exp
> @@ -25,56 +25,52 @@ if {![istarget "*-linux*"]} {
>      continue
>  }
> 
> -standard_testfile
> +standard_testfile .c -exit.c vforked-prog.c
> 
> -set compile_options debug
> +set binfile $testfile
> +set binfile2 ${testfile}-exit
> +set binfile3 vforked-prog
> 
> -if {[build_executable $testfile.exp $testfile $srcfile $compile_options] == -1} {
> +if {[build_executable "compile $binfile" $binfile $srcfile] == -1} {
>      untested "failed to compile main testcase"
>      return -1
>  }
> 
> -set testfile2 "vforked-prog"
> -set srcfile2 ${testfile2}.c
> +if {[build_executable "compile $binfile2" $binfile2 $srcfile2] == -1} {
> +    untested "failed to compile main testcase"

A slightly different message can be used here, to distinguish it from the
case above.  Also in the untested usage below.

> +    return -1
> +}
> 
> -if {[build_executable $testfile.exp $testfile2 $srcfile2 $compile_options] == -1} {
> -    untested "failed to compile secondary testcase"
> +if {[build_executable "compile $binfile3" $binfile3 $srcfile3] == -1} {
> +    untested "failed to compile main testcase"
>      return -1
>  }
> 
...
> +proc_with_prefix vfork_child_follow_to_exit { binfile srcfile } {
> +    setup_gdb $binfile $srcfile
> +
> +    gdb_test_no_output "set follow-fork child"
> +
> +    gdb_test_multiple "continue" "continue to child exit" {
> +	-re -wrap "Couldn't get registers.*" {
> +	    # PR gdb/14766
> +	    fail $gdb_test_name

Since this is known, we can use a setup_kfail, I think.

> +	}
> +	-re -wrap "\\\[Attaching after.* vfork to.*\\\[Detaching vfork parent .* after
> child exit.*" {
> +	    pass $gdb_test_name
> +	}
> +    }
> +
> +    # The parent has been detached; allow time for any output it might
> +    # generate to arrive, so that output doesn't get confused with
> +    # any gdb_expected debugger output from a subsequent testpoint.
> +    #
> +    exec sleep 1
> +}
> +
...
> +proc_with_prefix vfork_relations_in_info_inferiors { variant binfile srcfile
> non_stop } {
> +    setup_gdb $binfile $srcfile
> +
> +    gdb_test_no_output "set follow-fork child"
> +
> +    gdb_test_multiple "next" "next over vfork" {
> +	-re -wrap "\\\[Attaching after .* vfork to child.*if \\(pid == 0\\).*" {
> +	    pass $gdb_test_name
> +	}
> +    }
> +
> +    if { $non_stop } {
> +	gdb_test "inferior 2" ".*"
> +    }
> +
> +    gdb_test "info inferiors" \
> +	".*is vfork parent of inferior 2.*is vfork child of inferior 1" \
> +	"info inferiors shows vfork parent/child relation"
> +
> +    if { $variant == "exec" } {
> +	set linenum [gdb_get_line_number "Hello from vforked-prog" ${::srcfile3}]
> +	gdb_test_multiple "continue" "continue to bp" {
> +	    -re -wrap ".*xecuting new program.*Breakpoint.*vforked-prog.c:${linenum}.*"
> {
> +		pass $gdb_test_name
> +	    }
> +	}
> +    } else {
> +	gdb_test_multiple "continue" "continue to child exit" {
> +	   -re -wrap "exited normally.*" {
> +	       pass $gdb_test_name
>  	   }

How about using gdb_continue_to_end instead of this gdb_test_multiple?

Thanks
-Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  

Patch

diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp b/gdb/testsuite/gdb.base/foll-vfork.exp
index bdceff0f5de..be0715b05c0 100644
--- a/gdb/testsuite/gdb.base/foll-vfork.exp
+++ b/gdb/testsuite/gdb.base/foll-vfork.exp
@@ -25,56 +25,52 @@  if {![istarget "*-linux*"]} {
     continue
 }
 
-standard_testfile
+standard_testfile .c -exit.c vforked-prog.c
 
-set compile_options debug
+set binfile $testfile
+set binfile2 ${testfile}-exit
+set binfile3 vforked-prog
 
-if {[build_executable $testfile.exp $testfile $srcfile $compile_options] == -1} {
+if {[build_executable "compile $binfile" $binfile $srcfile] == -1} {
     untested "failed to compile main testcase"
     return -1
 }
 
-set testfile2 "vforked-prog"
-set srcfile2 ${testfile2}.c
+if {[build_executable "compile $binfile2" $binfile2 $srcfile2] == -1} {
+    untested "failed to compile main testcase"
+    return -1
+}
 
-if {[build_executable $testfile.exp $testfile2 $srcfile2 $compile_options] == -1} {
-    untested "failed to compile secondary testcase"
+if {[build_executable "compile $binfile3" $binfile3 $srcfile3] == -1} {
+    untested "failed to compile main testcase"
     return -1
 }
 
+# If required, download the program that we exec after vfork to the
+# remote target.
 if { [is_remote target] } {
-    gdb_remote_download target [standard_output_file $testfile2]
+    gdb_remote_download target [standard_output_file $binfile3]
 }
 
-# A few of these tests require a little more time than the standard
-# timeout allows.
-set oldtimeout $timeout
-set timeout [expr "$timeout + 10"]
-
 # Start with a fresh GDB, with verbosity enabled, and run to main.  On
 # error, behave as "return", so we don't try to continue testing with
 # a borked session.
-proc setup_gdb {} {
-    global testfile srcfile
-
-    clean_restart $testfile
+proc setup_gdb { binfile srcfile } {
+    clean_restart $binfile
 
     if ![runto_main] {
 	return -code return
     }
 
-    set tbreak_line [gdb_get_line_number " VFORK " $srcfile]
-    gdb_test "tbreak ${tbreak_line}"
-    gdb_continue_to_breakpoint ".*"
+    gdb_breakpoint [gdb_get_line_number " VFORK " $srcfile] temporary
+    gdb_continue_to_breakpoint "at VFORK"
 }
 
 proc check_vfork_catchpoints {} {
-  global gdb_prompt
-
   # Because setup_gdb uses 'return -code return' which would return to
   # our caller we need to wrap this call, spot when setup_gdb failed
   # (with return code 2), and then issue our own 'return -code return'.
-  set code [catch {setup_gdb} string]
+  set code [catch {setup_gdb $::testfile $::srcfile} string]
   if { $code == 2 } {
     unsupported "vfork catchpoints"
     return -code return
@@ -84,10 +80,10 @@  proc check_vfork_catchpoints {} {
   gdb_test "catch vfork" "Catchpoint \[0-9\]* \\(vfork\\)" "insert first vfork catchpoint"
   set has_vfork_catchpoints 0
   gdb_test_multiple "continue" "continue to first vfork catchpoint" {
-    -re ".*Your system does not support this type\r\nof catchpoint.*$gdb_prompt $" {
+    -re -wrap ".*Your system does not support this type\r\nof catchpoint.*" {
       unsupported "continue to first vfork catchpoint"
     }
-    -re ".*Catchpoint.*$gdb_prompt $" {
+    -re -wrap ".*Catchpoint.*" {
       set has_vfork_catchpoints 1
       pass "continue to first vfork catchpoint"
     }
@@ -99,319 +95,278 @@  proc check_vfork_catchpoints {} {
   }
 }
 
-proc vfork_parent_follow_through_step {} {
-  with_test_prefix "vfork parent follow, through step" {
-   global gdb_prompt
+proc_with_prefix vfork_parent_follow_through_step { binfile srcfile } {
+    setup_gdb $binfile $srcfile
 
-   setup_gdb
+    gdb_test_no_output "set follow-fork parent"
 
-   gdb_test_no_output "set follow-fork parent"
+    gdb_test_multiple "next" "" {
+	-re -wrap "\\\[Detaching after vfork from.*if \\(pid == 0\\).*" {
+	    pass $gdb_test_name
+	}
+    }
 
-   set test "step"
-   gdb_test_multiple "next" $test {
-       -re "\\\[Detaching after vfork from.*if \\(pid == 0\\).*$gdb_prompt " {
-	   pass $test
-       }
-   }
-   # The child has been detached; allow time for any output it might
-   # generate to arrive, so that output doesn't get confused with
-   # any gdb_expected debugger output from a subsequent testpoint.
-   #
-   exec sleep 1
-}}
-
-proc vfork_parent_follow_to_bp {} {
-  with_test_prefix "vfork parent follow, to bp" {
-   global gdb_prompt
-   global srcfile
-
-   setup_gdb
-
-   gdb_test_no_output "set follow-fork parent"
-
-   set bp_location [gdb_get_line_number "I'm the proud parent of child"]
-   gdb_test "break ${srcfile}:${bp_location}" ".*" "break, vfork to bp"
-
-   set test "continue to bp"
-   gdb_test_multiple "continue" $test {
-       -re ".*\\\[Detaching after vfork from child process.*Breakpoint.*${bp_location}.*$gdb_prompt " {
-	   pass $test
-       }
-   }
-   # The child has been detached; allow time for any output it might
-   # generate to arrive, so that output doesn't get confused with
-   # any expected debugger output from a subsequent testpoint.
-   #
-   exec sleep 1
-}}
-
-proc vfork_child_follow_to_exit {} {
-  with_test_prefix "vfork child follow, to exit" {
-   global gdb_prompt
-
-   setup_gdb
-
-   gdb_test_no_output "set follow-fork child"
-
-   set test "continue to child exit"
-   gdb_test_multiple "continue" $test {
-      -re "Couldn't get registers.*$gdb_prompt " {
-	  # PR gdb/14766
-	  fail "$test"
-      }
-       -re "\\\[Attaching after.* vfork to.*\\\[Detaching vfork parent .* after child exit.*$gdb_prompt " {
-	  pass $test
-      }
-   }
-   # The parent has been detached; allow time for any output it might
-   # generate to arrive, so that output doesn't get confused with
-   # any gdb_expected debugger output from a subsequent testpoint.
-   #
-   exec sleep 1
-}}
+    # The child has been detached; allow time for any output it might
+    # generate to arrive, so that output doesn't get confused with
+    # any gdb_expected debugger output from a subsequent testpoint.
+    #
+    exec sleep 1
+}
 
-proc vfork_and_exec_child_follow_to_main_bp {} {
-  with_test_prefix "vfork and exec child follow, to main bp" {
-   global gdb_prompt
-   global srcfile2
+proc_with_prefix vfork_parent_follow_to_bp { binfile srcfile } {
+    setup_gdb $binfile $srcfile
 
-   setup_gdb
+    gdb_test_no_output "set follow-fork parent"
 
-   gdb_test_no_output "set follow-fork child"
+    set bp_location \
+	[gdb_get_line_number "I'm the proud parent of child" $srcfile]
+    gdb_test "break ${srcfile}:${bp_location}" ".*" "break, vfork to bp"
 
-   set linenum [gdb_get_line_number "Hello from vforked-prog" ${srcfile2}]
+    gdb_test_multiple "continue" "continue to bp" {
+	-re -wrap ".*\\\[Detaching after vfork from child process.*Breakpoint.*${bp_location}.*" {
+	    pass $gdb_test_name
+	}
+    }
 
-   set test "continue to bp"
-   gdb_test_multiple "continue" $test {
-      -re "\\\[Attaching after.* vfork to.*\\\[Detaching vfork parent.*xecuting new program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
-	  pass $test
-      }
-   }
-   # The parent has been detached; allow time for any output it might
-   # generate to arrive, so that output doesn't get confused with
-   # any gdb_expected debugger output from a subsequent testpoint.
-   #
-   exec sleep 1
-}}
-
-proc vfork_and_exec_child_follow_through_step {} {
-  with_test_prefix "vfork and exec child follow, through step" {
-   global gdb_prompt
-   global srcfile2
-
-   setup_gdb
-
-   gdb_test_no_output "set follow-fork child"
-
-   set test "step over vfork"
-
-   # The ideal support is to be able to debug the child even
-   # before it execs.  Thus, "next" lands on the next line after
-   # the vfork.
-   gdb_test_multiple "next" $test {
-       -re "\\\[Attaching after .* vfork to child.*if \\(pid == 0\\).*$gdb_prompt " {
-	   pass "$test"
-       }
-   }
-   # The parent has been detached; allow time for any output it might
-   # generate to arrive, so that output doesn't get confused with
-   # any expected debugger output from a subsequent testpoint.
-   #
-   exec sleep 1
-}}
+    # The child has been detached; allow time for any output it might
+    # generate to arrive, so that output doesn't get confused with
+    # any expected debugger output from a subsequent testpoint.
+    #
+    exec sleep 1
+}
+
+proc_with_prefix vfork_child_follow_to_exit { binfile srcfile } {
+    setup_gdb $binfile $srcfile
+
+    gdb_test_no_output "set follow-fork child"
+
+    gdb_test_multiple "continue" "continue to child exit" {
+	-re -wrap "Couldn't get registers.*" {
+	    # PR gdb/14766
+	    fail $gdb_test_name
+	}
+	-re -wrap "\\\[Attaching after.* vfork to.*\\\[Detaching vfork parent .* after child exit.*" {
+	    pass $gdb_test_name
+	}
+    }
+
+    # The parent has been detached; allow time for any output it might
+    # generate to arrive, so that output doesn't get confused with
+    # any gdb_expected debugger output from a subsequent testpoint.
+    #
+    exec sleep 1
+}
+
+proc_with_prefix vfork_and_exec_child_follow_to_main_bp { binfile srcfile } {
+    setup_gdb $binfile $srcfile
+
+    gdb_test_no_output "set follow-fork child"
+
+    set linenum [gdb_get_line_number "Hello from vforked-prog" ${::srcfile3}]
+
+    gdb_test_multiple "continue" "continue to bp" {
+	-re -wrap "\\\[Attaching after.* vfork to.*\\\[Detaching vfork parent.*xecuting new program.*Breakpoint.*vforked-prog.c:${linenum}.*" {
+	    pass $gdb_test_name
+	}
+    }
+
+    # The parent has been detached; allow time for any output it might
+    # generate to arrive, so that output doesn't get confused with
+    # any gdb_expected debugger output from a subsequent testpoint.
+    #
+    exec sleep 1
+}
+
+proc_with_prefix vfork_and_exec_child_follow_through_step { binfile srcfile } {
+    setup_gdb $binfile $srcfile
+
+    gdb_test_no_output "set follow-fork child"
+
+    # The ideal support is to be able to debug the child even
+    # before it execs.  Thus, "next" lands on the next line after
+    # the vfork.
+    gdb_test_multiple "next" "next over vfork" {
+	-re -wrap "\\\[Attaching after .* vfork to child.*if \\(pid == 0\\).*" {
+	    pass $gdb_test_name
+	}
+    }
+
+    # The parent has been detached; allow time for any output it might
+    # generate to arrive, so that output doesn't get confused with
+    # any expected debugger output from a subsequent testpoint.
+    #
+    exec sleep 1
+}
 
 proc continue_to_vfork {} {
-   global gdb_prompt
-
-   # A vfork catchpoint may stop in either "vfork" or "_vfork".
-   set test "continue to vfork"
-   gdb_test_multiple "continue" $test {
-      -re "vfork \\(\\) at .*$gdb_prompt $" {
-	  pass $test
-      }
-      -re "0x\[0-9a-fA-F\]*.*(vfork|__kernel_v?syscall).*$gdb_prompt " {
-	  pass $test
-      }
-   }
+    # A vfork catchpoint may stop in either "vfork" or "_vfork".
+    gdb_test_multiple "continue" "continue to vfork" {
+	-re -wrap "vfork \\(\\) at .*" {
+	    pass $gdb_test_name
+	}
+	-re -wrap "0x\[0-9a-fA-F\]*.*(vfork|__kernel_v?syscall).*" {
+	    pass $gdb_test_name
+	}
+    }
 }
 
-proc tcatch_vfork_then_parent_follow {} {
-  with_test_prefix "vfork parent follow, finish after tcatch vfork" {
-   global gdb_prompt
-   global srcfile
-
-   setup_gdb
-
-   gdb_test_no_output "set follow-fork parent"
-
-   gdb_test "tcatch vfork" "Catchpoint .*(vfork).*"
-
-   continue_to_vfork
-
-   set linenum [gdb_get_line_number "pid = vfork ();"]
-   set test "finish"
-   gdb_test_multiple "finish" $test {
-      -re "Run till exit from.*vfork.*0x\[0-9a-fA-F\]* in main .* at .*${srcfile}:${linenum}.*$gdb_prompt " {
-	  pass $test
-      }
-      -re "Run till exit from.*__kernel_v?syscall.*0x\[0-9a-fA-F\]* in vfork .*$gdb_prompt " {
-	  send_gdb "finish\n"
-	  exp_continue
-      }
-   }
-   # The child has been detached; allow time for any output it might
-   # generate to arrive, so that output doesn't get confused with
-   # any expected debugger output from a subsequent testpoint.
-   #
-   exec sleep 1
-}}
-
-proc tcatch_vfork_then_child_follow_exec {} {
-  with_test_prefix "vfork child follow, finish after tcatch vfork" {
-   global gdb_prompt
-   global srcfile
-   global srcfile2
-
-   setup_gdb
-
-   gdb_test_no_output "set follow-fork child"
-
-   gdb_test "tcatch vfork" "Catchpoint .*(vfork).*"
-
-   continue_to_vfork
-
-   set linenum1 [gdb_get_line_number "pid = vfork ();"]
-   set linenum2 [gdb_get_line_number "Hello from vforked-prog" ${srcfile2}]
-
-   set test "finish"
-   gdb_test_multiple "finish" $test {
-      -re "Run till exit from.*vfork.*${srcfile}:${linenum1}.*$gdb_prompt " {
-	  pass $test
-      }
-      -re "Run till exit from.*__kernel_v?syscall.*0x\[0-9a-fA-F\]* in vfork .*$gdb_prompt " {
-	  send_gdb "finish\n"
-	  exp_continue
-      }
-      -re "Run till exit from.*vfork.*${srcfile2}:${linenum2}.*$gdb_prompt " {
-	  pass "$test (followed exec)"
-      }
-   }
-   # The parent has been detached; allow time for any output it might
-   # generate to arrive, so that output doesn't get confused with
-   # any expected debugger output from a subsequent testpoint.
-   #
-   exec sleep 1
-}}
-
-proc tcatch_vfork_then_child_follow_exit {} {
-  with_test_prefix "vfork child follow, finish after tcatch vfork" {
-   global gdb_prompt
-   global srcfile
-
-   setup_gdb
-
-   gdb_test_no_output "set follow-fork child"
-
-   gdb_test "tcatch vfork" "Catchpoint .*(vfork).*"
-
-   continue_to_vfork
-
-   set test "finish"
-   gdb_test_multiple "finish" $test {
-      -re "Run till exit from.*vfork.*exited normally.*$gdb_prompt " {
-	  setup_kfail "gdb/14762" *-*-*
-	  fail $test
-      }
-      -re "Run till exit from.*vfork.*pid = vfork \\(\\).*$gdb_prompt " {
-	  pass $test
-      }
-      -re "Run till exit from.*__kernel_v?syscall.*0x\[0-9a-fA-F\]* in vfork .*$gdb_prompt " {
-	  send_gdb "finish\n"
-	  exp_continue
-      }
-   }
-   # The parent has been detached; allow time for any output it might
-   # generate to arrive, so that output doesn't get confused with
-   # any expected debugger output from a subsequent testpoint.
-   #
-   exec sleep 1
-}}
+proc_with_prefix tcatch_vfork_then_parent_follow { binfile srcfile } {
+    setup_gdb $binfile $srcfile
 
-proc vfork_relations_in_info_inferiors { variant } {
-  with_test_prefix "vfork relations in info inferiors" {
-   global gdb_prompt
+    gdb_test_no_output "set follow-fork parent"
 
-   setup_gdb
+    gdb_test "tcatch vfork" "Catchpoint .*(vfork).*"
 
-   gdb_test_no_output "set follow-fork child"
+    continue_to_vfork
 
-   set test "step over vfork"
-   gdb_test_multiple "next" $test {
-       -re "\\\[Attaching after .* vfork to child.*if \\(pid == 0\\).*$gdb_prompt " {
-	   pass "$test"
-       }
-   }
+    set linenum [gdb_get_line_number "pid = vfork ();" $srcfile]
+    gdb_test_multiple "finish" "" {
+	-re -wrap "Run till exit from.*vfork.*0x\[0-9a-fA-F\]* in main .* at .*${srcfile}:${linenum}.*" {
+	    pass $gdb_test_name
+	}
+	-re -wrap "Run till exit from.*__kernel_v?syscall.*0x\[0-9a-fA-F\]* in vfork .*" {
+	    send_gdb "finish\n"
+	    exp_continue
+	}
+    }
 
-   gdb_test "info inferiors" \
-       ".*is vfork parent of inferior 2.*is vfork child of inferior 1" \
-       "info inferiors shows vfork parent/child relation"
+    # The child has been detached; allow time for any output it might
+    # generate to arrive, so that output doesn't get confused with
+    # any expected debugger output from a subsequent testpoint.
+    #
+    exec sleep 1
+}
 
-   if { $variant == "exec" } {
-       global srcfile2
+proc_with_prefix tcatch_vfork_then_child_follow_exec { binfile srcfile } {
+    setup_gdb $binfile $srcfile
 
-       set linenum [gdb_get_line_number "Hello from vforked-prog" ${srcfile2}]
-       set test "continue to bp"
-       gdb_test_multiple "continue" $test {
-	   -re ".*xecuting new program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
-	       pass $test
-	   }
-       }
-   } else {
-       set test "continue to child exit"
-       gdb_test_multiple "continue" $test {
-	   -re "exited normally.*$gdb_prompt " {
-	       pass $test
+    gdb_test_no_output "set follow-fork child"
+
+    gdb_test "tcatch vfork" "Catchpoint .*(vfork).*"
+
+    continue_to_vfork
+
+    set linenum1 [gdb_get_line_number "pid = vfork ();" $srcfile]
+    set linenum2 [gdb_get_line_number "Hello from vforked-prog" ${::srcfile3}]
+
+    gdb_test_multiple "finish" "" {
+	-re -wrap "Run till exit from.*vfork.*${srcfile}:${linenum1}.*" {
+	  pass $gdb_test_name
+	}
+	-re -wrap "Run till exit from.*__kernel_v?syscall.*0x\[0-9a-fA-F\]* in vfork .*" {
+	    send_gdb "finish\n"
+	    exp_continue
+	}
+	-re -wrap "Run till exit from.*vfork.*${::srcfile3}:${linenum2}.*" {
+	    pass "$gdb_test_name (followed exec)"
+	}
+    }
+
+    # The parent has been detached; allow time for any output it might
+    # generate to arrive, so that output doesn't get confused with
+    # any expected debugger output from a subsequent testpoint.
+    #
+    exec sleep 1
+}
+
+proc tcatch_vfork_then_child_follow_exit { binfile srcfile } {
+   setup_gdb $binfile $srcfile
+
+    gdb_test_no_output "set follow-fork child"
+
+    gdb_test "tcatch vfork" "Catchpoint .*(vfork).*"
+
+    continue_to_vfork
+
+    gdb_test_multiple "finish" "" {
+	-re -wrap "Run till exit from.*vfork.*exited normally.*" {
+	    setup_kfail "gdb/14762" *-*-*
+	    fail $gdb_test_name
+	}
+	-re -wrap "Run till exit from.*vfork.*pid = vfork \\(\\).*" {
+	  pass $gdb_test_name
+	}
+	-re -wrap "Run till exit from.*__kernel_v?syscall.*0x\[0-9a-fA-F\]* in vfork .*" {
+	    send_gdb "finish\n"
+	    exp_continue
+	}
+    }
+
+    # The parent has been detached; allow time for any output it might
+    # generate to arrive, so that output doesn't get confused with
+    # any expected debugger output from a subsequent testpoint.
+    #
+    exec sleep 1
+}
+
+proc_with_prefix vfork_relations_in_info_inferiors { variant binfile srcfile non_stop } {
+    setup_gdb $binfile $srcfile
+
+    gdb_test_no_output "set follow-fork child"
+
+    gdb_test_multiple "next" "next over vfork" {
+	-re -wrap "\\\[Attaching after .* vfork to child.*if \\(pid == 0\\).*" {
+	    pass $gdb_test_name
+	}
+    }
+
+    if { $non_stop } {
+	gdb_test "inferior 2" ".*"
+    }
+
+    gdb_test "info inferiors" \
+	".*is vfork parent of inferior 2.*is vfork child of inferior 1" \
+	"info inferiors shows vfork parent/child relation"
+
+    if { $variant == "exec" } {
+	set linenum [gdb_get_line_number "Hello from vforked-prog" ${::srcfile3}]
+	gdb_test_multiple "continue" "continue to bp" {
+	    -re -wrap ".*xecuting new program.*Breakpoint.*vforked-prog.c:${linenum}.*" {
+		pass $gdb_test_name
+	    }
+	}
+    } else {
+	gdb_test_multiple "continue" "continue to child exit" {
+	   -re -wrap "exited normally.*" {
+	       pass $gdb_test_name
 	   }
-       }
-   }
-
-   set test "vfork relation no longer appears in info inferiors"
-   gdb_test_multiple "info inferiors" $test {
-       -re -wrap "is vfork child of inferior 1.*" {
-	   fail $test
-       }
-       -re -wrap "is vfork parent of inferior 2.*" {
-	   fail $test
-       }
-       -re -wrap "" {
-	   pass $test
-       }
-   }
-}}
-
-proc do_vfork_and_follow_parent_tests {} {
-   global gdb_prompt
+	}
+    }
 
+    gdb_test_multiple "info inferiors" "vfork relation no longer appears in info inferiors" {
+	-re -wrap "is vfork child of inferior 1.*" {
+	   fail $gdb_test_name
+	}
+	-re -wrap "is vfork parent of inferior 2.*" {
+	    fail $gdb_test_name
+	}
+	-re -wrap "" {
+	    pass $gdb_test_name
+	}
+    }
+}
+
+proc do_vfork_and_follow_parent_tests { binfile srcfile } {
    # Try following the parent process by stepping through a call to
    # vfork.  Do this without catchpoints.
-   vfork_parent_follow_through_step
+   vfork_parent_follow_through_step $binfile $srcfile
 
    # Try following the parent process by setting a breakpoint on the
    # other side of a vfork, and running to that point.  Do this
    # without catchpoints.
-   vfork_parent_follow_to_bp
+   vfork_parent_follow_to_bp $binfile $srcfile
 
    # Try catching a vfork, and stepping out to the parent.
    #
-   tcatch_vfork_then_parent_follow
+   tcatch_vfork_then_parent_follow $binfile $srcfile
 }
 
-proc do_vfork_and_follow_child_tests_exec {} {
+proc do_vfork_and_follow_child_tests_exec { binfile srcfile non_stop } {
    # Try following the child process by just continuing through the
    # vfork, and letting the parent's breakpoint on "main" be auto-
    # magically reset in the child.
    #
-   vfork_and_exec_child_follow_to_main_bp
+   vfork_and_exec_child_follow_to_main_bp $binfile $srcfile
 
    # Try following the child process by stepping through a call to
    # vfork.  The child also executes an exec.  Since the child cannot
@@ -420,11 +375,11 @@  proc do_vfork_and_follow_child_tests_exec {} {
    # recomputed in the exec'd child, the step through a vfork should
    # land us in the "main" for the exec'd child, too.
    #
-   vfork_and_exec_child_follow_through_step
+   vfork_and_exec_child_follow_through_step $binfile $srcfile
 
    # Try catching a vfork, and stepping out to the child.
    #
-   tcatch_vfork_then_child_follow_exec
+   tcatch_vfork_then_child_follow_exec $binfile $srcfile
 
    # Test the ability to follow both child and parent of a vfork.  Do
    # this without catchpoints.
@@ -442,25 +397,25 @@  proc do_vfork_and_follow_child_tests_exec {} {
    # and confirm the relation is no longer displayed in "info
    # inferiors".
    #
-   vfork_relations_in_info_inferiors "exec"
+   vfork_relations_in_info_inferiors "exec" $binfile $srcfile $non_stop
 }
 
-proc do_vfork_and_follow_child_tests_exit {} {
+proc do_vfork_and_follow_child_tests_exit { binfile srcfile non_stop } {
    # Try following the child process by just continuing through the
    # vfork, and letting the child exit.
    #
-   vfork_child_follow_to_exit
+   vfork_child_follow_to_exit $binfile $srcfile
 
    # Try catching a vfork, and stepping out to the child.
    #
-   tcatch_vfork_then_child_follow_exit
+   tcatch_vfork_then_child_follow_exit $binfile $srcfile
 
    # Step over a vfork in the child, do "info inferiors" and check the
    # parent/child relation is displayed.  Run the child to completion,
    # and confirm the relation is no longer displayed in "info
    # inferiors".
    #
-   vfork_relations_in_info_inferiors "exit"
+   vfork_relations_in_info_inferiors "exit" $binfile $srcfile $non_stop
 }
 
 with_test_prefix "check vfork support" {
@@ -470,41 +425,58 @@  with_test_prefix "check vfork support" {
 }
 
 # Follow parent and follow child vfork tests with a child that execs.
-with_test_prefix "exec" {
+proc_with_prefix exec_tests { binfile srcfile non_stop } {
     # These are tests of gdb's ability to follow the parent of a Unix
     # vfork system call.  The child will subsequently call a variant
     # of the Unix exec system call.
-    do_vfork_and_follow_parent_tests
+    do_vfork_and_follow_parent_tests $binfile $srcfile
 
     # These are tests of gdb's ability to follow the child of a Unix
     # vfork system call.  The child will subsequently call a variant
     # of a Unix exec system call.
     #
-    do_vfork_and_follow_child_tests_exec
-}
-
-# Switch to test the case of the child exiting.  We can't use
-# standard_testfile here because we don't want to overwrite the binary
-# of the previous tests.
-set testfile "foll-vfork-exit"
-set srcfile ${testfile}.c
-set binfile [standard_output_file ${testfile}]
-
-if {[build_executable $testfile.exp $testfile $srcfile] == -1} {
-    untested "failed to build $testfile"
-    return
+    do_vfork_and_follow_child_tests_exec $binfile $srcfile $non_stop
 }
 
 # Follow parent and follow child vfork tests with a child that exits.
-with_test_prefix "exit" {
+proc_with_prefix exit_tests { binfile srcfile non_stop } {
     # These are tests of gdb's ability to follow the parent of a Unix
     # vfork system call.  The child will subsequently exit.
-    do_vfork_and_follow_parent_tests
+    do_vfork_and_follow_parent_tests $binfile $srcfile
 
     # These are tests of gdb's ability to follow the child of a Unix
     # vfork system call.  The child will subsequently exit.
     #
-    do_vfork_and_follow_child_tests_exit
+    do_vfork_and_follow_child_tests_exit $binfile $srcfile $non_stop
+}
+
+# Using the remote protocol with schedule-multiple turned triggers bug
+# gdb/30574, so avoid this for now.
+if {[target_info exists gdb_protocol]
+    && ([target_info gdb_protocol] == "remote"
+	|| [target_info gdb_protocol] == "extended-remote")} {
+    set sm_modes { off }
+} else {
+    set sm_modes { off on }
 }
 
-set timeout $oldtimeout
+# A few of these tests require a little more time than the standard timeout
+# allows.
+with_timeout_factor 2 {
+    foreach_with_prefix target-non-stop {auto on off} {
+	foreach_with_prefix non-stop {off on} {
+	    foreach_with_prefix schedule-multiple $sm_modes {
+		save_vars { ::GDBFLAGS } {
+		    # These flags will be picked up by the call to
+		    # clean_restart inside setup_gdb.
+		    append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\""
+		    append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\""
+		    append ::GDBFLAGS " -ex \"set schedule-multiple ${schedule-multiple}\""
+
+		    exec_tests $binfile $srcfile ${non-stop}
+		    exit_tests $binfile2 $srcfile2 ${non-stop}
+		}
+	    }
+	}
+    }
+}