/* This testcase is part of GDB, the GNU debugger.
Copyright 2025 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 . */
#include
#include
#include "gdb_watchdog.h"
#include
#include
#include
#include
#define CHECK(cmd) \
do \
{ \
hipError_t error = cmd; \
if (error != hipSuccess) \
{ \
fprintf (stderr, "error: '%s'(%d) at %s:%d\n", \
hipGetErrorString (error), error, __FILE__, __LINE__); \
exit (EXIT_FAILURE); \
} \
} while (0)
__device__ static void wait_all_kernels ();
/* A kernel with a name longer than GDB's longest possible name. */
__global__ void
long_kernel_name_abcdefghijklmnopqrstuvwxyz ()
{
wait_all_kernels ();
}
/* A kernel with a name longer than GDB's longest possible name. */
__global__ void
long_kernel_name_0123456789 ()
{
wait_all_kernels ();
}
/* A kernel with a normal, short name. */
__global__ void
kern ()
{
wait_all_kernels ();
}
/* Same, but with some parameters. */
__global__ void
kern (int)
{
wait_all_kernels ();
}
/* A kernel with extern "C" linkage, which doesn't include an argument
list in the demangled name. */
extern "C" __global__ void
c_kern ()
{
wait_all_kernels ();
}
/* A kernel in a namespace. */
namespace NS {
__global__ void
kern_ns ()
{
wait_all_kernels ();
}
}
/* A kernel in an anonymous namespace. */
namespace {
__global__ void
anonymous ()
{
wait_all_kernels ();
}
}
/* An enum with no enumerators to make the kernel symbol name have an
open parenthesis in the template argument list, like 'void
templ_non_type<(E)0>()'. This ensures GDB does not confuse the
template argument's opening parenthesis with the start of the
function's parameter list. */
enum E {};
template
__global__ void
templ_non_type ()
{
wait_all_kernels ();
}
template
__global__ void __attribute__ ((optnone))
templ_type (Args... args)
{
wait_all_kernels ();
}
/* List of kernel names and a callback that launches the named
kernel. */
static struct
{
/* The kernel name. */
const char *kernel;
/* Launch kernel on stream. */
void (*launch) (hipStream_t);
} kernels[] = {
{
"long_kernel_name_abcdefghijklmnopqrstuvwxyz", [] (hipStream_t st)
{
long_kernel_name_abcdefghijklmnopqrstuvwxyz
<<>> ();
}
},
{
"long_kernel_name_0123456789", [] (hipStream_t st)
{
long_kernel_name_0123456789
<<>> ();
}
},
{
"kern", [] (hipStream_t st)
{
kern
<<>> ();
}
},
{
"kern-1", [] (hipStream_t st)
{
kern
<<>> (1);
}
},
{
"c_kern", [] (hipStream_t st)
{
c_kern
<<>> ();
}
},
{
"kern_ns", [] (hipStream_t st)
{
NS::kern_ns
<<>> ();
}
},
{
"anonymous", [] (hipStream_t st)
{
anonymous
<<>> ();
}
},
{
"templ_non_type", [] (hipStream_t st)
{
templ_non_type<(E)0>
<<>> ();
}
},
{
"templ_type", [] (hipStream_t st)
{
templ_type
<<>> (1, 2);
}
},
};
/* Number of kernels seen so far by wait_all_kernels. */
__device__ static int kernels_seen = 0;
/* Number of kernels expected to be seen by wait_all_kernels. */
__device__ static int number_kernels = 0;
/* Wait until every kernel has reached here, so that they are all
running concurrently before any of them exits. */
__device__ static void
wait_all_kernels ()
{
atomicAdd (&kernels_seen, 1);
while (atomicAdd (&kernels_seen, 0) < number_kernels)
{
/* Sleep a bit to avoid hogging the GPU. */
__builtin_amdgcn_s_sleep (64);
}
}
/* True if we want to run the kernel named KERNEL. ARG is the
argument passed to the program. Either a kernel name, or
"all". */
static bool
should_run_kernel (const char *arg, const char *kernel)
{
return strcmp (arg, "all") == 0 || strcmp (arg, kernel) == 0;
}
int
main (int argc, char **argv)
{
/* So that the GPU threads don't spin forever. */
gdb_watchdog (30);
if (argc != 2)
{
std::cerr
<< "Usage: " << argv[0] << " KERNEL|all" << std::endl;
return EXIT_FAILURE;
}
int h_number_kernels = 0;
for (auto &k : kernels)
{
if (should_run_kernel (argv[1], k.kernel))
h_number_kernels++;
}
CHECK (hipMemcpyToSymbol (HIP_SYMBOL (number_kernels), &h_number_kernels,
sizeof (int)));
/* Launch each kernel on its own stream, so they can all run
concurrently. */
std::vector streams;
for (auto &k : kernels)
{
if (should_run_kernel (argv[1], k.kernel))
{
hipStream_t st;
CHECK (hipStreamCreate (&st));
streams.push_back (st);
k.launch (st);
CHECK (hipGetLastError ());
}
}
CHECK (hipDeviceSynchronize ());
for (hipStream_t &st : streams)
CHECK (hipStreamDestroy (st));
return 0;
}