Files
TheRock/debug-tools/rocgdb/source/gdb/testsuite/lib/hip/hip-driver.cc
T
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

340 lines
8.2 KiB
C++

/* Kernel entry point for testing against AMDGCN devices, using HIP.
Copyright (C) 2021-2024 Free Software Foundation, Inc.
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 <http://www.gnu.org/licenses/>. */
#include <hip/hip_runtime.h>
/* Pull in the gdb_hip_test_main declarations. */
#include "hip-test-main.h"
#include <unistd.h>
#include <cstddef>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
/* The device's malloc buffer, and the pointer to the current position
where we'll return memory from. We allocate this buffer from host
code instead of using static __device__ buffer because we don't
want any symbol attached to the buffer, as some testcases print
heap addresses not expecting to find any symbol. One example is
gdb.base/find.exp. */
static __device__ unsigned char *malloc_buf;
static __device__ unsigned char *malloc_head;
/* The size of the device malloc buffer, in bytes. */
static constexpr size_t malloc_buf_size = 0x10000;
/* For getenv. */
static __device__ char **d_environ;
/* The kernel's exit code. */
static __device__ int *exit_code;
/* The test kernel's entry point. Call the testcase's main, which has
been renamed to "gdb_hip_test_main" by the hip-test-wrapper.cc
wrapper. */
static __global__ void
kernel (unsigned char *malloc_buffer,
int *res, int argc, char **argv, char **envp)
{
/* Store global pointers. */
malloc_buf = malloc_buffer;
malloc_head = malloc_buf;
d_environ = envp;
exit_code = res;
/* See comments in hip-test-main.h. gdb_hip_test_main is weak -- we
call the version that is defined by the testcase. */
if (auto &m = static_cast<int (&)(int, char **, char **)>(gdb_hip_test_main))
*res = m (argc, argv, envp);
else if (auto &m = static_cast<int (&)(int, char **)>(gdb_hip_test_main))
*res = m (argc, argv);
else if (auto &m = static_cast<int (&)(int, const char **)>(gdb_hip_test_main))
*res = m (argc, (const char **) argv);
else if (auto &m = static_cast<int (&)(int, char *const *)>(gdb_hip_test_main))
*res = m (argc, argv);
else if (auto &m = static_cast<int (&)()>(gdb_hip_test_main))
*res = m ();
else
{
printf ("hip-driver.cc: error: gdb_hip_test_main not defined!\n");
*res = 1;
}
}
#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)
/* Clone an array of pointers in ARGV style to the device. Return the
pointer to the device array. */
static char **
copy_argv_device (int argc, char **argv)
{
char **argv_d;
CHECK (hipMalloc (&argv_d, sizeof (char *) * argc));
for (int i = 0; i < argc; i++)
{
char *arg_d;
if (argv[i] != nullptr)
{
size_t size = strlen (argv[i]) + 1;
CHECK (hipMalloc (&arg_d, size));
CHECK (hipMemcpy (arg_d, argv[i], size, hipMemcpyHostToDevice));
}
else
arg_d = nullptr;
CHECK (hipMemcpy (&argv_d[i], &arg_d, sizeof (char *), hipMemcpyHostToDevice));
}
return argv_d;
}
int
main (int argc, char **argv, char **envp)
{
int deviceId;
CHECK (hipGetDevice (&deviceId));
hipDeviceProp_t props;
CHECK (hipGetDeviceProperties (&props, deviceId));
printf("info: running on device #%d %s\n", deviceId, props.name);
printf("info: copy Host2Device\n");
unsigned char *malloc_buf_d;
CHECK (hipMalloc (&malloc_buf_d, 0x10000));
int exitcode_h;
int *exitcode_d;
CHECK (hipMalloc (&exitcode_d, sizeof (int)));
char **argv_d = copy_argv_device (argc, argv);
/* Count ENVP entries. */
int envc;
for (envc = 0; envp[envc] != nullptr; envc++)
;
/* One extra for the NULL terminator. */
char **envp_d = copy_argv_device (envc + 1, envp);
printf ("info: launch kernel\n");
const unsigned blocks = 1;
const unsigned threadsPerBlock = 1;
hipLaunchKernelGGL (kernel, dim3 (blocks), dim3 (threadsPerBlock), 0, 0,
malloc_buf_d, exitcode_d, argc, argv_d, envp_d);
printf("info: copy Device2Host\n");
CHECK (hipMemcpy (&exitcode_h, exitcode_d, sizeof (int), hipMemcpyDeviceToHost));
/* Wait until kernel finishes. */
CHECK (hipDeviceSynchronize ());
return exitcode_h;
}
/* Implement some standard functions used by tests, that are missing
on the device. */
int __device__
atoi (const char *nptr)
{
int res = 0;
for (int i = 0; nptr[i] != '\0'; i++)
res = res * 10 + nptr[i] - '0';
return res;
}
int __device__
puts (const char *s)
{
printf ("%s", s);
return 0;
}
static __device__ uint64_t
align_up (uint64_t v, int n)
{
return (v + n - 1) & -n;
}
/* A very simple and stupid malloc implementation that is not thread
safe and never frees any memory. This is sufficient for our needs
since we only run single-threaded programs. */
void *__device__
malloc (size_t size)
{
size = align_up (size, alignof (std::max_align_t));
assert (malloc_head + size > malloc_head
&& malloc_head + size < malloc_buf + malloc_buf_size);
void *ret = malloc_head;
malloc_head += size;
return ret;
}
void *__device__
free (void *ptr)
{
return 0;
}
void * __device__
calloc (size_t nmemb, size_t size)
{
void *res = malloc (nmemb * size);
return memset (res, 0, nmemb * size);
}
size_t __device__
strlen (const char *s)
{
const char *end = s;
while (*end != '\0')
end++;
return end - s;
}
char * __device__
strdup (const char *s)
{
size_t size = strlen (s) + 1;
char *ns = (char *) malloc (size);
memcpy (ns, s, size);
return ns;
}
char * __device__
strcpy (char *dest, const char *src)
{
size_t size = strlen (src) + 1;
memcpy (dest, src, size);
return dest;
}
int __device__
strncmp (const char *s1, const char *s2, size_t n)
{
/* Need to do the final subtraction in unsigned. */
auto *us1 = (unsigned char *) s1;
auto *us2 = (unsigned char *) s2;
size_t i = 0;
for (;;)
{
if (i == n)
return 0;
if (us1[i] == '\0' || us1[i] != us2[i])
return us1[i] - us2[i];
i++;
}
}
void __device__
exit (int code)
{
__threadfence ();
/* Store the exit code in the global. The host side uses this as
ultimate return code. */
*exit_code = code;
/* End the kernel program. */
asm ("s_endpgm");
/* This is an __attribute__((noreturn)) function. Need this other
Clang warns. */
__builtin_unreachable () ;
}
/* CLOCKS_PER_SEC is far from real. Multiply by a factor that gives a
close enough result in practice. Tested with a simple program that
calls sleep(10), and then doing "time ./a.out" until a close enough
number came out. This will obviously need tuning on different
systems... */
static constexpr uint64_t clocks_factor = 2000;
static void __device__
sleep_clocks (uint64_t clocks)
{
uint64_t start = clock64 ();
for (;;)
{
uint64_t now = clock64 ();
uint64_t elapsed = (now > start
? now - start
: now + (0xffffffffffffffff - start));
if (elapsed >= clocks)
return;
}
}
extern "C" int __device__
usleep (useconds_t usec)
{
uint64_t clocks = clocks_factor * CLOCKS_PER_SEC * usec / 1000000;
sleep_clocks (clocks);
return 0;
}
extern "C" unsigned int __device__
sleep (unsigned int seconds)
{
uint64_t clocks = clocks_factor * CLOCKS_PER_SEC * seconds;
sleep_clocks (clocks);
return 0;
}
char * __device__
getenv (const char *name)
{
size_t len = strlen (name);
for (char **p = d_environ; *p != nullptr; ++p)
if (strncmp (*p, name, len) == 0)
{
char *c = *p + len;
if (*c == '=')
return c + 1;
}
return nullptr;
}