root/src/simh/sim_hints.c

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

DEFINITIONS

This source file includes following definitions.
  1. sim_hrline
  2. is_jailed
  3. is_static_linked_selfelf
  4. is_static_linked_selfmap
  5. is_static_linked
  6. is_ntp_sync
  7. has_linux_capability
  8. check_cpu_frequencies
  9. is_raspberry_pi
  10. check_pi_issues
  11. check_scaling_governors
  12. atm_cwords
  13. processIsTranslated
  14. show_hints

   1 /*
   2  * sim_hints.c: configuration hints
   3  *
   4  * vim: filetype=c:tabstop=2:ai:expandtab
   5  * SPDX-License-Identifier: ICU
   6  * scspell-id: 1784dba8-00a2-11f0-b624-80ee73e9b8e7
   7  *
   8  * ---------------------------------------------------------------------------
   9  *
  10  * Copyright (c) 2025 Jeffrey H. Johnson
  11  * Copyright (c) 2025 The DPS8M Development Team
  12  *
  13  * This software is made available under the terms of the ICU License.
  14  * See the LICENSE.md file at the top-level directory of this distribution.
  15  *
  16  * ---------------------------------------------------------------------------
  17  */
  18 
  19 #include "sim_defs.h"
  20 
  21 #include <ctype.h>
  22 #include <dirent.h>
  23 #include <errno.h>
  24 #include <fcntl.h>
  25 #include <limits.h>
  26 #include <stdatomic.h>
  27 #include <stdbool.h>
  28 #include <stdint.h>
  29 #include <stdio.h>
  30 #include <stdlib.h>
  31 #include <string.h>
  32 #include <sys/stat.h>
  33 #if !defined(_WIN32)
  34 # include <sys/resource.h>
  35 #endif
  36 #include <time.h>
  37 #include <sys/time.h>
  38 #include <sys/types.h>
  39 #include <unistd.h>
  40 #if defined(__APPLE__)
  41 # include <sys/sysctl.h>
  42 #endif
  43 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__sun) || \
  44     defined(__illumos__) || defined(__linux__) && !defined(__ANDROID__)
  45 # include <sys/timex.h> // XXX: Check DragonFly
  46 #endif
  47 #if defined(__FreeBSD__) || defined(__DragonFly__)
  48 # include <sys/sysctl.h>
  49 #endif
  50 
  51 #include "linehistory.h"
  52 
  53 #include "../dps8/dps8.h"
  54 #include "../dps8/dps8_cpu.h"
  55 #include "../dps8/dps8_topo.h"
  56 
  57 #include "../simh/sim_os_mem.h"
  58 #include "../simh/scp.h"
  59 
  60 /*
  61  * We may run in early startup (before logging has been initialized),
  62  * so only use non-logging libsir calls from functions in this file.
  63  */
  64 
  65 #include "../dps8/dps8_sir.h"
  66 
  67 #if !defined(HAS_INCLUDE)
  68 # if defined __has_include
  69 #  define HAS_INCLUDE(inc) __has_include(inc)
  70 # else
  71 #  define HAS_INCLUDE(inc) 0
  72 # endif
  73 #endif
  74 
  75 #if defined(__linux__)
  76 # if HAS_INCLUDE(<linux/capability.h>)
  77 #  include <linux/capability.h>
  78 # endif
  79 # if !defined(CAP_SYS_NICE)
  80 #  define CAP_SYS_NICE 23
  81 # endif
  82 # if !defined(CAP_IPC_LOCK)
  83 #  define CAP_IPC_LOCK 14
  84 # endif
  85 #endif
  86 
  87 #if !defined(__APPLE__)
  88 # if HAS_INCLUDE(<elf.h>)
  89 #  include <elf.h>
  90 # endif
  91 #endif
  92 
  93 #undef USE_ELF_H
  94 #if defined(EI_MAG0) && defined(EI_MAG1) && defined(EI_MAG2) && defined(EI_MAG3) && \
  95     defined(ELFMAG0) && defined(ELFMAG1) && defined(ELFMAG2) && defined(ELFMAG3) && \
  96     defined(PT_DYNAMIC) && !defined(_WIN32)
  97 # if defined(USHRT_MAX)
  98 #  define MAX_HEADERS USHRT_MAX
  99 # else
 100 #  define MAX_HEADERS 65535
 101 # endif
 102 # define USE_ELF_H
 103 #endif
 104 
 105 #if !defined(_WIN32)
 106 # include <sys/mman.h>
 107 #endif
 108 
 109 #undef PROC_SELF
 110 #if defined(__linux__) || defined(__serenity__)
 111 # define PROC_SELF "/proc/self/exe"
 112 #elif defined(__NetBSD__)
 113 # define PROC_SELF "/proc/curproc/exe"
 114 #elif defined(__FreeBSD__) || defined(__DragonFly__)
 115 # define PROC_SELF "/proc/curproc/file"
 116 #elif defined(__sun) || defined(__illumos__)
 117 # define PROC_SELF "/proc/self/path/a.out"
 118 #endif
 119 
 120 #if defined(__managarm__)
 121 # if !defined(FORCE_STATIC)
 122 #  define FORCE_STATIC
 123 # endif
 124 #endif
 125 
 126 #if !defined(CHAR_BIT)
 127 # define CHAR_BIT 8
 128 #endif
 129 
 130 unsigned int hint_count = 0;
 131 
 132 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 133 
 134 static inline void
 135 sim_hrline(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 136 {
 137   sim_printf("\r\n------------------------------------------------------------------------------\r\n");
 138 }
 139 
 140 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 141 
 142 static int
 143 is_jailed(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 144 {
 145   int jailed = 0;
 146   size_t len = sizeof(jailed);
 147 
 148 #if defined(__FreeBSD__) || defined(__DragonFly__)
 149   if (-1 == sysctlbyname("security.jail.jailed", &jailed, &len, NULL, 0))
 150     return false;
 151 #endif
 152 
 153   (void)len;
 154 
 155   if (jailed) //-V547
 156     return true;
 157 
 158   return false;
 159 }
 160 
 161 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 162 
 163 #if !defined(__MINGW64__) && !defined(__MINGW32__) && !defined(CROSS_MINGW64) && !defined(CROSS_MINGW32) && !defined(FORCE_STATIC) && \
 164     !defined(__FILC__)
 165 static int
 166 is_static_linked_selfelf(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 167 {
 168 # if !defined(USE_ELF_H)
 169   return -1;
 170 # elif defined(__APPLE__)
 171   return -1;
 172 # else
 173   char *self_exe;
 174 
 175 #  ifdef PROC_SELF
 176   self_exe = PROC_SELF;
 177 #  else
 178   self_exe = sim_appfilename;
 179 #  endif
 180 
 181   int fd = open(self_exe, O_RDONLY);
 182 
 183   if (fd < 0) {
 184     self_exe = sim_appfilename;
 185     fd = open(self_exe, O_RDONLY);
 186     if (fd < 0)
 187       return -1;
 188   }
 189 
 190   struct stat st;
 191   if (fstat(fd, &st) < 0) {
 192     (void)close(fd);
 193     return -1;
 194   }
 195 
 196   void *map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
 197   if (MAP_FAILED == map) {
 198     (void)close(fd);
 199     return -1;
 200   }
 201 
 202   Elf64_Ehdr *ehdr = (Elf64_Ehdr *)map;
 203 
 204   if (ELFMAG0 != ehdr->e_ident[EI_MAG0] ||
 205       ELFMAG1 != ehdr->e_ident[EI_MAG1] ||
 206       ELFMAG2 != ehdr->e_ident[EI_MAG2] ||
 207       ELFMAG3 != ehdr->e_ident[EI_MAG3]) {
 208     (void)munmap(map, st.st_size);
 209     (void)close(fd);
 210     return -1;
 211   }
 212 
 213   Elf64_Phdr *phdr;
 214   void *phdr_address = (void *)((char *)map + ehdr->e_phoff);
 215   (void)memcpy(&phdr, &phdr_address, sizeof(phdr));
 216 
 217   if (0 == ehdr->e_phnum || ehdr->e_phnum > MAX_HEADERS) { //-V560
 218     (void)munmap(map, st.st_size);
 219     (void)close(fd);
 220     return -1;
 221   }
 222 
 223   for (unsigned int i = 0; i < ehdr->e_phnum; i++)
 224     if (PT_DYNAMIC == phdr[i].p_type) {
 225       (void)munmap(map, st.st_size);
 226       (void)close(fd);
 227       return 0;
 228     }
 229 
 230   (void)munmap(map, st.st_size);
 231   (void)close(fd);
 232   return 1;
 233 # endif
 234 }
 235 #endif
 236 
 237 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 238 
 239 #if !defined(__MINGW64__) && !defined(__MINGW32__) && !defined(CROSS_MINGW64) && !defined(CROSS_MINGW32) && !defined(FORCE_STATIC) && \
 240     !defined(__FILC__)
 241 static int
 242 is_static_linked_selfmap (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 243 {
 244   FILE *maps = fopen ("/proc/self/maps", "r");
 245 
 246   if (!maps)
 247     return -1;
 248 
 249   char line[256];
 250   while (fgets (line, sizeof (line), maps))
 251    if (strstr(line, "/ld-") ||
 252        strstr(line, "/system/bin/linker") ||
 253        strstr(line, "/lib/ld")) {
 254       (void)fclose (maps);
 255       return 0;
 256     }
 257 
 258   (void)fclose (maps);
 259   return 1;
 260 }
 261 #endif
 262 
 263 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 264 
 265 #if !defined(__MINGW64__) && !defined(__MINGW32__) && !defined(CROSS_MINGW64) && !defined(CROSS_MINGW32) && !defined(FORCE_STATIC) && \
 266     !defined(__FILC__)
 267 static int
 268 is_static_linked (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 269 {
 270   int map = is_static_linked_selfmap ();
 271   int elf = is_static_linked_selfelf ();
 272 
 273 # if defined(STATIC_TESTING)
 274   (void)fprintf(stderr, "is_static_linked_selfmap=%d\r\n", map);
 275   (void)fprintf(stderr, "is_static_linked_selfelf=%d\r\n", elf);
 276 # endif
 277 
 278   if (-1 == map && -1 == elf)
 279     return -1;
 280 
 281   if (-1 == map)
 282     return elf;
 283 
 284   if (-1 == elf)
 285     return map;
 286 
 287   if (map != elf)
 288     return -1;
 289 
 290   return map;
 291 }
 292 #endif
 293 
 294 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 295 
 296 static int
 297 is_ntp_sync (void) // -1 == Failed to check     0 == Not synchronized
     /* [previous][next][first][last][top][bottom][index][help] */
 298 {                  //  1 == Maybe synchronized  2 == Synchronized OK
 299 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__sun) || \
 300     defined(__illumos__) || defined(__linux__) && !defined(__ANDROID__) && \
 301     !defined(__FILC__)
 302   struct timex tx = { 0 };
 303 # if defined(__FreeBSD__) || defined(__NetBSD__) || \
 304      defined(__sun) || defined(__illumos__)
 305   int result = ntp_adjtime (&tx); // XXX: DragonFly?
 306 # else
 307   int result = adjtimex (&tx);
 308 # endif
 309   if (-1 == result)
 310     return -1;
 311 # if defined(__sun) || defined(__illumos__)
 312   if (tx.status & STA_UNSYNC)
 313 # else
 314   if (TIME_OK != result)
 315 # endif
 316     if (1000000 >= tx.maxerror)
 317       return 1;
 318     else
 319       return 0;
 320   else
 321     return 2;
 322 #else
 323   return -1;
 324 #endif
 325 }
 326 
 327 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 328 
 329 #if defined(__linux__) && !defined(__ANDROID__)
 330 static int
 331 has_linux_capability(const pid_t pid, const int capability)
     /* [previous][next][first][last][top][bottom][index][help] */
 332 {
 333   char filename[SIR_MAXPATH];
 334   snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
 335 
 336   FILE *file = fopen(filename, "r");
 337   if (!file)
 338     return -1;
 339 
 340   char line[1024];
 341   uint64_t cap_eff = 0;
 342 
 343   while (fgets(line, sizeof(line), file))
 344     if (1 == sscanf(line, "CapEff: %llx", (long long unsigned int*)&cap_eff))
 345       break;
 346 
 347   fclose(file);
 348 
 349   return 0 != (cap_eff & (1ULL << capability));
 350 }
 351 #endif
 352 
 353 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 354 
 355 static int
 356 check_cpu_frequencies (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 357 {
 358 #if !defined(__linux__)
 359   return 0;
 360 #else
 361   struct dirent *entry;
 362   char path[SIR_MAXPATH];
 363   FILE *file;
 364   int min_freq = 0, max_freq = 0;
 365   int mismatch = 0;
 366 
 367   DIR *dir = opendir ("/sys/devices/system/cpu");
 368 
 369   if (!dir)
 370     return 0;
 371 
 372   while ((entry = readdir (dir)) != NULL) {
 373     if (DT_DIR == entry->d_type && 0 == strncmp (entry->d_name, "cpu", 3)
 374         && isdigit (entry->d_name[3])) {
 375       snprintf (path, sizeof (path), "/sys/devices/system/cpu/%s/cpufreq/cpuinfo_min_freq",
 376                 entry->d_name);
 377       file = fopen (path, "r");
 378       if (file) {
 379         if (1 != fscanf (file, "%d", &min_freq)) {
 380           fclose (file);
 381           min_freq = -1;
 382         } else {
 383           fclose (file);
 384         }
 385       }
 386       snprintf (path, sizeof (path), "/sys/devices/system/cpu/%s/cpufreq/cpuinfo_max_freq",
 387                 entry->d_name);
 388       file = fopen (path, "r");
 389       if (file) {
 390         if (1 != fscanf (file, "%d", &max_freq)) {
 391           fclose (file);
 392           max_freq = -1;
 393         } else {
 394           fclose (file);
 395         }
 396       }
 397       if (-1 != min_freq && -1 != max_freq && min_freq != max_freq) {
 398         mismatch = 1;
 399       }
 400     }
 401   }
 402   closedir (dir);
 403 
 404   if (mismatch) {
 405     return 1;
 406   } else {
 407     return 0;
 408   }
 409 #endif
 410 }
 411 
 412 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 413 
 414 #define RPI_TEXT "Raspberry Pi"
 415 
 416 static int
 417 is_raspberry_pi (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 418 {
 419   FILE *file;
 420   char line[1024];
 421   int is_pi = 0;
 422 
 423   file = fopen("/proc/device-tree/model", "r");
 424   if (file) {
 425     if (fgets(line, sizeof(line), file) && strstr(line, RPI_TEXT))
 426       is_pi = 1;
 427     fclose(file);
 428     if (is_pi)
 429       return 1;
 430   }
 431 
 432   file = fopen("/proc/cpuinfo", "r");
 433   if (!file)
 434     return 0;
 435 
 436   while (fgets(line, sizeof(line), file)) {
 437     if (strncmp(line, "Model", 5) == 0 && strstr(line, RPI_TEXT)) {
 438       is_pi = 1;
 439       break;
 440     }
 441   }
 442   fclose(file);
 443 
 444   return is_pi;
 445 }
 446 
 447 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 448 
 449 static uint32_t
 450 check_pi_issues (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 451 {
 452   uint32_t a_issues = 0;
 453 
 454   FILE *fp = popen("vcgencmd get_throttled 2> /dev/null", "r");
 455   if (!fp)
 456     return a_issues;
 457 
 458   char buffer[1024];
 459   if (NULL == fgets(buffer, sizeof(buffer), fp)) {
 460     pclose(fp);
 461     return a_issues;
 462   }
 463   pclose(fp);
 464 
 465   char *p = strstr(buffer, "0x");
 466   if (!p)
 467     return a_issues;
 468 
 469   uint32_t throttle = (uint32_t)strtoul(p, NULL, 16);
 470   uint32_t c_issues = throttle & 0xF;
 471   uint32_t p_issues = (throttle >> 16) & 0xF;
 472   a_issues = c_issues | p_issues;
 473 
 474   return a_issues;
 475 }
 476 
 477 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 478 
 479 static uint32_t
 480 check_scaling_governors (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 481 {
 482 #if !defined(__linux__)
 483   return 0;
 484 #else
 485   struct dirent *entry;
 486   char path[SIR_MAXPATH];
 487   FILE *file;
 488   char governor[256];
 489   static uint32_t bad_govs;
 490 
 491   DIR *dir = opendir ("/sys/devices/system/cpu");
 492 
 493   if (!dir)
 494     return 0;
 495 
 496   bad_govs = 0;
 497   while ((entry = readdir (dir)) != NULL) {
 498     if (DT_DIR == entry->d_type && 0 == strncmp (entry->d_name, "cpu", 3)
 499         && isdigit (entry->d_name[3])) {
 500       snprintf (path, sizeof (path), "/sys/devices/system/cpu/%s/cpufreq/scaling_governor",
 501                 entry->d_name);
 502       file = fopen (path, "r");
 503       if (file) {
 504         if (fgets (governor, sizeof (governor), file)) {
 505           governor[strcspn (governor, "\n")] = '\0'; //-NLOK
 506           if ( 0 == strcmp (governor, "powersave" )
 507             || 0 == strcmp (governor, "conservative") ) {
 508             bad_govs++;
 509           }
 510         }
 511         fclose (file);
 512       }
 513     }
 514   }
 515   closedir (dir);
 516 
 517   return bad_govs;
 518 #endif
 519 }
 520 
 521 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 522 
 523 const char *
 524 atm_cwords (int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 525 {
 526   static const char *words[] = { "Zero", "One", "Two", "Three", "Four", "Five", "Six" };
 527   static char buffer[20];
 528 
 529   if (count >= 0 && count <= 6) {
 530     return words[count];
 531   } else {
 532     snprintf (buffer, sizeof (buffer), "%d", count);
 533     return buffer;
 534   }
 535 }
 536 
 537 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 538 
 539 #if defined(__APPLE__)
 540 static int
 541 processIsTranslated (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 542 {
 543   int ret = 0;
 544   size_t size = sizeof(ret);
 545   if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
 546     if (errno == ENOENT)
 547       return 0;
 548     return -1;
 549   }
 550   return ret;
 551 }
 552 #endif /* if defined(_APPLE_) */
 553 
 554 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 555 
 556 t_stat
 557 show_hints (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 558 {
 559   /* NOTE: We override the use of flag to enable early startup mode */
 560 
 561   hint_count = 0;
 562 
 563 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 564 /* HINT: Check if time_t is less than 64-bit and recommend mitigations. */
 565 
 566   if (64 > (sizeof (time_t) * CHAR_BIT)) {
 567     if (!flag) {
 568       sim_hrline ();
 569       sim_printf ("\r\n* Hint #%u - LESS THAN 64-BIT TIME REPRESENTATION DETECTED\r\n", ++hint_count);
 570       sim_printf ("\r\n");
 571       sim_printf ("  The simulator has detected timekeeping using a %ld-bit representation.\r\n",
 572                   (long)(sizeof (time_t) * CHAR_BIT));
 573     } else {
 574       ++hint_count;
 575     }
 576   }
 577 
 578 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 579 /* HINT: Check if running as root and recommend not doing that. */
 580 
 581 #if !defined(__HAIKU__) && !defined(_WIN32)
 582   if (0 == geteuid ()) {
 583     if (!flag) {
 584       sim_hrline ();
 585       sim_printf ("\r\n* Hint #%u - SIMULATOR RUNNING AS ROOT OR SUPERUSER DETECTED\r\n", ++hint_count);
 586       sim_printf ("\r\n");
 587       sim_printf ("  You are running the simulator as `root` (or equivalent superuser).\r\n");
 588       sim_printf ("\r\n");
 589       sim_printf ("  We highly recommend running the simulator as a non-privileged user.\r\n");
 590     } else {
 591       ++hint_count;
 592     }
 593   }
 594 #endif
 595 
 596 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 597 /* HINT: Check for TESTING build and warn about it. */
 598 
 599 #if defined(TESTING)
 600   if (!flag) {
 601     sim_hrline ();
 602     sim_printf ("\r\n* Hint #%u - TESTING BUILD DETECTED\r\n", ++hint_count);
 603     sim_printf ("\r\n");
 604     sim_printf ("  You are running a TESTING build.\r\n");
 605     sim_printf ("\r\n");
 606     sim_printf ("  TESTING builds intended strictly for development purposes.\r\n");
 607     sim_printf ("\r\n");
 608     sim_printf ("  Reliability, stability, and performance will be adversely impacted.\r\n");
 609   } else {
 610     ++hint_count;
 611   }
 612 #endif
 613 
 614 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 615 /* HINT: Check if we are running with unsupported or experimental build options enabled. */
 616 
 617 #if defined(WITH_ABSI_DEV) || defined(WITH_MGP_DEV)
 618   if (!flag) {
 619     sim_hrline ();
 620     sim_printf ("\r\n* Hint #%u - UNSUPPORTED OR EXPERIMENTAL BUILD OPTIONS DETECTED\r\n", ++hint_count);
 621     sim_printf ("\r\n");
 622     sim_printf ("  You are running a build with unsupported or experimental options enabled:\r\n");
 623     sim_printf ("\r\n");
 624 # if defined(WITH_ABSI_DEV)
 625     sim_printf ("  * WITH_ABSI_DEV is enabled.\r\n");
 626 # endif
 627 # if defined(WITH_MGP_DEV)
 628     sim_printf ("  * WITH_MGP_DEV is enabled.\r\n");
 629 # endif
 630     sim_printf ("\r\n");
 631     sim_printf ("  Reliability, stability, and performance will be adversely impacted.\r\n");
 632   } else {
 633     ++hint_count;
 634   }
 635 #endif
 636 
 637 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 638 /* HINT: Check if we are running on Cygwin, and recommend using a native build instead. */
 639 
 640 #if defined(__CYGWIN__)
 641   if (!flag) {
 642     sim_hrline ();
 643     sim_printf ("\r\n* Hint #%u - CYGWIN WINDOWS BUILD DETECTED\r\n", ++hint_count);
 644     sim_printf ("\r\n");
 645     sim_printf ("  You are using a Cygwin Windows build.\r\n");
 646     sim_printf ("\r\n");
 647     sim_printf ("  The simulator supports native Windows builds (using the MinGW toolchain).\r\n");
 648     sim_printf ("  Unless you have a specific reason for using a Cygwin build, we recommend\r\n");
 649     sim_printf ("  using a native build instead, which has better performance for most users.\r\n");
 650   } else {
 651     ++hint_count;
 652   }
 653 #endif
 654 
 655 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 656 /* HINT: Check that we are not using a power-saving CPU governor configuration. */
 657 
 658   if (check_scaling_governors ()) {
 659     if (!flag) {
 660       unsigned long long reduced = (unsigned long long)check_scaling_governors();
 661       unsigned long long total = (unsigned long long)nprocs;
 662       sim_hrline ();
 663       sim_printf ("\r\n* Hint #%u - POWER-SAVING CPU GOVERNOR CONFIGURATION DETECTED\r\n", ++hint_count);
 664       sim_printf ("\r\n");
 665       if (total > reduced)
 666         sim_printf("  You have %llu (out of %llu) processors using a power-saving governor.\r\n",
 667                    reduced, total);
 668       else
 669         sim_printf("  You have %llu logical processors configured using a power-saving governor.\r\n",
 670                    reduced);
 671     } else {
 672       ++hint_count;
 673     }
 674   }
 675 
 676 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 677 /* HINT: Check if we are running a threaded simulator on a uniprocessor system. */
 678 
 679 #if defined(LOCKLESS)
 680   if (nprocs < 2 && ncores < 2) {
 681     if (!flag) {
 682       sim_hrline ();
 683       sim_printf ("\r\n* Hint #%u - UNIPROCESSOR HOST SYSTEM DETECTED\r\n", ++hint_count);
 684       sim_printf ("\r\n");
 685       sim_printf ("  You seem to be running the simulator on a uniprocessor host system.\r\n");
 686       sim_printf ("\r\n");
 687       sim_printf ("  The simulator supports a single-threaded build option (`NO_LOCKLESS`)\r\n");
 688       sim_printf ("  which may perform better on uniprocessor systems such as this one.\r\n");
 689     } else {
 690       ++hint_count;
 691     }
 692   }
 693 #endif
 694 
 695 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 696 /* HINT: Check for deficient TERM values. */
 697 
 698 #if defined(HAVE_LINEHISTORY) && !defined(_WIN32)
 699   const char *term = getenv ("TERM");
 700   if (NULL == term || 0 == strcmp (term, "dumb") || 0 == strcmp (term, "") || 0 == strcmp (term, "cons25")) {
 701     if (!flag) {
 702       sim_hrline ();
 703       sim_printf ("\r\n* Hint #%u - TERM ENVIRONMENT VARIABLE SET TO UNSUPPORTED VALUE\r\n", ++hint_count);
 704       sim_printf ("\r\n");
 705       sim_printf ("  The TERM environment variable is unset (or is set to an unsupported value).\r\n");
 706       sim_printf ("\r\n");
 707       sim_printf ("  This disables the line-editing and history recall functionality of the\r\n");
 708       sim_printf ("  simulator shell.  This is usually an unintentional misconfiguration.\r\n");
 709     } else {
 710       ++hint_count;
 711     }
 712   }
 713 #endif
 714 
 715 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 716 /* HINT: Check that core dumps have not been limited or disabled. */
 717 
 718 #if !defined(_WIN32) && !defined(__HAIKU__)
 719   struct rlimit core_limit;
 720   if (getrlimit(RLIMIT_CORE, &core_limit) == 0) {
 721     if (core_limit.rlim_cur == 0) {
 722       if (!flag) {
 723         sim_hrline();
 724         sim_printf("\r\n* Hint #%u - CORE DUMPS ARE DISABLED\r\n", ++hint_count);
 725         sim_printf("\r\n");
 726         sim_printf("  Core dumps are disabled.  If the simulator crashes, it will not be able\r\n");
 727         sim_printf("  to produce a core file, which can help determine the cause of the crash.\r\n");
 728         sim_printf("\r\n");
 729         sim_printf("  Try setting `ulimit -c unlimited` (or sometimes `ulimit -Hc unlimited`)\r\n");
 730         sim_printf("  in your shell to remove this restriction.\r\n");
 731       } else {
 732         ++hint_count;
 733       }
 734     }
 735     else if (core_limit.rlim_cur != RLIM_INFINITY) {
 736       if (!flag) {
 737         sim_hrline();
 738         sim_printf("\r\n* Hint #%u - CORE DUMPS ARE RESTRICTED\r\n", ++hint_count);
 739         sim_printf("\r\n");
 740         sim_printf("  Core dumps are restricted.  If the simulator crashes, it may not be able\r\n");
 741         sim_printf("  to produce a core file, which can help determine the cause of the crash.\r\n");
 742         sim_printf("\r\n");
 743         sim_printf("  Your configuration is currently restricting core dumps to %llu KB.\r\n",
 744                    (unsigned long long)core_limit.rlim_cur / 1024);
 745         sim_printf("\r\n");
 746         sim_printf("  Try setting `ulimit -c unlimited` (or sometimes `ulimit -Hc unlimited`)\r\n");
 747         sim_printf("  in your shell to remove this restriction.\r\n");
 748       } else {
 749         ++hint_count;
 750       }
 751     }
 752   }
 753 #endif
 754 
 755 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 756 /* HINT: Check if running on a suboptimally configured Raspberry Pi board. */
 757 
 758   if (is_raspberry_pi ()) {
 759     uint32_t a_issues;
 760     if (check_cpu_frequencies()) {
 761       if (!flag) {
 762         sim_hrline ();
 763         sim_printf ("\r\n* Hint #%u - RASPBERRY PI FIXED FREQUENCY CONFIGURATION\r\n", ++hint_count);
 764         sim_printf ("\r\n");
 765         sim_printf ("  The simulator has detected it is running on a Raspberry Pi single-board\r\n");
 766         sim_printf ("  computer, which is not configured for fixed frequency operation.\r\n");
 767         sim_printf ("  Using a fixed-frequency configuration allows for more deterministic\r\n");
 768         sim_printf ("  response times and enhances performance and simulation fidelity.\r\n");
 769         sim_printf ("\r\n");
 770         sim_printf ("  Use `cpupower frequency-info` (from the `linux-cpupower` package) to view\r\n");
 771         sim_printf ("  the current configuration, or `sudo cpupower frequency-set -f` to change\r\n");
 772         sim_printf ("  it, for example, `sudo cpupower frequency-set -f 1200Mhz`.  Refer to the\r\n");
 773         sim_printf ("  documentation for your specific Raspberry Pi model for proper values.\r\n");
 774       } else {
 775         ++hint_count;
 776       }
 777     }
 778     a_issues = check_pi_issues ();
 779     if ( a_issues & (1 << 0) ||
 780 #if defined(RPI_CAPPING)
 781          a_issues & (1 << 1) ||
 782 #endif
 783          a_issues & (1 << 2) ||
 784          a_issues & (1 << 3) ) {
 785       if (!flag) {
 786         sim_hrline ();
 787         sim_printf ("\r\n* Hint #%u - RASPBERRY PI ADVERSE HARDWARE EVENTS RECORDED\r\n", ++hint_count);
 788         sim_printf ("\r\n");
 789         sim_printf ("  Your Raspberry Pi has recorded the following adverse hardware event(s):\r\n");
 790         sim_printf ("\r\n");
 791         if (a_issues & (1 << 0))
 792           sim_printf("    * Undervoltage events have occurred since boot.\r\n");
 793 #if defined(RPI_CAPPING)
 794         if (a_issues & (1 << 1))
 795           sim_printf("    * CPU frequency capping events have occurred since boot.\r\n");
 796 #endif
 797         if (a_issues & (1 << 2))
 798           sim_printf("    * Throttling events have occurred since boot.\r\n");
 799         if (a_issues & (1 << 3))
 800           sim_printf("    * Soft temperature limits have been reached or exceeded since boot.\r\n");
 801       } else {
 802         ++hint_count;
 803       }
 804     }
 805   }
 806 
 807 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 808 /* HINT: Check if topology information was available, recommend installing libhwloc if not. */
 809 
 810 #if !defined(_WIN32) && !defined(FORCE_STATIC) && !defined(__FILC__)
 811   if (1 < nprocs && false == dps8_topo_used && 1 != is_static_linked()) {
 812     if (!flag) {
 813       sim_hrline ();
 814       sim_printf ("\r\n* Hint #%u - UNABLE TO DETERMINE SYSTEM TOPOLOGY USING LIBHWLOC\r\n", ++hint_count);
 815       sim_printf ("\r\n");
 816       sim_printf ("  No overall topology information could be determined for this system.\r\n");
 817       sim_printf ("  The simulator is aware this system has %llu logical processors available,\r\n",
 818                   (unsigned long long)nprocs);
 819       sim_printf ("  but it was not able to determine the number of actual physical cores.\r\n");
 820       sim_printf ("\r\n");
 821       sim_printf ("  The simulator uses topology information to optimize performance and\r\n");
 822       sim_printf ("  to warn about potentially dangerous or suboptimal configurations.\r\n");
 823       sim_printf ("  We use libhwloc (https://www-lb.open-mpi.org/projects/hwloc/) to query\r\n");
 824       sim_printf ("  this information in an architecture and operating system agnostic way.\r\n");
 825 # if defined(__APPLE__)
 826       sim_printf ("\r\n");
 827       sim_printf ("  On macOS, you can install the libhwloc library using the Homebrew\r\n");
 828       sim_printf ("  package manager (https://brew.sh/) by running `brew install hwloc`.\r\n");
 829 # elif defined(__FreeBSD__)
 830       sim_printf ("\r\n");
 831       sim_printf ("  On FreeBSD, you can install the hwloc from packages (`pkg install hwloc`)\r\n");
 832       sim_printf ("  or build from ports (`cd /usr/ports/devel/hwloc/ && make install clean`).\r\n");
 833 # elif defined(__linux__)
 834       sim_printf ("\r\n");
 835       sim_printf ("  Linux distributions usually provide this library as the `hwloc` package.\r\n");
 836 # endif
 837       sim_printf ("\r\n");
 838       sim_printf ("  No recompilation of the simulator is necessary for it to use libhwloc.\r\n");
 839     } else {
 840       ++hint_count;
 841     }
 842   }
 843 #endif
 844 
 845 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 846 /* HINT: On Linux, check if we have real-time is allowed via capabilities. */
 847 
 848 #if defined(__linux__) && defined(CAP_SYS_NICE) && !defined(__ANDROID__)
 849   const pid_t pid = _sir_getpid();
 850   const int cap_sys_nice = CAP_SYS_NICE;
 851 
 852   if (nprocs > 1 && !has_linux_capability(pid, cap_sys_nice)) {
 853     if (!flag) {
 854       sim_hrline ();
 855       sim_printf ("\r\n* Hint #%u - LINUX REAL-TIME SCHEDULING CAPABILITY IS NOT ENABLED\r\n", ++hint_count);
 856       sim_printf ("\r\n");
 857       sim_printf ("  Linux real-time scheduling capability is not enabled.\r\n");
 858       sim_printf ("  Real-time support allows for accurate and deterministic response times\r\n");
 859       sim_printf ("  and improved thread scheduling behavior, enhancing simulation fidelity.\r\n");
 860       if (nprocs > 2) {
 861         if ((unsigned long long)ncores > 1 && (unsigned long long)ncores != (unsigned long long)nprocs) {
 862           sim_printf("  Since this system seems to have %llu logical (and %llu physical) processors,\r\n",
 863                      (unsigned long long)nprocs, (unsigned long long)ncores);
 864         } else {
 865           sim_printf("  Since this system seems to have at least %llu logical processors available,\r\n",
 866                    (unsigned long long)nprocs);
 867         }
 868         sim_printf ("  the real-time mode (requested by the `-p` argument) may be considered.\r\n");
 869       }
 870       sim_printf ("\r\n");
 871       sim_printf ("  To enable the real-time capability, run the following shell command:\r\n");
 872       sim_printf ("\r\n");
 873       sim_printf ("  sudo setcap 'cap_sys_nice,cap_ipc_lock+ep' %s\r\n", sim_appfilename);
 874     } else {
 875       ++hint_count;
 876     }
 877   }
 878 #endif
 879 
 880 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 881 /* HINT: Check if mlock failed to work and tell the user how to fix it. */
 882 
 883 #if !defined(__ANDROID__)
 884   if (true == mlock_failure) {
 885     if (!flag) {
 886       sim_hrline ();
 887       sim_printf ("\r\n* Hint #%u - UNABLE TO LOCK SIMULATOR MEMORY WITH MLOCK()\r\n", ++hint_count);
 888       sim_printf ("\r\n");
 889       sim_printf ("  The simulator attempted, but failed, to use memory locking with mlock().\r\n");
 890       sim_printf ("  Memory locking prevents the simulated memory from being swapped out to\r\n");
 891       sim_printf ("  disk.  This avoids page faults and enables more deterministic performance.\r\n");
 892       sim_printf ("  Memory locking also enhances response times for real-time mode operation.\r\n");
 893 # if defined(__linux__) && defined(CAP_IPC_LOCK)
 894       const pid_t pid = _sir_getpid();
 895       const int cap_ipc_lock = CAP_IPC_LOCK;
 896       if (!has_linux_capability(pid, cap_ipc_lock)) {
 897         sim_printf ("\r\n");
 898         sim_printf ("  You can enable real-time and memory locking by running this shell command:\r\n");
 899         sim_printf ("\r\n");
 900         sim_printf ("  sudo setcap 'cap_sys_nice,cap_ipc_lock+ep' %s\r\n", sim_appfilename);
 901       }
 902 # elif defined(__FreeBSD__)
 903       sim_printf ("\r\n");
 904       sim_printf ("  See https://man.freebsd.org/cgi/man.cgi?login.conf(5) for details.\r\n");
 905 # elif defined(__sun) || defined(__illumos__)
 906       sim_printf ("\r\n");
 907       sim_printf ("  You can allow memory locking by running the following shell commands:\r\n");
 908       sim_printf ("\r\n");
 909       sim_printf ("  sudo chown root:root %s\r\n", sim_appfilename);
 910       sim_printf ("  sudo chmod u+s %s\r\n", sim_appfilename);
 911       sim_printf ("\r\n");
 912       sim_printf ("  This is safe - we drop all unnecessary privileges immediately at start-up.\r\n");
 913 # else
 914       sim_printf("\r\n");
 915       sim_printf("  You can check `ulimit -l` in your shell to verify locking is unrestricted.\r\n");
 916 # endif
 917     } else {
 918       ++hint_count;
 919     }
 920   }
 921 #endif
 922 
 923 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 924 /* HINT: Check if atomic types are not lock-free and warn the user about it. */
 925 
 926 #if defined(LOCKLESS) && !defined(__SUNPRO_C) && !defined(__SUNPRO_CC)
 927 # define CHECK_LOCK_FREE(var, type_name, counter, names, index) \
 928    do {                                                         \
 929      if (!atomic_is_lock_free (&var)) {                         \
 930        names[index] = type_name;                                \
 931        index++;                                                 \
 932        counter++;                                               \
 933      }                                                          \
 934    } while (0)
 935 
 936   atomic_uint_fast32_t af32_t = 0;
 937   atomic_size_t asize_t       = 0;
 938   atomic_bool abool           = 0;
 939   atomic_int aint             = 0;
 940   atomic_long along           = 0;
 941   int *naptr;
 942   atomic_uintptr_t aptr;
 943 
 944   atomic_init (&aptr, (uintptr_t)&naptr);
 945   (void)naptr;
 946 
 947   const char *non_lock_free_names[6];
 948   int non_lock_free_count = 0;
 949   int index = 0;
 950 
 951   CHECK_LOCK_FREE (af32_t, "atomic_uint_fast32_t",
 952                    non_lock_free_count, non_lock_free_names, index);
 953   CHECK_LOCK_FREE (asize_t, "atomic_size_t",
 954                    non_lock_free_count, non_lock_free_names, index);
 955   CHECK_LOCK_FREE (abool, "atomic_bool",
 956                    non_lock_free_count, non_lock_free_names, index);
 957   CHECK_LOCK_FREE (aint, "atomic_int",
 958                    non_lock_free_count, non_lock_free_names, index);
 959   CHECK_LOCK_FREE (along, "atomic_long",
 960                    non_lock_free_count, non_lock_free_names, index);
 961   CHECK_LOCK_FREE (aptr, "atomic_uintptr_t",
 962                    non_lock_free_count, non_lock_free_names, index);
 963 
 964   if (non_lock_free_count > 0) {
 965     if (!flag) {
 966       const char *count_str = atm_cwords (non_lock_free_count);
 967       sim_hrline ();
 968       sim_printf ("\r\n* Hint #%u - ATOMIC TYPES DO NOT SUPPORT LOCK-FREE OPERATION\r\n", ++hint_count);
 969       sim_printf ("\r\n  %s atomic type%s %s not lock-free on this system:\r\n\r\n",
 970                   count_str, (non_lock_free_count == 1) ? "" : "s",
 971                   (non_lock_free_count == 1) ? "is" : "are");
 972       for (unsigned int i = 0; i < non_lock_free_count; i++)
 973         sim_printf ("    * %s\r\n", non_lock_free_names[i]);
 974       sim_printf ("\r\n");
 975       sim_printf ("  Atomic operations are lock-free only if the CPU provides the appropriate\r\n");
 976       sim_printf ("  instructions and your compiler knows how to take advantage of them.\r\n");
 977       sim_printf ("\r\n");
 978       sim_printf ("  You could try upgrading the compiler, but this is usually a hardware\r\n");
 979       sim_printf ("  limitation.  When the CPU does not support atomic operations, they rely\r\n");
 980       sim_printf ("  on locking, which adversely impacts the performance of threaded programs.\r\n");
 981       if (1 == nprocs) {
 982         sim_printf ("\r\n");
 983         sim_printf ("  Since it seems this system only has a single processor, you should try\r\n");
 984         sim_printf ("  building the `NO_LOCKLESS` non-threaded simulator for better performance.\r\n");
 985       }
 986     } else {
 987       ++hint_count;
 988     }
 989   }
 990 #endif
 991 
 992 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 993 /* HINT: Check for Apple Rosetta binary translation. */
 994 
 995 #if defined(__APPLE__)
 996   if (1 == processIsTranslated()) {
 997     if (!flag) {
 998       sim_hrline ();
 999       sim_printf ("\r\n* Hint #%u - APPLE ROSETTA BINARY TRANSLATION DETECTED\r\n", ++hint_count);
1000       sim_printf ("\r\n  Rosetta is an x86_64 to ARM64 (Apple Silicon) binary translation process.\r\n");
1001       sim_printf ("\r\n");
1002       sim_printf ("  This means that the simulator binary you are executing was not compiled\r\n");
1003       sim_printf ("  for the native architecture of your system.  Although fully functional\r\n");
1004       sim_printf ("  performance will be significantly reduced.  It is highly recommended to\r\n");
1005       sim_printf ("  use a native Apple Silicon (ARM64) build of the simulator.\r\n");
1006     } else {
1007       ++hint_count;
1008     }
1009   }
1010 #endif
1011 
1012 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
1013 /* HINT: Check available memory and warn the user if it seems too low. */
1014 
1015   if (sim_free_memory < 192000000 && sim_free_memory > 0) {
1016     if (!flag) {
1017       sim_hrline ();
1018       sim_printf ("\r\n* Hint #%u - LOW SYSTEM MEMORY DETECTED\r\n", ++hint_count);
1019       sim_printf ("\r\n");
1020       sim_printf ("  Currently %llu MB of memory is available (%llu MB at process start-up).\r\n",
1021                   ((long long unsigned)sim_memory_available() / 1000000),
1022                   ((long long unsigned)sim_free_memory / 1000000));
1023       sim_printf ("\r\n");
1024       sim_printf ("  We recommend a minimum of 192 MB of available physical system memory\r\n");
1025       sim_printf ("  for optimum simulator performance.  Additionally, memory locking is not\r\n");
1026       sim_printf ("  attempted when less than 192 MB of memory is available at start-up time.\r\n");
1027     } else {
1028       ++hint_count;
1029     }
1030   }
1031 
1032 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
1033 /* HINT: Check for lack of external clock synchronization (like NTP). */
1034 
1035   if (0 == is_ntp_sync()) {
1036     if (!flag) {
1037       sim_hrline ();
1038       sim_printf ("\r\n* Hint #%u - NO EXTERNAL CLOCK SYNCHRONIZATION DETECTED\r\n", ++hint_count);
1039       sim_printf ("\r\n");
1040       sim_printf ("  The system is reporting that your clock is NOT externally synchronized.\r\n");
1041       sim_printf ("  Accurate time synchronization is critical for proper simulator operation.\r\n");
1042       sim_printf ("  This can usually be resolved by ensuring that NTP (Network Time Protocol)\r\n");
1043       sim_printf ("  software (e.g. ntpd, chrony, timesyncd, etc.) is installed and configured.\r\n");
1044       if (is_jailed()) {
1045         sim_printf ("\r\n");
1046         sim_printf ("  NOTE: The simulator is running in a BSD jail.  The time synchronization\r\n");
1047         sim_printf ("  daemon must be installed and configured on the host, and not in the jail.\r\n");
1048       }
1049     } else {
1050       ++hint_count;
1051     }
1052   }
1053 
1054 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
1055 /* Hint notifications */
1056 
1057   if (hint_count) {
1058     if (!flag) {
1059       sim_hrline ();
1060       if (getenv("DPS8M_NO_HINTS") == NULL) {
1061         sim_printf ("\r\n");
1062         sim_printf ("To disable hint notifications, set the environment variable `DPS8M_NO_HINTS=1`\r\n");
1063       }
1064       sim_printf ("\r\n");
1065     } else {
1066       sim_printf("There %s %d %s available; use \"%s\" to view %s.\r\n\r\n",
1067                  (hint_count == 1) ? "is" : "are", hint_count, (hint_count == 1) ? "hint" : "hints",
1068                  (hint_count == 1) ? "SHOW HINT" : "SHOW HINTS", (hint_count == 1) ? "it" : "them");
1069     }
1070   } else {
1071     if (!flag) {
1072       sim_printf ("No hints are currently available.\r\n");
1073     }
1074   }
1075 
1076   return 0;
1077 }

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