/* This testcase is part of GDB, the GNU debugger.

   Copyright 2023-2026 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 <http://www.gnu.org/licenses/>.  */

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <hip/hip_runtime.h>
#include <stdio.h>
#include <stdlib.h>

#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
kern ()
{
  asm ("s_sleep 1");
}

/* Spawn one child process per detected GPU.  */

static int
parent (int argc, char **argv)
{
  /* Identify how many GPUs we have, and spawn one child for each.  */
  int num_devices;
  CHECK (hipGetDeviceCount (&num_devices));

  /* Break here.  */

  for (int i = 0; i < num_devices; i++)
    {
      char n[32] = {};
      snprintf (n, sizeof (n), "%d", i);
      pid_t pid = fork ();
      if (pid == -1)
	{
	  perror ("Fork failed");
	  return -1;
	}

      if (pid == 0)
	{
	  /* Exec to force the child to re-initialize the ROCm runtime.  */
	  if (execl (argv[0], argv[0], n, nullptr) == -1)
	    {
	      perror ("Failed to exec");
	      return -1;
	    }
	}
    }

  /* Wait for all children.  */
  while (true)
    {
      int ws;
      pid_t ret = waitpid (-1, &ws, 0);
      if (ret == -1 && errno == ECHILD)
	break;
    }

  /* Last break here.  */
  return 0;
}

static int
child (int argc, char **argv)
{
  int dev_number;
  if (sscanf (argv[1], "%d", &dev_number) != 1)
    {
      fprintf (stderr, "Invalid argument \"%s\"\n", argv[1]);
      return -1;
    }

  CHECK (hipSetDevice (dev_number));
  kern<<<1, 1>>> ();
  CHECK (hipDeviceSynchronize ());
  return 0;
}

/* When called with no argument, identify how many AMDGPU devices are
   available on the system and spawn one worker process per GPU.  If a
   command-line argument is provided, it is the index of the GPU to use.  */

int
main (int argc, char **argv)
{
  if (argc <= 1)
    return parent (argc, argv);
  else
    return child (argc, argv);
}
