# Copyright (C) 2021-2024 Advanced Micro Devices, Inc. All rights reserved. # This file is part of GDB. # 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 3 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, see . # Verify that when stepping with schedlock on (only the stepped wave should # run), we don't have a spurious stop in newly created waves. The test scenario # is: # # - Start a background process to hog some GPU resources # - Start an inferior in GDB. Not all of its waves are created because of the # hogging process. # - Stop the hogging process. # - Step a wave of the inferior. A faulty GDB would present a stop in a # newly created wave. load_lib rocm.exp require allow_hip_tests if { [hip_device_is_less_than gfx90a] } { # Scheduler locking does not work in an intuitive manner before gfx90a due # to hardware limitations of forward progress and halted waves, leading to # some deadlocks. unsupported "device older than gfx90a" return } standard_testfile .cpp hog.cpp set binfile_hog ${binfile}-hog if {[build_executable "failed to prepare main file" ${binfile} ${srcfile} {debug hip}]} { return } if {[build_executable "failed to prepare hog file" ${binfile_hog} ${srcfile2} \ {debug hip additional_flags=-std=c++17}]} { return } proc do_test {} { with_rocm_gpu_lock { clean_restart $::binfile if { ![runto_main] } { return } set hog_pid [spawn ${::binfile_hog}] if { $hog_pid == 0 } { fail "start hog" return } set hog_spawn_id $spawn_id verbose -log "spawned hog as pid $hog_pid, spawn id $spawn_id" # Wait for 5 seconds to be sure the hog process has had time to # submit waves. after 5000 gdb_breakpoint kern allow-pending set res [gdb_test \ "continue" \ "Thread $::decimal .* hit Breakpoint $::decimal.*"] set initial_count 0 with_test_prefix "after breakpoint" { set initial_count [info_threads_get_wave_count 1] verbose -log "Inferior 1 has $initial_count waves" } # Do a blocking wait to make sure the hog process really goes # away before we proceed. kill_wait_spawned_process $hog_spawn_id -block verbose -log "killed hog" if { $res != 0 } { return } delete_breakpoints set cur_thread "" gdb_test_multiple "thread" "" { -re -wrap "Current thread is ($::decimal), .*" { set cur_thread $expect_out(1,string) pass $gdb_test_name } } # Place a breakpoint just before the end of the kernel to be sure # no wave can run to completion unnoticed. gdb_breakpoint end_of_kernel allow-pending gdb_test_no_output "set scheduler-locking on" # "next" a few times. Verify that we don't get a stop in another thread # (the only printed line is the source line number and the source line). set num_nexts 5 for {set i 0} {$i < 5} {incr i} { with_timeout_factor 2 { gdb_test "next" "^${::decimal}\[^\r\n\]+" \ "next, iter $i" } } gdb_test "thread" "Current thread is $cur_thread, .*" \ "current thread has not changed while next'ing" with_test_prefix "after steps" { set count [info_threads_get_wave_count 1] verbose -log "Inferior 1 has $count waves" } gdb_assert { $count == $initial_count } "no new waves" # If we disable scheduler-locking and step one more time, we should see # that new waves can now be created and hit the breakpoint. gdb_test_no_output "set scheduler-locking off" # Issue a few "next" commands until some other thread has had a # chance to make enough progress to reach the "end_of_kernel" # breakpoint. max_iter is only a fail-safe cap on the number of # "next" commands, not an expected step count. At -O0 another # wave gets there within ~10 steps, but under -O1/-O2/-O3 each # "next" covers a different amount of code, so allow a small # cushion over that -O0 budget to keep the test reliable. set max_iter 15 gdb_test_multiple "next" "next" { -re -wrap "\r\nThread $::decimal .* hit Breakpoint $::decimal.*" { pass $gdb_test_name } -re -wrap "\r\n${::decimal}\t\[^\r\n\]+" { if { $max_iter > 0 } { incr max_iter -1 send_gdb "next\n" exp_continue } else { fail $gdb_test_name } } } with_test_prefix "after no scheduler-locking" { set count [info_threads_get_wave_count 1] verbose -log "Inferior 1 has $count waves" gdb_assert { $count > $initial_count} "new waves created" } } } do_test