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

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