/* This testcase is part of GDB, the GNU debugger. Copyright 2024 Free Software Foundation, Inc. Copyright (C) 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 . */ #include #include #include #include #define CHECK(cmd) \ { \ hipError_t error = cmd; \ if (error != hipSuccess) \ { \ fprintf (stderr, "error: '%s'(%d) at %s:%d\n", \ hipGetErrorString (error), error, __FILE__, __LINE__); \ exit (EXIT_FAILURE); \ } \ } __global__ void use_shared () { constexpr size_t shared_buffer_size = SHARED_SIZE / sizeof (uint32_t); __shared__ uint32_t shared_buffer[shared_buffer_size]; /* Helper function generating the value to store in share memory. IDX is the position in the shared buffer. This function is called once to generate values to store in shared memory, and a second time to validate values read from shared. Between those two calls, it is expected that the debugger has updated the memory for one of the work-groups in the dispatch. The UPDATED_BY_DEBUGGER flag indicates that we should generate the values by taking into account the updates the debugger should have made. The generated values contain: - "marker" values at the beginning, middle and end of the buffer, - data that can vary from one work-group to the next (we use the work-group coordinates for that), - known padding data everywhere else. */ auto generator = [shared_buffer_size] (size_t idx, bool updated_by_debugger) -> uint32_t { /* Beginning marker of the buffer marker. */ if (idx == 0) return updated_by_debugger ? 0 : 0xdeadbeef; /* Data that can vary from one work-group to the next. */ if (idx == 1) return blockIdx.x; if (idx == 2) return blockIdx.y; if (idx == 3) return blockIdx.z; /* Middle of the buffer marker. */ if (idx == shared_buffer_size / 2) return updated_by_debugger ? 0xffffffff : 0xbadf00d; /* End of the buffer marker. */ if (idx == shared_buffer_size - 1) return updated_by_debugger ? 0xabcd0123 : 0x0123abcd; /* Anything else. */ return 0xaaaaaaaa; }; /* Initialize the shared buffer with known values. */ for (size_t idx = threadIdx.x; idx < shared_buffer_size; idx += blockDim.x) shared_buffer[idx] = generator (idx, false); /* Insert a breakpoint after all waves are done initializing shared memory. */ __syncthreads (); /* Break here. */ /* The debugger only stops one wave in the selected work-group. Make sure that all waves in the work-group synchronize here to avoid waves not stopped by the debugger starting to validate the content of shared_buffer before the debugger has finished updating it. */ __syncthreads (); /* Check that after the breakpoint has been hit, shared memory still contains the expected values. */ for (size_t idx = threadIdx.x; idx < shared_buffer_size; idx += blockDim.x) { /* We expect that the debugger has modified a couple of values in the shared buffer, only for work-group with X == 23. */ const bool modified_by_debugger = blockIdx.x == 23; assert (shared_buffer[idx] == generator (idx, modified_by_debugger)); } } int main (int argc, char **argv) { int deviceId; CHECK (hipGetDevice (&deviceId)); hipDeviceProp_t props; CHECK (hipGetDeviceProperties(&props, deviceId)); assert (props.sharedMemPerBlock == SHARED_SIZE); use_shared<<<128, props.maxThreadsPerBlock>>> (); CHECK (hipDeviceSynchronize ()); }