<pre style='margin:0'>
Christopher Nielsen (mascguy) pushed a commit to branch master
in repository macports-legacy-support.

</pre>
<p><a href="https://github.com/macports/macports-legacy-support/commit/24117c0e5b06bf660c95e257c61bcb1bc20328e0">https://github.com/macports/macports-legacy-support/commit/24117c0e5b06bf660c95e257c61bcb1bc20328e0</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit 24117c0e5b06bf660c95e257c61bcb1bc20328e0
</span>Author: Fred Wright <fw@fwright.net>
AuthorDate: Fri Apr 11 14:04:56 2025 -0700

<span style='display:block; white-space:pre;color:#404040;'>    test_clocks: Add mach_time scaling check.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    This adds reporting and checking of mach_time scaling.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    TESTED:
</span><span style='display:block; white-space:pre;color:#404040;'>    Reports plausibly, and detects both forms of bad scaling on PPC.
</span>---
 test/test_clocks.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 83 insertions(+), 4 deletions(-)

<span style='display:block; white-space:pre;color:#808080;'>diff --git a/test/test_clocks.c b/test/test_clocks.c
</span><span style='display:block; white-space:pre;color:#808080;'>index 58cd4af..9010dfe 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/test/test_clocks.c
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/test/test_clocks.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -67,10 +67,13 @@ typedef int64_t sns_time_t;
</span> #define BILLION64  1000000000ULL
 
 /* Parameters for collection sequence */
<span style='display:block; white-space:pre;background:#ffe0e0;'>-#define MAX_STEP_NS 700000    /* Maximum delta not considered a step */
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-#define MAX_STEP_TRIES 50     /* Maximum tries to avoid a step */
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-#define STD_SLEEP_US 1000     /* Standard sleep before collecting */
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-#define MAX_SLEEP_US 100000   /* Maximum sleep when retrying */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define MAX_STEP_NS     700000  /* Maximum delta not considered a step */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define MAX_STEP_TRIES      50  /* Maximum tries to avoid a step */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define STD_SLEEP_US      1000  /* Standard sleep before collecting */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define MAX_SLEEP_US    100000  /* Maximum sleep when retrying */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Mach_time scaling tolerance */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define MACH_SCALE_TOLER 10E-6
</span> 
 #ifndef TEST_TEMP
 #define TEST_TEMP "/dev/null"
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -1297,6 +1300,81 @@ report_all_clock_compares(int dump, int verbose, int quiet)
</span>   return ret;
 }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Check mach_time scaling
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * This checks the computation that scales mach_time to nanoseconds.  It
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * only checks the accuracy of applying the scale factor reported by the OS;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * it does *not* check the scale factor itself.  See the comment titled
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * "Mach timebase scaling" in src/time.c for details of right and wrong ways
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * to do this.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * The basic approach is to grab a "sandwich" of CLOCK_UPTIME_RAW (ns)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * between samples of mach_absolute_time(), and check the ratio of values.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * With any uptime of more than about a second, the inaccuracy of the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * "divide first" approach on PowerPC is reliably observable, and with any
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * uptime of more than about 7.4 minutes, the overflow and wraparound of the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * "multiply first" approach on PowerPC can be observed.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * If and when the overflow occurs from using the "multiply first" approach
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * on PowrerPC, the computed nanosecond value wraps around, after which its
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * value is never more than half the correct value.  Thus, it can be detected
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * on the basis of a very large negative error in the scale (at least 0.5).
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+check_mach_scaling(int verbose)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  int ret = 0, idx, best = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  mach_time_t mt[4];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  ns_time_t nst[3];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  double nslo, nshi, nsmean, nstoler, nscur, nserr, err_mult;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  const char *err_units, *msg;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (verbose) printf("  Checking mach_time scaling\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  /* Get tightest "sandwich" out of 4/3 */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  mt[0] = mach_absolute_time();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  nst[0] = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  mt[1] = mach_absolute_time();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  nst[1] = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  mt[2] = mach_absolute_time();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  nst[2] = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  mt[3] = mach_absolute_time();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  for(idx = 1; idx < 3; ++idx) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (mt[idx+1] - mt[idx] < mt[best+1] - mt[best]) best = idx;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  nslo = mt[best] * mach2nanos; nshi = mt[best+1] * mach2nanos;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  nscur = nst[best];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  nsmean = (nslo + nshi) / 2.0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  nstoler = (nshi - nslo) / 2.0 / nsmean;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  nserr = (nscur - nsmean) / nsmean;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (nserr < -0.499) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    printf("    *** Mach scaling wraparound, mach = %llu->%llu, ns = %llu\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           ULL mt[best], ULL mt[best+1], ULL nst[best]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (fabs(nserr) >= 1E-6) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    err_mult = 1E6; err_units = "ppm";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    err_mult = 1E9; err_units = "ppb";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (fabs(nserr) >= MACH_SCALE_TOLER) ret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (verbose || ret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    msg = ret ? "*** Excessive mach scaling error"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              : "Measured mach scaling error";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    printf("    %s = %+.6f %s +/- %.6f %s\n", msg,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           nserr * err_mult, err_units, nstoler * err_mult, err_units);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> /* Main function */
 int
 main(int argc, char *argv[])
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -1329,6 +1407,7 @@ main(int argc, char *argv[])
</span>   while (!err) {
     err |= report_all_clocks(dump, verbose, quiet);
     err |= report_all_clock_compares(dump, verbose, quiet);
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    err |= check_mach_scaling(verbose && !quiet);
</span>     if (!continuous) break;
   }
 
</pre><pre style='margin:0'>

</pre>