root/src/dps8/dps8_topo.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. file_exists
  2. is_compatible_architecture
  3. get_candidate_lib_dirs
  4. free_candidate_lib_dirs
  5. find_libhwloc_path
  6. get_core_count

   1 /*
   2  * vim: filetype=c:tabstop=2:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * scspell-id: e39fb11a-ffab-11ef-b6d4-80ee73e9b8e7
   5  *
   6  * ---------------------------------------------------------------------------
   7  *
   8  * Copyright (c) 2025 Jeffrey H. Johnson
   9  * Copyright (c) 2025 The DPS8M Development Team
  10  *
  11  * This software is made available under the terms of the ICU License.
  12  * See the LICENSE.md file at the top-level directory of this distribution.
  13  *
  14  * ---------------------------------------------------------------------------
  15  */
  16 
  17 /*
  18  * get_core_count - returns the number of physical cores (or zero on error).
  19  *
  20  * XXX: This works by using libhwloc (dynamically loading it at runtime).
  21  * Fallbacks might be implemented for common systems using other methods.
  22  *
  23  * Fully working on Haiku, Linux, FreeBSD, macOS, Solaris, and illumos.
  24  *
  25  * AIX seems to work but is not well tested.  The program MUST be compiled
  26  * with -D_THREAD_SAFE (e.g. `xlc_r`, `ibm-clang_r`) and -lpthread linked.
  27  * We check if it returns greater than 1 on AIX, otherwise assume an error.
  28  *
  29  * On NetBSD, the program MUST be linked with -lpthread (or else it crashes
  30  * at startup).  However, it still doesn't seem to actually work, so for
  31  * NetBSD -- for now -- we'll avoid potential problems and just return 0.
  32  *
  33  * Haiku's libhwloc port is deficient so we use the native API instead.
  34  *
  35  * Windows support is just returning 0 for now.  Cygwin remains untested.
  36  *
  37  * These functions are not intended to be thread-safe or reentrant and
  38  * should be called only from the main program thread once, preferably close
  39  * to program start-up.
  40  */
  41 
  42 #include <ctype.h>
  43 #if !defined(__MINGW32__) && !defined(__MINGW64__) && !defined(CROSS_MINGW32) && !defined(CROSS_MINGW64)
  44 # include <dlfcn.h>
  45 #endif
  46 #include <limits.h>
  47 #include <stdint.h>
  48 #include <stdbool.h>
  49 #include <stdio.h>
  50 #include <stdlib.h>
  51 #include <string.h>
  52 #include <sys/stat.h>
  53 #include <unistd.h>
  54 #include "dps8_sir.h"
  55 
  56 #if defined(__HAIKU__)
  57 # include <OS.h>
  58 #endif
  59 
  60 #if !defined(CHAR_BIT)
  61 # define CHAR_BIT 8
  62 #endif
  63 
  64 #if !defined(HAS_INCLUDE)
  65 # if defined __has_include
  66 #  define HAS_INCLUDE(inc) __has_include(inc)
  67 # else
  68 #  define HAS_INCLUDE(inc) 0
  69 # endif
  70 #endif
  71 
  72 #if !defined(__HAIKU__)
  73 # if HAS_INCLUDE(<elf.h>)
  74 #  include <elf.h>
  75 # endif
  76 # if HAS_INCLUDE(<hwloc.h>)
  77 #  include <hwloc.h>
  78 # endif
  79 #endif
  80 
  81 #if !defined(HWLOC_OBJ_CORE)
  82 # define HWLOC_OBJ_CORE 5
  83 #endif
  84 
  85 #if defined(EI_CLASS) && defined(EI_NIDENT) && defined(ELFCLASS32) && \
  86     defined(ELFCLASS64) && defined(ELFMAG) && defined(SELFMAG)
  87 # define USE_ELF_H
  88 #endif
  89 
  90 #if defined(FREE)
  91 # undef FREE
  92 #endif /* if defined(FREE) */
  93 #define FREE(p) do  \
  94   {                 \
  95     free((p));      \
  96     (p) = NULL;     \
  97   } while(0)
  98 
  99 bool dps8_topo_used = false;
 100 
 101 #if !defined(__HAIKU__)
 102 static inline int
 103 file_exists (const char *path)
     /* [previous][next][first][last][top][bottom][index][help] */
 104 {
 105   struct stat st;
 106   return 0 == stat(path, &st);
 107 }
 108 #endif
 109 
 110 #if !defined(__HAIKU__)
 111 static inline int
 112 is_compatible_architecture (const char *path)
     /* [previous][next][first][last][top][bottom][index][help] */
 113 {
 114 # if !defined(USE_ELF_H)
 115   (void)path;
 116   return 1;
 117 # else
 118   FILE *f = fopen(path, "rb");
 119   if (NULL == f) {
 120 #  if defined(TOPO_TESTING)
 121     (void)fprintf(stderr, "WARNING: Unable to open '%s'!\r\n", path);
 122 #  endif
 123     return 0;
 124   }
 125 
 126   unsigned char e_ident[EI_NIDENT];
 127   if (EI_NIDENT != fread(e_ident, 1, EI_NIDENT, f)) {
 128 #  if defined(TOPO_TESTING)
 129     (void)fprintf(stderr, "WARNING: Bad header in '%s'!\r\n", path);
 130 #  endif
 131     fclose(f);
 132     return 0;
 133   }
 134   fclose(f);
 135 
 136   if (0 != memcmp(e_ident, ELFMAG, SELFMAG)) {
 137 #  if defined(TOPO_TESTING)
 138     (void)fprintf(stderr, "WARNING: Bad header in '%s'!\r\n", path);
 139 #  endif
 140     return 0;
 141   }
 142 
 143   const uint32_t bits = (int)sizeof(void *) * CHAR_BIT;
 144 
 145   if (64 == bits) {
 146     /* cppcheck-suppress arrayIndexOutOfBounds */
 147     if (EI_NIDENT >= EI_CLASS && ELFCLASS64 == e_ident[EI_CLASS]) { //-V560
 148 #  if defined(TOPO_TESTING)
 149       (void)fprintf(stderr, "NOTICE: '%s' is valid 64-bit ELF.\r\n", path);
 150 #  endif
 151       return 1;
 152     }
 153   } else if (32 == bits) {
 154     /* cppcheck-suppress arrayIndexOutOfBounds */
 155     if (EI_NIDENT >= EI_CLASS && ELFCLASS32 == e_ident[EI_CLASS]) { //-V560
 156 #  if defined(TOPO_TESTING)
 157       (void)fprintf(stderr, "NOTICE: '%s' is valid 32-bit ELF.\r\n", path);
 158 #  endif
 159       return 1;
 160     }
 161   }
 162 #  if defined(TOPO_TESTING)
 163   (void)fprintf(stderr, "WARNING: '%s' could not be validated!\r\n", path);
 164 #  endif
 165   return 0;
 166 # endif
 167 }
 168 #endif
 169 
 170 #if !defined(__HAIKU__) && !defined(__NetBSD__)
 171 static char **
 172 get_candidate_lib_dirs (int32_t *n_dirs)
     /* [previous][next][first][last][top][bottom][index][help] */
 173 {
 174   size_t count = 0;
 175   char **dirs = NULL;
 176   const char *ld_path = getenv("LD_LIBRARY_PATH");
 177 
 178   if (ld_path && *ld_path) {
 179     char *copy = strdup(ld_path);
 180     if (NULL == copy) {
 181       /* cppcheck-suppress memleak */
 182       return NULL;
 183     }
 184 
 185     char *token = strtok(copy, ":");
 186     while (token) {
 187       count++;
 188       token = strtok(NULL, ":");
 189     }
 190     FREE(copy);
 191   }
 192 
 193   const char *defaults[] = {
 194       "/lib",
 195       "/lib64",
 196       "/usr/lib",
 197       "/usr/lib64",
 198       "/usr/lib/64",
 199       "/usr/local/lib",
 200       "/opt/lib",
 201       "/opt/local/lib",
 202       "/opt/freeware/lib",
 203       "/boot/system/lib",
 204       "/opt/hwloc/lib",
 205       "/usr/local/hwloc",
 206       "/home/linuxbrew/.linuxbrew/lib",
 207       "/usr/pkg/lib",
 208       "/usr/lib/32",
 209       "/usr/lib/aarch64-linux-gnu",
 210       "/usr/lib/arm-linux-gnueabihf",
 211   };
 212 
 213   uint32_t n_defaults = sizeof(defaults) / sizeof(defaults[0]);
 214   count += n_defaults;
 215 
 216   FILE *fp = fopen("/proc/self/maps", "r");
 217   char *temp_paths[SIR_MAXPATH];
 218   uint32_t temp_count = 0;
 219   if (fp) {
 220     char line[SIR_MAXPATH];
 221     while (fgets(line, sizeof(line), fp) && temp_count < SIR_MAXPATH) {
 222       char *path = strstr(line, "/");
 223       if (path && strstr(path, ".so")) {
 224         char *dir_end = strrchr(path, '/');
 225         if (dir_end) {
 226           *dir_end = '\0';
 227           int is_dup = 0;
 228           for (uint32_t i = 0; i < temp_count; i++) {
 229             if (0 == strcmp(temp_paths[i], path)) {
 230               is_dup = 1;
 231               break;
 232             }
 233           }
 234           if (!is_dup) {
 235             temp_paths[temp_count++] = strdup(path);
 236             if (NULL == temp_paths[temp_count - 1]) {
 237               for (uint32_t i = 0; i < temp_count - 1; i++)
 238                 FREE(temp_paths[i]);
 239               fclose(fp);
 240               return NULL;
 241             }
 242           }
 243         }
 244       }
 245     }
 246     fclose(fp);
 247     count += temp_count;
 248   }
 249 
 250   dirs = malloc(sizeof(char *) * (count + 1));
 251   if (NULL == dirs) {
 252     for (uint32_t i = 0; i < temp_count; i++)
 253       FREE(temp_paths[i]);
 254     return NULL;
 255   }
 256 
 257   int32_t index = 0;
 258 
 259   if (ld_path && *ld_path) {
 260     char *copy = strdup(ld_path);
 261     if (NULL == copy) {
 262       FREE(dirs);
 263       for (uint32_t i = 0; i < temp_count; i++)
 264         FREE(temp_paths[i]);
 265       return NULL;
 266     }
 267     char *token = strtok(copy, ":");
 268     while (token) {
 269       dirs[index++] = strdup(token);
 270       if (NULL == dirs[index - 1]) {
 271         for (int32_t i = 0; i < index - 1; i++)
 272           FREE(dirs[i]);
 273         FREE(dirs);
 274         FREE(copy);
 275         for (uint32_t i = 0; i < temp_count; i++)
 276           FREE(temp_paths[i]);
 277         return NULL;
 278       }
 279       token = strtok(NULL, ":");
 280     }
 281     FREE(copy);
 282   }
 283 
 284   for (uint32_t i = 0; i < temp_count; i++) {
 285     dirs[index++] = temp_paths[i];
 286   }
 287 
 288   for (uint32_t i = 0; i < n_defaults; i++) {
 289     dirs[index++] = strdup(defaults[i]);
 290     if (NULL == dirs[index - 1]) {
 291       for (int32_t j = 0; j < index - 1; j++)
 292         FREE(dirs[j]);
 293       FREE(dirs);
 294       return NULL;
 295     }
 296   }
 297 
 298   dirs[index] = NULL;
 299   *n_dirs = index;
 300   return dirs;
 301 }
 302 #endif
 303 
 304 #if !defined(__HAIKU__)
 305 static inline void
 306 free_candidate_lib_dirs (char **dirs)
     /* [previous][next][first][last][top][bottom][index][help] */
 307 {
 308   if (NULL == dirs)
 309     return;
 310 
 311   for (uint32_t i = 0; dirs[i] != NULL; i++)
 312     FREE(dirs[i]);
 313 
 314   FREE(dirs);
 315 }
 316 #endif
 317 
 318 /* Find libhwloc */
 319 #if !defined(__HAIKU__) && !defined(__MINGW64__) && !defined(__MINGW32__) && \
 320     !defined(CROSS_MINGW64) && !defined(CROSS_MINGW32) && !defined(__NetBSD__)
 321 static char *
 322 find_libhwloc_path (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 323 {
 324   const char *candidates[] = {
 325       "libhwloc.so.15",
 326       "libhwloc.so.5",
 327       "libhwloc.so",
 328       "libhwloc.15.dylib",
 329       "libhwloc.5.dylib",
 330       "libhwloc.dylib",
 331       NULL
 332   };
 333 
 334   int32_t n_dirs = 0;
 335   char *found = NULL;
 336   char **dirs = get_candidate_lib_dirs(&n_dirs);
 337 
 338   if (NULL == dirs)
 339     return NULL;
 340 
 341   for (uint32_t i = 0; candidates[i] != NULL; i++) {
 342     for (uint32_t j = 0; j < n_dirs; j++) {
 343       char fullpath[SIR_MAXPATH];
 344       (void)snprintf(fullpath, sizeof(fullpath), "%s/%s", dirs[j], candidates[i]);
 345       if (file_exists(fullpath) && is_compatible_architecture(fullpath)) {
 346         found = strdup(fullpath);
 347         break;
 348       }
 349     }
 350     if (NULL != found)
 351       break;
 352   }
 353 
 354   free_candidate_lib_dirs(dirs);
 355   return found;
 356 }
 357 #endif
 358 
 359 /* libhwloc */
 360 typedef void *dl_hwloc_topology_t;
 361 typedef int (*fp_hwloc_topology_init) (dl_hwloc_topology_t *);
 362 typedef int (*fp_hwloc_topology_load) (dl_hwloc_topology_t);
 363 typedef void (*fp_hwloc_topology_destroy) (dl_hwloc_topology_t);
 364 typedef int (*fp_hwloc_get_type_depth) (dl_hwloc_topology_t, int);
 365 typedef unsigned (*fp_hwloc_get_nbobjs_by_depth) (dl_hwloc_topology_t, int);
 366 
 367 /* get_core_count */
 368 uint32_t
 369 get_core_count(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 370 {
 371   static uint32_t num_cores = 0;
 372 #if defined(__HAIKU__)
 373   const uint32_t maxnodes = 1024;
 374 
 375   dps8_topo_used = true;
 376   cpu_topology_node_info* topology = malloc(sizeof(cpu_topology_node_info) * maxnodes);
 377   if (NULL == topology)
 378     return 0; /* Memory allocation failure */
 379 
 380   uint32_t nodecount = maxnodes;
 381   status_t status = get_cpu_topology_info(topology, &nodecount);
 382 
 383   if (B_OK == status) {
 384     for (uint32_t i = 0; i < nodecount; i++) {
 385       if (B_TOPOLOGY_CORE == topology[i].type)
 386         num_cores++;
 387     }
 388     return num_cores;
 389   } else {
 390     return 0;
 391   }
 392 
 393   FREE(topology);
 394 #elif defined(__MINGW32__) || defined(__MINGW64__) || defined(CROSS_MINGW32) || defined(CROSS_MINGW64)
 395   dps8_topo_used = true;
 396   return 0;
 397 #elif defined(__NetBSD__)
 398   dps8_topo_used = true;
 399   return 0;
 400 #else
 401   char *lib_path = find_libhwloc_path();
 402 
 403   if (NULL == lib_path)
 404     return 0; /* Could not find usable libhwloc */
 405 
 406   void *handle = dlopen(lib_path, RTLD_LAZY);
 407   FREE(lib_path);
 408   if (NULL == handle) {
 409 # if defined(TOPO_TESTING)
 410     char *dl_error = dlerror();
 411     if (dl_error && *dl_error)
 412       (void)fprintf(stderr, "ERROR: dlopen: %s\r\n", dl_error);
 413 # endif
 414     return 0;
 415   }
 416   (void)dlerror();
 417 
 418   dps8_topo_used = true;
 419 
 420   fp_hwloc_topology_init hwloc_topology_init =
 421     (fp_hwloc_topology_init)dlsym(handle, "hwloc_topology_init");
 422   if (NULL == hwloc_topology_init) {
 423 # if defined(TOPO_TESTING)
 424     char *dl_error = dlerror();
 425     if (dl_error && *dl_error)
 426       (void)fprintf(stderr, "ERROR: no hwloc_topology_init: %s\r\n", dl_error);
 427 # endif
 428     (void)dlclose(handle);
 429     return 0;
 430   }
 431 
 432   fp_hwloc_topology_load hwloc_topology_load =
 433     (fp_hwloc_topology_load)dlsym(handle, "hwloc_topology_load");
 434   if (NULL == hwloc_topology_load) {
 435 # if defined(TOPO_TESTING)
 436     char *dl_error = dlerror();
 437     if (dl_error && *dl_error)
 438       (void)fprintf(stderr, "ERROR: no hwloc_topology_load: %s\r\n", dl_error);
 439 # endif
 440     (void)dlclose(handle);
 441     return 0;
 442   }
 443 
 444   fp_hwloc_topology_destroy hwloc_topology_destroy =
 445     (fp_hwloc_topology_destroy)dlsym(handle, "hwloc_topology_destroy");
 446   if (NULL == hwloc_topology_destroy) {
 447 # if defined(TOPO_TESTING)
 448     char *dl_error = dlerror();
 449     if (dl_error && *dl_error)
 450       (void)fprintf(stderr, "ERROR: no hwloc_topology_destroy: %s\r\n", dl_error);
 451 # endif
 452     (void)dlclose(handle);
 453     return 0;
 454   }
 455 
 456   fp_hwloc_get_type_depth hwloc_get_type_depth =
 457     (fp_hwloc_get_type_depth)dlsym(handle, "hwloc_get_type_depth");
 458   if (NULL == hwloc_get_type_depth) {
 459 # if defined(TOPO_TESTING)
 460     char *dl_error = dlerror();
 461     if (dl_error && *dl_error)
 462       (void)fprintf(stderr, "ERROR: no hwloc_get_type_depth: %s\r\n", dl_error);
 463 # endif
 464     (void)dlclose(handle);
 465     return 0;
 466   }
 467 
 468   fp_hwloc_get_nbobjs_by_depth hwloc_get_nbobjs_by_depth =
 469     (fp_hwloc_get_nbobjs_by_depth)dlsym(handle, "hwloc_get_nbobjs_by_depth");
 470   if (NULL == hwloc_get_nbobjs_by_depth) {
 471 # if defined(TOPO_TESTING)
 472     char *dl_error = dlerror();
 473     if (dl_error && *dl_error)
 474       (void)fprintf(stderr, "ERROR: no hwloc_get_nbobjs_by_depth: %s\r\n", dl_error);
 475 # endif
 476     (void)dlclose(handle);
 477     return 0;
 478   }
 479 
 480   dl_hwloc_topology_t topology;
 481   if (0 != hwloc_topology_init(&topology)) {
 482 # if defined(TOPO_TESTING)
 483     (void)fprintf(stderr, "ERROR: hwloc_topology_init failure.\r\n");
 484 # endif
 485     (void)dlclose(handle);
 486     return 0;
 487   }
 488 
 489   if (0 != hwloc_topology_load(topology)) {
 490 # if defined(TOPO_TESTING)
 491     (void)fprintf(stderr, "ERROR: hwloc_topology_load failure.\r\n");
 492     hwloc_topology_destroy(topology);
 493 # endif
 494     (void)dlclose(handle);
 495     return 0;
 496   }
 497 
 498   int32_t core_depth = hwloc_get_type_depth(topology, HWLOC_OBJ_CORE);
 499   if (-1 == core_depth) {
 500 # if defined(TOPO_TESTING)
 501     (void)fprintf(stderr, "ERROR: hwloc_get_type_depth failure.\r\n");
 502     hwloc_topology_destroy(topology);
 503 # endif
 504     (void)dlclose(handle);
 505     return 0;
 506   }
 507 
 508   num_cores = hwloc_get_nbobjs_by_depth(topology, core_depth);
 509   hwloc_topology_destroy(topology);
 510   (void)dlclose(handle);
 511 #endif
 512 #if defined(_AIX)
 513   if (num_cores < 2)
 514     return 0;
 515 #endif
 516   return num_cores;
 517 }

/* [previous][next][first][last][top][bottom][index][help] */