return
}
-# Each argument is a different testing axis, most of them obvious.
+# Test stepping/continuing at an exit syscall instruction.
+#
+# Each argument is a different testing axis.
+#
+# STEP_OVER_MODE can be one of:
+#
+# - none: don't put a breakpoint on the exit syscall instruction.
+#
+# - inline: put a breakpoint on the exit syscall instruction, and
+# use in-line stepping to step over it (disable
+# displaced-stepping).
+#
+# - displaced: same, but use displaced stepping.
+#
+# SCHEDLOCK can be "on" or "off".
+#
+# CMD is the GDB command to run when at the exit syscall instruction.
+#
# NS_STOP_ALL is only used if testing "set non-stop on", and indicates
# whether to have GDB explicitly stop all threads before continuing to
# thread exit.
-proc test {displaced-stepping non-stop target-non-stop schedlock cmd ns_stop_all} {
+#
+proc test {step_over_mode non-stop target-non-stop schedlock cmd ns_stop_all} {
if {${non-stop} == "off" && $ns_stop_all} {
error "invalid arguments"
}
clean_restart $::binfile
}
- gdb_test_no_output "set displaced-stepping ${displaced-stepping}"
-
- if { ![runto_main] } {
- return
+ if { $step_over_mode == "none" } {
+ # Nothing to do.
+ } elseif { $step_over_mode == "inline" } {
+ gdb_test_no_output "set displaced-stepping off"
+ } elseif { $step_over_mode == "displaced" } {
+ gdb_test_no_output "set displaced-stepping on"
+ } else {
+ error "Invalid step_over_mode value: $step_over_mode"
}
- gdb_breakpoint "my_exit_syscall"
-
if {$schedlock
|| (${non-stop} == "on" && $ns_stop_all)} {
- gdb_test "continue" \
- "Thread 2 .*hit Breakpoint $::decimal.* my_exit_syscall .*" \
- "continue until syscall"
+
+ gdb_test_no_output "set args 1"
+
+ if { ![runto my_exit_syscall] } {
+ return
+ }
if {${non-stop} == "on"} {
# The test only spawns one thread at a time, so this just
- # stops the main thread.
+ # stops the main thread. IOW, we only need to wait for
+ # one stop.
gdb_test_multiple "interrupt -a" "" {
-re "$::gdb_prompt " {
gdb_test_multiple "" $gdb_test_name {
}
}
}
- }
- gdb_test "thread 2" "Switching to thread 2 .*"
+ gdb_test "thread 2" "Switching to thread 2 .*"
+ }
gdb_test_no_output "set scheduler-locking ${schedlock}"
+ # If testing a step-over is requested, leave the breakpoint at
+ # the current instruction to force a step-over; otherwise,
+ # remove it.
+ if { $step_over_mode == "none" } {
+ delete_breakpoints
+ }
+
if {$cmd == "continue"} {
gdb_test "continue" \
"No unwaited-for children left." \
}
}
} else {
+ # Schedlock is off here.
+ #
+ # With "continue" and no scheduler-locking, GDB doesn't stop
+ # with "Command aborted, thread exited." when the thread
+ # exits, it just lets the inferior continue running freely.
+ # So we test that we can move past the thread exit, and that
+ # other threads can be freely scheduled. We do that by
+ # spawning another thread as soon as the first exit. We test
+ # that a number of times. This should also exercise GDB's
+ # handling of inline or displaced step-overs, that GDB handles
+ # the related resource accounting correctly when the stepping
+ # thread exits, etc.
+ #
+ # With "continue" and $step_over_mode == "none" however, after
+ # the first my_exit_syscall breakpoint hit, we will remove the
+ # breakpoint, so no other thread would ever hit it again. So
+ # might as well just test one thread.
+ #
+ # With step/next, GDB aborts the execution command with
+ # "Command aborted, thread exited." when the stepping thread
+ # exits. If we let the main spawn another thread as soon as
+ # the first exits, it would be possible for that new thread to
+ # hit the exit syscall insn breakpoint quickly enough that it
+ # would be reported to be user before the first thread exit
+ # would be, which would confuse testing. To avoid that, we
+ # only spawn one thread, too.
+ #
+ if {$cmd != "continue" || $step_over_mode == "none"} {
+ set n_threads 1
+ } else {
+ set n_threads 100
+ }
+
+ gdb_test_no_output "set args $n_threads"
+
+ if { ![runto_main] } {
+ return
+ }
+
+ gdb_breakpoint "my_exit_syscall"
+
gdb_test_no_output "set scheduler-locking ${schedlock}"
- if {$cmd != "continue"} {
+ if {$cmd != "continue" || $step_over_mode == "none"} {
set thread "<unknown>"
gdb_test_multiple "continue" "" {
-re -wrap "Thread ($::decimal) .*hit Breakpoint $::decimal.* my_exit_syscall .*" {
"switch to event thread"
}
- gdb_test_multiple $cmd "command aborts when thread exits" {
- -re "Command aborted, thread exited\\.\r\n$::gdb_prompt " {
- pass $gdb_test_name
+ # If testing a step-over is requested, leave the breakpoint at
+ # the current instruction to force a step-over; otherwise,
+ # remove it.
+ if { $step_over_mode == "none" } {
+ delete_breakpoints
+ }
+
+ if {$cmd == "continue"} {
+ gdb_continue_to_end "continue to end" "continue" 1
+ } else {
+ gdb_test_multiple $cmd "command aborts when thread exits" {
+ -re "Command aborted, thread exited\\.\r\n$::gdb_prompt " {
+ pass $gdb_test_name
+ }
}
+ gdb_test "p \$_thread == $thread" "= 1" \
+ "selected thread didn't change"
}
} else {
for { set i 0 } { $i < 100 } { incr i } {
}
}
-foreach_with_prefix displaced-stepping {off auto} {
+foreach_with_prefix step_over_mode {none inline displaced} {
foreach_with_prefix non-stop {off on} {
foreach_with_prefix target-non-stop {off on} {
if {${non-stop} == "on" && ${target-non-stop} == "off"} {
foreach_with_prefix cmd {"next" "continue"} {
if {${non-stop} == "on"} {
foreach_with_prefix ns_stop_all {0 1} {
- test ${displaced-stepping} ${non-stop} ${target-non-stop} \
+ test ${step_over_mode} ${non-stop} ${target-non-stop} \
${schedlock} ${cmd} ${ns_stop_all}
}
} else {
- test ${displaced-stepping} ${non-stop} ${target-non-stop} ${schedlock} ${cmd} 0
+ test ${step_over_mode} ${non-stop} ${target-non-stop} ${schedlock} ${cmd} 0
}
}
}