Skip to content

Commit a00cfb3

Browse files
committed
gdb, testsuite: improve gdb.rocm cooperative group GWS tests
Refine the cooperative-group GWS tests for robustness and to make the debugger behaviour under test more explicit: * coop-group-grid-sync.exp: use temporary breakpoints for the in-kernel locations (still pending, since the device code is loaded at dispatch time) and drop the redundant delete_breakpoints calls. * coop-group-grid-sync.exp: report UNSUPPORTED instead of FAIL when the inferior self-skips because no device advertises cooperativeLaunch. * coop-group-grid-sync.exp: have test_threads_in_coop_kernel check distinct per-wave blockIdx.x (per-wave register context) and per-lane threadIdx.x divergence (per-lane SIMT state), instead of only confirming that the backtrace names the kernel. * coop-group-multi-grid-sync.{cpp,exp}: read n_gpus from a marker line that is reached on every execution (no early return before it), so that when fewer than two cooperative-capable GPUs are available -- including when a parallel test run restricts the visible GPUs -- the test reports UNSUPPORTED rather than FAILing. * coop-group-{grid,multi-grid}-sync.{cpp,exp}: minor comment and GNU-style cleanups -- tab-align the in-kernel marker comment, keep hipLaunchCooperativeKernelMultiDevice on a single line, and clarify the Phase 2 data-dependency comment. * coop-group-{grid,multi-grid}-sync.{cpp,exp}: give the per-wave and per-lane value reads explicit, unique test names, and keep the "n-gpus-final" marker string unique so gdb_get_line_number resolves the intended line. Tested on gfx942 with an in-tree build and the 7.14 nightly rocgdb, both with all GPUs visible and with the visible set restricted to one.
1 parent 714c354 commit a00cfb3

4 files changed

Lines changed: 90 additions & 72 deletions

File tree

gdb/testsuite/gdb.rocm/coop-group-grid-sync.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,9 @@ namespace cg = cooperative_groups;
4040
in the GWS barrier. Two workgroups of 64 threads each give 128
4141
threads in total.
4242
43-
N is exactly one slot per thread so Phase 2 has no intra-thread race
44-
and the host-side expected values can be computed straightforwardly. */
43+
N is exactly one slot per thread so Phase 2 has no inter-thread
44+
write conflict and the host-side expected values can be computed
45+
straightforwardly. */
4546

4647
constexpr unsigned int group_size = 64;
4748
constexpr unsigned int num_groups = 2;

gdb/testsuite/gdb.rocm/coop-group-grid-sync.exp

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,22 @@ proc_with_prefix test_break_around_grid_sync {} {
5555
# Place a breakpoint before grid.sync (). Use "allow-pending"
5656
# because the kernel is loaded as device code at dispatch time.
5757
gdb_breakpoint \
58-
[gdb_get_line_number "before-sync line"] allow-pending
59-
60-
# The kernel must hit the pre-sync breakpoint.
61-
if {[gdb_continue_to_breakpoint "stop before grid.sync"] != 0} {
58+
[gdb_get_line_number "before-sync line"] allow-pending temporary
59+
60+
# Continue into the kernel. If the program self-skips (no device
61+
# advertises cooperativeLaunch) it exits cleanly; treat that as
62+
# UNSUPPORTED rather than a FAIL.
63+
set reached 0
64+
gdb_test_multiple "continue" "stop before grid.sync" {
65+
-re -wrap "Temporary breakpoint $::decimal,\[^\r\n\]*coop_grid_sync_kernel .*" {
66+
set reached 1
67+
pass $gdb_test_name
68+
}
69+
-re -wrap "$::inferior_exited_re normally.*" {
70+
unsupported "cooperative launch not available"
71+
}
72+
}
73+
if { !$reached } {
6274
return
6375
}
6476

@@ -90,27 +102,25 @@ proc_with_prefix test_break_around_grid_sync {} {
90102
".*AMDGPU Dispatch\[^\r\n\]*coop_grid_sync_kernel.*" \
91103
"info dispatches lists cooperative dispatch"
92104

93-
# Replace the pre-sync breakpoint with one after grid.sync ().
94-
# The after-sync breakpoint fires once all waves have crossed
95-
# the GWS barrier, which proves GWS-protected code can be
96-
# debugged across the barrier.
97-
delete_breakpoints
105+
# Place a breakpoint after grid.sync (). It fires once all waves
106+
# have crossed the GWS barrier, which proves GWS-protected code
107+
# can be debugged across the barrier.
98108
gdb_breakpoint \
99-
[gdb_get_line_number "after-sync line"] allow-pending
109+
[gdb_get_line_number "after-sync line"] allow-pending temporary
100110

101111
gdb_continue_to_breakpoint "stop after grid.sync"
102112

103113
# The kernel should be able to run to completion afterwards.
104-
delete_breakpoints
105114
gdb_continue_to_end "continue to program end" "continue" 1
106115
}
107116
}
108117

109-
# Test: place a breakpoint inside the cooperative kernel, then iterate
110-
# over every stopped GPU wave and verify that each one has a backtrace
111-
# pointing into coop_grid_sync_kernel. This exercises the wave
112-
# organization a debugger sees when several waves participate in a GWS
113-
# barrier.
118+
# Test: stop inside the cooperative kernel with several co-resident
119+
# waves parked at the barrier and check that gdb reports correct,
120+
# distinct per-wave and per-lane state. This is the debugger
121+
# behaviour of interest: that gdb selects the right register context
122+
# per wave and the right SIMT state per lane, not merely that the
123+
# waves are in the kernel.
114124

115125
proc_with_prefix test_threads_in_coop_kernel {} {
116126
clean_restart $::testfile
@@ -121,29 +131,56 @@ proc_with_prefix test_threads_in_coop_kernel {} {
121131
}
122132

123133
gdb_breakpoint \
124-
[gdb_get_line_number "before-sync line"] allow-pending
125-
126-
if {[gdb_continue_to_breakpoint "stop in cooperative kernel"] != 0} {
134+
[gdb_get_line_number "before-sync line"] allow-pending temporary
135+
136+
# Continue into the kernel. If the program self-skips (no device
137+
# advertises cooperativeLaunch) it exits cleanly; treat that as
138+
# UNSUPPORTED rather than a FAIL.
139+
set reached 0
140+
gdb_test_multiple "continue" "stop in cooperative kernel" {
141+
-re -wrap "Temporary breakpoint $::decimal,\[^\r\n\]*coop_grid_sync_kernel .*" {
142+
set reached 1
143+
pass $gdb_test_name
144+
}
145+
-re -wrap "$::inferior_exited_re normally.*" {
146+
unsupported "cooperative launch not available"
147+
}
148+
}
149+
if { !$reached } {
127150
return
128151
}
129152

130-
# Collect the list of AMDGPU waves and check that every one of
131-
# them has a backtrace pointing into coop_grid_sync_kernel.
132153
set waves [info_thread_get_wave_list]
133154
gdb_assert {[llength $waves] >= 2} "multiple GPU waves listed"
134155

156+
# Per-wave register context: switching to each wave and reading
157+
# blockIdx.x must yield that wave's owning workgroup, and across
158+
# all waves we must observe more than one distinct workgroup.
159+
set blocks {}
135160
foreach wave $waves {
136161
with_test_prefix "wave $wave" {
137162
gdb_test "thread $wave" "Switching to thread $wave.*" \
138163
"switch to wave $wave"
139-
gdb_test "bt 1" \
140-
"#0\[^\r\n\]*coop_grid_sync_kernel\[^\r\n\]*" \
141-
"backtrace inside coop_grid_sync_kernel"
164+
set bidx [get_integer_valueof "blockIdx.x" -1 "blockIdx.x"]
165+
if {[lsearch -exact $blocks $bidx] == -1} {
166+
lappend blocks $bidx
167+
}
142168
}
143169
}
170+
gdb_assert {[llength $blocks] >= 2} \
171+
"distinct workgroups observed across waves"
172+
173+
# Per-lane (SIMT) state: within a single wave, different lanes map
174+
# to different threads, so threadIdx.x must differ between lanes.
175+
gdb_test "thread [lindex $waves 0]" "Switching to thread .*" \
176+
"switch to first wave for lane inspection"
177+
gdb_test "lane 0" ".*" "switch to lane 0"
178+
set tid0 [get_integer_valueof "threadIdx.x" -1 "threadIdx.x at lane 0"]
179+
gdb_test "lane 1" ".*" "switch to lane 1"
180+
set tid1 [get_integer_valueof "threadIdx.x" -1 "threadIdx.x at lane 1"]
181+
gdb_assert {$tid0 != $tid1} "distinct threadIdx.x across lanes"
144182

145183
# Resume to completion.
146-
delete_breakpoints
147184
gdb_continue_to_end "continue to program end" "continue" 1
148185
}
149186
}

gdb/testsuite/gdb.rocm/coop-group-multi-grid-sync.cpp

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ coop_multi_grid_sync_kernel (int *data, unsigned int n_elements,
6262

6363
/* Phase 1: each thread writes data into its slot. */
6464
for (unsigned int i = tid; i < n_elements; i += stride)
65-
data[i] = (int) ((grid_rank + 1) * (i + 1)); /* before-grid-sync line */
65+
data[i] = (int) ((grid_rank + 1) * (i + 1)); /* before-grid-sync line */
6666

6767
/* Intra-device grid sync (GWS). */
6868
grid.sync (); /* grid-sync line */
@@ -97,17 +97,14 @@ main ()
9797
{
9898
int n_devices = 0;
9999
CHECK (hipGetDeviceCount (&n_devices));
100-
if (n_devices < N_USED_GPUS)
101-
{
102-
printf ("Multi-device cooperative test needs >= %d GPUs"
103-
" (found %d), skipping.\n", N_USED_GPUS, n_devices);
104-
return 0;
105-
}
106100

107101
/* Pick the first N_USED_GPUS devices that support
108102
cooperativeMultiDeviceLaunch. In a mixed-architecture system not
109103
every GPU has GWS support, so we ignore unsupported ones rather
110-
than failing the whole test. */
104+
than failing the whole test. This loop runs unconditionally (it
105+
is bounded by n_devices) so the marker below is always reached,
106+
even when too few devices are available; the companion .exp reads
107+
n_gpus there to decide whether to skip. */
111108
int selected[N_USED_GPUS];
112109
int n_gpus = 0;
113110
for (int id = 0; id < n_devices && n_gpus < N_USED_GPUS; id++)
@@ -120,15 +117,14 @@ main ()
120117
n_gpus++;
121118
}
122119
}
123-
if (n_gpus < N_USED_GPUS)
120+
if (n_gpus < N_USED_GPUS) /* n-gpus-final line */
124121
{
125-
printf ("Fewer than %d devices in 0..%d support cooperative"
122+
printf ("Fewer than %d of the %d HIP device(s) support cooperative"
126123
" multi-device launch, skipping.\n",
127-
N_USED_GPUS, n_devices - 1);
124+
N_USED_GPUS, n_devices);
128125
return 0;
129126
}
130127

131-
/* n-gpus-final line. */
132128
int *data_d[N_USED_GPUS] = {};
133129
hipStream_t stream[N_USED_GPUS] = {};
134130

gdb/testsuite/gdb.rocm/coop-group-multi-grid-sync.exp

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
# - read the number of participating GPUs from the inferior,
2727
# - hit a kernel breakpoint inside the cooperative dispatch on
2828
# *every* participating GPU (matched via the per-device
29-
# "Breakpoint <id>.<instance>" subscript that hipLaunchCooperative
30-
# KernelMultiDevice produces), and
29+
# "Breakpoint <id>.<instance>" subscript that
30+
# hipLaunchCooperativeKernelMultiDevice produces), and
3131
# - resume the cross-device dispatch through both the intra-device
3232
# this_grid ().sync () and the cross-device this_multi_grid ().sync ()
3333
# to normal program completion.
@@ -63,34 +63,19 @@ proc_with_prefix test_break_in_multi_coop_kernel {} {
6363
return
6464
}
6565

66-
# Advance to the point where the inferior has finalized "n_gpus"
67-
# (the count of GPUs it will actually use for the cooperative
68-
# dispatch) so we can read it from gdb. The require gates above
69-
# cover architecture-level support; per-device runtime gating
70-
# (>= 2 devices that advertise cooperativeMultiDeviceLaunch) is
71-
# done inside main () in the .cpp, which self-skips with a clean
72-
# exit when those preconditions are not met. Treat that clean
73-
# exit as UNSUPPORTED rather than a FAIL.
74-
gdb_breakpoint \
75-
[gdb_get_line_number "n-gpus-final line"] allow-pending
76-
77-
set eligible 0
78-
gdb_test_multiple "continue" "advance to n-gpus-final" {
79-
-re "hit Breakpoint $::decimal,\[^\r\n\]*main \\(\\)\[^\r\n\]*\r\n\[^\r\n\]*\r\n$::gdb_prompt " {
80-
set eligible 1
81-
pass $gdb_test_name
82-
}
83-
-re "\\\[Inferior 1 \[^\r\n\]* exited normally\\\]\[^\r\n\]*\r\n$::gdb_prompt " {
84-
unsupported "multi-device cooperative launch not available"
85-
}
86-
}
87-
if { !$eligible } {
88-
return
89-
}
66+
# Break where the inferior has finalized "n_gpus" (the count of
67+
# GPUs it will use for the cooperative dispatch). This line runs
68+
# on every execution, before the inferior's own eligibility
69+
# check, so reading "n_gpus" from gdb lets us decide here whether
70+
# to proceed. The require gates above cover architecture-level
71+
# support; per-device runtime gating (>= 2 devices that advertise
72+
# cooperativeMultiDeviceLaunch) can only be known at this point.
73+
gdb_breakpoint [gdb_get_line_number "n-gpus-final line"] temporary
74+
gdb_continue_to_breakpoint "advance to n-gpus-final"
9075

9176
set n_gpus [get_integer_valueof "n_gpus" 0]
92-
gdb_assert {$n_gpus >= 2} "n_gpus is at least 2"
9377
if { $n_gpus < 2 } {
78+
unsupported "fewer than two cooperative-capable GPUs"
9479
return
9580
}
9681

@@ -107,12 +92,11 @@ proc_with_prefix test_break_in_multi_coop_kernel {} {
10792
}
10893
}
10994

110-
# In non-stop mode, hipLaunchCooperativeKernelMultiDevice
111-
# produces one child breakpoint instance per participating GPU
112-
# ("Breakpoint <id>.<inst>"). Collect distinct <inst> values
113-
# until we have observed a stop on every GPU; only then is it
114-
# safe to delete the breakpoint and let the dispatch run
115-
# through both grid syncs.
95+
# Check that gdb resolves the single source breakpoint to one
96+
# child location per participating GPU ("Breakpoint <id>.<inst>")
97+
# and, in non-stop mode, reports each device-side stop
98+
# independently. Collect distinct <inst> values until a stop has
99+
# been seen for every GPU.
116100
array unset gpu_seen
117101
set distinct_gpus 0
118102
gdb_test_multiple "" "breakpoint hit on every participating GPU" {

0 commit comments

Comments
 (0)