# Copyright (C) 2019-2023 Free Software Foundation, Inc.
#
# 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 .
#
# Support library for testing ROCm (AMD GPU) GDB features.
# Get the list of gpu targets to compile for.
#
# If HCC_AMDGPU_TARGET is set in the environment, use it. Otherwise,
# try reading it from the system using the rocm_agent_enumerator
# utility.
proc hcc_amdgpu_targets {} {
# Look for HCC_AMDGPU_TARGET (same env var hipcc uses). If
# that fails, try using rocm_agent_enumerator (again, same as
# hipcc does).
if {[info exists ::env(HCC_AMDGPU_TARGET)]} {
return [split $::env(HCC_AMDGPU_TARGET) ","]
}
set rocm_agent_enumerator "rocm_agent_enumerator"
# If available, use ROCM_PATH to locate rocm_agent_enumerator.
if { [info exists ::env(ROCM_PATH)] } {
set rocm_agent_enumerator \
"$::env(ROCM_PATH)/bin/rocm_agent_enumerator"
}
# If we fail to locate the rocm_agent_enumerator, just return an empty
# list of targets and let the caller decide if this should be an error.
if { [which $rocm_agent_enumerator] == 0 } {
return [list]
}
set result [remote_exec host $rocm_agent_enumerator]
if { [lindex $result 0] != 0 } {
error "rocm_agent_enumerator failed"
}
set targets [list]
foreach target [lindex $result 1] {
# Ignore gfx000 which is the host CPU.
if { $target ne "gfx000" } {
lappend targets $target
}
}
return $targets
}
gdb_caching_proc allow_hipcc_tests {} {
# Only the native target supports ROCm debugging. E.g., when
# testing against GDBserver, there's no point in running the ROCm
# tests.
if {[target_info gdb_protocol] != ""} {
return {0 "remote debugging"}
}
if {![istarget "*-linux*"]} {
return {0 "target platform is not Linux"}
}
# Ensure that GDB is built with amd-dbgapi support.
set output [remote_exec host $::GDB "$::INTERNAL_GDBFLAGS --configuration"]
if { [string first "--with-amd-dbgapi" $output] == -1 } {
return {0 "amd-dbgapi not supported"}
}
# Check we have a working hipcc compiler available.
set targets [hcc_amdgpu_targets]
if { [llength $targets] == 0} {
return {0 "no suitable amdgpu targets found"}
}
set flags [list hip additional_flags=--offload-arch=[join $targets ","]]
if {![gdb_simple_compile hipprobe {
#include
__global__ void
kern () {}
int
main ()
{
kern<<<1, 1>>> ();
if (hipDeviceSynchronize () != hipSuccess)
return -1;
return 0;
}
} executable $flags]} {
return {0 "failed to compile hip program"}
}
return 1
}
# The lock file used to ensure that only one GDB has access to the GPU
# at a time.
set gpu_lock_filename $objdir/gpu-parallel.lock
# Acquire lock file LOCKFILE. Tries forever until the lock file is
# successfully created.
proc lock_file_acquire {lockfile} {
verbose -log "acquiring lock file: $::subdir/${::gdb_test_file_name}.exp"
while {true} {
if {![catch {open $lockfile {WRONLY CREAT EXCL}} rc]} {
set msg "locked by $::subdir/${::gdb_test_file_name}.exp"
verbose -log "lock file: $msg"
# For debugging, put info in the lockfile about who owns
# it.
puts $rc $msg
flush $rc
return [list $rc $lockfile]
}
after 10
}
}
# Release a lock file.
proc lock_file_release {info} {
verbose -log "releasing lock file: $::subdir/${::gdb_test_file_name}.exp"
if {![catch {fconfigure [lindex $info 0]}]} {
if {![catch {
close [lindex $info 0]
file delete -force [lindex $info 1]
} rc]} {
return ""
} else {
return -code error "Error releasing lockfile: '$rc'"
}
} else {
error "invalid lock"
}
}
# Run body under the GPU lock. Also calls gdb_exit before releasing
# the GPU lock.
proc with_rocm_gpu_lock { body } {
if {[info exists ::GDB_PARALLEL]} {
set lock_rc [lock_file_acquire $::gpu_lock_filename]
}
set code [catch {uplevel 1 $body} result]
# In case BODY returned early due to some testcase failing, and
# left GDB running, debugging the GPU.
gdb_exit
if {[info exists ::GDB_PARALLEL]} {
lock_file_release $lock_rc
}
if {$code == 1} {
global errorInfo errorCode
return -code $code -errorinfo $errorInfo -errorcode $errorCode $result
} else {
return -code $code $result
}
}
# Return true if all the devices support debugging multiple processes
# using the GPU.
proc hip_devices_support_debug_multi_process {} {
set unsupported_targets \
{gfx900 gfx906 gfx908 gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032}
set targets [hcc_amdgpu_targets]
if { [llength $targets] == 0 } {
return 0
}
foreach target $targets {
if { [lsearch -exact $unsupported_targets $target] != -1 } {
return 0
}
}
return 1
}
# Return true if all the devices on the host support precise memory.
proc hip_devices_support_precise_memory {} {
set unsupported_targets \
{gfx900 gfx906 gfx908 gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032}
set targets [hcc_amdgpu_targets]
if { [llength $targets] == 0 } {
return 0
}
foreach target $targets {
if { [lsearch -exact $unsupported_targets $target] != -1 } {
return 0
}
}
return 1
}