1# Copyright (C) 1996-2021 Free Software Foundation, Inc. 2 3# This program is free software; you can redistribute it and/or modify 4# it under the terms of the GNU General Public License as published by 5# the Free Software Foundation; either version 3 of the License, or 6# (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16# This file was written by Daniel Jacobowitz <drow@mvista.com> 17# (parts based on pthreads.exp by Fred Fish (fnf@cygnus.com). 18# 19# This test covers the various forms of "set scheduler-locking". 20 21# This test requires sending ^C to interrupt the running target. 22 23if [target_info exists gdb,nointerrupts] { 24 verbose "Skipping schedlock.exp because of nointerrupts." 25 return 26} 27 28standard_testfile 29 30# The number of threads, including the main thread. 31set NUM 2 32 33if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable debug] != "" } { 34 return -1 35} 36 37# Now we can proceed with the real testing. 38 39# Get the current contents of the `args` array in the test program. 40# Description is appended to the test message. 41 42proc get_args { description } { 43 global gdb_prompt 44 global NUM 45 46 set pattern "(\[0-9\]+)" 47 for {set i 1} {[expr $i < $NUM]} {incr i} { 48 append pattern ", (\[0-9\]+)" 49 } 50 51 set test "listed args ($description)" 52 gdb_test_multiple "print args" $test { 53 -re "\\\$\[0-9\]+ = {$pattern}.*$gdb_prompt $" { 54 pass $test 55 56 set result "" 57 for {set i 1} {[expr $i <= $NUM]} {incr i} { 58 lappend result $expect_out($i,string) 59 } 60 return $result 61 } 62 } 63} 64 65proc stop_process { description } { 66 global gdb_prompt 67 68 # For this to work we must be sure to consume the "Continuing." 69 # message first, or GDB's signal handler may not be in place. 70 after 1000 {send_gdb "\003"} 71 gdb_expect { 72 -re "Thread .* received signal SIGINT.*$gdb_prompt $" 73 { 74 pass $description 75 } 76 timeout 77 { 78 fail "$description (timeout)" 79 } 80 } 81} 82 83proc get_current_thread { description } { 84 global gdb_prompt 85 86 set test "find current thread ($description)" 87 88 gdb_test_multiple "bt" $test { 89 -re "thread_function \\(arg=0x(\[0-9\])\\).*$gdb_prompt $" { 90 pass $test 91 return $expect_out(1,string) 92 } 93 } 94 return "" 95} 96 97# Make sure we're stopped in the loop, in one of the non-main threads. 98 99proc goto_loop { msg } { 100 gdb_breakpoint [concat [gdb_get_line_number "schedlock.exp: main loop"] " if arg != 0"] 101 102 set test "return to loop" 103 if {$msg != ""} { 104 set test "$test ($msg)" 105 } 106 gdb_continue_to_breakpoint $test 107 delete_breakpoints 108} 109 110proc my_continue { msg } { 111 set test "continue ($msg)" 112 gdb_test_multiple "continue" $test { 113 -re "Continuing" { 114 pass $test 115 } 116 } 117 118 stop_process "stop all threads ($msg)" 119 120 goto_loop $msg 121} 122 123# Use CMD to step the loop 10 times. CMD may be "step" or "next". 124 125proc step_ten_loops { cmd } { 126 global gdb_prompt 127 128 for {set i 0} {[expr $i < 10]} {set i [expr $i + 1]} { 129 set other_step 0 130 set test "$cmd to increment ($i)" 131 gdb_test_multiple $cmd $test { 132 -re ".*myp\\) \\+\\+;\[\r\n\]+$gdb_prompt $" { 133 pass $test 134 } 135 -re "$gdb_prompt $" { 136 if {$other_step == 0} { 137 set other_step 1 138 send_gdb "$cmd\n" 139 exp_continue 140 } else { 141 fail $test 142 # FIXME cascade? 143 } 144 } 145 } 146 } 147} 148 149# Start with a fresh gdb. 150 151gdb_exit 152gdb_start 153gdb_reinitialize_dir $srcdir/$subdir 154 155# We'll need this when we send_gdb a ^C to GDB. Need to do it before we 156# run the program and gdb starts saving and restoring tty states. 157gdb_test "shell stty intr '^C'" ".*" 158 159gdb_load ${binfile} 160 161gdb_test_no_output "set print sevenbit-strings" 162gdb_test_no_output "set width 0" 163 164runto_main 165 166# See if scheduler locking is available on this target. 167global gdb_prompt 168gdb_test_multiple "set scheduler-locking off" "scheduler locking set to none" { 169 -re "Target .* cannot support this command" { 170 unsupported "target does not support scheduler locking" 171 return 172 } 173 -re "$gdb_prompt $" { 174 pass "scheduler locking set to none" 175 } 176 timeout { 177 unsupported "target does not support scheduler locking (timeout)" 178 return 179 } 180} 181 182gdb_breakpoint [gdb_get_line_number "schedlock.exp: last thread start"] 183gdb_continue_to_breakpoint "all threads started" 184 185set start_args [get_args "before initial"] 186 187# First make sure that all threads are alive. 188my_continue "initial" 189 190set cont_args [get_args "after initial"] 191 192set bad 0 193for {set i 0} {[expr $i < $NUM]} {set i [expr $i + 1]} { 194 if {[lindex $start_args $i] == [lindex $cont_args $i]} { 195 incr bad 196 } 197} 198if { $bad == 0 } { 199 pass "all threads alive" 200} else { 201 fail "all threads alive ($bad/$NUM did not run)" 202} 203 204# Compare the previous thread and args with the current thread and 205# args. Check that we didn't switch threads, and that the threads 206# incremented their args counter the amounts expected. CMD is the 207# command being tested. BEFORE_THREAD is the thread that was selected 208# before the command was run. BEFORE_ARGS is the value of the 209# thread's args before the command was run. LOCKED indicates whether 210# we expect threads other than the selected thread remained locked. 211 212proc check_result { cmd before_thread before_args locked } { 213 global NUM 214 215 # Make sure we're still in the same thread. 216 set newthread [get_current_thread "after"] 217 218 set test "$cmd does not change thread" 219 if {$before_thread == $newthread} { 220 pass "$test" 221 } else { 222 fail "$test (switched to thread $newthread)" 223 } 224 225 set after_args [get_args "after"] 226 227 set test "current thread advanced" 228 if { $locked } { 229 set test "$test - locked" 230 } else { 231 set test "$test - unlocked" 232 } 233 234 set num_other_threads 0 235 for {set i 0} {$i < $NUM} {incr i} { 236 if {[lindex $before_args $i] == [lindex $after_args $i]} { 237 if {$i == $before_thread} { 238 fail "$test (didn't run)" 239 } 240 } else { 241 if {$i == $before_thread} { 242 if {$cmd == "continue" 243 || [lindex $before_args $i] == [expr [lindex $after_args $i] - 10]} { 244 pass "$test" 245 } else { 246 fail "$test (wrong amount)" 247 } 248 } else { 249 incr num_other_threads 250 } 251 } 252 } 253 254 if { $locked } { 255 gdb_assert {$num_other_threads == 0} "other threads didn't run - locked" 256 } else { 257 gdb_assert {$num_other_threads > 0} "other threads ran - unlocked" 258 } 259} 260 261with_test_prefix "schedlock=on: cmd=continue" { 262 # Use whichever we stopped in. 263 set curthread [get_current_thread "before"] 264 265 # Test continue with scheduler locking. 266 gdb_test "set scheduler-locking on" "" 267 268 my_continue "with lock" 269 270 check_result "continue" $curthread $cont_args 1 271} 272 273# Test stepping/nexting with different modes of scheduler locking. 274proc test_step { schedlock cmd call_function } { 275 global NUM 276 277 gdb_test_no_output "set scheduler-locking off" 278 goto_loop "" 279 280 set curthread [get_current_thread "before"] 281 282 # No need to set to off again. This avoids a duplicate message. 283 if {$schedlock != "off"} { 284 gdb_test_no_output "set scheduler-locking $schedlock" 285 } 286 287 gdb_test "print call_function = $call_function" \ 288 " = $call_function" 289 290 set before_args [get_args "before"] 291 292 step_ten_loops $cmd 293 294 if { $schedlock == "on" || $schedlock == "step" } { 295 set locked 1 296 } else { 297 set locked 0 298 } 299 300 check_result $cmd $curthread $before_args $locked 301} 302 303# Test stepping/nexting with different modes of scheduler locking. 304foreach schedlock {"off" "step" "on"} { 305 with_test_prefix "schedlock=$schedlock" { 306 with_test_prefix "cmd=step" { 307 test_step $schedlock "step" 0 308 } 309 with_test_prefix "cmd=next" { 310 # In GDB <= 7.9, with schedlock "step", "next" would 311 # unlock threads when stepping over a function call. This 312 # exercises "next" with and without a function call. WRT 313 # "schedlock step", "next" should behave just like "step". 314 foreach call_function {0 1} { 315 with_test_prefix "call_function=$call_function" { 316 test_step $schedlock "next" $call_function 317 } 318 } 319 } 320 } 321} 322