/******************************************************************************
 * Copyright (c) 2024 Lawrence Livermore National Security, LLC and other
 * HYPRE Project Developers. See the top-level COPYRIGHT file for details.
 *
 * SPDX-License-Identifier: MIT
 ******************************************************************************/

#ifndef __APPLE__
#define _GNU_SOURCE 1
#endif
#include "internal/info.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include "HYPREDRV_config.h"
#include "HYPRE_config.h"
#include "internal/utils.h"
#ifdef HYPRE_USING_OPENMP
#include <omp.h>
#endif

#ifdef HAVE_HWLOC
#include <hwloc.h>
#endif

#ifdef __APPLE__
#include <mach-o/dyld.h>
#include <mach/mach.h>
#include <sys/sysctl.h>
#else
#include <dirent.h>
#include <elf.h>
#include <link.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysinfo.h>
#if defined(__has_include)
#if __has_include(<drm/xe_drm.h>)
#include <drm/xe_drm.h>
#define HYPREDRV_HAVE_INTEL_XE_QUERY 1
#endif
#endif
#endif

#ifndef STRINGIFY
#define STRINGIFY(x) #x
#endif
#ifndef TOSTRING
#define TOSTRING(x) STRINGIFY(x)
#endif

#define HYPRE_MAX_HOSTNAME 256
#define HYPRE_MAX_GPU_BINDING 512

#ifndef __APPLE__
struct DynamicLibList;
struct DependencyGraph;
struct PrintedNodeSet;

static void PrintLinuxNumaInformation(double bytes_to_gib);
static void PrintNetworkInformation(void);
static void PrintAcceleratorRuntimeInformation(void);
static void PrintLinuxKernelTuningInformation(void);
#endif
static void BuildGpuBindingString(char *buffer, size_t len);
static void PrintMpiRuntimeInformation(MPI_Comm comm);
static void PrintThreadingEnvironmentInformation(void);
static void PrintCpuMemoryInformation(double bytes_to_gib);
static int  ReadUllFromProcMeminfo(const char *field, unsigned long long *value);

#ifdef HAVE_HWLOC
typedef struct
{
   char        name[64];
   char        vendor[32];
   char        model[128];
   char        uuid[64];
   char        pci_busid[20];
   int         smi_id;
   hwloc_obj_t obj;      // PCI device object
   hwloc_obj_t ancestor; // Non-I/O ancestor (has nodeset)
} GpuInfo;

/* sscanf("%s") into vendor[] is unsafe; width must match sizeof(GpuInfo::vendor). */
static int
GpuInfoScanVendorFirstWord(const char *vendor_name, GpuInfo *gpu)
{
   char fmt[24];

   (void)snprintf(fmt, sizeof(fmt), "%%%lus", (unsigned long)(sizeof(gpu->vendor) - 1u));
   return sscanf(vendor_name, fmt, gpu->vendor);
}

static hwloc_topology_t topology = NULL;

// Prototypes
static int  InitHwlocTopology(void);
static void CleanupHwlocTopology(void);
static void hypredrv_PrintSystemInfoHwloc(MPI_Comm comm);
#endif
void hypredrv_PrintSystemInfoLegacy(MPI_Comm comm);

static int
CopyExecutablePathIfSafe(const char *candidate, char *resolved, size_t len)
{
   char canon[PATH_MAX];

   if (!candidate || !resolved || len == 0)
   {
      return 0;
   }
   if (realpath(candidate, canon) && canon[0] == '/')
   {
      if (!hypredrv_BinaryPathPrefixIsSafe(canon))
      {
         return 0;
      }
      return snprintf(resolved, len, "%s", canon) > 0;
   }

   return 0;
}

static int
FindExecutableInPath(const char *name, char *resolved, size_t len)
{
   static const char *k_safe_exec_dirs[] = {
      "/usr/bin", "/bin", "/usr/sbin", "/sbin", "/usr/local/bin", "/opt/rocm/bin",
   };

   if (!name || !name[0] || !resolved || len == 0)
   {
      return 0;
   }

   resolved[0] = '\0';
   if (strchr(name, '/'))
   {
      if (access(name, X_OK) == 0)
      {
         return CopyExecutablePathIfSafe(name, resolved, len);
      }
      return 0;
   }

   {
      size_t i;
      for (i = 0; i < sizeof(k_safe_exec_dirs) / sizeof(k_safe_exec_dirs[0]); i++)
      {
         char candidate[PATH_MAX];
         int  written = 0;

         written =
            snprintf(candidate, sizeof(candidate), "%s/%s", k_safe_exec_dirs[i], name);
         if (written > 0 && (size_t)written < sizeof(candidate) &&
             access(candidate, X_OK) == 0)
         {
            return CopyExecutablePathIfSafe(candidate, resolved, len);
         }
      }
   }

   return 0;
}

static void
ResolveDriverName(const char *argv0, char *resolved, size_t resolved_len)
{
   if (!resolved || resolved_len == 0)
   {
      return;
   }

   if (!argv0 || !argv0[0])
   {
      (void)snprintf(resolved, resolved_len, "%s", "Driver");
      return;
   }

   char candidate[PATH_MAX];
   int  written = 0;

   if (strchr(argv0, '/'))
   {
      written = snprintf(candidate, sizeof(candidate), "%s", argv0);
      if (written < 0 || (size_t)written >= sizeof(candidate))
      {
         (void)snprintf(resolved, resolved_len, "%s", argv0);
         return;
      }
   }
   else if (!FindExecutableInPath(argv0, candidate, sizeof(candidate)))
   {
      (void)snprintf(resolved, resolved_len, "%s", argv0);
      return;
   }

   char        expanded[PATH_MAX];
   const char *expanded_path = realpath(candidate, expanded);
   if (expanded_path && expanded_path[0] != '\0')
   {
      if (snprintf(resolved, resolved_len, "%s", expanded_path) > 0)
      {
         return;
      }
   }

   if (snprintf(resolved, resolved_len, "%s", candidate) <= 0)
   {
      (void)snprintf(resolved, resolved_len, "%s", argv0);
   }
}

static int
RunCommandCapture(const char *exe_path, char *const argv[], int suppress_stderr,
                  char *buffer, size_t len)
{
   enum
   {
      RUN_COMMAND_CAPTURE_ARGV_CAP = 32
   };
   int    pipefd[2]   = {-1, -1};
   pid_t  child_pid   = -1;
   size_t used        = 0;
   int    status      = 0;
   int    read_failed = 0;
   char   discard[1024];
   char   exe_storage[PATH_MAX];

   if (!exe_path || !argv || !buffer || len == 0)
   {
      return 0;
   }
   if (!hypredrv_BinaryPathPrefixIsSafe(exe_path))
   {
      return 0;
   }
   {
      int w = snprintf(exe_storage, sizeof(exe_storage), "%s", exe_path);
      if (w < 0 || (size_t)w >= sizeof(exe_storage))
      {
         return 0;
      }
   }
   if (!hypredrv_BinaryPathPrefixIsSafe(exe_storage))
   {
      return 0;
   }
   buffer[0] = '\0';

   if (pipe(pipefd) != 0)
   {
      return 0;
   }

   child_pid = fork();
   if (child_pid < 0)
   {
      close(pipefd[0]);
      close(pipefd[1]);
      return 0;
   }

   if (child_pid == 0)
   {
      int devnull = -1;
      close(pipefd[0]);
      if (dup2(pipefd[1], STDOUT_FILENO) < 0)
      {
         _Exit(127);
      }
      if (suppress_stderr)
      {
         devnull = open("/dev/null", O_WRONLY);
         if (devnull >= 0)
         {
            (void)dup2(devnull, STDERR_FILENO);
            close(devnull);
         }
      }
      close(pipefd[1]);
      if (!hypredrv_BinaryPathPrefixIsSafe(exe_storage))
      {
         _Exit(127);
      }
      if (argv[0] == exe_path)
      {
         char *alt_argv[RUN_COMMAND_CAPTURE_ARGV_CAP];
         int   i;

         for (i = 0; i < RUN_COMMAND_CAPTURE_ARGV_CAP - 1 && argv[i] != NULL; i++)
         {
            alt_argv[i] = (i == 0) ? exe_storage : argv[i];
         }
         if (argv[i] != NULL)
         {
            _Exit(127);
         }
         alt_argv[i] = NULL;
         execv(exe_storage, alt_argv);
      }
      else
      {
         execv(exe_storage, argv);
      }
      _Exit(127);
   }

   close(pipefd[1]);
   while (1)
   {
      int     store_output = (used + 1u < len);
      char   *target       = store_output ? buffer + used : discard;
      size_t  space        = store_output ? len - used - 1u : sizeof(discard);
      ssize_t nread;

      nread = read(pipefd[0], target, space);
      if (nread > 0)
      {
         if (store_output)
         {
            used += (size_t)nread;
         }
         continue;
      }
      if (nread == 0)
      {
         break;
      }
      if (errno == EINTR)
      {
         continue;
      }
      read_failed = 1;
      break;
   }
   buffer[used] = '\0';
   close(pipefd[0]);

   while (waitpid(child_pid, &status, 0) < 0)
   {
      if (errno == EINTR)
      {
         continue;
      }
      return 0;
   }

   return !read_failed && WIFEXITED(status) && WEXITSTATUS(status) == 0;
}

static int GetIntelXeCardByOrdinal(int gpu_ordinal, char *drm_path, size_t len);
static int QueryIntelClinfoMemoryByOrdinal(int gpu_ordinal, size_t *total);
static int HYPREDRV_MAYBE_UNUSED QueryIntelXeMemoryByBusId(const char *pci_busid,
                                                           int *gpu_index, size_t *total,
                                                           size_t *used);
static int QueryIntelXeMemoryByOrdinal(int gpu_ordinal, size_t *total, size_t *used);

#ifndef __APPLE__

static int
ParseLscpuFallback(const char *lscpu_path, int *num_sockets, char *model_name,
                   size_t model_name_len)
{
   char  output[32768];
   char *line    = NULL;
   char *saveptr = NULL;
   char *argv[]  = {(char *)lscpu_path, NULL};
   int   found   = 0;

   if (!lscpu_path || !num_sockets || !model_name || model_name_len == 0)
   {
      return 0;
   }

   if (!RunCommandCapture(lscpu_path, argv, 1, output, sizeof(output)))
   {
      return 0;
   }

   line = strtok_r(output, "\n", &saveptr);
   while (line)
   {
      while (*line == ' ' || *line == '\t')
      {
         line++;
      }
      if (strncmp(line, "Socket(s):", strlen("Socket(s):")) == 0)
      {
         const char *value = strchr(line, ':');
         if (value)
         {
            *num_sockets = atoi(value + 1);
            if (*num_sockets > 0)
            {
               found = 1;
            }
         }
      }
      else if (strncmp(line, "Model name:", strlen("Model name:")) == 0)
      {
         const char *value = strchr(line, ':');
         if (value)
         {
            while (*(value + 1) == ' ')
            {
               value++;
            }
            snprintf(model_name, model_name_len, "%s", value + 1);
            hypredrv_TrimTrailingWhitespace(model_name);
            if (model_name[0] != '\0')
            {
               found = 1;
            }
         }
      }

      line = strtok_r(NULL, "\n", &saveptr);
   }

   return found;
}

static int
ParseGpuControllerLine(const char *line, char *gpu_info, size_t gpu_info_len)
{
   const char *start           = NULL;
   const char *controller_type = NULL;

   if (!line || !gpu_info || gpu_info_len == 0)
   {
      return 0;
   }

   if (strstr(line, "Matrox") || strstr(line, "ASPEED") || strstr(line, "Nuvoton"))
   {
      return 0;
   }

   start           = strstr(line, "VGA compatible controller");
   controller_type = "VGA compatible controller: ";
   if (!start)
   {
      start           = strstr(line, "3D controller");
      controller_type = "3D controller: ";
   }
   if (!start)
   {
      start           = strstr(line, "2D controller");
      controller_type = "2D controller: ";
   }
   if (!start)
   {
      start           = strstr(line, "Display controller");
      controller_type = "Display controller: ";
   }
   if (!start)
   {
      start           = strstr(line, "Processing accelerators");
      controller_type = "Processing accelerators: ";
   }
   if (!start)
   {
      return 0;
   }

   snprintf(gpu_info, gpu_info_len, "%s", start + strlen(controller_type));
   hypredrv_TrimTrailingWhitespace(gpu_info);
   return gpu_info[0] != '\0';
}

static int
CompareFixedStrings(const void *a, const void *b)
{
   return strcmp((const char *)a, (const char *)b);
}

static int
AppendUniqueString(char ***items, int *count, int *capacity, const char *value)
{
   if (!items || !count || !capacity || !value)
   {
      return 0;
   }

   for (int i = 0; i < *count; i++)
   {
      if (strcmp((*items)[i], value) == 0)
      {
         return 1;
      }
   }

   if (*count == *capacity)
   {
      int    new_capacity = *capacity ? *capacity * 2 : 16;
      char **new_items = (char **)realloc(*items, (size_t)new_capacity * sizeof(char *));
      if (!new_items)
      {
         return 0;
      }
      *items    = new_items;
      *capacity = new_capacity;
   }

   (*items)[*count] = strdup(value);
   if (!(*items)[*count])
   {
      return 0;
   }

   (*count)++;
   return 1;
}

/*--------------------------------------------------------------------------
 * hypredrv_dlpi_callback
 *
 * Linux: Use dl_iterate_phdr to list dynamic libraries
 *--------------------------------------------------------------------------*/

int
hypredrv_dlpi_callback(struct dl_phdr_info *info, size_t size, void *data)
{
   (void)size;
   (void)data;
   if (info->dlpi_name && info->dlpi_name[0])
   {
      const char *filename = strrchr(info->dlpi_name, '/');
      filename             = filename ? filename + 1 : info->dlpi_name;
      printf("   %s => %s (0x%lx)\n", filename, info->dlpi_name, info->dlpi_addr);
   }
   return 0;
}

struct DynamicLibList
{
   char **paths;
   int    count;
   int    capacity;
   int    failed;
};

struct DependencyNode
{
   char  *path;
   char **needed;
   int    needed_count;
   int    parsed;
   int    parse_ok;
};

struct DependencyGraph
{
   struct DependencyNode *nodes;
   int                    count;
   int                    capacity;
   int                    failed;
};

struct PrintedNodeSet
{
   int *flags;
   int  capacity;
};

static const char *
PathBasename(const char *path)
{
   if (!path)
   {
      return "";
   }

   const char *base = strrchr(path, '/');
   return base ? base + 1 : path;
}

static void
FreeStringArray(char **items, int count)
{
   for (int i = 0; i < count; i++)
   {
      free(items[i]);
   }
   free(items);
}

static int
CollectDynamicLibsCallback(struct dl_phdr_info *info, size_t size, void *data)
{
   (void)size;
   if (!data)
   {
      return 0;
   }

   struct DynamicLibList *list = (struct DynamicLibList *)data;
   if (list->failed || !info->dlpi_name || !info->dlpi_name[0])
   {
      return 0;
   }

   if (!AppendUniqueString(&list->paths, &list->count, &list->capacity, info->dlpi_name))
   {
      list->failed = 1;
   }

   return 0;
}

static void
FreeDynamicLibList(struct DynamicLibList *list)
{
   if (!list)
   {
      return;
   }

   FreeStringArray(list->paths, list->count);
   list->paths    = NULL;
   list->count    = 0;
   list->capacity = 0;
   list->failed   = 0;
}

static const char *
PciBusIdComparableSuffix(const char *pci_busid)
{
   if (!pci_busid)
   {
      return "";
   }

   const char *first_colon = strchr(pci_busid, ':');
   if (!first_colon)
   {
      return pci_busid;
   }

   const char *second_colon = strchr(first_colon + 1, ':');
   if (!second_colon)
   {
      return pci_busid;
   }

   return first_colon + 1;
}

static int
PciBusIdMatches(const char *lhs, const char *rhs)
{
   if (!lhs || !rhs)
   {
      return 0;
   }

   return strcmp(PciBusIdComparableSuffix(lhs), PciBusIdComparableSuffix(rhs)) == 0;
}

static int HYPREDRV_MAYBE_UNUSED
QueryNvidiaMemoryByBusId(const char *pci_busid, int *gpu_index, size_t *total,
                         size_t *used)
{
   char nvidia_path[PATH_MAX];
   char output[32768];

   if (!pci_busid || !total || !used)
   {
      return 0;
   }

   if (!FindExecutableInPath("nvidia-smi", nvidia_path, sizeof(nvidia_path)))
   {
      return 0;
   }

   {
      char *argv[] = {nvidia_path,
                      "--query-gpu=index,pci.bus_id,memory.total,memory.used",
                      "--format=csv,noheader,nounits", NULL};
      if (!RunCommandCapture(nvidia_path, argv, 1, output, sizeof(output)))
      {
         return 0;
      }
   }

   char *line    = NULL;
   char *saveptr = NULL;
   line          = strtok_r(output, "\n", &saveptr);
   while (line)
   {
      int    idx       = -1;
      char   busid[64] = {0};
      size_t gpu_total = 0;
      size_t gpu_used  = 0;

      if (sscanf(line, "%d, %63[^,], %zu, %zu", &idx, busid, &gpu_total, &gpu_used) == 4)
      {
         char *trimmed_busid = busid;
         while (*trimmed_busid == ' ')
         {
            trimmed_busid++;
         }
         hypredrv_TrimTrailingWhitespace(trimmed_busid);

         if (PciBusIdMatches(pci_busid, trimmed_busid))
         {
            if (gpu_index)
            {
               *gpu_index = idx;
            }
            *total = gpu_total;
            *used  = gpu_used;
            return 1;
         }
      }

      line = strtok_r(NULL, "\n", &saveptr);
   }

   return 0;
}

static int
QueryAmdMetricMemoryByIndex(const char *amd_path, int gpu_index, size_t *total,
                            size_t *used)
{
   char output[32768];
   char gpu_idx[32];

   if (!amd_path || !total || !used || gpu_index < 0)
   {
      return 0;
   }

   snprintf(gpu_idx, sizeof(gpu_idx), "%d", gpu_index);
   {
      char *argv[] = {(char *)amd_path, "metric", "-g", gpu_idx, "-m", "--json", NULL};
      if (!RunCommandCapture(amd_path, argv, 1, output, sizeof(output)))
      {
         return 0;
      }
   }

   const char *total_vram_str = "\"total_vram\"";
   const char *used_vram_str  = "\"used_vram\"";
   const char *ptr            = output;

   *total = 0;
   *used  = 0;

   ptr = strstr(ptr, total_vram_str);
   if (ptr)
   {
      ptr = strstr(ptr, "\"value\"");
      if (ptr)
      {
         ptr = strchr(ptr, ':');
         if (ptr)
         {
            ptr++;
            while (*ptr == ' ') ptr++;
            *total = strtoull(ptr, NULL, 10) * 1024 * 1024;
         }
      }
   }

   ptr = output;
   ptr = strstr(ptr, used_vram_str);
   if (ptr)
   {
      ptr = strstr(ptr, "\"value\"");
      if (ptr)
      {
         ptr = strchr(ptr, ':');
         if (ptr)
         {
            ptr++;
            while (*ptr == ' ') ptr++;
            *used = strtoull(ptr, NULL, 10) * 1024 * 1024;
         }
      }
   }

   return *total > 0;
}

static int HYPREDRV_MAYBE_UNUSED
QueryAmdMemoryByBusId(const char *pci_busid, int *gpu_index, size_t *total, size_t *used)
{
   char amd_path[PATH_MAX];
   char output[32768];

   if (!pci_busid || !total || !used)
   {
      return 0;
   }

   if (!FindExecutableInPath("amd-smi", amd_path, sizeof(amd_path)))
   {
      return 0;
   }

   {
      char *argv[] = {amd_path, "list", "--json", NULL};
      if (!RunCommandCapture(amd_path, argv, 1, output, sizeof(output)))
      {
         return 0;
      }
   }

   const char *ptr = output;
   while ((ptr = strstr(ptr, "\"gpu\"")) != NULL)
   {
      const char *obj_end = strchr(ptr, '}');
      if (!obj_end)
      {
         break;
      }

      int idx = -1;
      {
         const char *idx_ptr = strchr(ptr, ':');
         if (idx_ptr && idx_ptr < obj_end)
         {
            char *endptr;
            idx = (int)strtol(idx_ptr + 1, &endptr, 10);
         }
      }

      const char *bdf_ptr = strstr(ptr, "\"bdf\"");
      if (bdf_ptr && bdf_ptr < obj_end)
      {
         const char *value_start = strchr(bdf_ptr, ':');
         if (value_start && value_start < obj_end)
         {
            value_start = strchr(value_start, '"');
            if (value_start && value_start < obj_end)
            {
               char        bdf[64]   = {0};
               const char *value_end = strchr(value_start + 1, '"');
               if (value_end && value_end < obj_end)
               {
                  size_t len = (size_t)(value_end - (value_start + 1));
                  if (len >= sizeof(bdf))
                  {
                     len = sizeof(bdf) - 1;
                  }
                  memcpy(bdf, value_start + 1, len);
                  bdf[len] = '\0';

                  if (idx >= 0 && PciBusIdMatches(pci_busid, bdf) &&
                      QueryAmdMetricMemoryByIndex(amd_path, idx, total, used))
                  {
                     if (gpu_index)
                     {
                        *gpu_index = idx;
                     }
                     return 1;
                  }
               }
            }
         }
      }

      ptr = obj_end + 1;
   }

   return 0;
}

static int
QueryIntelClinfoMemoryByOrdinal(int gpu_ordinal, size_t *total)
{
   char clinfo_path[PATH_MAX];
   char output[65536];

   if (gpu_ordinal < 0 || !total)
   {
      return 0;
   }

   if (!FindExecutableInPath("clinfo", clinfo_path, sizeof(clinfo_path)))
   {
      return 0;
   }

   {
      char *argv[] = {clinfo_path, NULL};
      if (!RunCommandCapture(clinfo_path, argv, 1, output, sizeof(output)))
      {
         return 0;
      }
   }

   int   current_ordinal      = -1;
   int   current_is_intel_gpu = 0;
   char *line                 = NULL;
   char *saveptr              = NULL;

   line = strtok_r(output, "\n", &saveptr);
   while (line)
   {
      while (*line == ' ' || *line == '\t')
      {
         line++;
      }

      if (strncmp(line, "Name:", 5) == 0)
      {
         current_is_intel_gpu =
            strstr(line, "Intel(R) Graphics") != NULL ||
            (strstr(line, "Intel") != NULL && strstr(line, "Graphics") != NULL);
         if (current_is_intel_gpu)
         {
            current_ordinal++;
         }
      }
      else if (current_is_intel_gpu && strncmp(line, "Global memory size:", 19) == 0)
      {
         const char *value = line + 19;
         while (*value == ' ' || *value == '\t')
         {
            value++;
         }

         if (current_ordinal == gpu_ordinal)
         {
            *total = strtoull(value, NULL, 10);
            return *total > 0;
         }
      }

      line = strtok_r(NULL, "\n", &saveptr);
   }

   return 0;
}

#ifdef HYPREDRV_HAVE_INTEL_XE_QUERY
typedef struct
{
   int  card_number;
   char pci_busid[20];
   char drm_path[PATH_MAX];
} IntelXeCard;

static int
CompareIntelXeCards(const void *lhs, const void *rhs)
{
   const IntelXeCard *a = (const IntelXeCard *)lhs;
   const IntelXeCard *b = (const IntelXeCard *)rhs;

   if (a->card_number < b->card_number)
   {
      return -1;
   }
   if (a->card_number > b->card_number)
   {
      return 1;
   }
   return 0;
}

static int
ReadUeventValue(const char *uevent_path, const char *key, char *value, size_t len)
{
   FILE  *stream = NULL;
   char   line[256];
   int    found = 0;
   size_t key_len;

   if (!uevent_path || !key || !value || len == 0)
   {
      return 0;
   }

   stream = fopen(uevent_path, "r");
   if (!stream)
   {
      return 0;
   }

   key_len = strlen(key);
   while (fgets(line, sizeof(line), stream))
   {
      if (strncmp(line, key, key_len) == 0 && line[key_len] == '=')
      {
         char  *entry = line + key_len + 1;
         size_t value_len;

         hypredrv_TrimTrailingWhitespace(entry);
         value_len = strlen(entry);
         if (value_len >= len)
         {
            value_len = len - 1;
         }
         memcpy(value, entry, value_len);
         value[value_len] = '\0';
         found            = 1;
         break;
      }
   }

   fclose(stream);
   return found;
}

static int
CollectIntelXeCards(IntelXeCard *cards, int max_cards)
{
   DIR           *dir = NULL;
   struct dirent *entry;
   int            count = 0;

   if (!cards || max_cards <= 0)
   {
      return 0;
   }

   dir = opendir("/sys/class/drm");
   if (!dir)
   {
      return 0;
   }

   while ((entry = readdir(dir)) != NULL)
   {
      int  card_number = -1;
      char trailing    = '\0';
      char uevent_path[PATH_MAX];
      char driver[32];
      char pci_slot_name[20];

      if (sscanf(entry->d_name, "card%d%c", &card_number, &trailing) != 1)
      {
         continue;
      }
      if (count >= max_cards)
      {
         break;
      }

      snprintf(uevent_path, sizeof(uevent_path), "/sys/class/drm/%s/device/uevent",
               entry->d_name);
      if (!ReadUeventValue(uevent_path, "DRIVER", driver, sizeof(driver)) ||
          strcmp(driver, "xe") != 0)
      {
         continue;
      }
      if (!ReadUeventValue(uevent_path, "PCI_SLOT_NAME", pci_slot_name,
                           sizeof(pci_slot_name)))
      {
         continue;
      }

      cards[count].card_number = card_number;
      snprintf(cards[count].pci_busid, sizeof(cards[count].pci_busid), "%s",
               pci_slot_name);
      snprintf(cards[count].drm_path, sizeof(cards[count].drm_path), "/dev/dri/card%d",
               card_number);
      count++;
   }

   closedir(dir);

   qsort(cards, (size_t)count, sizeof(cards[0]), CompareIntelXeCards);
   return count;
}

static int
QueryIntelXeMemoryFromDrmPath(const char *drm_path, size_t *total, size_t *used)
{
   struct drm_xe_device_query       query      = {0};
   struct drm_xe_query_mem_regions *regions    = NULL;
   size_t                           vram_total = 0, vram_used = 0;
   size_t                           sys_total = 0, sys_used = 0;
   int                              fd = -1;
   int                              ok = 0;

   if (!drm_path || !total || !used)
   {
      return 0;
   }

   fd = open(drm_path, O_RDONLY | O_CLOEXEC);
   if (fd < 0)
   {
      return 0;
   }

   query.query = DRM_XE_DEVICE_QUERY_MEM_REGIONS;
   if (ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query) != 0 ||
       query.size < sizeof(struct drm_xe_query_mem_regions))
   {
      close(fd);
      return 0;
   }

   regions = (struct drm_xe_query_mem_regions *)calloc(1, query.size);
   if (!regions)
   {
      close(fd);
      return 0;
   }

   query.data = (uintptr_t)regions;
   if (ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query) != 0)
   {
      goto cleanup;
   }

   for (uint32_t i = 0; i < regions->num_mem_regions; i++)
   {
      const struct drm_xe_mem_region *region = &regions->mem_regions[i];

      if (region->mem_class == DRM_XE_MEM_REGION_CLASS_VRAM)
      {
         vram_total += (size_t)region->total_size;
         vram_used += (size_t)region->used;
      }
      else if (region->mem_class == DRM_XE_MEM_REGION_CLASS_SYSMEM)
      {
         sys_total += (size_t)region->total_size;
         sys_used += (size_t)region->used;
      }
   }

   if (vram_total > 0)
   {
      *total = vram_total;
      *used  = vram_used;
      ok     = 1;
   }
   else if (sys_total > 0)
   {
      *total = sys_total;
      *used  = sys_used;
      ok     = 1;
   }

cleanup:
   free(regions);
   close(fd);
   return ok;
}

static int
FindIntelXeCardByBusId(const char *pci_busid, int *gpu_index, char *drm_path, size_t len)
{
   IntelXeCard cards[16];
   int         count;

   if (!pci_busid)
   {
      return 0;
   }

   count = CollectIntelXeCards(cards, (int)(sizeof(cards) / sizeof(cards[0])));
   for (int i = 0; i < count; i++)
   {
      if (PciBusIdMatches(pci_busid, cards[i].pci_busid))
      {
         if (gpu_index)
         {
            *gpu_index = i;
         }
         if (drm_path && len > 0)
         {
            snprintf(drm_path, len, "%s", cards[i].drm_path);
         }
         return 1;
      }
   }

   return 0;
}

static int
GetIntelXeCardByOrdinal(int gpu_ordinal, char *drm_path, size_t len)
{
   IntelXeCard cards[16];
   int         count;

   if (gpu_ordinal < 0)
   {
      return 0;
   }

   count = CollectIntelXeCards(cards, (int)(sizeof(cards) / sizeof(cards[0])));
   if (gpu_ordinal >= count)
   {
      return 0;
   }

   if (drm_path && len > 0)
   {
      snprintf(drm_path, len, "%s", cards[gpu_ordinal].drm_path);
   }
   return 1;
}

static int
QueryIntelXeMemoryByBusId(const char *pci_busid, int *gpu_index, size_t *total,
                          size_t *used)
{
   char drm_path[PATH_MAX];

   if (!FindIntelXeCardByBusId(pci_busid, gpu_index, drm_path, sizeof(drm_path)))
   {
      return 0;
   }

   return QueryIntelXeMemoryFromDrmPath(drm_path, total, used);
}

static int
QueryIntelXeMemoryByOrdinal(int gpu_ordinal, size_t *total, size_t *used)
{
   char drm_path[PATH_MAX];

   if (!GetIntelXeCardByOrdinal(gpu_ordinal, drm_path, sizeof(drm_path)))
   {
      return 0;
   }

   return QueryIntelXeMemoryFromDrmPath(drm_path, total, used);
}
#else
static int
GetIntelXeCardByOrdinal(int gpu_ordinal, char *drm_path, size_t len)
{
   (void)gpu_ordinal;
   (void)drm_path;
   (void)len;
   return 0;
}

static int
QueryIntelXeMemoryByBusId(const char *pci_busid, int *gpu_index, size_t *total,
                          size_t *used)
{
   (void)pci_busid;
   (void)gpu_index;
   (void)total;
   (void)used;
   return 0;
}

static int
QueryIntelXeMemoryByOrdinal(int gpu_ordinal, size_t *total, size_t *used)
{
   (void)gpu_ordinal;
   (void)total;
   (void)used;
   return 0;
}
#endif

static int
RangeInBuffer(size_t offset, size_t length, size_t total)
{
   return offset <= total && length <= total - offset;
}

static int
ReadFileBytes(const char *path, unsigned char **data, size_t *size)
{
   if (!path || !data || !size)
   {
      return 0;
   }

   int fd = open(path, O_RDONLY);
   if (fd < 0)
   {
      return 0;
   }

   struct stat st;
   if (fstat(fd, &st) != 0 || st.st_size <= 0)
   {
      close(fd);
      return 0;
   }

   size_t         fsize = (size_t)st.st_size;
   unsigned char *buf   = (unsigned char *)malloc(fsize);
   if (!buf)
   {
      close(fd);
      return 0;
   }

   size_t total = 0;
   while (total < fsize)
   {
      ssize_t nread = read(fd, buf + total, fsize - total);
      if (nread <= 0)
      {
         free(buf);
         close(fd);
         return 0;
      }
      total += (size_t)nread;
   }

   close(fd);
   *data = buf;
   *size = fsize;
   return 1;
}

static int
ReadElfNeededEntries(const char *path, char ***needed, int *needed_count)
{
   if (!needed || !needed_count)
   {
      return 0;
   }

   *needed       = NULL;
   *needed_count = 0;

   unsigned char *file = NULL;
   size_t         size = 0;
   if (!ReadFileBytes(path, &file, &size))
   {
      return 0;
   }

   char **entries  = NULL;
   int    count    = 0;
   int    capacity = 0;
   int    ok       = 0;

   if (size < EI_NIDENT || memcmp(file, ELFMAG, SELFMAG) != 0)
   {
      goto cleanup;
   }

   if (file[EI_CLASS] == ELFCLASS64)
   {
      if (size < sizeof(Elf64_Ehdr))
      {
         goto cleanup;
      }

      const Elf64_Ehdr *ehdr = (const Elf64_Ehdr *)file;
      if (ehdr->e_shoff == 0 || ehdr->e_shnum == 0 ||
          ehdr->e_shentsize < sizeof(Elf64_Shdr))
      {
         goto cleanup;
      }

      size_t shoff  = (size_t)ehdr->e_shoff;
      size_t shtlen = (size_t)ehdr->e_shnum * (size_t)ehdr->e_shentsize;
      if (!RangeInBuffer(shoff, shtlen, size))
      {
         goto cleanup;
      }

      const unsigned char *shbase = file + shoff;
      for (int i = 0; i < (int)ehdr->e_shnum; i++)
      {
         const Elf64_Shdr *sh =
            (const Elf64_Shdr *)(shbase + (size_t)i * (size_t)ehdr->e_shentsize);
         if (sh->sh_type != SHT_DYNAMIC)
         {
            continue;
         }

         if (!RangeInBuffer((size_t)sh->sh_offset, (size_t)sh->sh_size, size))
         {
            continue;
         }
         if (sh->sh_link >= ehdr->e_shnum)
         {
            continue;
         }

         const Elf64_Shdr *str_sh =
            (const Elf64_Shdr *)(shbase +
                                 (size_t)sh->sh_link * (size_t)ehdr->e_shentsize);
         if (!RangeInBuffer((size_t)str_sh->sh_offset, (size_t)str_sh->sh_size, size))
         {
            continue;
         }

         size_t dyn_entsize = sh->sh_entsize ? (size_t)sh->sh_entsize : sizeof(Elf64_Dyn);
         if (dyn_entsize < sizeof(Elf64_Dyn))
         {
            continue;
         }

         const unsigned char *dyn_base = file + (size_t)sh->sh_offset;
         const char          *dynstr   = (const char *)(file + (size_t)str_sh->sh_offset);
         size_t               dyn_count = (size_t)sh->sh_size / dyn_entsize;
         size_t               dynstr_sz = (size_t)str_sh->sh_size;

         for (size_t j = 0; j < dyn_count; j++)
         {
            const Elf64_Dyn *dyn = (const Elf64_Dyn *)(dyn_base + j * dyn_entsize);
            if (dyn->d_tag != DT_NEEDED)
            {
               continue;
            }
            size_t off = (size_t)dyn->d_un.d_val;
            if (off >= dynstr_sz)
            {
               continue;
            }
            if (!AppendUniqueString(&entries, &count, &capacity, dynstr + off))
            {
               goto cleanup;
            }
         }
      }
   }
   else if (file[EI_CLASS] == ELFCLASS32)
   {
      if (size < sizeof(Elf32_Ehdr))
      {
         goto cleanup;
      }

      const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)file;
      if (ehdr->e_shoff == 0 || ehdr->e_shnum == 0 ||
          ehdr->e_shentsize < sizeof(Elf32_Shdr))
      {
         goto cleanup;
      }

      size_t shoff  = (size_t)ehdr->e_shoff;
      size_t shtlen = (size_t)ehdr->e_shnum * (size_t)ehdr->e_shentsize;
      if (!RangeInBuffer(shoff, shtlen, size))
      {
         goto cleanup;
      }

      const unsigned char *shbase = file + shoff;
      for (int i = 0; i < (int)ehdr->e_shnum; i++)
      {
         const Elf32_Shdr *sh =
            (const Elf32_Shdr *)(shbase + (size_t)i * (size_t)ehdr->e_shentsize);
         if (sh->sh_type != SHT_DYNAMIC)
         {
            continue;
         }

         if (!RangeInBuffer((size_t)sh->sh_offset, (size_t)sh->sh_size, size))
         {
            continue;
         }
         if (sh->sh_link >= ehdr->e_shnum)
         {
            continue;
         }

         const Elf32_Shdr *str_sh =
            (const Elf32_Shdr *)(shbase +
                                 (size_t)sh->sh_link * (size_t)ehdr->e_shentsize);
         if (!RangeInBuffer((size_t)str_sh->sh_offset, (size_t)str_sh->sh_size, size))
         {
            continue;
         }

         size_t dyn_entsize = sh->sh_entsize ? (size_t)sh->sh_entsize : sizeof(Elf32_Dyn);
         if (dyn_entsize < sizeof(Elf32_Dyn))
         {
            continue;
         }

         const unsigned char *dyn_base = file + (size_t)sh->sh_offset;
         const char          *dynstr   = (const char *)(file + (size_t)str_sh->sh_offset);
         size_t               dyn_count = (size_t)sh->sh_size / dyn_entsize;
         size_t               dynstr_sz = (size_t)str_sh->sh_size;

         for (size_t j = 0; j < dyn_count; j++)
         {
            const Elf32_Dyn *dyn = (const Elf32_Dyn *)(dyn_base + j * dyn_entsize);
            if (dyn->d_tag != DT_NEEDED)
            {
               continue;
            }
            size_t off = (size_t)dyn->d_un.d_val;
            if (off >= dynstr_sz)
            {
               continue;
            }
            if (!AppendUniqueString(&entries, &count, &capacity, dynstr + off))
            {
               goto cleanup;
            }
         }
      }
   }
   else
   {
      goto cleanup;
   }

   *needed       = entries;
   *needed_count = count;
   entries       = NULL;
   count         = 0;
   ok            = 1;

cleanup:
   free(file);
   FreeStringArray(entries, count);
   return ok;
}

static const char *
ResolveNeededPath(const struct DynamicLibList *loaded, const char *needed)
{
   if (!loaded || !needed || !needed[0])
   {
      return NULL;
   }

   if (strchr(needed, '/'))
   {
      return access(needed, R_OK) == 0 ? needed : NULL;
   }

   for (int i = 0; i < loaded->count; i++)
   {
      if (strcmp(PathBasename(loaded->paths[i]), needed) == 0)
      {
         return loaded->paths[i];
      }
   }

   return NULL;
}

static int
FindOrAddDependencyNode(struct DependencyGraph *graph, const char *path)
{
   if (!graph || !path || !path[0])
   {
      return -1;
   }

   for (int i = 0; i < graph->count; i++)
   {
      if (strcmp(graph->nodes[i].path, path) == 0)
      {
         return i;
      }
   }

   if (graph->count == graph->capacity)
   {
      int                    new_capacity = graph->capacity ? graph->capacity * 2 : 16;
      struct DependencyNode *new_nodes    = (struct DependencyNode *)realloc(
         graph->nodes, (size_t)new_capacity * sizeof(struct DependencyNode));
      if (!new_nodes)
      {
         graph->failed = 1;
         return -1;
      }
      graph->nodes    = new_nodes;
      graph->capacity = new_capacity;
   }

   struct DependencyNode *node = &graph->nodes[graph->count];
   memset(node, 0, sizeof(*node));
   node->path = strdup(path);
   if (!node->path)
   {
      graph->failed = 1;
      return -1;
   }

   graph->count++;
   return graph->count - 1;
}

static int
LoadDependencyNode(struct DependencyGraph *graph, int node_index)
{
   if (!graph || node_index < 0 || node_index >= graph->count)
   {
      return 0;
   }

   struct DependencyNode *node = &graph->nodes[node_index];
   if (node->parsed)
   {
      return node->parse_ok;
   }

   node->parsed   = 1;
   node->parse_ok = ReadElfNeededEntries(node->path, &node->needed, &node->needed_count);
   return node->parse_ok;
}

static int
NodeIsInStack(const int *stack, int depth, int node_index)
{
   for (int i = 0; i < depth; i++)
   {
      if (stack[i] == node_index)
      {
         return 1;
      }
   }
   return 0;
}

static void
FreePrintedNodeSet(struct PrintedNodeSet *set)
{
   if (!set)
   {
      return;
   }
   free(set->flags);
   set->flags    = NULL;
   set->capacity = 0;
}

static int
EnsurePrintedNodeCapacity(struct PrintedNodeSet *set, int required)
{
   if (!set || required <= 0)
   {
      return 0;
   }

   if (set->capacity >= required)
   {
      return 1;
   }

   int new_capacity = set->capacity ? set->capacity : 32;
   while (new_capacity < required)
   {
      if (new_capacity > INT_MAX / 2)
      {
         return 0;
      }
      new_capacity *= 2;
   }

   int *new_flags =
      (int *)realloc(set->flags, (size_t)new_capacity * sizeof(*set->flags));
   if (!new_flags)
   {
      return 0;
   }

   memset(new_flags + set->capacity, 0,
          (size_t)(new_capacity - set->capacity) * sizeof(*new_flags));
   set->flags    = new_flags;
   set->capacity = new_capacity;
   return 1;
}

static void
PrintDependencySubtree(struct DependencyGraph *graph, const struct DynamicLibList *loaded,
                       int node_index, const char *prefix, int *stack, int depth,
                       struct PrintedNodeSet *printed)
{
   if (!graph || !loaded || !prefix || !stack || !printed || depth <= 0 || depth >= 256)
   {
      return;
   }

   if (!LoadDependencyNode(graph, node_index))
   {
      return;
   }

   struct DependencyNode *node = &graph->nodes[node_index];
   if (node->needed_count <= 0)
   {
      return;
   }

   /* Snapshot the current node's dependency list before the graph grows.
    * FindOrAddDependencyNode() may realloc graph->nodes, which would invalidate
    * the cached node pointer while we are still iterating this node's DT_NEEDED
    * entries. */
   char **needed_entries = node->needed;
   int    needed_count   = node->needed_count;

   const char **line_names = (const char **)malloc((size_t)needed_count * sizeof(char *));
   int         *child_ids  = (int *)malloc((size_t)needed_count * sizeof(int));
   if (!line_names || !child_ids)
   {
      free(line_names);
      free(child_ids);
      return;
   }

   int line_count = 0;
   for (int i = 0; i < needed_count; i++)
   {
      const char *needed   = needed_entries[i];
      const char *resolved = ResolveNeededPath(loaded, needed);
      if (!resolved)
      {
         line_names[line_count] = needed;
         child_ids[line_count]  = -1;
         line_count++;
         continue;
      }

      int child_index = FindOrAddDependencyNode(graph, resolved);
      if (child_index < 0 || NodeIsInStack(stack, depth, child_index))
      {
         continue;
      }
      if (!EnsurePrintedNodeCapacity(printed, child_index + 1))
      {
         continue;
      }
      if (printed->flags[child_index])
      {
         continue;
      }

      line_names[line_count] = resolved;
      child_ids[line_count]  = child_index;
      line_count++;
   }

   for (int i = 0; i < line_count; i++)
   {
      if (child_ids[i] >= 0 && printed->flags[child_ids[i]])
      {
         continue;
      }

      int is_last = 1;
      for (int j = i + 1; j < line_count; j++)
      {
         if (child_ids[j] < 0 || !printed->flags[child_ids[j]])
         {
            is_last = 0;
            break;
         }
      }

      if (child_ids[i] >= 0)
      {
         printf("%s%s %s\n", prefix, is_last ? "`--" : "|--", line_names[i]);
      }
      else
      {
         printf("%s%s %s [unresolved]\n", prefix, is_last ? "`--" : "|--", line_names[i]);
      }

      if (child_ids[i] < 0)
      {
         continue;
      }

      printed->flags[child_ids[i]] = 1;

      char next_prefix[4096];
      int  written = snprintf(next_prefix, sizeof(next_prefix), "%s%s", prefix,
                             is_last ? "    " : "|   ");
      if (written < 0 || (size_t)written >= sizeof(next_prefix))
      {
         continue;
      }

      stack[depth] = child_ids[i];
      PrintDependencySubtree(graph, loaded, child_ids[i], next_prefix, stack, depth + 1,
                             printed);
   }

   free(line_names);
   free(child_ids);
}

static void
FreeDependencyGraph(struct DependencyGraph *graph)
{
   if (!graph)
   {
      return;
   }

   for (int i = 0; i < graph->count; i++)
   {
      struct DependencyNode *node = &graph->nodes[i];
      free(node->path);
      FreeStringArray(node->needed, node->needed_count);
   }
   free(graph->nodes);
   graph->nodes    = NULL;
   graph->count    = 0;
   graph->capacity = 0;
   graph->failed   = 0;
}

static int
PrintDynamicLibrariesTree(void)
{
#if defined(HYPRE_USING_SYCL)
   /* The ELF dependency tree walker is brittle with the oneAPI/UR/OpenCL loader
    * stack used by SYCL builds. Fall back to the flat dl_iterate_phdr() listing,
    * which is what the callers already use as their non-tree path. */
   return 0;
#else
   struct DynamicLibList loaded = {0};
   dl_iterate_phdr(CollectDynamicLibsCallback, &loaded);
   if (loaded.failed)
   {
      FreeDynamicLibList(&loaded);
      return 0;
   }

   char    exe[PATH_MAX] = "<main executable>";
   ssize_t nread         = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
   if (nread <= 0)
   {
      FreeDynamicLibList(&loaded);
      return 0;
   }
   exe[nread] = '\0';

   struct DependencyGraph graph = {0};
   int                    root  = FindOrAddDependencyNode(&graph, exe);
   if (root < 0 || !LoadDependencyNode(&graph, root))
   {
      FreeDependencyGraph(&graph);
      FreeDynamicLibList(&loaded);
      return 0;
   }

   struct PrintedNodeSet printed = {0};
   if (!EnsurePrintedNodeCapacity(&printed, root + 1))
   {
      FreeDependencyGraph(&graph);
      FreeDynamicLibList(&loaded);
      return 0;
   }

   printf("%s\n", exe);
   printed.flags[root] = 1;
   int stack[256]      = {root};
   PrintDependencySubtree(&graph, &loaded, root, "", stack, 1, &printed);

   FreePrintedNodeSet(&printed);
   FreeDependencyGraph(&graph);
   FreeDynamicLibList(&loaded);
   return 1;
#endif
}

#endif

/*--------------------------------------------------------------------------
 * PrintSystemInfo
 *--------------------------------------------------------------------------*/

void
hypredrv_PrintSystemInfo(MPI_Comm comm)
{
   int use_hwloc = 0;

#ifdef HAVE_HWLOC
   if (InitHwlocTopology() == 0)
   {
      use_hwloc = 1;
   }
#endif

   if (use_hwloc)
   {
#ifdef HAVE_HWLOC
      hypredrv_PrintSystemInfoHwloc(comm);
#endif
   }
   else
   {
      hypredrv_PrintSystemInfoLegacy(comm);
   }

#ifdef HAVE_HWLOC
   if (use_hwloc)
   {
      CleanupHwlocTopology();
   }
#endif
}

/*--------------------------------------------------------------------------
 * PrintSystemInfoLegacy
 *--------------------------------------------------------------------------*/

void
hypredrv_PrintSystemInfoLegacy(MPI_Comm comm)
{
   int    myid = 0, nprocs = 0;
   char   hostname[HYPRE_MAX_HOSTNAME];
   double bytes_to_gib = (double)(1 << 30);
   double mib_to_gib   = (double)(1 << 10);
   int    gcount;
   size_t total = 0, used = 0;
   FILE  *fp = NULL;
   char   buffer[32768];

   MPI_Comm_rank(comm, &myid);
   MPI_Comm_size(comm, &nprocs);

   /* Get the hostname for this process. POSIX may leave the buffer unterminated if
    * the hostname is too long, so zero-fill and force a terminator. */
   memset(hostname, 0, sizeof(hostname));
   if (gethostname(hostname, sizeof(hostname)) != 0)
   {
      hostname[0] = '\0';
   }
   hostname[sizeof(hostname) - 1] = '\0';

   /* Gather hostnames on all ranks (needed for unique counts, avoids huge stack VLAs) */
   char *allHostnames = NULL;
   if (nprocs > 0)
   {
      allHostnames = (char *)malloc((size_t)nprocs * HYPRE_MAX_HOSTNAME);
   }
   int host_alloc_ok = allHostnames ? 1 : 0;
   MPI_Allreduce(MPI_IN_PLACE, &host_alloc_ok, 1, MPI_INT, MPI_MIN, comm);
   if (!host_alloc_ok)
   {
      free(allHostnames);
      return;
   }
   MPI_Allgather(hostname, HYPRE_MAX_HOSTNAME, MPI_CHAR, allHostnames, HYPRE_MAX_HOSTNAME,
                 MPI_CHAR, comm);

   /* Gather per-rank GPU binding strings on rank 0 */
   char gpuBindingLocal[HYPRE_MAX_GPU_BINDING];
   BuildGpuBindingString(gpuBindingLocal, sizeof(gpuBindingLocal));
   char *gpuBindingAll = NULL;
   if (!myid && nprocs > 0)
   {
      gpuBindingAll = (char *)malloc((size_t)nprocs * HYPRE_MAX_GPU_BINDING);
   }
   MPI_Gather(gpuBindingLocal, HYPRE_MAX_GPU_BINDING, MPI_CHAR, gpuBindingAll,
              HYPRE_MAX_GPU_BINDING, MPI_CHAR, 0, comm);

   if (!myid)
   {
      printf("================================== System Information "
             "=================================\n\n");

      // 1. CPU cores and model
      int  numPhysicalCPUs   = 0;
      int  numCPUs           = 0;
      char cpuModels[8][256] = {{0}};
      char gpuInfo[256]      = "Unknown";

      /* Count unique hostnames */
      int numNodes = 0;
      for (int i = 0; i < nprocs; i++)
      {
         int isUnique = 1;
         for (int j = 0; j < i; j++)
         {
            if (strncmp(&allHostnames[i * HYPRE_MAX_HOSTNAME],
                        &allHostnames[j * HYPRE_MAX_HOSTNAME], HYPRE_MAX_HOSTNAME) == 0)
            {
               isUnique = 0;
               break;
            }
         }
         if (isUnique)
         {
            numNodes++;
         }
      }

#ifdef __APPLE__
      size_t msize = sizeof(numCPUs);
      sysctlbyname("hw.ncpu", &numCPUs, &msize, NULL, 0);

      msize = sizeof(numPhysicalCPUs);
      sysctlbyname("hw.packages", &numPhysicalCPUs, &msize, NULL, 0);

      /* Clamp to the fixed cpuModels capacity to avoid a stack overflow when the
       * system reports more than 8 packages. */
      for (int i = 0; i < numPhysicalCPUs && i < 8; i++)
      {
         msize = sizeof(cpuModels[i]);
         sysctlbyname("machdep.cpu.brand_string", &cpuModels[i], &msize, NULL, 0);
      }
#else
      int physicalCPUSeen = 0;
      fp                  = fopen("/proc/cpuinfo", "r");
      if (fp != NULL)
      {
         while (fgets(buffer, sizeof(buffer), fp))
         {
            if (strncmp(buffer, "physical id", 11) == 0)
            {
               const char *colon = strchr(buffer, ':');
               if (!colon)
               {
                  continue;
               }
               int physicalID = atoi(colon + 1);
               if (physicalID >= 0 && physicalID < 8)
               {
                  unsigned long long mask = 1ULL << (unsigned)physicalID;
                  if (!((unsigned long long)physicalCPUSeen & mask))
                  {
                     physicalCPUSeen = (int)((unsigned long long)physicalCPUSeen | mask);
                     numPhysicalCPUs++;
                  }
               }
            }

            if (strncmp(buffer, "model name", 10) == 0)
            {
               int physicalID = numPhysicalCPUs - 1;
               if (physicalID >= 0 && physicalID < 8)
               {
                  const char *colon = strchr(buffer, ':');
                  if (!colon)
                  {
                     continue;
                  }
                  const char *model = colon + 1;
                  while (*model == ' ')
                  {
                     model++;
                  }
                  strncpy(cpuModels[physicalID], model,
                          sizeof(cpuModels[physicalID]) - 1);
                  cpuModels[physicalID][sizeof(cpuModels[physicalID]) - 1] = '\0';
               }
            }
         }
         fclose(fp);

         if (numPhysicalCPUs == 0)
         {
            char lscpu_path[PATH_MAX];
            char lscpu_model[sizeof(cpuModels[0])] = {0};
            if (FindExecutableInPath("lscpu", lscpu_path, sizeof(lscpu_path)) &&
                ParseLscpuFallback(lscpu_path, &numPhysicalCPUs, lscpu_model,
                                   sizeof(lscpu_model)) &&
                lscpu_model[0] != '\0')
            {
               int fill_count = (numPhysicalCPUs < 8) ? numPhysicalCPUs : 8;
               for (int i = 0; i < fill_count; i++)
               {
                  snprintf(cpuModels[i], sizeof(cpuModels[i]), "%s", lscpu_model);
               }
            }
         }
      }

      numCPUs = (int)sysconf(_SC_NPROCESSORS_ONLN);
#endif
      if (strlen(gpuInfo) == 0)
      {
         strncpy(gpuInfo, "Unknown", sizeof(gpuInfo) - 1);
         gpuInfo[sizeof(gpuInfo) - 1] = '\0';
      }

      printf("Processing Units\n");
      printf("-----------------\n");
      printf("Number of Nodes       : %d\n", numNodes);
      printf("Number of Processors  : %d\n", numPhysicalCPUs);
      printf("Number of CPU threads : %d\n", numCPUs);
      printf("Tot. # of Processors  : %lld\n",
             (long long)numNodes * (long long)numPhysicalCPUs);
      printf("Tot. # of CPU threads : %lld\n", (long long)numNodes * (long long)numCPUs);
      for (int i = 0; i < ((numPhysicalCPUs < 8) ? numPhysicalCPUs : 8); i++)
      {
         printf("CPU Model #%d          : %s\n", i, cpuModels[i]);
      }

#ifndef __APPLE__
      gcount = 0;
      {
         char lspci_path[PATH_MAX];
         if (FindExecutableInPath("lspci", lspci_path, sizeof(lspci_path)))
         {
            char  lspci_output[32768];
            char *line    = NULL;
            char *saveptr = NULL;

            {
               char *argv[] = {lspci_path, NULL};
               if (RunCommandCapture(lspci_path, argv, 1, lspci_output,
                                     sizeof(lspci_output)))
               {
                  line = strtok_r(lspci_output, "\n", &saveptr);
                  while (line)
                  {
                     if ((strcasestr(line, "vga") || strcasestr(line, "3d") ||
                          strcasestr(line, "2d") || strcasestr(line, "display") ||
                          strcasestr(line, "accel")) &&
                         ParseGpuControllerLine(line, gpuInfo, sizeof(gpuInfo)))
                     {
                        printf("GPU Model #%d          : %s\n", gcount++, gpuInfo);
                     }
                     line = strtok_r(NULL, "\n", &saveptr);
                  }
               }
            }
         }
         else
         {
            char nvidia_path[PATH_MAX];
            if (FindExecutableInPath("nvidia-smi", nvidia_path, sizeof(nvidia_path)))
            {
               char  nvidia_output[32768];
               char *line    = NULL;
               char *saveptr = NULL;
               char *argv[]  = {nvidia_path, "--query-gpu=name", "--format=csv,noheader",
                                NULL};
               if (RunCommandCapture(nvidia_path, argv, 1, nvidia_output,
                                     sizeof(nvidia_output)))
               {
                  line = strtok_r(nvidia_output, "\n", &saveptr);
                  while (line)
                  {
                     hypredrv_TrimTrailingWhitespace(line);
                     if (line[0] != '\0')
                     {
                        printf("GPU Model #%d          : %s\n", gcount++, line);
                     }
                     line = strtok_r(NULL, "\n", &saveptr);
                  }
               }
            }
         }
      }
      gcount = 0;
#endif
      printf("\n");

      // 2. Memory available and used
      printf("Memory Information\n");
      printf("------------------\n");
      PrintCpuMemoryInformation(bytes_to_gib);

      /* NVIDIA GPU Memory Information */
      {
         char nvidia_path[PATH_MAX];
         if (FindExecutableInPath("nvidia-smi", nvidia_path, sizeof(nvidia_path)))
         {
            char  nvidia_output[32768];
            char *line    = NULL;
            char *saveptr = NULL;

            {
               char *argv[] = {nvidia_path, "--query-gpu=memory.total,memory.used",
                               "--format=csv,noheader,nounits", NULL};
               if (RunCommandCapture(nvidia_path, argv, 1, nvidia_output,
                                     sizeof(nvidia_output)))
               {
                  gcount = 0;
                  line   = strtok_r(nvidia_output, "\n", &saveptr);
                  while (line)
                  {
                     int gpu_index = -1;
                     if (sscanf(line, "%zu, %zu", &total, &used) == 2)
                     {
                        gpu_index = gcount++;
                        printf("GPU RAM NVIDIA #%d     : %6.2f / %6.2f  (%5.2f %%) GiB\n",
                               gpu_index, (double)used / mib_to_gib,
                               (double)total / mib_to_gib,
                               100.0 * (double)used / (double)total);
                     }
                     line = strtok_r(NULL, "\n", &saveptr);
                  }
               }
            }
         }
      }

      /* AMD GPU Memory Information */
      {
         char amd_path[PATH_MAX];
         if (FindExecutableInPath("amd-smi", amd_path, sizeof(amd_path)))
         {
            char  json_output[32768];
            char *argv[] = {amd_path, "metric", "-m", "--json", NULL};
            if (RunCommandCapture(amd_path, argv, 1, json_output, sizeof(json_output)))
            {
               // Parse amd-smi JSON format for all GPUs
               const char *total_vram_str = "\"total_vram\"";
               const char *used_vram_str  = "\"used_vram\"";
               const char *ptr            = json_output;

               while ((ptr = strstr(ptr, "\"gpu\"")) != NULL)
               {
                  // Find GPU index
                  int gpu_index = -1;
                  ptr           = strchr(ptr, ':');
                  if (ptr)
                  {
                     ptr++;
                     while (*ptr == ' ') ptr++;
                     {
                        char *endptr;
                        gpu_index = (int)strtol(ptr, &endptr, 10);
                        ptr       = endptr;
                     }

                     // Find total_vram for this GPU
                     const char *gpu_start = ptr;
                     const char *total_ptr = strstr(gpu_start, total_vram_str);
                     const char *used_ptr  = strstr(gpu_start, used_vram_str);

                     if (total_ptr && used_ptr)
                     {
                        // Extract total_vram value
                        const char *val_ptr = strstr(total_ptr, "\"value\"");
                        if (val_ptr)
                        {
                           val_ptr = strchr(val_ptr, ':');
                           if (val_ptr)
                           {
                              val_ptr++;
                              while (*val_ptr == ' ') val_ptr++;
                              total =
                                 strtoull(val_ptr, NULL, 10) * 1024 * 1024; // MB to bytes
                           }
                        }

                        // Extract used_vram value
                        val_ptr = strstr(used_ptr, "\"value\"");
                        if (val_ptr)
                        {
                           val_ptr = strchr(val_ptr, ':');
                           if (val_ptr)
                           {
                              val_ptr++;
                              while (*val_ptr == ' ') val_ptr++;
                              used =
                                 strtoull(val_ptr, NULL, 10) * 1024 * 1024; // MB to bytes
                           }
                        }

                        if (total > 0)
                        {
                           printf(
                              "GPU RAM AMD #%d        : %6.2f / %6.2f  (%5.2f %%) GiB\n",
                              gpu_index >= 0 ? gpu_index : gcount,
                              (double)used / bytes_to_gib, (double)total / bytes_to_gib,
                              100.0 * (double)used / (double)total);
                           gcount++;
                        }
                     }
                  }

                  // Move to next GPU entry
                  ptr = strstr(ptr, "}");
                  if (ptr) ptr++;
                  else break;
               }
            }
         }
      }

      /* Intel GPU Memory Information */
      {
         int intel_index = 0;
         while (GetIntelXeCardByOrdinal(intel_index, NULL, 0))
         {
            if (QueryIntelXeMemoryByOrdinal(intel_index, &total, &used))
            {
               printf("GPU RAM Intel #%d      : %6.2f / %6.2f  (%5.2f %%) GiB\n",
                      intel_index, (double)used / bytes_to_gib,
                      (double)total / bytes_to_gib, 100.0 * (double)used / (double)total);
            }
            else if (QueryIntelClinfoMemoryByOrdinal(intel_index, &total))
            {
               printf("GPU RAM Intel #%d      :    n/a / %6.2f  (  n/a ) GiB\n",
                      intel_index, (double)total / bytes_to_gib);
            }
            else
            {
               break;
            }
            intel_index++;
         }

         if (intel_index == 0)
         {
            while (QueryIntelClinfoMemoryByOrdinal(intel_index, &total))
            {
               printf("GPU RAM Intel #%d      :    n/a / %6.2f  (  n/a ) GiB\n",
                      intel_index, (double)total / bytes_to_gib);
               intel_index++;
            }
         }
      }

#ifndef __APPLE__
      PrintLinuxNumaInformation(bytes_to_gib);
      PrintNetworkInformation();
      PrintAcceleratorRuntimeInformation();
#endif

      if (gpuBindingAll)
      {
         printf("Accelerator Binding (per rank)\n");
         printf("-------------------------------\n");
         for (int r = 0; r < nprocs; r++)
         {
            printf("Rank %-3d              : %s\n", r,
                   gpuBindingAll + (size_t)r * HYPRE_MAX_GPU_BINDING);
         }
      }

      // 3. OS system info, release, version, machine
      printf("\nOperating System\n");
      printf("-----------------\n");
      struct utsname sysinfo;
      if (uname(&sysinfo) == 0)
      {
         printf("System Name           : %s\n", sysinfo.sysname);
         printf("Node Name             : %s\n", sysinfo.nodename);
         printf("Release               : %s\n", sysinfo.release);
         printf("Version               : %s\n", sysinfo.version);
         printf("Machine Architecture  : %s\n\n", sysinfo.machine);
      }

      // 4. Compilation Flags Information
      printf("Compilation Information\n");
      printf("------------------------\n");
      printf("Date                  : %s at %s\n", __DATE__, __TIME__);

      /* Check optimization level */
#if defined(__OPTIMIZE__)
      printf("Optimization          : Enabled\n");
#elif defined(__OPTIMIZE_SIZE__)
      printf("Optimization          : Enabled (size)\n");
#elif defined(_MSC_VER)
      /* MSVC doesn't define __OPTIMIZE__, optimization detection is complex */
      printf("Optimization          : Unknown (MSVC)\n");
#else
      printf("Optimization          : Disabled\n");
#endif
      /* Check debug symbols:
       * - NDEBUG means "not debug" (when defined, assertions are disabled, typically
       * release)
       * - _DEBUG is MSVC's debug macro
       * - DEBUG might be user-defined
       * Note: This detects debug macros, not necessarily debug symbols (-g flag) */
#if defined(HYPRE_DEBUG)
      printf("Debugging             : Enabled (HYPRE)\n");
#elif defined(_DEBUG) || defined(DEBUG)
      printf("Debugging             : Enabled\n");
#elif defined(NDEBUG)
      printf("Debugging             : Disabled\n");
#else
      /* NDEBUG not defined - could be debug or release, default to unknown */
      printf("Debugging             : Unknown\n");
#endif
#ifdef __clang_version__
      printf("Compiler              : Clang %s\n", (const char *)__clang_version__);
#elif defined(__clang__)
      printf("Compiler              : Clang %d.%d.%d\n", __clang_major__, __clang_minor__,
             __clang_patchlevel__);
#elif defined(__INTEL_COMPILER)
      printf("Compiler              : Intel %d.%d\n", __INTEL_COMPILER / 100,
             (__INTEL_COMPILER % 100) / 10);
#elif defined(__GNUC__)
      printf("Compiler              : GCC %d.%d.%d\n", __GNUC__, __GNUC_MINOR__,
             __GNUC_PATCHLEVEL__);
#else
      printf("Compiler              : Unknown\n");
#endif
#if defined(HYPRE_USING_OPENMP) && defined(_OPENMP)
      printf("OpenMP                : Supported (Version: %d)\n", _OPENMP);
#else
      printf("OpenMP                : Not used\n");
#endif
      printf("MPI library           : ");
#ifdef CRAY_MPICH_VERSION
      printf("Cray MPI (Version: %s)\n", TOSTRING(CRAY_MPICH_VERSION));
#elif defined(INTEL_MPI_VERSION)
      printf("Intel MPI (Version: %s)\n", (const char *)INTEL_MPI_VERSION);
#elif defined(__IBM_MPI__)
      printf("IBM Spectrum MPI (Version: %d.%d.%d)\n", __IBM_MPI_MAJOR_VERSION,
             __IBM_MPI_MINOR_VERSION, __IBM_MPI_RELEASE_VERSION);
#elif defined(MVAPICH2_VERSION)
      printf("MVAPICH2 (Version: %s)\n", (const char *)MVAPICH2_VERSION);
#elif defined(MPICH_NAME)
      printf("MPICH (Version: %s)\n", MPICH_VERSION);
#elif defined(OMPI_MAJOR_VERSION)
      printf("OpenMPI (Version: %d.%d.%d)\n", OMPI_MAJOR_VERSION, OMPI_MINOR_VERSION,
             OMPI_RELEASE_VERSION);
#elif defined(SGI_MPI)
      printf("SGI MPI\n");
#else
      printf("N/A\n");
#endif
#ifdef __x86_64__
      printf("Target architecture   : x86_64\n");
#elif defined(__i386__)
      printf("Target architecture   : x86 (32-bit)\n");
#elif defined(__aarch64__)
      printf("Target architecture   : ARM64\n");
#elif defined(__arm__)
      printf("Target architecture   : ARM\n");
#else
      printf("Target architecture   : Unknown\n");
#endif
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
      printf("Endianness            : Little-endian\n");
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
      printf("Endianness            : Big-endian\n");
#else
      printf("Endianness            : Unknown\n");
#endif
      printf("\n");

      // 5. Current working directory
      printf("Current Working Directory\n");
      printf("--------------------------\n");
      char cwd[4096];
      if (getcwd(cwd, sizeof(cwd)) != NULL)
      {
         printf("%s\n\n", cwd);
      }

      // 6. Dynamic libraries used
      printf("Dynamic Libraries Loaded\n");
      printf("------------------------\n");
#ifdef __APPLE__
      uint32_t dcount = _dyld_image_count();
      for (uint32_t i = 0; i < dcount; i++)
      {
         const char               *name     = _dyld_get_image_name(i);
         const struct mach_header *header   = _dyld_get_image_header(i);
         const char               *filename = strrchr(name, '/');

         filename = filename ? filename + 1 : name;
         printf("   %s => %s (0x%lx)\n", filename, name, (unsigned long)header);
      }
#else
      if (!PrintDynamicLibrariesTree())
      {
         dl_iterate_phdr(hypredrv_dlpi_callback, NULL);
      }
#endif
      printf("\n");

      PrintMpiRuntimeInformation(comm);
      PrintThreadingEnvironmentInformation();
#ifndef __APPLE__
      PrintLinuxKernelTuningInformation();
#endif

      printf("\n================================== System Information "
             "=================================\n\n");
      printf("Running on %d MPI rank%s\n", nprocs, nprocs > 1 ? "s" : "");

      /* Number of OpenMP threads per rank used in hypre */
#if defined(HYPRE_USING_OPENMP) && defined(_OPENMP)
      int num_threads = omp_get_max_threads();
      printf("Running on %d OpenMP thread%s per MPI rank\n", num_threads,
             num_threads > 1 ? "s" : "");
#endif
   }

   free(allHostnames);
   free(gpuBindingAll);
}

static void
BuildGpuBindingString(char *buffer, size_t len)
{
   struct BindingVar
   {
      const char *label;
      const char *env;
   };
   static const struct BindingVar vars[] = {
      {"CUDA", "CUDA_VISIBLE_DEVICES"}, {"HIP", "HIP_VISIBLE_DEVICES"},
      {"ROCR", "ROCR_VISIBLE_DEVICES"}, {"ONEAPI", "ONEAPI_DEVICE_SELECTOR"},
      {"ZE", "ZE_AFFINITY_MASK"},       {"SYCL", "SYCL_DEVICE_FILTER"}};

   if (!buffer || len == 0)
   {
      return;
   }

   buffer[0] = '\0';
   for (size_t i = 0; i < sizeof(vars) / sizeof(vars[0]); i++)
   {
      const char *value = getenv(vars[i].env);
      if (!value || !value[0])
      {
         continue;
      }

      size_t used = strlen(buffer);
      if (used >= len - 1)
      {
         break;
      }
      int written = snprintf(buffer + used, len - used, "%s%s=%s", used ? ", " : "",
                             vars[i].label, value);
      if (written < 0)
      {
         break;
      }
   }

   if (buffer[0] == '\0')
   {
      strncpy(buffer, "unset", len - 1);
      buffer[len - 1] = '\0';
   }
}

static void
PrintMpiRuntimeInformation(MPI_Comm comm)
{
   int myid = 0;
   MPI_Comm_rank(comm, &myid);
   if (myid)
   {
      return;
   }

   printf("MPI Runtime Information\n");
   printf("------------------------\n");

   int mpi_major = 0;
   int mpi_minor = 0;
   if (MPI_Get_version(&mpi_major, &mpi_minor) == MPI_SUCCESS)
   {
      printf("MPI Standard          : %d.%d\n", mpi_major, mpi_minor);
   }

   char lib_version[MPI_MAX_LIBRARY_VERSION_STRING + 1];
   int  lib_len = 0;
   if (MPI_Get_library_version(lib_version, &lib_len) == MPI_SUCCESS)
   {
      if (lib_len < 0)
      {
         lib_len = 0;
      }
      if ((size_t)lib_len >= sizeof(lib_version))
      {
         lib_len = (int)(sizeof(lib_version) - 1);
      }
      lib_version[lib_len]                      = '\0';
      lib_version[strcspn(lib_version, "\r\n")] = '\0';
      hypredrv_NormalizeWhitespace(lib_version);
      if (lib_version[0])
      {
         printf("MPI Implementation    : %s\n", lib_version);
      }
   }

   char processor[MPI_MAX_PROCESSOR_NAME + 1];
   int  processor_len = 0;
   if (MPI_Get_processor_name(processor, &processor_len) == MPI_SUCCESS)
   {
      if (processor_len < 0)
      {
         processor_len = 0;
      }
      if ((size_t)processor_len >= sizeof(processor))
      {
         processor_len = (int)(sizeof(processor) - 1);
      }
      processor[processor_len] = '\0';
      printf("Rank 0 Processor Name : %s\n", processor);
   }
   printf("\n");
}

static void
PrintCpuMemoryInformation(double bytes_to_gib)
{
#ifdef __APPLE__
   size_t mem_total = 0, mem_used = 0;
   size_t memSizeLen = sizeof(mem_total);
   sysctlbyname("hw.memsize", &mem_total, &memSizeLen, NULL, 0);

   mach_msg_type_number_t count  = HOST_VM_INFO_COUNT;
   vm_statistics_data_t   vmstat = {0};
   if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count) ==
       KERN_SUCCESS)
   {
      mem_used = mem_total - (size_t)vmstat.free_count * sysconf(_SC_PAGESIZE);

      printf("CPU RAM used          : %6.2f / %6.2f  (%5.2f %%) GiB\n",
             (double)mem_used / bytes_to_gib, (double)mem_total / bytes_to_gib,
             100.0 * (double)mem_used / (double)mem_total);
   }
#else
   unsigned long long mem_total_kb     = 0;
   unsigned long long mem_available_kb = 0;

   if (ReadUllFromProcMeminfo("MemTotal", &mem_total_kb) &&
       ReadUllFromProcMeminfo("MemAvailable", &mem_available_kb) &&
       mem_total_kb >= mem_available_kb)
   {
      double mem_total_gib     = (double)mem_total_kb * 1024.0 / bytes_to_gib;
      double mem_available_gib = (double)mem_available_kb * 1024.0 / bytes_to_gib;
      double mem_used_gib      = mem_total_gib - mem_available_gib;

      printf("CPU RAM used          : %6.2f / %6.2f  (%5.2f %%) GiB\n", mem_used_gib,
             mem_total_gib, 100.0 * mem_used_gib / mem_total_gib);
   }
   else
   {
      struct sysinfo info;
      if (sysinfo(&info) == 0)
      {
         double mem_total_gib     = (double)info.totalram * info.mem_unit / bytes_to_gib;
         double mem_available_gib = (double)info.freeram * info.mem_unit / bytes_to_gib;
         double mem_used_gib      = mem_total_gib - mem_available_gib;

         printf("CPU RAM used          : %6.2f / %6.2f  (%5.2f %%) GiB\n", mem_used_gib,
                mem_total_gib, 100.0 * mem_used_gib / mem_total_gib);
      }
   }
#endif
}

static void
PrintThreadingEnvironmentInformation(void)
{
   struct ThreadEnvVar
   {
      const char *label;
      const char *name;
   };

   static const struct ThreadEnvVar vars[] = {
      {"OMP_NUM_THREADS", "OMP_NUM_THREADS"}, {"OMP_PROC_BIND", "OMP_PROC_BIND"},
      {"OMP_PLACES", "OMP_PLACES"},           {"OMP_SCHEDULE", "OMP_SCHEDULE"},
      {"KMP_AFFINITY", "KMP_AFFINITY"},       {"GOMP_CPU_AFFINITY", "GOMP_CPU_AFFINITY"},
   };

   printf("Threading Environment\n");
   printf("----------------------\n");
#if defined(HYPRE_USING_OPENMP) && defined(_OPENMP)
   printf("OpenMP max threads    : %d\n", omp_get_max_threads());
#else
   printf("OpenMP max threads    : not available (OpenMP disabled)\n");
#endif

   int printed_env = 0;
   for (size_t i = 0; i < sizeof(vars) / sizeof(vars[0]); i++)
   {
      const char *value = getenv(vars[i].name);
      if (value && value[0])
      {
         printf("%-22s : %s\n", vars[i].label, value);
         printed_env = 1;
      }
   }

   if (!printed_env)
   {
      printf("OpenMP environment    : unset\n");
   }
   printf("\n");
}

#ifdef HAVE_HWLOC
static int
InitHwlocTopology(void)
{
   if (topology != NULL)
   {
      return 0; // Already initialized
   }
   if (hwloc_topology_init(&topology) < 0)
   {
      return -1;
   }

   // Set flags for accurate binding information
   hwloc_topology_set_flags(topology, HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM);

   // Include PCI and OS devices
   hwloc_topology_set_type_filter(topology, HWLOC_OBJ_PCI_DEVICE,
                                  HWLOC_TYPE_FILTER_KEEP_IMPORTANT);
   hwloc_topology_set_type_filter(topology, HWLOC_OBJ_OS_DEVICE,
                                  HWLOC_TYPE_FILTER_KEEP_IMPORTANT);

   if (hwloc_topology_load(topology) < 0)
   {
      hwloc_topology_destroy(topology);
      topology = NULL;
      return -1;
   }

   return 0;
}

static void
CleanupHwlocTopology(void)
{
   if (topology != NULL)
   {
      hwloc_topology_destroy(topology);
      topology = NULL;
   }
}

static void
PrintCacheHierarchy(void)
{
   if (topology == NULL)
   {
      return;
   }

   printf("\nCache Hierarchy\n");
   printf("-----------------\n");

   // Group caches by level and show summary
   for (int level = 1; level <= 5; level++)
   {
      hwloc_obj_type_t cache_type;
      switch (level)
      {
         case 1:
            cache_type = HWLOC_OBJ_L1CACHE;
            break;
         case 2:
            cache_type = HWLOC_OBJ_L2CACHE;
            break;
         case 3:
            cache_type = HWLOC_OBJ_L3CACHE;
            break;
         case 4:
            cache_type = HWLOC_OBJ_L4CACHE;
            break;
         case 5:
            cache_type = HWLOC_OBJ_L5CACHE;
            break;
         default:
            continue;
      }

      int count = hwloc_get_nbobjs_by_type(topology, cache_type);
      if (count == 0)
      {
         continue;
      }

      // Get first cache to get size info
      hwloc_obj_t first_cache = hwloc_get_obj_by_type(topology, cache_type, 0);
      if (!first_cache)
      {
         continue;
      }

      char type_name[32];
      hwloc_obj_type_snprintf(type_name, sizeof(type_name), first_cache, 0);

      unsigned long long size_kb     = first_cache->attr->cache.size / 1024;
      int                sharing_pus = hwloc_bitmap_weight(first_cache->cpuset);

      // Check if all caches have the same size
      bool uniform_size = true;
      for (int i = 1; i < count; i++)
      {
         hwloc_obj_t cache = hwloc_get_obj_by_type(topology, cache_type, i);
         if (cache && cache->attr->cache.size != first_cache->attr->cache.size)
         {
            uniform_size = false;
            break;
         }
      }

      if (count == 1)
      {
         printf("  %s: %llu KB, shared by %d PU\n", type_name, size_kb, sharing_pus);
      }
      else if (uniform_size)
      {
         printf("  %s: %d x %llu KB, each shared by %d PU\n", type_name, count, size_kb,
                sharing_pus);
      }
      else
      {
         // Show range of sizes
         unsigned long long min_size = size_kb, max_size = size_kb;
         for (int i = 1; i < count; i++)
         {
            hwloc_obj_t cache = hwloc_get_obj_by_type(topology, cache_type, i);
            if (cache)
            {
               unsigned long long sz = cache->attr->cache.size / 1024;
               if (sz < min_size) min_size = sz;
               if (sz > max_size) max_size = sz;
            }
         }
         if (min_size == max_size)
         {
            printf("  %s: %d x %llu KB, each shared by %d PU\n", type_name, count,
                   min_size, sharing_pus);
         }
         else
         {
            printf("  %s: %d x %llu-%llu KB, each shared by %d PU\n", type_name, count,
                   min_size, max_size, sharing_pus);
         }
      }
   }
}

static void
PrintCpuTopologyInfo(MPI_Comm comm)
{
   if (topology == NULL)
   {
      return;
   }

   int myid = 0, nprocs = 0;
   MPI_Comm_rank(comm, &myid);
   MPI_Comm_size(comm, &nprocs);

   // Gather hostnames for multi-node information
   char hostname[HYPRE_MAX_HOSTNAME];
   memset(hostname, 0, sizeof(hostname));
   if (gethostname(hostname, sizeof(hostname)) != 0)
   {
      hostname[0] = '\0';
   }
   hostname[sizeof(hostname) - 1] = '\0';
   char *allHostnames             = NULL;
   if (nprocs > 0)
   {
      allHostnames = (char *)malloc((size_t)nprocs * HYPRE_MAX_HOSTNAME);
   }
   int host_alloc_ok = allHostnames ? 1 : 0;
   MPI_Allreduce(MPI_IN_PLACE, &host_alloc_ok, 1, MPI_INT, MPI_MIN, comm);
   if (!host_alloc_ok)
   {
      free(allHostnames);
      return;
   }
   MPI_Allgather(hostname, HYPRE_MAX_HOSTNAME, MPI_CHAR, allHostnames, HYPRE_MAX_HOSTNAME,
                 MPI_CHAR, comm);

   if (!myid)
   {
      // Count unique hostnames
      int numNodes = 0;
      for (int i = 0; i < nprocs; i++)
      {
         int isUnique = 1;
         for (int j = 0; j < i; j++)
         {
            if (strncmp(&allHostnames[i * HYPRE_MAX_HOSTNAME],
                        &allHostnames[j * HYPRE_MAX_HOSTNAME], HYPRE_MAX_HOSTNAME) == 0)
            {
               isUnique = 0;
               break;
            }
         }
         if (isUnique)
         {
            numNodes++;
         }
      }

      int packages = hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_PACKAGE);
      int cores    = hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_CORE);
      int pus      = hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_PU);
      int numas    = hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_NUMANODE);

      printf("CPU Topology\n");
      printf("------------\n");
      printf("Number of Nodes       : %d\n", numNodes);
      printf("Packages (sockets)    : %d\n", packages);
      printf("Cores                : %d\n", cores);
      printf("Processing Units     : %d\n", pus);
      printf("NUMA domains         : %d\n", numas);
      if (cores > 0 && packages > 0)
      {
         printf("Cores per package    : %d\n", cores / packages);
      }
      if (pus > 0 && cores > 0)
      {
         printf("PUs per core (SMT)   : %d-way\n", pus / cores);
      }
      if (numNodes > 1)
      {
         printf("Tot. # of Processors : %lld\n",
                (long long)numNodes * (long long)packages);
         printf("Tot. # of CPU threads: %lld\n", (long long)numNodes * (long long)pus);
      }

      // Print CPU model for each package
      for (int i = 0; i < packages; i++)
      {
         hwloc_obj_t package = hwloc_get_obj_by_type(topology, HWLOC_OBJ_PACKAGE, i);
         if (package)
         {
            const char *cpuvendor = hwloc_obj_get_info_by_name(package, "CPUVendor");
            const char *cpumodel  = hwloc_obj_get_info_by_name(package, "CPUModel");
            if (cpuvendor || cpumodel)
            {
               char cpu_desc[256] = "";
               if (cpuvendor && cpumodel)
               {
                  snprintf(cpu_desc, sizeof(cpu_desc), "%s %s", cpuvendor, cpumodel);
               }
               else if (cpuvendor)
               {
                  snprintf(cpu_desc, sizeof(cpu_desc), "%s", cpuvendor);
               }
               else
               {
                  snprintf(cpu_desc, sizeof(cpu_desc), "%s", cpumodel);
               }
               hypredrv_TrimTrailingWhitespace(cpu_desc);

               if (packages > 1)
               {
                  printf("CPU Model #%d         : %s\n", i, cpu_desc);
               }
               else
               {
                  printf("CPU Model             : %s\n", cpu_desc);
               }
            }
         }
      }

      // Get CPU frequency if available
      hwloc_obj_t pu = hwloc_get_obj_by_type(topology, HWLOC_OBJ_PU, 0);
      if (pu)
      {
         const char *freq_str = hwloc_obj_get_info_by_name(pu, "CPUFrequency");
         if (freq_str)
         {
            double freq_mhz = atof(freq_str);
            if (freq_mhz > 0)
            {
               printf("CPU Frequency         : %.2f GHz\n", freq_mhz / 1000.0);
            }
         }
      }

      PrintCacheHierarchy();
   }
   free(allHostnames);
}

static hwloc_obj_t
GetPciAncestor(hwloc_obj_t obj)
{
   while (obj && obj->type != HWLOC_OBJ_PCI_DEVICE)
   {
      obj = obj->parent;
   }
   return obj;
}

static int
DiscoverGpus(GpuInfo **gpus, int *count)
{
   if (topology == NULL)
   {
      return -1;
   }

   int max_gpus = 16;
   *gpus        = (GpuInfo *)calloc(max_gpus, sizeof(GpuInfo));
   if (!*gpus)
   {
      *count = 0;
      return -1;
   }

   int gpu_count = 0;

   // First try: Use OS devices to find GPUs (like mpibind does)
   hwloc_obj_t os_dev = NULL;
   while ((os_dev = hwloc_get_next_osdev(topology, os_dev)) != NULL)
   {
      if (os_dev->attr->osdev.type != HWLOC_OBJ_OSDEV_GPU &&
          os_dev->attr->osdev.type != HWLOC_OBJ_OSDEV_COPROC)
      {
         continue;
      }

      // Skip CUDA/OpenCL coprocessors (use NVML/RSMI instead)
      if (os_dev->attr->osdev.type == HWLOC_OBJ_OSDEV_COPROC)
      {
         const char *subtype = os_dev->subtype;
         if (subtype && (strcmp(subtype, "CUDA") == 0 || strcmp(subtype, "OpenCL") == 0))
         {
            continue;
         }
      }

      if (gpu_count >= max_gpus)
      {
         break;
      }

      // Get PCI ancestor
      hwloc_obj_t pci_obj = GetPciAncestor(os_dev);
      if (!pci_obj || pci_obj->attr->pcidev.class_id >> 8 != 0x03)
      {
         continue;
      }

      GpuInfo *gpu  = &(*gpus)[gpu_count];
      gpu->obj      = pci_obj;
      gpu->ancestor = hwloc_get_non_io_ancestor_obj(topology, os_dev);
      gpu->smi_id   = -1; // Not set (would need OS device name parsing)

      snprintf(gpu->pci_busid, sizeof(gpu->pci_busid), "%04x:%02x:%02x.%d",
               pci_obj->attr->pcidev.domain, pci_obj->attr->pcidev.bus,
               pci_obj->attr->pcidev.dev, pci_obj->attr->pcidev.func);

      // Get UUID from OS device (this is the key fix!)
      const char *uuid = hwloc_obj_get_info_by_name(os_dev, "NVIDIAUUID");
      if (!uuid)
      {
         uuid = hwloc_obj_get_info_by_name(os_dev, "AMDUUID");
      }
      if (!uuid)
      {
         uuid = hwloc_obj_get_info_by_name(os_dev, "LevelZeroUUID");
      }
      if (uuid)
      {
         strncpy(gpu->uuid, uuid, sizeof(gpu->uuid) - 1);
         gpu->uuid[sizeof(gpu->uuid) - 1] = '\0';
      }
      else
      {
         strncpy(gpu->uuid, "N/A", sizeof(gpu->uuid) - 1);
      }

      // Vendor and Model from OS device if available, otherwise from PCI
      const char *vendor_name = hwloc_obj_get_info_by_name(os_dev, "GPUVendor");
      if (!vendor_name)
      {
         vendor_name = hwloc_obj_get_info_by_name(os_dev, "LevelZeroVendor");
      }
      if (!vendor_name)
      {
         vendor_name = hwloc_obj_get_info_by_name(pci_obj, "PCIVendor");
      }
      if (vendor_name)
      {
         // Get first word only
         (void)GpuInfoScanVendorFirstWord(vendor_name, gpu);
      }
      else
      {
         snprintf(gpu->vendor, sizeof(gpu->vendor), "0x%04x",
                  pci_obj->attr->pcidev.vendor_id);
      }

      const char *model_name = hwloc_obj_get_info_by_name(os_dev, "GPUModel");
      if (!model_name)
      {
         model_name = hwloc_obj_get_info_by_name(os_dev, "LevelZeroModel");
      }
      if (!model_name)
      {
         model_name = hwloc_obj_get_info_by_name(pci_obj, "PCIDevice");
      }
      if (model_name)
      {
         strncpy(gpu->model, model_name, sizeof(gpu->model) - 1);
         gpu->model[sizeof(gpu->model) - 1] = '\0';
      }
      else
      {
         snprintf(gpu->model, sizeof(gpu->model), "0x%04x",
                  pci_obj->attr->pcidev.device_id);
      }

      gpu_count++;
   }

   // Fallback: If no GPUs found via OS devices, try PCI devices directly
   if (gpu_count == 0)
   {
      hwloc_obj_t pci_obj = NULL;
      while ((pci_obj = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_PCI_DEVICE,
                                                   pci_obj)) != NULL)
      {
         // Class 0x03xx is a display controller
         if (pci_obj->attr->pcidev.class_id >> 8 != 0x03)
         {
            continue;
         }

         if (gpu_count >= max_gpus)
         {
            break;
         }

         GpuInfo *gpu  = &(*gpus)[gpu_count];
         gpu->obj      = pci_obj;
         gpu->ancestor = hwloc_get_non_io_ancestor_obj(topology, pci_obj);
         gpu->smi_id   = -1;

         snprintf(gpu->pci_busid, sizeof(gpu->pci_busid), "%04x:%02x:%02x.%d",
                  pci_obj->attr->pcidev.domain, pci_obj->attr->pcidev.bus,
                  pci_obj->attr->pcidev.dev, pci_obj->attr->pcidev.func);

         // Try to find OS device for UUID
         hwloc_obj_t os_dev = NULL;
         while ((os_dev = hwloc_get_next_osdev(topology, os_dev)) != NULL)
         {
            hwloc_obj_t os_pci = GetPciAncestor(os_dev);
            if (os_pci == pci_obj)
            {
               const char *uuid = hwloc_obj_get_info_by_name(os_dev, "NVIDIAUUID");
               if (!uuid)
               {
                  uuid = hwloc_obj_get_info_by_name(os_dev, "AMDUUID");
               }
               if (!uuid)
               {
                  uuid = hwloc_obj_get_info_by_name(os_dev, "LevelZeroUUID");
               }
               if (uuid)
               {
                  strncpy(gpu->uuid, uuid, sizeof(gpu->uuid) - 1);
                  gpu->uuid[sizeof(gpu->uuid) - 1] = '\0';
                  break;
               }
            }
         }
         if (gpu->uuid[0] == '\0')
         {
            strncpy(gpu->uuid, "N/A", sizeof(gpu->uuid) - 1);
         }

         // Vendor
         const char *vendor_name = hwloc_obj_get_info_by_name(pci_obj, "PCIVendor");
         if (vendor_name)
         {
            (void)GpuInfoScanVendorFirstWord(vendor_name, gpu);
         }
         else
         {
            snprintf(gpu->vendor, sizeof(gpu->vendor), "0x%04x",
                     pci_obj->attr->pcidev.vendor_id);
         }

         // Model
         const char *model_name = hwloc_obj_get_info_by_name(pci_obj, "PCIDevice");
         if (model_name)
         {
            strncpy(gpu->model, model_name, sizeof(gpu->model) - 1);
            gpu->model[sizeof(gpu->model) - 1] = '\0';
         }
         else
         {
            snprintf(gpu->model, sizeof(gpu->model), "0x%04x",
                     pci_obj->attr->pcidev.device_id);
         }

         gpu_count++;
      }
   }

   *count = gpu_count;
   if (gpu_count == 0)
   {
      free(*gpus);
      *gpus = NULL;
   }

   return 0;
}

static void
PrintGpuInfo(GpuInfo *gpus, int gpu_count)
{
   if (gpu_count == 0)
   {
      printf("\nGPU Information       : Not detected\n");
      return;
   }

   printf("\nGPU Information\n");
   printf("----------------\n");

   double mib_to_gib    = (double)(1 << 10);
   double bytes_to_gib  = (double)(1 << 30);
   int    intel_ordinal = 0;

   for (int i = 0; i < gpu_count; i++)
   {
      printf("GPU #%d\n", i);
      printf("  Model                : %s %s\n", gpus[i].vendor, gpus[i].model);
      printf("  PCI Bus ID           : %s\n", gpus[i].pci_busid);

      // Get PCIe link information
      if (gpus[i].obj)
      {
         double linkspeed = gpus[i].obj->attr->pcidev.linkspeed;
         if (linkspeed > 0)
         {
            printf("  PCIe Link Speed      : %.2f GT/s\n", linkspeed);
         }
      }

      if (strcmp(gpus[i].uuid, "N/A") != 0)
      {
         printf("  UUID                 : %s\n", gpus[i].uuid);
      }

      if (gpus[i].smi_id >= 0)
      {
         printf("  SMI ID               : %d\n", gpus[i].smi_id);
      }

      size_t total = 0, used = 0;
      bool   found_memory = false;

      {
         int gpu_index = -1;
         if (QueryNvidiaMemoryByBusId(gpus[i].pci_busid, &gpu_index, &total, &used))
         {
            printf("  Memory               : %6.2f / %6.2f GiB (%5.2f %%) [NVIDIA #%d]\n",
                   used / mib_to_gib, total / mib_to_gib, 100.0 * used / (double)total,
                   gpu_index);
            found_memory = true;
         }
      }

      if (!found_memory)
      {
         int gpu_index = -1;
         if (QueryAmdMemoryByBusId(gpus[i].pci_busid, &gpu_index, &total, &used))
         {
            printf("  Memory               : %6.2f / %6.2f GiB (%5.2f %%) [AMD #%d]\n",
                   used / bytes_to_gib, total / bytes_to_gib,
                   100.0 * used / (double)total, gpu_index);
            found_memory = true;
         }
      }

      if (!found_memory)
      {
         if (strcasestr(gpus[i].vendor, "intel") || strcasestr(gpus[i].model, "Intel"))
         {
            int gpu_index = -1;
            if (QueryIntelXeMemoryByBusId(gpus[i].pci_busid, &gpu_index, &total, &used))
            {
               printf(
                  "  Memory               : %6.2f / %6.2f GiB (%5.2f %%) [Intel #%d]\n",
                  used / bytes_to_gib, total / bytes_to_gib, 100.0 * used / (double)total,
                  gpu_index);
               found_memory = true;
            }
            /* clinfo fallback: no PCI bus id is exposed, so we index Intel
             * GPUs by the order they appear in clinfo. This matches the xe
             * DRM enumeration order in practice but is not guaranteed — the
             * reported total may belong to a sibling Intel GPU if the two
             * orderings diverge. */
            else if (QueryIntelClinfoMemoryByOrdinal(intel_ordinal, &total))
            {
               printf("  Memory               :    n/a / %6.2f GiB (usage unavailable) "
                      "[Intel #%d]\n",
                      total / bytes_to_gib, intel_ordinal);
               found_memory = true;
            }
            intel_ordinal++;
         }
      }

      if (!found_memory)
      {
         printf("  Memory               : unavailable\n");
      }
   }
   printf("\n");
}

static void
PrintNumaInfo(double bytes_to_gib, GpuInfo *gpus, int gpu_count)
{
   if (topology == NULL)
   {
      return;
   }

   int num_numas = hwloc_get_nbobjs_by_type(topology, HWLOC_OBJ_NUMANODE);
   if (num_numas <= 0)
   {
      printf("NUMA Information       : Not available\n\n");
      return;
   }

   printf("NUMA Information\n");
   printf("-----------------\n");

   hwloc_obj_t numa = NULL;
   while ((numa = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_NUMANODE, numa)) != NULL)
   {
      char cpuset_str[256];
      hwloc_bitmap_list_snprintf(cpuset_str, sizeof(cpuset_str), numa->cpuset);

      unsigned long long total_mem = numa->attr->numanode.local_memory;
      int                pu_count  = hwloc_bitmap_weight(numa->cpuset);

      printf("NUMA node %u\n", numa->os_index);
      printf("  CPUs                 : %s (%d PUs)\n", cpuset_str, pu_count);
      printf("  Memory (GiB)         : %.2f\n", total_mem / bytes_to_gib);

      // Count local GPUs
      int local_gpu_count = 0;
      if (gpus)
      {
         for (int i = 0; i < gpu_count; i++)
         {
            if (gpus[i].ancestor &&
                hwloc_bitmap_isset(gpus[i].ancestor->nodeset, numa->os_index))
            {
               local_gpu_count++;
            }
         }
      }
      if (local_gpu_count > 0)
      {
         printf("  Local GPUs           : %d", local_gpu_count);
         if (gpus)
         {
            bool first = true;
            for (int i = 0; i < gpu_count; i++)
            {
               if (gpus[i].ancestor &&
                   hwloc_bitmap_isset(gpus[i].ancestor->nodeset, numa->os_index))
               {
                  printf("%s%s", first ? " (" : ", ", gpus[i].pci_busid);
                  first = false;
               }
            }
            if (!first)
            {
               printf(")");
            }
         }
         printf("\n");
      }
   }
   printf("\n");
}

static void
PrintNetworkInfoHwloc(void)
{
   if (topology == NULL)
   {
      return;
   }

   printf("Network / Interconnect\n");
   printf("----------------------\n");

   int         found_ib  = 0;
   int         found_net = 0;
   hwloc_obj_t obj       = NULL;

   while ((obj = hwloc_get_next_osdev(topology, obj)) != NULL)
   {
      if (obj->attr->osdev.type == HWLOC_OBJ_OSDEV_OPENFABRICS)
      {
         found_ib            = 1;
         hwloc_obj_t pci_dev = obj;
         while (pci_dev && pci_dev->type != HWLOC_OBJ_PCI_DEVICE)
         {
            pci_dev = pci_dev->parent;
         }
         char pci_busid[20] = {0};
         if (pci_dev)
         {
            snprintf(pci_busid, sizeof(pci_busid), "%04x:%02x:%02x.%d",
                     pci_dev->attr->pcidev.domain, pci_dev->attr->pcidev.bus,
                     pci_dev->attr->pcidev.dev, pci_dev->attr->pcidev.func);
         }

         const char *nodeguid = hwloc_obj_get_info_by_name(obj, "NodeGUID");
         const char *address  = hwloc_obj_get_info_by_name(obj, "Address");

         printf("InfiniBand %-9s : %s\n", obj->name, obj->name);
         if (pci_busid[0])
         {
            printf("  PCI Bus ID           : %s\n", pci_busid);
         }
         if (nodeguid)
         {
            printf("  NodeGUID             : %s\n", nodeguid);
         }
         if (address)
         {
            printf("  Address              : %s\n", address);
         }
      }
      else if (obj->attr->osdev.type == HWLOC_OBJ_OSDEV_NETWORK)
      {
         found_net = 1;
         printf("Interface %-11s : detected\n", obj->name);
      }
   }

   if (!found_ib)
   {
      printf("InfiniBand            : Not detected\n");
   }

   if (!found_net)
   {
      // Fallback to legacy method
      PrintNetworkInformation();
   }
   printf("\n");
}

static void
PrintProcessBinding(void)
{
   if (topology == NULL)
   {
      return;
   }

   printf("Process Binding\n");
   printf("-----------------\n");

   hwloc_bitmap_t cpuset = hwloc_bitmap_alloc();
   hwloc_bitmap_t memset = hwloc_bitmap_alloc();

   if (hwloc_get_cpubind(topology, cpuset, HWLOC_CPUBIND_PROCESS) == 0)
   {
      char str[256];
      hwloc_bitmap_list_snprintf(str, sizeof(str), cpuset);
      printf("Process CPU binding    : %s\n", str);
   }

   if (hwloc_get_membind(topology, memset, NULL, HWLOC_MEMBIND_PROCESS) == 0)
   {
      char str[256];
      hwloc_bitmap_list_snprintf(str, sizeof(str), memset);
      printf("Process memory binding : %s\n", str);
   }

   hwloc_bitmap_free(cpuset);
   hwloc_bitmap_free(memset);
   printf("\n");
}

static void
PrintThreadAffinity(MPI_Comm comm, GpuInfo *gpus, int gpu_count)
{
#if defined(HYPRE_USING_OPENMP) && defined(_OPENMP)
   int myid = 0, nprocs = 0;
   MPI_Comm_rank(comm, &myid);
   MPI_Comm_size(comm, &nprocs);

   if (topology == NULL)
   {
      return;
   }

   int nthreads = omp_get_max_threads();
   if (nthreads <= 1)
   {
      return; // No threads to report
   }

   // Synchronize all ranks before printing
   MPI_Barrier(comm);

   if (myid == 0)
   {
      printf("Thread Affinity\n");
      printf("----------------\n");
      printf("OpenMP threads per rank: %d\n", nthreads);
      printf("\n");
   }

// Each thread reports its own affinity
#pragma omp parallel
   {
      int tid            = omp_get_thread_num();
      int nthreads_local = omp_get_num_threads();

      hwloc_bitmap_t cpuset = hwloc_bitmap_alloc();
      if (hwloc_get_cpubind(topology, cpuset, HWLOC_CPUBIND_THREAD) == 0)
      {
         char str[256];
         hwloc_bitmap_list_snprintf(str, sizeof(str), cpuset);
         printf("Rank %-3d Thread %-3d/%d: CPUs %s", myid, tid, nthreads_local, str);

         // Try to get GPU assignment for this thread
         if (gpus && gpu_count > 0)
         {
            // For now, just show which GPUs are visible to this process
            // Actual GPU assignment would require CUDA/HIP runtime calls
            printf(" (GPUs visible: %d)", gpu_count);
         }
         printf("\n");
      }
      hwloc_bitmap_free(cpuset);
   }

   // Synchronize all ranks after printing
   MPI_Barrier(comm);

   if (myid == 0)
   {
      printf("\n");
   }
#endif
}

static void
CountChildrenRecursive(hwloc_obj_t obj, hwloc_obj_type_t type, int *count, int *first_idx,
                       int *last_idx)
{
   // Check this object
   if (obj->type == type)
   {
      (*count)++;
      int idx = obj->logical_index;
      if (*first_idx < 0) *first_idx = idx;
      if (idx > *last_idx) *last_idx = idx;
   }

   // Recurse into ALL children (including through cache objects to find cores/PUs)
   for (unsigned i = 0; i < obj->arity; i++)
   {
      CountChildrenRecursive(obj->children[i], type, count, first_idx, last_idx);
   }
}

static void
PrintTopologyTreeCompact(hwloc_obj_t obj, int depth)
{
   char indent[32];
   /* Clamp the indent width to the buffer size; deep or synthetic hwloc topologies
    * can exceed depth 15 and would otherwise overflow this stack buffer. */
   int indent_len = depth * 2;
   if (indent_len < 0)
   {
      indent_len = 0;
   }
   if (indent_len > (int)sizeof(indent) - 1)
   {
      indent_len = (int)sizeof(indent) - 1;
   }
   memset(indent, ' ', (size_t)indent_len);
   indent[indent_len] = '\0';

   char type_str[32];
   hwloc_obj_type_snprintf(type_str, sizeof(type_str), obj, 1);

   // Skip cache objects
   if (obj->type == HWLOC_OBJ_L1CACHE || obj->type == HWLOC_OBJ_L2CACHE ||
       obj->type == HWLOC_OBJ_L3CACHE || obj->type == HWLOC_OBJ_L4CACHE ||
       obj->type == HWLOC_OBJ_L5CACHE)
   {
      for (unsigned i = 0; i < obj->arity; i++)
      {
         PrintTopologyTreeCompact(obj->children[i], depth);
      }
      return;
   }

   // For Package: just show the name, no summary
   if (obj->type == HWLOC_OBJ_PACKAGE)
   {
      printf("%s%s[%d]\n", indent, type_str, obj->logical_index);
      // Recurse into Dies
      for (unsigned i = 0; i < obj->arity; i++)
      {
         hwloc_obj_t child = obj->children[i];
         if (child->type != HWLOC_OBJ_L1CACHE && child->type != HWLOC_OBJ_L2CACHE &&
             child->type != HWLOC_OBJ_L3CACHE && child->type != HWLOC_OBJ_L4CACHE &&
             child->type != HWLOC_OBJ_L5CACHE && child->type != HWLOC_OBJ_PU)
         {
            PrintTopologyTreeCompact(child, depth + 1);
         }
      }
   }
   // For Die: show summary with counts and ranges, don't recurse into cores
   else if (obj->type == HWLOC_OBJ_DIE)
   {
      int core_count = 0, core_first = -1, core_last = -1;
      int pu_count = 0, pu_first = -1, pu_last = -1;

      CountChildrenRecursive(obj, HWLOC_OBJ_CORE, &core_count, &core_first, &core_last);
      CountChildrenRecursive(obj, HWLOC_OBJ_PU, &pu_count, &pu_first, &pu_last);

      printf("%s%s[%d]:", indent, type_str, obj->logical_index);
      bool first = true;

      if (core_count > 0)
      {
         if (core_count == 1)
         {
            printf(" %d Core (Core[%d])", core_count, core_first);
         }
         else
         {
            printf(" %d Cores (Core[%d-%d])", core_count, core_first, core_last);
         }
         first = false;
      }

      if (pu_count > 0)
      {
         if (!first) printf(",");
         if (pu_count == 1)
         {
            printf(" %d PU (PU[%d])", pu_count, pu_first);
         }
         else
         {
            printf(" %d PUs (PU[%d-%d])", pu_count, pu_first, pu_last);
         }
      }

      printf("\n");
      // Don't recurse - we've shown the summary
   }
   else if (obj->type == HWLOC_OBJ_CORE || obj->type == HWLOC_OBJ_PU)
   {
      // Don't print cores and PUs individually - they're summarized at Die level
      return;
   }
   else
   {
      // For Machine and other types, just show the object
      printf("%s%s[%d]\n", indent, type_str, obj->logical_index);
      // Recurse
      for (unsigned i = 0; i < obj->arity; i++)
      {
         hwloc_obj_t child = obj->children[i];
         if (child->type != HWLOC_OBJ_L1CACHE && child->type != HWLOC_OBJ_L2CACHE &&
             child->type != HWLOC_OBJ_L3CACHE && child->type != HWLOC_OBJ_L4CACHE &&
             child->type != HWLOC_OBJ_L5CACHE)
         {
            PrintTopologyTreeCompact(child, depth + 1);
         }
      }
   }
}

static void
PrintTopologyTree(void)
{
   if (topology == NULL)
   {
      return;
   }

   printf("\nTopology Tree\n");
   printf("---------------\n");

   hwloc_obj_t root = hwloc_get_root_obj(topology);
   PrintTopologyTreeCompact(root, 0);
   printf("\n");
}

static void
PrintMemoryInformation(double bytes_to_gib, double mib_to_gib)
{
   (void)mib_to_gib;
   printf("Memory Information\n");
   printf("------------------\n");
   PrintCpuMemoryInformation(bytes_to_gib);
   printf("\n");
}

static void
PrintOperatingSystemInfo(void)
{
   printf("Operating System\n");
   printf("-----------------\n");
   struct utsname sysinfo;
   if (uname(&sysinfo) == 0)
   {
      printf("System Name           : %s\n", sysinfo.sysname);
      printf("Node Name             : %s\n", sysinfo.nodename);
      printf("Release               : %s\n", sysinfo.release);
      printf("Version               : %s\n", sysinfo.version);
      printf("Machine Architecture  : %s\n\n", sysinfo.machine);
   }
}

static void
PrintCompilationInfo(void)
{
   printf("Compilation Information\n");
   printf("------------------------\n");
   printf("Date                  : %s at %s\n", __DATE__, __TIME__);

   /* Check optimization level */
#if defined(__OPTIMIZE__)
   printf("Optimization          : Enabled\n");
#elif defined(__OPTIMIZE_SIZE__)
   printf("Optimization          : Enabled (size)\n");
#elif defined(_MSC_VER)
   printf("Optimization          : Unknown (MSVC)\n");
#else
   printf("Optimization          : Disabled\n");
#endif
   /* Check debug symbols */
#if defined(HYPRE_DEBUG)
   printf("Debugging             : Enabled (HYPRE)\n");
#elif defined(_DEBUG) || defined(DEBUG)
   printf("Debugging             : Enabled\n");
#elif defined(NDEBUG)
   printf("Debugging             : Disabled\n");
#else
   printf("Debugging             : Unknown\n");
#endif
#ifdef __clang_version__
   printf("Compiler              : Clang %s\n", (const char *)__clang_version__);
#elif defined(__clang__)
   printf("Compiler              : Clang %d.%d.%d\n", __clang_major__, __clang_minor__,
          __clang_patchlevel__);
#elif defined(__INTEL_COMPILER)
   printf("Compiler              : Intel %d.%d\n", __INTEL_COMPILER / 100,
          (__INTEL_COMPILER % 100) / 10);
#elif defined(__GNUC__)
   printf("Compiler              : GCC %d.%d.%d\n", __GNUC__, __GNUC_MINOR__,
          __GNUC_PATCHLEVEL__);
#else
   printf("Compiler              : Unknown\n");
#endif
#if defined(HYPRE_USING_OPENMP) && defined(_OPENMP)
   printf("OpenMP                : Supported (Version: %d)\n", _OPENMP);
#else
   printf("OpenMP                : Not used\n");
#endif
   printf("MPI library           : ");
#ifdef CRAY_MPICH_VERSION
   printf("Cray MPI (Version: %s)\n", TOSTRING(CRAY_MPICH_VERSION));
#elif defined(INTEL_MPI_VERSION)
   printf("Intel MPI (Version: %s)\n", (const char *)INTEL_MPI_VERSION);
#elif defined(__IBM_MPI__)
   printf("IBM Spectrum MPI (Version: %d.%d.%d)\n", __IBM_MPI_MAJOR_VERSION,
          __IBM_MPI_MINOR_VERSION, __IBM_MPI_RELEASE_VERSION);
#elif defined(MVAPICH2_VERSION)
   printf("MVAPICH2 (Version: %s)\n", (const char *)MVAPICH2_VERSION);
#elif defined(MPICH_NAME)
   printf("MPICH (Version: %s)\n", MPICH_VERSION);
#elif defined(OMPI_MAJOR_VERSION)
   printf("OpenMPI (Version: %d.%d.%d)\n", OMPI_MAJOR_VERSION, OMPI_MINOR_VERSION,
          OMPI_RELEASE_VERSION);
#elif defined(SGI_MPI)
   printf("SGI MPI\n");
#else
   printf("N/A\n");
#endif
#ifdef __x86_64__
   printf("Target architecture   : x86_64\n");
#elif defined(__i386__)
   printf("Target architecture   : x86 (32-bit)\n");
#elif defined(__aarch64__)
   printf("Target architecture   : ARM64\n");
#elif defined(__arm__)
   printf("Target architecture   : ARM\n");
#else
   printf("Target architecture   : Unknown\n");
#endif
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
   printf("Endianness            : Little-endian\n");
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
   printf("Endianness            : Big-endian\n");
#else
   printf("Endianness            : Unknown\n");
#endif
   printf("\n");
}

static void
PrintWorkingDirectory(void)
{
   printf("Current Working Directory\n");
   printf("--------------------------\n");
   char cwd[4096];
   if (getcwd(cwd, sizeof(cwd)) != NULL)
   {
      printf("%s\n\n", cwd);
   }
}

static void
PrintDynamicLibraries(void)
{
   printf("Dynamic Libraries Loaded\n");
   printf("------------------------\n");
#ifdef __APPLE__
   uint32_t dcount = _dyld_image_count();
   for (uint32_t i = 0; i < dcount; i++)
   {
      const char               *name     = _dyld_get_image_name(i);
      const struct mach_header *header   = _dyld_get_image_header(i);
      const char               *filename = strrchr(name, '/');

      filename = filename ? filename + 1 : name;
      printf("   %s => %s (0x%lx)\n", filename, name, (unsigned long)header);
   }
#else
   if (!PrintDynamicLibrariesTree())
   {
      dl_iterate_phdr(hypredrv_dlpi_callback, NULL);
   }
#endif
   printf("\n");
}

static void
PrintRunningInfo(MPI_Comm comm)
{
   int myid = 0, nprocs = 0;
   MPI_Comm_rank(comm, &myid);
   MPI_Comm_size(comm, &nprocs);

   if (!myid)
   {
      printf("Running on %d MPI rank%s\n", nprocs, nprocs > 1 ? "s" : "");

      /* Number of OpenMP threads per rank used in hypre */
#if defined(HYPRE_USING_OPENMP) && defined(_OPENMP)
      int num_threads = omp_get_max_threads();
      printf("Running on %d OpenMP thread%s per MPI rank\n", num_threads,
             num_threads > 1 ? "s" : "");
#endif
   }
}

static void
hypredrv_PrintSystemInfoHwloc(MPI_Comm comm)
{
   int myid = 0, nprocs = 0;
   MPI_Comm_rank(comm, &myid);
   MPI_Comm_size(comm, &nprocs);

   double bytes_to_gib = (double)(1 << 30);
   double mib_to_gib   = (double)(1 << 10);

   // Discover GPUs on all ranks (needed for thread affinity)
   GpuInfo *gpus      = NULL;
   int      gpu_count = 0;
   DiscoverGpus(&gpus, &gpu_count);

   // Gather per-rank GPU binding strings on rank 0
   char gpuBindingLocal[HYPRE_MAX_GPU_BINDING];
   BuildGpuBindingString(gpuBindingLocal, sizeof(gpuBindingLocal));
   char *gpuBindingAll = NULL;
   if (!myid && nprocs > 0)
   {
      gpuBindingAll = (char *)malloc((size_t)nprocs * HYPRE_MAX_GPU_BINDING);
   }
   /* Ensure the root receive buffer allocation succeeded on all ranks before the
    * collective; passing a NULL recvbuf on the root is erroneous per the MPI
    * standard. */
   int gpu_gather_ok = (myid || gpuBindingAll) ? 1 : 0;
   MPI_Allreduce(MPI_IN_PLACE, &gpu_gather_ok, 1, MPI_INT, MPI_MIN, comm);
   if (gpu_gather_ok)
   {
      MPI_Gather(gpuBindingLocal, HYPRE_MAX_GPU_BINDING, MPI_CHAR, gpuBindingAll,
                 HYPRE_MAX_GPU_BINDING, MPI_CHAR, 0, comm);
   }
   else
   {
      free(gpuBindingAll);
      gpuBindingAll = NULL;
   }

   if (!myid)
   {
      printf("================================ System Information (hwloc) "
             "================================\n\n");
   }

   /* CPU Topology performs MPI collectives (Allreduce/Allgather) and must be
    * entered by every rank; it prints on rank 0 internally. */
   PrintCpuTopologyInfo(comm);

   if (!myid)
   {
      // 3. GPU Information
      PrintGpuInfo(gpus, gpu_count);

      // 4. Memory Information
      PrintMemoryInformation(bytes_to_gib, mib_to_gib);

      // 5. NUMA Information
      PrintNumaInfo(bytes_to_gib, gpus, gpu_count);

      // 6. Network Information
      PrintNetworkInfoHwloc();

      // 7. Accelerator Runtime Information
#ifndef __APPLE__
      PrintAcceleratorRuntimeInformation();
#endif

      // 8. Accelerator Binding (per rank)
      if (gpuBindingAll)
      {
         printf("Accelerator Binding (per rank)\n");
         printf("-------------------------------\n");
         for (int r = 0; r < nprocs; r++)
         {
            printf("Rank %-3d              : %s\n", r,
                   gpuBindingAll + (size_t)r * HYPRE_MAX_GPU_BINDING);
         }
         printf("\n");
      }

      // 9. Process Binding
      PrintProcessBinding();
   }

   /* Thread Affinity uses MPI_Barrier and reports per-rank data, so every rank
    * must enter it; it gates its banner on rank 0 internally. */
   PrintThreadAffinity(comm, gpus, gpu_count);

   if (!myid)
   {
      // 11. Topology Tree
      PrintTopologyTree();

      // 12. Operating System
      PrintOperatingSystemInfo();

      // 13. Compilation Information
      PrintCompilationInfo();

      // 14. MPI Runtime Information
      PrintMpiRuntimeInformation(comm);

      // 15. Threading Environment
      PrintThreadingEnvironmentInformation();

#ifndef __APPLE__
      // 16. Linux Kernel Tuning
      PrintLinuxKernelTuningInformation();
#endif

      // 17. Current Working Directory
      PrintWorkingDirectory();

      // 18. Dynamic Libraries
      PrintDynamicLibraries();

      // 19. hwloc Information
      printf("hwloc Information\n");
      printf("-----------------\n");
      unsigned version = hwloc_get_api_version();
      printf("hwloc API version    : %u.%u.%u\n", (version >> 16) & 0xff,
             (version >> 8) & 0xff, version & 0xff);
      printf("\n");

      // 20. Running Information
      PrintRunningInfo(comm);

      if (gpuBindingAll)
      {
         free(gpuBindingAll);
      }

      printf("================================ System Information (hwloc) "
             "================================\n\n");
   }

   // Free GPUs on all ranks
   if (gpus)
   {
      free(gpus);
   }
}
#endif

#ifndef __APPLE__

static int
ReadLineFromFile(const char *path, char *out, size_t len)
{
   if (!path || !out || len == 0)
   {
      return 0;
   }

   FILE *fp = fopen(path, "r");
   if (!fp)
   {
      return 0;
   }

   if (!fgets(out, (int)len, fp))
   {
      fclose(fp);
      return 0;
   }
   fclose(fp);

   out[strcspn(out, "\n")] = '\0';
   return 1;
}

static int
ReadIntFromFile(const char *path, int *value)
{
   if (!value)
   {
      return 0;
   }

   char line[64];
   if (!ReadLineFromFile(path, line, sizeof(line)))
   {
      return 0;
   }

   *value = atoi(line);
   return 1;
}

static int
ReadUllFromProcMeminfo(const char *field, unsigned long long *value)
{
   if (!field || !value)
   {
      return 0;
   }

   FILE *fp = fopen("/proc/meminfo", "r");
   if (!fp)
   {
      return 0;
   }

   int  found = 0;
   char line[256];
   while (fgets(line, sizeof(line), fp))
   {
      char               key[128];
      unsigned long long parsed = 0;
      if (sscanf(line, "%127[^:]: %llu", key, &parsed) == 2 && strcmp(key, field) == 0)
      {
         *value = parsed;
         found  = 1;
         break;
      }
   }

   fclose(fp);
   return found;
}

static int
ExtractBracketedToken(const char *line, char *token, size_t len)
{
   if (!line || !token || len == 0)
   {
      return 0;
   }

   const char *start = strchr(line, '[');
   if (!start)
   {
      return 0;
   }
   start++;

   const char *end = strchr(start, ']');
   if (!end || end <= start)
   {
      return 0;
   }

   size_t n = (size_t)(end - start);
   if (n >= len)
   {
      n = len - 1;
   }
   memcpy(token, start, n);
   token[n] = '\0';
   return 1;
}

static void
PrintLinuxKernelTuningInformation(void)
{
   printf("Linux Kernel Tuning\n");
   printf("--------------------\n");

   struct sysinfo info;
   if (sysinfo(&info) == 0)
   {
      long uptime = info.uptime;
      long days   = uptime / 86400;
      uptime      = uptime % 86400;
      long hours  = uptime / 3600;
      uptime      = uptime % 3600;
      long mins   = uptime / 60;

      printf("System uptime         : %ldd %02ldh %02ldm\n", days, hours, mins);
      printf("Load average (1/5/15) : %.2f / %.2f / %.2f\n",
             (double)info.loads[0] / 65536.0, (double)info.loads[1] / 65536.0,
             (double)info.loads[2] / 65536.0);
   }

   char line[256];
   char token[64];

   if (ReadLineFromFile("/sys/kernel/mm/transparent_hugepage/enabled", line,
                        sizeof(line)))
   {
      if (ExtractBracketedToken(line, token, sizeof(token)))
      {
         printf("THP policy            : %s\n", token);
      }
      else
      {
         printf("THP policy            : %s\n", line);
      }
   }

   if (ReadLineFromFile("/sys/kernel/mm/transparent_hugepage/defrag", line, sizeof(line)))
   {
      if (ExtractBracketedToken(line, token, sizeof(token)))
      {
         printf("THP defrag policy     : %s\n", token);
      }
      else
      {
         printf("THP defrag policy     : %s\n", line);
      }
   }

   int numa_balancing = -1;
   if (ReadIntFromFile("/proc/sys/kernel/numa_balancing", &numa_balancing))
   {
      printf("NUMA balancing        : %s\n", numa_balancing ? "enabled" : "disabled");
   }

   int overcommit = -1;
   if (ReadIntFromFile("/proc/sys/vm/overcommit_memory", &overcommit))
   {
      const char *mode = "unknown";
      if (overcommit == 0)
      {
         mode = "heuristic";
      }
      else if (overcommit == 1)
      {
         mode = "always";
      }
      else if (overcommit == 2)
      {
         mode = "never";
      }
      printf("Overcommit policy     : %d (%s)\n", overcommit, mode);
   }

   int zone_reclaim = -1;
   if (ReadIntFromFile("/proc/sys/vm/zone_reclaim_mode", &zone_reclaim))
   {
      printf("Zone reclaim mode     : %d\n", zone_reclaim);
   }

   unsigned long long hugepages_total = 0;
   unsigned long long hugepages_free  = 0;
   unsigned long long hugepage_size   = 0;
   if (ReadUllFromProcMeminfo("HugePages_Total", &hugepages_total))
   {
      printf("HugePages total/free  : %llu / ", hugepages_total);
      if (ReadUllFromProcMeminfo("HugePages_Free", &hugepages_free))
      {
         printf("%llu\n", hugepages_free);
      }
      else
      {
         printf("unknown\n");
      }
   }
   if (ReadUllFromProcMeminfo("Hugepagesize", &hugepage_size))
   {
      printf("HugePage size         : %llu kB\n", hugepage_size);
   }

   printf("\n");
}

static void
PrintLinuxNumaInformation(double bytes_to_gib)
{
   printf("\nNUMA Information\n");
   printf("-----------------\n");

   DIR *node_dir = opendir("/sys/devices/system/node");
   if (!node_dir)
   {
      printf("NUMA details unavailable (missing /sys/devices/system/node)\n\n");
      return;
   }

   struct dirent *entry = NULL;
   int            nodes = 0;
   while ((entry = readdir(node_dir)) != NULL)
   {
      if (strncmp(entry->d_name, "node", 4) != 0)
      {
         continue;
      }
      if (!isdigit((unsigned char)entry->d_name[4]))
      {
         continue;
      }

      char meminfo_path[PATH_MAX];
      snprintf(meminfo_path, sizeof(meminfo_path), "/sys/devices/system/node/%s/meminfo",
               entry->d_name);

      FILE *memfp = fopen(meminfo_path, "r");
      if (!memfp)
      {
         continue;
      }

      unsigned long long mem_total_kb = 0;
      unsigned long long mem_free_kb  = 0;
      char               line[256];
      while (fgets(line, sizeof(line), memfp))
      {
         if (strncmp(line, "MemTotal:", 9) == 0)
         {
            sscanf(line, "MemTotal: %llu kB", &mem_total_kb);
         }
         else if (strncmp(line, "MemFree:", 8) == 0)
         {
            sscanf(line, "MemFree: %llu kB", &mem_free_kb);
         }
      }
      fclose(memfp);

      double total_gib = (double)mem_total_kb * 1024.0 / bytes_to_gib;
      double free_gib  = (double)mem_free_kb * 1024.0 / bytes_to_gib;
      double used_gib  = total_gib - free_gib;
      double used_pct  = total_gib > 0.0 ? (used_gib / total_gib) * 100.0 : 0.0;

      int node_index = atoi(entry->d_name + 4);
      if (mem_total_kb > 0)
      {
         printf("NUMA node %-3d         : %6.2f / %6.2f  (%5.2f %%) GiB used\n",
                node_index, used_gib, total_gib, used_pct);
      }
      else
      {
         printf("NUMA node %-3d         : memory data unavailable\n", node_index);
      }
      nodes++;
   }
   closedir(node_dir);

   if (nodes == 0)
   {
      printf("No NUMA nodes detected.\n");
   }
   printf("\n");
}

static void
PrintNetworkInformation(void)
{
   printf("Network / Interconnect\n");
   printf("----------------------\n");

   DIR *ib_dir = opendir("/sys/class/infiniband");
   if (ib_dir)
   {
      struct dirent *entry = NULL;
      int            found = 0;
      char           line[256];
      while ((entry = readdir(ib_dir)) != NULL)
      {
         if (entry->d_name[0] == '.')
         {
            continue;
         }
         found = 1;
         char desc_path[PATH_MAX];
         snprintf(desc_path, sizeof(desc_path), "/sys/class/infiniband/%s/node_desc",
                  entry->d_name);
         if (ReadLineFromFile(desc_path, line, sizeof(line)))
         {
            printf("InfiniBand %-9s : %s\n", entry->d_name, line);
         }
         else
         {
            printf("InfiniBand %-9s : (description unavailable)\n", entry->d_name);
         }
      }
      closedir(ib_dir);
      if (!found)
      {
         printf("InfiniBand            : Not detected\n");
      }
   }
   else
   {
      printf("InfiniBand            : Not detected\n");
   }

   DIR *net_dir = opendir("/sys/class/net");
   if (net_dir)
   {
      struct dirent *entry = NULL;
      while ((entry = readdir(net_dir)) != NULL)
      {
         if (entry->d_name[0] == '.')
         {
            continue;
         }
         if (strcmp(entry->d_name, "lo") == 0)
         {
            continue;
         }

         char speed_path[PATH_MAX];
         snprintf(speed_path, sizeof(speed_path), "/sys/class/net/%s/speed",
                  entry->d_name);
         int speed_mbps = -1;
         ReadIntFromFile(speed_path, &speed_mbps);

         char state_path[PATH_MAX];
         snprintf(state_path, sizeof(state_path), "/sys/class/net/%s/operstate",
                  entry->d_name);
         char state[64];
         state[0] = '\0';
         ReadLineFromFile(state_path, state, sizeof(state));

         if (speed_mbps > 0)
         {
            printf("Interface %-11s : %4d Mbps (%s)\n", entry->d_name, speed_mbps,
                   state[0] ? state : "unknown");
         }
         else
         {
            printf("Interface %-11s : unknown speed (%s)\n", entry->d_name,
                   state[0] ? state : "unknown");
         }
      }
      closedir(net_dir);
   }
   else
   {
      printf("Network interfaces    : Unavailable\n");
   }
   printf("\n");
}

static void
PrintAcceleratorRuntimeInformation(void)
{
   printf("Accelerator Runtime Information\n");
   printf("--------------------------------\n");

   int printed = 0;

   {
      char nvidia_path[PATH_MAX];
      if (FindExecutableInPath("nvidia-smi", nvidia_path, sizeof(nvidia_path)))
      {
         {
            char  output[32768];
            char *first_driver = NULL;
            char *argv[]       = {nvidia_path, "--query-gpu=driver_version",
                                  "--format=csv,noheader", NULL};

            if (RunCommandCapture(nvidia_path, argv, 1, output, sizeof(output)))
            {
               first_driver = strtok(output, "\n");
               if (first_driver)
               {
                  hypredrv_TrimTrailingWhitespace(first_driver);
                  if (first_driver[0] != '\0')
                  {
                     printf("NVIDIA driver         : %s\n", first_driver);
                     printed = 1;
                  }
               }
            }
         }

         {
            char  output[32768];
            char *argv[] = {nvidia_path, "--query-gpu=compute_cap",
                            "--format=csv,noheader", NULL};
            if (RunCommandCapture(nvidia_path, argv, 1, output, sizeof(output)))
            {
               char  caps[64][32];
               int   cap_count = 0;
               char *entry     = NULL;
               char *saveptr   = NULL;

               entry = strtok_r(output, "\n", &saveptr);
               while (entry)
               {
                  int duplicate = 0;
                  hypredrv_TrimTrailingWhitespace(entry);
                  if (entry[0] != '\0')
                  {
                     for (int i = 0; i < cap_count; i++)
                     {
                        if (strcmp(caps[i], entry) == 0)
                        {
                           duplicate = 1;
                           break;
                        }
                     }
                     if (!duplicate && cap_count < (int)(sizeof(caps) / sizeof(caps[0])))
                     {
                        snprintf(caps[cap_count], sizeof(caps[cap_count]), "%s", entry);
                        cap_count++;
                     }
                  }
                  entry = strtok_r(NULL, "\n", &saveptr);
               }

               if (cap_count > 0)
               {
                  qsort(caps, (size_t)cap_count, sizeof(caps[0]), CompareFixedStrings);
                  for (int i = 0; i < cap_count; i++)
                  {
                     if (i == 0)
                     {
                        printf("CUDA comp. capability : %s\n", caps[i]);
                     }
                     else
                     {
                        printf("                        %s\n", caps[i]);
                     }
                     printed = 1;
                  }
               }
            }
         }
      }
   }

   {
      char amd_path[PATH_MAX];
      if (FindExecutableInPath("amd-smi", amd_path, sizeof(amd_path)))
      {
         {
            char  output[32768];
            char *argv[] = {amd_path, "version", NULL};
            if (RunCommandCapture(amd_path, argv, 1, output, sizeof(output)))
            {
               char *first_line = strtok(output, "\n");
               if (first_line)
               {
                  const char *rocm_ver = strstr(first_line, "ROCm version:");
                  if (rocm_ver)
                  {
                     rocm_ver += strlen("ROCm version:");
                     while (*rocm_ver == ' ') rocm_ver++;
                     // Extract version number (until next | or end of line)
                     char version[64] = {0};
                     int  i           = 0;
                     while (*rocm_ver && *rocm_ver != '|' &&
                            i < (int)(sizeof(version) - 1))
                     {
                        version[i++] = *rocm_ver++;
                     }
                     version[i] = '\0';
                     // Trim trailing spaces
                     while (i > 0 && version[i - 1] == ' ') version[--i] = '\0';
                     printf("AMD driver            : ROCm %s\n", version);
                  }
                  else
                  {
                     hypredrv_TrimTrailingWhitespace(first_line);
                     printf("AMD driver            : %s\n", first_line);
                  }
                  printed = 1;
               }
            }
         }
      }
   }

   const char *level_zero = getenv("ONEAPI_DEVICE_SELECTOR");
   if (level_zero && level_zero[0])
   {
      printf("oneAPI selector       : %s\n", level_zero);
      printed = 1;
   }

   if (!printed)
   {
      printf("No accelerator tooling detected.\n");
   }
   printf("\n");
}

#else /* __APPLE__ */

/* Keep linker-visible Intel helper symbols on Apple and other non-Linux builds
 * where the DRM/XE query path is intentionally unavailable. */
static int
GetIntelXeCardByOrdinal(int gpu_ordinal, char *drm_path, size_t len)
{
   (void)gpu_ordinal;
   (void)drm_path;
   (void)len;
   return 0;
}

static int
QueryIntelClinfoMemoryByOrdinal(int gpu_ordinal, size_t *total)
{
   (void)gpu_ordinal;
   if (total)
   {
      *total = 0;
   }
   return 0;
}

static int
QueryIntelXeMemoryByBusId(const char *pci_busid, int *gpu_index, size_t *total,
                          size_t *used)
{
   (void)pci_busid;
   if (gpu_index)
   {
      *gpu_index = -1;
   }
   if (total)
   {
      *total = 0;
   }
   if (used)
   {
      *used = 0;
   }
   return 0;
}

static int
QueryIntelXeMemoryByOrdinal(int gpu_ordinal, size_t *total, size_t *used)
{
   (void)gpu_ordinal;
   if (total)
   {
      *total = 0;
   }
   if (used)
   {
      *used = 0;
   }
   return 0;
}

#endif /* __APPLE__ */

/*--------------------------------------------------------------------------
 * PrintLibInfo
 *--------------------------------------------------------------------------*/

void
hypredrv_PrintLibInfo(MPI_Comm comm, int print_datetime)
{
   int myid = 0;

   MPI_Comm_rank(comm, &myid);

   if (!myid)
   {
      if (print_datetime)
      {
         time_t    t = 0;
         struct tm tm_buf;
         char      buffer[100];

         /* Get current time */
         time(&t);
         if (localtime_r(&t, &tm_buf))
         {
            strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm_buf);
            printf("Date and time: %s\n", buffer);
         }
      }

#if defined(HYPREDRV_DEVELOP_STRING) && defined(HYPREDRV_BRANCH_NAME)
      printf("\nUsing HYPREDRV_DEVELOP_STRING: %s (%s)\n", HYPREDRV_DEVELOP_STRING,
             HYPREDRV_BRANCH_NAME);
#elif defined(HYPREDRV_DEVELOP_STRING)
      printf("\nUsing HYPREDRV_DEVELOP_STRING: %s\n", HYPREDRV_DEVELOP_STRING);
#elif defined(HYPREDRV_GIT_SHA)
      printf("\nUsing HYPREDRV_GIT_SHA: %s\n", HYPREDRV_GIT_SHA);
#elif defined(HYPREDRV_RELEASE_VERSION)
      printf("\nUsing HYPREDRV_RELEASE_VERSION: %s\n", HYPREDRV_RELEASE_VERSION);
#endif

#if defined(HYPRE_DEVELOP_STRING) && defined(HYPRE_BRANCH_NAME)
      printf("Using HYPRE_DEVELOP_STRING: %s (%s)\n", HYPRE_DEVELOP_STRING,
             HYPRE_BRANCH_NAME);
#elif defined(HYPRE_DEVELOP_STRING) && !defined(HYPRE_BRANCH_NAME)
      printf("Using HYPRE_DEVELOP_STRING: %s\n", HYPRE_DEVELOP_STRING);
#elif defined(HYPRE_RELEASE_VERSION)
      printf("Using HYPRE_RELEASE_VERSION: %s\n", HYPRE_RELEASE_VERSION);
#endif
   }
}

/*--------------------------------------------------------------------------
 * ExecutionPolicyName
 *--------------------------------------------------------------------------*/

const char *
hypredrv_ExecutionPolicyName(int exec_policy)
{
   HYPRE_ExecutionPolicy hypre_exec_policy =
      exec_policy ? HYPRE_EXEC_DEVICE : HYPRE_EXEC_HOST;

#if HYPRE_CHECK_MIN_VERSION(30000, 0)
   return HYPRE_GetExecutionPolicyName(hypre_exec_policy);
#else
   return hypre_exec_policy == HYPRE_EXEC_DEVICE ? "Device" : "Host";
#endif
}

/*--------------------------------------------------------------------------
 * PrintExecutionPolicy
 *--------------------------------------------------------------------------*/

void
hypredrv_PrintExecutionPolicy(MPI_Comm comm, int exec_policy, FILE *stream)
{
   int myid = 0;

   if (!stream)
   {
      return;
   }

   MPI_Comm_rank(comm, &myid);

   if (!myid)
   {
      fprintf(stream, "HYPRE execution policy: %s\n\n",
              hypredrv_ExecutionPolicyName(exec_policy));
   }
}

/*--------------------------------------------------------------------------
 * PrintExitInfo
 *--------------------------------------------------------------------------*/

void
hypredrv_PrintExitInfo(MPI_Comm comm, const char *argv0)
{
   int myid = 0;

   MPI_Comm_rank(comm, &myid);

   if (!myid)
   {
      char      buffer[100];
      char      driver_name[PATH_MAX];
      time_t    t = 0;
      struct tm tm_buf;

      /* Get current time */
      time(&t);
      if (localtime_r(&t, &tm_buf))
      {
         strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm_buf);
      }
      else
      {
         (void)snprintf(buffer, sizeof(buffer), "(time unavailable)");
      }

      ResolveDriverName(argv0, driver_name, sizeof(driver_name));
      printf("Date and time: %s\n%s done!\n", buffer, driver_name);
   }
}
