/* Copyright (C) 2026 Free Software Foundation, Inc. Copyright (C) 2026 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 . */ /* Build a HIP graph by capturing two kernel launches from a stream into a single graph, instantiate it, and then launch (replay) the executable graph several times with hipGraphLaunch. This exercises the debugger's ability to associate stops with the right kernel and source line when the kernels reach the GPU through a graph launch -- the host submits a whole pre-recorded graph of operations with hipGraphLaunch, which dispatches the kernels -- rather than through a direct kernel<<<>>>() call. The two kernels operate on the same buffer, in order, so each graph replay computes out = (out + 1) * 3. Starting from 0 this gives 3, 12 and 39 after the first, second and third replay respectively. The deterministic values let the .exp verify that each node executed in the right order with the right data on every replay. */ #include #include #include "rocm-test-utils.h" /* Number of times the executable graph is replayed. The .exp overrides this via -DNUM_REPLAYS=... so the source and test stay in sync; the default lets the program build on its own. */ #ifndef NUM_REPLAYS #define NUM_REPLAYS 3 #endif /* First graph node. */ __global__ void add_one (int *out) { int tid = threadIdx.x; out[tid] = out[tid] + 1; /* break add_one */ } /* Second graph node, run after add_one on the same buffer. */ __global__ void times_three (int *out) { int tid = threadIdx.x; out[tid] = out[tid] * 3; /* break times_three */ } int main () { constexpr unsigned int num_elems = 1; int *result_ptr; CHECK (hipMalloc (&result_ptr, num_elems * sizeof (int))); CHECK (hipMemset (result_ptr, 0, num_elems * sizeof (int))); hipStream_t stream; CHECK (hipStreamCreate (&stream)); /* Capture two kernel launches into a single graph. */ CHECK (hipStreamBeginCapture (stream, hipStreamCaptureModeGlobal)); add_one<<>> (result_ptr); times_three<<>> (result_ptr); hipGraph_t graph; CHECK (hipStreamEndCapture (stream, &graph)); /* Turn the graph into an executable graph. */ hipGraphExec_t graph_exec; CHECK (hipGraphInstantiate (&graph_exec, graph, nullptr, nullptr, 0)); /* This is the "launch graph" execution path: instead of the host issuing each kernel<<<>>>(), hipGraphLaunch submits the whole graph and re-dispatches both kernels on every replay. The debugger should stop in them on each launch. */ for (int i = 0; i < NUM_REPLAYS; i++) { CHECK (hipGraphLaunch (graph_exec, stream)); CHECK (hipStreamSynchronize (stream)); } int result; CHECK (hipMemcpyDtoH (&result, result_ptr, sizeof (int))); printf ("result is %d\n", result); CHECK (hipGraphExecDestroy (graph_exec)); CHECK (hipGraphDestroy (graph)); CHECK (hipStreamDestroy (stream)); CHECK (hipFree (result_ptr)); return 0; }