<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/57cf29f8dfe18b50b8a6401eba4f9fe228af0e84">https://github.com/macports/macports-legacy-support/commit/57cf29f8dfe18b50b8a6401eba4f9fe228af0e84</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit 57cf29f8dfe18b50b8a6401eba4f9fe228af0e84
</span>Author: Fred Wright <fw@fwright.net>
AuthorDate: Wed Apr 23 22:35:51 2025 -0700
<span style='display:block; white-space:pre;color:#404040;'> test_clocks: Add process/thread time check.
</span><span style='display:block; white-space:pre;color:#404040;'>
</span><span style='display:block; white-space:pre;color:#404040;'> Adds checks for process and thread times, including cross checks for
</span><span style='display:block; white-space:pre;color:#404040;'> consistency.
</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;'> Passes on all platforms, with updated time.c.
</span>---
test/test_clocks.c | 384 ++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 365 insertions(+), 19 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 9010dfe..b7a31c4 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;'>@@ -27,6 +27,7 @@
</span> #include <errno.h>
#include <libgen.h>
#include <math.h>
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <pthread.h>
</span> #include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -75,6 +76,13 @@ typedef int64_t sns_time_t;
</span> /* Mach_time scaling tolerance */
#define MACH_SCALE_TOLER 10E-6
<span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Parameters (microseconds) for process/thread time test */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define THREAD_RUN_TIME 100000 /* Wall clock time for thread runs */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define PROCESS_SLEEP_TIME 250000 /* Overall sleep delay during test */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define THREAD_SLEEP_MAX_CPU 10000 /* Max CPU time for sleeping thread */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define TIME_SUM_TOLERANCE 20E-2 /* Tolerance on CPU time ratio */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define THREAD_TEST_TRIES 10 /* Number of tries for thread time test */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> #ifndef TEST_TEMP
#define TEST_TEMP "/dev/null"
#endif
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -110,6 +118,7 @@ static volatile union scratch_u {
</span> ONE_ERROR(noerrno,"Error with errno not set") \
ONE_ERROR(early,"Test clock too early") \
ONE_ERROR(late,"Test clock too late") \
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ ONE_ERROR(badptime,"Process/thread time inconsistent") \
</span>
#define ONE_ERROR(name,text) err_##name,
typedef enum errs { err_dummy, /* Avoid zero */
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -411,30 +420,19 @@ ts2nsec(timespec_t *ts)
</span> return ts->tv_sec * BILLION64 + ts->tv_nsec;
}
<span style='display:block; white-space:pre;background:#ffe0e0;'>-/* Plus a microsecond conversion */
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-static useconds_t
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-mt2usec(mach_time_t mach_time)
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-{
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- return mach_time * mach2usecs;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-}
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-/* Mach time in microseconds */
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-static useconds_t
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-mach_usec(void)
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-{
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- return mt2usec(mach_absolute_time());
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-}
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-
</span> /* Version of usleep() that defends against signals */
static void
usleepx(useconds_t usecs)
{
<span style='display:block; white-space:pre;background:#ffe0e0;'>- useconds_t now = mach_usec();
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- useconds_t target = now + usecs;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ int ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ timespec_t ts;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ts.tv_sec = usecs / MILLION;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ts.tv_nsec = (usecs - ts.tv_sec * MILLION) * 1000;
</span>
do {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- (void) usleep(target - now);
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- } while ((now = mach_usec()) < target);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = nanosleep(&ts, &ts);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } while (ret && errno == EINTR);
</span> }
/*
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -1375,13 +1373,347 @@ check_mach_scaling(int verbose)
</span> return ret;
}
<span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Check process and thread clocks
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * This launches three subthreads, two spinning and one sleeping, letting
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * them run for PROCESS_SLEEP_TIME, and then checking the reported times
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * for consistency, where "consistency" means:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 1) Neither spinning thread time exceeds wall-clock time.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 2) The sleeping thread time doesn't exceed THREAD_SLEEP_MAX_CPU.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * 3) The total thread time matches the process's within TIME_SUM_TOLERANCE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * In addition, each time capture sandwiches a timespec version between
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * two nanosecond versions, and checks the triplet for consistency.
</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;'>+typedef void * (pthread_fn_t)(void *);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Struct for both nsec and timespec captures */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef struct ptime_s {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ volatile ns_time_t before;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ volatile ns_time_t middle;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ volatile ns_time_t after;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ volatile timespec_t ts;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ volatile int ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ volatile int errnum;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} ptime_t;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Struct with start and end times, plus other info */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef struct dptime_s {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ptime_t start;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ptime_t end;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pthread_t id;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ volatile int ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ volatile int errnum;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pthread_fn_t *func;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ const char *name;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+} dptime_t;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Get specified time two ways, and compare */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+get_ptime(clockid_t clkid, ptime_t *pt)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ int ret = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ timespec_t ts;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pt->errnum = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ do {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ errno = -err_noerrno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (!(pt->before = clock_gettime_nsec_np(clkid))) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pt->errnum = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = -2; break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ errno = -err_noerrno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if ((ret = clock_gettime(clkid, &ts))) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pt->errnum = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ errno = -err_noerrno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (!(pt->after = clock_gettime_nsec_np(clkid))) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pt->errnum = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = -3; break;
</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 ((unsigned int) ts.tv_nsec >= BILLION) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pt->errnum = -err_badnanos;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = -4; break;
</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;'>+ pt->ts = ts;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pt->middle = ts2nsec(&ts);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (pt->before > pt->middle || pt->middle > pt->after) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pt->errnum = -err_badptime;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = -5; break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } while (0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pt->ret = ret;
</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><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Start a given thread */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+start_thread(dptime_t *dpt)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ int ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dpt->ret = dpt->errnum = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if ((ret = pthread_create(&dpt->id, NULL, dpt->func, (void *)dpt))) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dpt->ret = ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dpt->errnum = errno;
</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><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Stop multiple threads */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static void
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+stop_threads(dptime_t *threads[], int numthreads)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ while (--numthreads >= 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (void) pthread_cancel(threads[numthreads]->id);
</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;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Start multiple threads */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static dptime_t *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+start_threads(dptime_t *threads[], int numthreads)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ int thread, ret = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ for (thread = 0; thread < numthreads; ++thread) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = start_thread(threads[thread]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (ret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ stop_threads(threads, thread);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return threads[thread];
</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 NULL;
</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;'>+/* Wait for threads to stop */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+wait_threads(dptime_t *threads[], int numthreads)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ int ret = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dptime_t *dpt;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ while (--numthreads >= 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dpt = threads[numthreads];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if ((dpt->ret = pthread_join(dpt->id, NULL))) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dpt->errnum = errno;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = 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;'>+ return ret;
</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;'>+/* Report errors from threads */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+report_thread_errs(dptime_t *threads[], int numthreads)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ int ret = 0, thread;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dptime_t *dpt;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ for (thread = 0; thread < numthreads; ++thread) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dpt = threads[thread];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (dpt->start.ret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" *** Error (%d) getting thread %s start time (%d): %s\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dpt->start.ret, dpt->name,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dpt->start.errnum, get_errstr(dpt->start.errnum));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (dpt->end.ret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" *** Error (%d) getting thread %s end time (%d): %s\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dpt->end.ret, dpt->name,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dpt->end.errnum, get_errstr(dpt->end.errnum));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = 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;'>+ return ret;
</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;'>+/* Report time range for one process or thread */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static void
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+report_times(dptime_t *dpt, int verbose)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ const char *tp = dpt->name ? "Thread" : "Process";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ const char *tn = dpt->name ? dpt->name : "";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (verbose < 2) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" %s %s times %llu -> %llu, = %.6f ms\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ tp, tn, ULL dpt->start.middle, ULL dpt->end.middle,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (dpt->end.middle - dpt->start.middle) / 1E6);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" %s %s times %llu/%llu/%llu -> %llu/%llu/%llu,"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ " = %.6f/%.6f/%.6f ms\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ tp, tn,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ULL dpt->start.before, ULL dpt->start.middle, ULL dpt->start.after,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ULL dpt->end.before, ULL dpt->end.middle, ULL dpt->end.after,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (dpt->end.before - dpt->start.after) / 1E6,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (dpt->end.middle - dpt->start.middle) / 1E6,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (dpt->end.after - dpt->start.before) / 1E6);
</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;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Report times from all threads */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static void
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+report_thread_times(dptime_t *threads[], int numthreads, int verbose)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ int thread;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ for (thread = 0; thread < numthreads; ++thread) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ report_times(threads[thread], verbose);
</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;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Spinning thread */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static void *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+thread_spin(void *arg)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dptime_t *dpt = (dptime_t *) arg;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ mach_time_t stop_time;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (get_ptime(CLOCK_THREAD_CPUTIME_ID, &dpt->start)) return NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ stop_time = mach_absolute_time() + THREAD_RUN_TIME / mach2usecs;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ while (mach_absolute_time() < stop_time) ;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (void) get_ptime(CLOCK_THREAD_CPUTIME_ID, &dpt->end);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return NULL;
</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;'>+/* Sleeping thread */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static void *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+thread_sleep(void *arg)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dptime_t *dpt = (dptime_t *) arg;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (get_ptime(CLOCK_THREAD_CPUTIME_ID, &dpt->start)) return NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ usleepx(THREAD_RUN_TIME);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (void) get_ptime(CLOCK_THREAD_CPUTIME_ID, &dpt->end);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return NULL;
</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;'>+/* Main checking function */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+check_thread_times(int verbose, int quiet)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ int ret = 0, wret = 0, eret = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ns_time_t start, end, wall, difflo, diffhi;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dptime_t spin1 = {.func = thread_spin, .name = "spin1"};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dptime_t spin2 = {.func = thread_spin, .name = "spin2"};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dptime_t sleeper = {.func = thread_sleep, .name = "sleeper"};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dptime_t top = {.func = NULL};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dptime_t *threads[] = {&spin1, &spin2, &sleeper};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ const int numthreads = sizeof(threads) / sizeof(threads[0]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dptime_t *failed;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ double proclo, prochi, threadlo = 0.0, threadhi = 0.0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (verbose && !quiet) printf(" Checking thread times.\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ start = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (get_ptime(CLOCK_PROCESS_CPUTIME_ID, &top.start)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" *** Error getting process starting time (%d): %s\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ top.errnum, get_errstr(top.errnum));
</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 ((failed = start_threads(threads, numthreads))) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" *** Error starting threads (%d): %s\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ failed->errnum, get_errstr(failed->errnum));
</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;'>+ usleepx(PROCESS_SLEEP_TIME);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ wret = wait_threads(threads, numthreads);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ eret = get_ptime(CLOCK_PROCESS_CPUTIME_ID, &top.end);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ end = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ wall = end - start;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (wret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ stop_threads(threads, numthreads);
</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 (wret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" *** Error waiting for threads, running = %d\n", wret);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (eret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" *** Error getting process ending time (%d): %s\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ top.errnum, get_errstr(top.errnum));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = -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;'>+ ret |= report_thread_errs(threads, numthreads);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (verbose && !quiet && !ret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" Total wall clock time = %.3f ms\n", wall / 1E6);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ report_thread_times(threads, numthreads, verbose);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ report_times(&top, verbose);
</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 (!ret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ difflo = spin1.end.before - spin1.start.after;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ diffhi = spin1.end.after - spin1.start.before;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ threadlo += difflo / 1E6; threadhi += diffhi / 1E6;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (diffhi > wall) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" *** Spin1 CPU %llu/%llu exceeds wall time %llu\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ULL difflo, ULL diffhi, ULL wall);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = 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;'>+ difflo = spin2.end.before - spin2.start.after;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ diffhi = spin2.end.after - spin2.start.before;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ threadlo += difflo / 1E6; threadhi += diffhi / 1E6;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (diffhi > wall) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" *** Spin2 CPU %llu/%llu exceeds wall time %llu\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ULL difflo, ULL diffhi, ULL wall);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = 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;'>+ difflo = sleeper.end.before - sleeper.start.after;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ diffhi = sleeper.end.after - sleeper.start.before;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ threadlo += difflo / 1E6; threadhi += diffhi / 1E6;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (diffhi > THREAD_SLEEP_MAX_CPU * 1000) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" *** Sleeper CPU %llu/%llu exceeds limit %llu\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ULL difflo, ULL diffhi, ULL THREAD_SLEEP_MAX_CPU * 1000);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = 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;'>+ proclo = (top.end.before - top.start.after) / 1E6;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ prochi = (top.end.after - top.start.before) / 1E6;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (fabs(threadlo / prochi - 1.0) > TIME_SUM_TOLERANCE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ || fabs(threadhi / proclo - 1.0) > TIME_SUM_TOLERANCE) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" *** Total thread time %.6f/%.6f ms"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ " mismatches process time %.6f/%.6f ms\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ threadlo, threadhi, proclo, prochi);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret = 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 (verbose && !quiet) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" Total thread time = process time %+.2f%%/%.2f%%\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (threadlo / prochi - 1.0) * 100.0,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (threadhi / proclo - 1.0) * 100.0);
</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;'>+
</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[])
{
int argn = 1;
int continuous = 0, dump = 0, quiet = 0, verbose = 0;
<span style='display:block; white-space:pre;background:#ffe0e0;'>- int err = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ int err = 0, tterr, ttries;
</span> const char *cp;
char chr;
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -1408,6 +1740,20 @@ main(int argc, char *argv[])
</span> err |= report_all_clocks(dump, verbose, quiet);
err |= report_all_clock_compares(dump, verbose, quiet);
err |= check_mach_scaling(verbose && !quiet);
<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;'>+ * Even with relaxed margins, the thread time test occasionally fails
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * (including with Apple's functions), particularly on a heavily loaded
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * system. So we do a few retries of the retriable errors (designated by
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * the positive return value). Since this is a fairly rare case, we don't
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * bother to suppress the error messages.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ttries = THREAD_TEST_TRIES;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ do {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ tterr = check_thread_times(verbose, quiet);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } while (tterr > 0 && --ttries);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ err |= tterr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> if (!continuous) break;
}
</pre><pre style='margin:0'>
</pre>