Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

452 lines
12 KiB
Plaintext

# Copyright 2021-2024 Free Software Foundation, Inc.
# Copyright (C) 2021-2024 Advanced Micro Devices, Inc. All rights reserved.
# 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 <http://www.gnu.org/licenses/>.
# This testcase exercises execution-related scenarios involving lanes,
# and some related lane debugging features. Mainly:
#
# - Test that GDB automatically steps over code regions where the
# current lane is inactive.
#
# - Test breakpoint hits with some lanes inactive. Also test that GDB
# evaluates the breakpoint condition on each lane.
#
# - Test that GDB warns when you select an inactive lane.
#
# - Test $_lane and the "lane" command.
#
# Note the tests below assume we don't have DW_AT_LLVM_lane_pc info,
# meaning GDB has no idea what is the logical PC a divergent lane is
# stopped at. All it knows is the current wave/physical PC, and that
# the lane is inactive.
load_lib rocm.exp
require allow_hip_tests
# The debug behavior this test checks (inspecting device locals
# and/or single-stepping) is not reliable on optimized device
# code, so skip it for optimized builds.
require no_optimized_code
standard_testfile .cpp
if {[build_executable "failed to prepare" $testfile $srcfile {debug hip}]} {
return -1
}
# Usage:
# my_lineno [TEXT]...
#
# For each TEXT, find line number of the first line containing TEXT.
# The found line number is stored in a global called TEXT_line.
proc my_lineno {args} {
foreach text $args {
global ${text}_line
set ${text}_line [gdb_get_line_number $text]
}
}
my_lineno \
"if_1_cond" "if_1_then" "if_1_else" "if_1_end" \
"if_2_cond" "if_2_then" "if_2_else" "if_2_end" \
"if_3_cond" "if_3_then" "if_3_else" "if_3_end"
# On some systems, stepping out of the 'else' branch brings you to the
# 'then' branch directly. On other systems, that stops at the 'if'
# condition first, and so we need another step to get to the 'then'
# branch. This variable is true for the latter systems. It is
# set/cleared by the 'setup' procedure below, which detects the system
# we have.
set step_out_of_else_stops_at_cond 0
# Issue a "step" or "next" command, expecting to stop at a source line
# with LABEL text. MESSAGE is the message to use.
proc do_step_1 {cmd label {message ""}} {
if {$message eq ""} {
set message "$cmd to \"$label\""
}
gdb_test $cmd "$label.*" $message
}
# Like do_step_1, but for the case of stepping out of an 'else'
# branch.
proc do_step_out_of_else_1 {cmd label label_cond} {
do_step_1 $cmd $label
if {$::step_out_of_else_stops_at_cond} {
do_step_1 $cmd $label_cond \
"$cmd to \"$label_cond\" after \"$label\""
}
}
# Convenience wrapper around do_step_1, for the "step" command.
proc do_step {label {message ""}} {
do_step_1 "step" $label $message
}
# Convenience wrapper around do_step_out_of_else_1, for the "step"
# command.
proc do_step_out_of_else {label label_cond} {
do_step_out_of_else_1 "step" $label $label_cond
}
# Convenience wrapper around do_step_1, for the "next" command.
proc do_next {label {message ""}} {
do_step_1 "next" $label $message
}
# Convenience wrapper around do_step_out_of_else_1, for the "next"
# command.
proc do_next_out_of_else {label label_cond} {
do_step_out_of_else_1 "next" $label $label_cond
}
# Switch to lane LANE, expecting that GDB does not print a warning
# about the lane being inactive. LABEL is the expected text comment
# present in the current source line. MESSAGE is the test message.
proc lane_cmd_active {lane label message} {
gdb_test_multiple "lane $lane" $message {
-re -wrap "warning.*" {
fail $gdb_test_name
}
-re -wrap " $label .*" {
pass $gdb_test_name
}
}
}
# Switch to lane LANE, expecting that GDB prints a warning about the
# lane being inactive. LABEL is the expected text comment present in
# the current source line. MESSAGE is the test message.
proc lane_cmd_inactive {lane label message} {
gdb_test "lane $lane" \
"warning: Current lane is inactive.* $label .*"\
$message
}
# Setup for testing. Restarts GDB, and extracts which lane is the
# "then" lane, and which lane is the "else" lane out of lanes 0-2.
# The results are written to the LANE_THEN_NAME and LANE_ELSE_NAME
# variables in the caller. Returns true on success, false otherwise.
proc setup {lane_then_name lane_else_name} {
upvar $lane_then_name lane_then
upvar $lane_else_name lane_else
clean_restart $::binfile
if {![runto "lane_pc_test" allow-pending]} {
return 0
}
gdb_test_no_output "set scheduler-locking on"
gdb_test_no_output "maint set lane-divergence-support off"
do_step " if_0_else "
set lane_then -1
set lane_else -1
gdb_test_multiple "info lanes 0-2" "" {
-re "($::decimal) (A|I)" {
set lane $expect_out(1,string)
set state $expect_out(2,string)
if {$state == "A"} {
set lane_else $lane
} else {
set lane_then $lane
}
exp_continue
}
-wrap -re "" {
gdb_assert {$lane_else != -1 && $lane_then != -1} \
$gdb_test_name
}
}
verbose -log "lane_else=$lane_else, lane_then=$lane_then"
if {$lane_then == -1 || $lane_else == -1} {
return 0
}
# Check if we're on a system where stepping out of 'else' stops at
# the 'if' condition first, and so we need another step to get to
# the 'then' branch.
set ::step_out_of_else_stops_at_cond 0
gdb_test_multiple "step" "probe step at end of else" {
-re -wrap " if_0_then .*" {
pass $gdb_test_name
}
-re -wrap " if_0_cond .*" {
if {!$::step_out_of_else_stops_at_cond} {
set ::step_out_of_else_stops_at_cond 1
send_gdb "step\n"
exp_continue
} else {
fail $gdb_test_name
}
}
}
do_step "const_struct.int_elem"
gdb_test_no_output "maint set lane-divergence-support on"
return 1
}
# If BRANCH is "then", switch to LANE_THEN lane. Otherwise, switch to
# the LANE_ELSE lane.
proc switch_to_lane_branch {branch lane_then lane_else} {
if {$branch == "then"} {
gdb_test "lane $lane_then" \
"Switching to thread $::decimal, lane $lane_then.*" \
"switch to \"then\" lane"
} else {
gdb_test "lane $lane_else" \
"Switching to thread $::decimal, lane $lane_else.*" \
"switch to \"else\" lane"
}
}
# Test stepping lanes with "step". BRANCH indicates which set of lane
# we expect to be active -- it's either "then" or "else".
proc test_step {branch} {
with_test_prefix "step: $branch" {
if ![setup lane_then lane_else] {
return
}
switch_to_lane_branch $branch $lane_then $lane_else
do_step "const_struct.char_elem"
do_step " if_1_cond "
if {$branch == "then"} {
do_step " if_1_then "
# Check that when we're stopped at the "then", the "else"
# lane is inactive and GDB warns about it.
lane_cmd_inactive $lane_else \
"if_1_then" \
"else lane inactive at if_1_then"
# Switching back to the "then" lane, GDB should not warn.
lane_cmd_active $lane_then \
"if_1_then" \
"then lane active at if_1_then"
do_step " if_1_then_2 "
do_step " if_1_then_3 "
do_step " if_2_cond "
do_step " if_2_then "
# The next step is interesting, because it steps into a
# function, while the else lanes are divergent.
# Step, and check that lane LANE_ELSE is divergent.
proc do_step_check_div {label} {
upvar lane_else lane_else
do_step $label
gdb_test "info lanes $lane_else" \
"$lane_else \+I.*"\
"lane else is inactive at '$label'"
}
do_step_check_div "foo.*return bar \\(val\\);"
gdb_test "bt" \
[multi_line \
"bt" \
"#0 foo \\(val=.*\\) at .*${::srcfile}:${::decimal}" \
"#1 $::hex in lane_pc_test \\(.*\\) at .*:$::if_2_then_line" \
"#2 $::hex in kernel .*"] \
"bt on then lane"
do_step_check_div "bar \\(val=.*\\) at "
} else {
do_step " if_1_else "
# Check that when we're stopped at the "else", the "then"
# lane is inactive.
lane_cmd_inactive $lane_then \
"if_1_else" \
"then lane inactive at if_1_else"
# Switching back to the then lane, GDB should not warn.
lane_cmd_active $lane_else \
"if_1_else" \
"else lane active at if_1_else"
do_step_out_of_else " if_1_else_2 " " if_1_cond "
do_step " if_2_cond "
do_step_out_of_else " if_2_else " " if_2_cond "
}
# The last "if" condition is always false for all lanes.
do_step " if_3_cond "
do_step_out_of_else " if_3_else " " if_3_cond "
do_step "atomicAdd "
}
}
# Test issuing the "step" command while the current lane is divergent.
proc test_step_divergent {branch} {
with_test_prefix "step divergent: $branch" {
if ![setup lane_then lane_else] {
return
}
switch_to_lane_branch $branch $lane_then $lane_else
do_step "const_struct.char_elem"
do_step " if_1_cond "
if {$branch == "then"} {
do_step " if_1_then "
lane_cmd_inactive $lane_else \
"if_1_then" \
"else lane inactive at if_1_then"
do_step " if_2_cond "
} else {
do_step " if_1_else "
lane_cmd_inactive $lane_then \
"if_1_else" \
"then lane inactive at if_1_else"
do_step " if_1_then "
}
}
}
# Test stepping lanes with "next". BRANCH is either "then" or "else".
proc test_next {branch} {
with_test_prefix "next: $branch" {
if ![setup lane_then lane_else] {
return
}
switch_to_lane_branch $branch $lane_then $lane_else
do_next "const_struct.char_elem"
do_next " if_1_cond "
if {$branch == "then"} {
do_next " if_1_then "
# Check that when we're stopped at the "then", the "else"
# lane is inactive.
lane_cmd_inactive $lane_else \
"if_1_then" \
"else lane inactive at if_2_cond"
lane_cmd_active $lane_then \
"if_1_then" \
"then lane active at if_1_then"
do_next " if_1_then_2 "
do_next " if_1_then_3 "
do_next " if_2_cond "
do_next " if_2_then "
} else {
do_next " if_1_else "
# Check that when we're stopped at the "else", the "then"
# lane is inactive.
lane_cmd_inactive $lane_then \
"if_1_else" \
"then lane inactive at if_1_else"
lane_cmd_active $lane_else \
"if_1_else" \
"else lane active at if_1_else"
do_next_out_of_else " if_1_else_2 " " if_1_cond "
do_next " if_2_cond "
do_next_out_of_else " if_2_else " " if_2_cond "
}
# The last "if" condition is always false for all lanes.
do_next " if_3_cond "
do_next_out_of_else " if_3_else " " if_3_cond "
do_next "atomicAdd "
}
}
# Test conditional breakpoints with conditions that only eval true for
# some lane or lanes.
proc_with_prefix test_breakpoint_lane_cond {} {
if ![setup lane_then lane_else] {
return
}
# Set a breakpoint with a condition that only evals true on a
# given lane.
gdb_test "break $::if_1_else_line if \$_lane == $lane_else" ".*"
# And a breakpoint with a breakpoint condition that we know should
# eval false.
gdb_test "break $::if_1_then_line if \$_lane == $lane_else" ".*"
# There are multiple lanes active, but GDB should only mention the
# lanes where the condition was true.
gdb_test "continue" \
"hit Breakpoint $::decimal, with lane $lane_else, lane_pc_test .*" \
"continue to '\$lane_else' breakpoint"
# Make sure GDB doesn't present a stop for lane 0 if the condition
# is to stop for all lanes but 0. GDB's breakpoint condition
# evaluation code used to have a left shift overflow bug (doing '1
# << lane' with lane > 32) that would result in showing a stop for
# lanes that evaluated false.
gdb_test "break $::if_2_then_line if \$_lane > 0" \
"Breakpoint $::decimal at .*"
gdb_test_multiple "continue" "continue to '\$_lane > 0' breakpoint" {
-wrap -re "hit Breakpoint $::decimal, with lane(s)? \\\[0.*" {
fail $gdb_test_name
}
-wrap -re "hit Breakpoint $::decimal, with lanes \\\[$::decimal.*" {
pass $gdb_test_name
}
}
}
with_rocm_gpu_lock {
test_step "then"
test_step "else"
test_step_divergent "then"
test_step_divergent "else"
test_next "then"
test_next "else"
test_breakpoint_lane_cond
}