]> git.saurik.com Git - apple/xnu.git/blame_incremental - tools/tests/zero-to-n/zero-to-n.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / tools / tests / zero-to-n / zero-to-n.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <unistd.h>
29#include <stdio.h>
30#include <math.h>
31#include <sys/kdebug.h>
32#include <stdlib.h>
33#include <pthread.h>
34#include <errno.h>
35#include <err.h>
36#include <string.h>
37#include <assert.h>
38#include <sysexits.h>
39#include <sys/sysctl.h>
40#include <getopt.h>
41
42#include <spawn.h>
43#include <spawn_private.h>
44#include <sys/spawn_internal.h>
45#include <mach-o/dyld.h>
46
47#include <mach/mach_time.h>
48#include <mach/mach.h>
49#include <mach/task.h>
50#include <mach/semaphore.h>
51
52#include <pthread/qos_private.h>
53
54#include <sys/resource.h>
55
56#include <stdatomic.h>
57
58#include <os/tsd.h>
59#include <TargetConditionals.h>
60
61typedef enum wake_type { WAKE_BROADCAST_ONESEM, WAKE_BROADCAST_PERTHREAD, WAKE_CHAIN, WAKE_HOP } wake_type_t;
62typedef enum my_policy_type { MY_POLICY_REALTIME, MY_POLICY_TIMESHARE, MY_POLICY_FIXEDPRI } my_policy_type_t;
63
64#define mach_assert_zero(error) do { if ((error) != 0) { fprintf(stderr, "[FAIL] error %d (%s) ", (error), mach_error_string(error)); assert(error == 0); } } while (0)
65#define mach_assert_zero_t(tid, error) do { if ((error) != 0) { fprintf(stderr, "[FAIL] Thread %d error %d (%s) ", (tid), (error), mach_error_string(error)); assert(error == 0); } } while (0)
66#define assert_zero_t(tid, error) do { if ((error) != 0) { fprintf(stderr, "[FAIL] Thread %d error %d ", (tid), (error)); assert(error == 0); } } while (0)
67
68#define CONSTRAINT_NANOS (20000000ll) /* 20 ms */
69#define COMPUTATION_NANOS (10000000ll) /* 10 ms */
70#define LL_CONSTRAINT_NANOS ( 2000000ll) /* 2 ms */
71#define LL_COMPUTATION_NANOS ( 1000000ll) /* 1 ms */
72#define RT_CHURN_COMP_NANOS ( 1000000ll) /* 1 ms */
73#define TRACEWORTHY_NANOS (10000000ll) /* 10 ms */
74#define TRACEWORTHY_NANOS_TEST ( 2000000ll) /* 2 ms */
75
76#if DEBUG
77#define debug_log(args ...) printf(args)
78#else
79#define debug_log(args ...) do { } while(0)
80#endif
81
82/* Declarations */
83static void* worker_thread(void *arg);
84static void usage();
85static int thread_setup(uint32_t my_id);
86static my_policy_type_t parse_thread_policy(const char *str);
87static void selfexec_with_apptype(int argc, char *argv[]);
88static void parse_args(int argc, char *argv[]);
89
90static __attribute__((aligned(128))) _Atomic uint32_t g_done_threads;
91static __attribute__((aligned(128))) _Atomic boolean_t g_churn_stop = FALSE;
92static __attribute__((aligned(128))) _Atomic uint64_t g_churn_stopped_at = 0;
93
94/* Global variables (general) */
95static uint32_t g_numcpus;
96static uint32_t g_nphysicalcpu;
97static uint32_t g_nlogicalcpu;
98static uint32_t g_numthreads;
99static wake_type_t g_waketype;
100static policy_t g_policy;
101static uint32_t g_iterations;
102static struct mach_timebase_info g_mti;
103static semaphore_t g_main_sem;
104static uint64_t *g_thread_endtimes_abs;
105static boolean_t g_verbose = FALSE;
106static boolean_t g_do_affinity = FALSE;
107static uint64_t g_starttime_abs;
108static uint32_t g_iteration_sleeptime_us = 0;
109static uint32_t g_priority = 0;
110static uint32_t g_churn_pri = 0;
111static uint32_t g_churn_count = 0;
112static uint32_t g_rt_churn_count = 0;
113
114static pthread_t* g_churn_threads = NULL;
115static pthread_t* g_rt_churn_threads = NULL;
116
117/* should we skip test if run on non-intel */
118static boolean_t g_run_on_intel_only = FALSE;
119
120/* Threshold for dropping a 'bad run' tracepoint */
121static uint64_t g_traceworthy_latency_ns = TRACEWORTHY_NANOS;
122
123/* Have we re-execed to set apptype? */
124static boolean_t g_seen_apptype = FALSE;
125
126/* usleep in betweeen iterations */
127static boolean_t g_do_sleep = TRUE;
128
129/* Every thread spins until all threads have checked in */
130static boolean_t g_do_all_spin = FALSE;
131
132/* Every thread backgrounds temporarily before parking */
133static boolean_t g_drop_priority = FALSE;
134
135/* Use low-latency (sub 4ms deadline) realtime threads */
136static boolean_t g_rt_ll = FALSE;
137
138/* Test whether realtime threads are scheduled on the separate CPUs */
139static boolean_t g_test_rt = FALSE;
140
141static boolean_t g_rt_churn = FALSE;
142
143/* On SMT machines, test whether realtime threads are scheduled on the correct CPUs */
144static boolean_t g_test_rt_smt = FALSE;
145
146/* Test whether realtime threads are successfully avoiding CPU 0 on Intel */
147static boolean_t g_test_rt_avoid0 = FALSE;
148
149/* Print a histgram showing how many threads ran on each CPU */
150static boolean_t g_histogram = FALSE;
151
152/* One randomly chosen thread holds up the train for a certain duration. */
153static boolean_t g_do_one_long_spin = FALSE;
154static uint32_t g_one_long_spin_id = 0;
155static uint64_t g_one_long_spin_length_abs = 0;
156static uint64_t g_one_long_spin_length_ns = 0;
157
158/* Each thread spins for a certain duration after waking up before blocking again. */
159static boolean_t g_do_each_spin = FALSE;
160static uint64_t g_each_spin_duration_abs = 0;
161static uint64_t g_each_spin_duration_ns = 0;
162
163/* Global variables (broadcast) */
164static semaphore_t g_broadcastsem;
165static semaphore_t g_leadersem;
166static semaphore_t g_readysem;
167static semaphore_t g_donesem;
168static semaphore_t g_rt_churn_sem;
169static semaphore_t g_rt_churn_start_sem;
170
171/* Global variables (chain) */
172static semaphore_t *g_semarr;
173
174typedef struct {
175 __attribute__((aligned(128))) uint32_t current;
176 uint32_t accum;
177} histogram_t;
178
179static histogram_t *g_cpu_histogram;
180static _Atomic uint64_t *g_cpu_map;
181
182static uint64_t
183abs_to_nanos(uint64_t abstime)
184{
185 return (uint64_t)(abstime * (((double)g_mti.numer) / ((double)g_mti.denom)));
186}
187
188static uint64_t
189nanos_to_abs(uint64_t ns)
190{
191 return (uint64_t)(ns * (((double)g_mti.denom) / ((double)g_mti.numer)));
192}
193
194inline static void
195yield(void)
196{
197#if defined(__arm__) || defined(__arm64__)
198 asm volatile ("yield");
199#elif defined(__x86_64__) || defined(__i386__)
200 asm volatile ("pause");
201#else
202#error Unrecognized architecture
203#endif
204}
205
206static void *
207churn_thread(__unused void *arg)
208{
209 uint64_t spin_count = 0;
210
211 /*
212 * As a safety measure to avoid wedging, we will bail on the spin if
213 * it's been more than 1s after the most recent run start
214 */
215
216 while (g_churn_stop == FALSE &&
217 mach_absolute_time() < (g_starttime_abs + NSEC_PER_SEC)) {
218 spin_count++;
219 yield();
220 }
221
222 /* This is totally racy, but only here to detect if anyone stops early */
223 atomic_fetch_add_explicit(&g_churn_stopped_at, spin_count, memory_order_relaxed);
224
225 return NULL;
226}
227
228static void
229create_churn_threads()
230{
231 if (g_churn_count == 0) {
232 g_churn_count = g_numcpus - 1;
233 }
234
235 errno_t err;
236
237 struct sched_param param = { .sched_priority = (int)g_churn_pri };
238 pthread_attr_t attr;
239
240 /* Array for churn threads */
241 g_churn_threads = (pthread_t*) valloc(sizeof(pthread_t) * g_churn_count);
242 assert(g_churn_threads);
243
244 if ((err = pthread_attr_init(&attr))) {
245 errc(EX_OSERR, err, "pthread_attr_init");
246 }
247
248 if ((err = pthread_attr_setschedparam(&attr, &param))) {
249 errc(EX_OSERR, err, "pthread_attr_setschedparam");
250 }
251
252 if ((err = pthread_attr_setschedpolicy(&attr, SCHED_RR))) {
253 errc(EX_OSERR, err, "pthread_attr_setschedpolicy");
254 }
255
256 for (uint32_t i = 0; i < g_churn_count; i++) {
257 pthread_t new_thread;
258
259 if ((err = pthread_create(&new_thread, &attr, churn_thread, NULL))) {
260 errc(EX_OSERR, err, "pthread_create");
261 }
262 g_churn_threads[i] = new_thread;
263 }
264
265 if ((err = pthread_attr_destroy(&attr))) {
266 errc(EX_OSERR, err, "pthread_attr_destroy");
267 }
268}
269
270static void
271join_churn_threads(void)
272{
273 if (atomic_load_explicit(&g_churn_stopped_at, memory_order_seq_cst) != 0) {
274 printf("Warning: Some of the churn threads may have stopped early: %lld\n",
275 g_churn_stopped_at);
276 }
277
278 atomic_store_explicit(&g_churn_stop, TRUE, memory_order_seq_cst);
279
280 /* Rejoin churn threads */
281 for (uint32_t i = 0; i < g_churn_count; i++) {
282 errno_t err = pthread_join(g_churn_threads[i], NULL);
283 if (err) {
284 errc(EX_OSERR, err, "pthread_join %d", i);
285 }
286 }
287}
288
289/*
290 * Set policy
291 */
292static int
293rt_churn_thread_setup(void)
294{
295 kern_return_t kr;
296 thread_time_constraint_policy_data_t pol;
297
298 /* Hard-coded realtime parameters (similar to what Digi uses) */
299 pol.period = 100000;
300 pol.constraint = (uint32_t) nanos_to_abs(CONSTRAINT_NANOS * 2);
301 pol.computation = (uint32_t) nanos_to_abs(RT_CHURN_COMP_NANOS * 2);
302 pol.preemptible = 0; /* Ignored by OS */
303
304 kr = thread_policy_set(mach_thread_self(), THREAD_TIME_CONSTRAINT_POLICY,
305 (thread_policy_t) &pol, THREAD_TIME_CONSTRAINT_POLICY_COUNT);
306 mach_assert_zero_t(0, kr);
307
308 return 0;
309}
310
311static void *
312rt_churn_thread(__unused void *arg)
313{
314 rt_churn_thread_setup();
315
316 for (uint32_t i = 0; i < g_iterations; i++) {
317 kern_return_t kr = semaphore_wait_signal(g_rt_churn_start_sem, g_rt_churn_sem);
318 mach_assert_zero_t(0, kr);
319
320 volatile double x = 0.0;
321 volatile double y = 0.0;
322
323 uint64_t endspin = mach_absolute_time() + nanos_to_abs(RT_CHURN_COMP_NANOS);
324 while (mach_absolute_time() < endspin) {
325 y = y + 1.5 + x;
326 x = sqrt(y);
327 }
328 }
329
330 kern_return_t kr = semaphore_signal(g_rt_churn_sem);
331 mach_assert_zero_t(0, kr);
332
333 return NULL;
334}
335
336static void
337wait_for_rt_churn_threads(void)
338{
339 for (uint32_t i = 0; i < g_rt_churn_count; i++) {
340 kern_return_t kr = semaphore_wait(g_rt_churn_sem);
341 mach_assert_zero_t(0, kr);
342 }
343}
344
345static void
346start_rt_churn_threads(void)
347{
348 for (uint32_t i = 0; i < g_rt_churn_count; i++) {
349 kern_return_t kr = semaphore_signal(g_rt_churn_start_sem);
350 mach_assert_zero_t(0, kr);
351 }
352}
353
354static void
355create_rt_churn_threads(void)
356{
357 if (g_rt_churn_count == 0) {
358 /* Leave 1 CPU to ensure that the main thread can make progress */
359 g_rt_churn_count = g_numcpus - 1;
360 }
361
362 errno_t err;
363
364 struct sched_param param = { .sched_priority = (int)g_churn_pri };
365 pthread_attr_t attr;
366
367 /* Array for churn threads */
368 g_rt_churn_threads = (pthread_t*) valloc(sizeof(pthread_t) * g_rt_churn_count);
369 assert(g_rt_churn_threads);
370
371 if ((err = pthread_attr_init(&attr))) {
372 errc(EX_OSERR, err, "pthread_attr_init");
373 }
374
375 if ((err = pthread_attr_setschedparam(&attr, &param))) {
376 errc(EX_OSERR, err, "pthread_attr_setschedparam");
377 }
378
379 if ((err = pthread_attr_setschedpolicy(&attr, SCHED_RR))) {
380 errc(EX_OSERR, err, "pthread_attr_setschedpolicy");
381 }
382
383 for (uint32_t i = 0; i < g_rt_churn_count; i++) {
384 pthread_t new_thread;
385
386 if ((err = pthread_create(&new_thread, &attr, rt_churn_thread, NULL))) {
387 errc(EX_OSERR, err, "pthread_create");
388 }
389 g_rt_churn_threads[i] = new_thread;
390 }
391
392 if ((err = pthread_attr_destroy(&attr))) {
393 errc(EX_OSERR, err, "pthread_attr_destroy");
394 }
395
396 /* Wait until all threads have checked in */
397 wait_for_rt_churn_threads();
398}
399
400static void
401join_rt_churn_threads(void)
402{
403 /* Rejoin rt churn threads */
404 for (uint32_t i = 0; i < g_rt_churn_count; i++) {
405 errno_t err = pthread_join(g_rt_churn_threads[i], NULL);
406 if (err) {
407 errc(EX_OSERR, err, "pthread_join %d", i);
408 }
409 }
410}
411
412/*
413 * Figure out what thread policy to use
414 */
415static my_policy_type_t
416parse_thread_policy(const char *str)
417{
418 if (strcmp(str, "timeshare") == 0) {
419 return MY_POLICY_TIMESHARE;
420 } else if (strcmp(str, "realtime") == 0) {
421 return MY_POLICY_REALTIME;
422 } else if (strcmp(str, "fixed") == 0) {
423 return MY_POLICY_FIXEDPRI;
424 } else {
425 errx(EX_USAGE, "Invalid thread policy \"%s\"", str);
426 }
427}
428
429/*
430 * Figure out what wakeup pattern to use
431 */
432static wake_type_t
433parse_wakeup_pattern(const char *str)
434{
435 if (strcmp(str, "chain") == 0) {
436 return WAKE_CHAIN;
437 } else if (strcmp(str, "hop") == 0) {
438 return WAKE_HOP;
439 } else if (strcmp(str, "broadcast-single-sem") == 0) {
440 return WAKE_BROADCAST_ONESEM;
441 } else if (strcmp(str, "broadcast-per-thread") == 0) {
442 return WAKE_BROADCAST_PERTHREAD;
443 } else {
444 errx(EX_USAGE, "Invalid wakeup pattern \"%s\"", str);
445 }
446}
447
448/*
449 * Set policy
450 */
451static int
452thread_setup(uint32_t my_id)
453{
454 kern_return_t kr;
455 errno_t ret;
456 thread_time_constraint_policy_data_t pol;
457
458 if (g_priority) {
459 int policy = SCHED_OTHER;
460 if (g_policy == MY_POLICY_FIXEDPRI) {
461 policy = SCHED_RR;
462 }
463
464 struct sched_param param = {.sched_priority = (int)g_priority};
465 if ((ret = pthread_setschedparam(pthread_self(), policy, &param))) {
466 errc(EX_OSERR, ret, "pthread_setschedparam: %d", my_id);
467 }
468 }
469
470 switch (g_policy) {
471 case MY_POLICY_TIMESHARE:
472 break;
473 case MY_POLICY_REALTIME:
474 /* Hard-coded realtime parameters (similar to what Digi uses) */
475 pol.period = 100000;
476 if (g_rt_ll) {
477 pol.constraint = (uint32_t) nanos_to_abs(LL_CONSTRAINT_NANOS);
478 pol.computation = (uint32_t) nanos_to_abs(LL_COMPUTATION_NANOS);
479 } else {
480 pol.constraint = (uint32_t) nanos_to_abs(CONSTRAINT_NANOS);
481 pol.computation = (uint32_t) nanos_to_abs(COMPUTATION_NANOS);
482 }
483 pol.preemptible = 0; /* Ignored by OS */
484
485 kr = thread_policy_set(mach_thread_self(), THREAD_TIME_CONSTRAINT_POLICY,
486 (thread_policy_t) &pol, THREAD_TIME_CONSTRAINT_POLICY_COUNT);
487 mach_assert_zero_t(my_id, kr);
488 break;
489 case MY_POLICY_FIXEDPRI:
490 ret = pthread_set_fixedpriority_self();
491 if (ret) {
492 errc(EX_OSERR, ret, "pthread_set_fixedpriority_self");
493 }
494 break;
495 default:
496 errx(EX_USAGE, "invalid policy type %d", g_policy);
497 }
498
499 if (g_do_affinity) {
500 thread_affinity_policy_data_t affinity;
501
502 affinity.affinity_tag = my_id % 2;
503
504 kr = thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY,
505 (thread_policy_t)&affinity, THREAD_AFFINITY_POLICY_COUNT);
506 mach_assert_zero_t(my_id, kr);
507 }
508
509 return 0;
510}
511
512/*
513 * Wait for a wakeup, potentially wake up another of the "0-N" threads,
514 * and notify the main thread when done.
515 */
516static void*
517worker_thread(void *arg)
518{
519 uint32_t my_id = (uint32_t)(uintptr_t)arg;
520 kern_return_t kr;
521
522 volatile double x = 0.0;
523 volatile double y = 0.0;
524
525 /* Set policy and so forth */
526 thread_setup(my_id);
527
528 for (uint32_t i = 0; i < g_iterations; i++) {
529 if (my_id == 0) {
530 /*
531 * Leader thread either wakes everyone up or starts the chain going.
532 */
533
534 /* Give the worker threads undisturbed time to finish before waiting on them */
535 if (g_do_sleep) {
536 usleep(g_iteration_sleeptime_us);
537 }
538
539 debug_log("%d Leader thread wait for ready\n", i);
540
541 /*
542 * Wait for everyone else to declare ready
543 * Is there a better way to do this that won't interfere with the rest of the chain?
544 * TODO: Invent 'semaphore wait for N signals'
545 */
546
547 for (uint32_t j = 0; j < g_numthreads - 1; j++) {
548 kr = semaphore_wait(g_readysem);
549 mach_assert_zero_t(my_id, kr);
550 }
551
552 debug_log("%d Leader thread wait\n", i);
553
554 if (i > 0) {
555 for (int cpuid = 0; cpuid < g_numcpus; cpuid++) {
556 if (g_cpu_histogram[cpuid].current == 1) {
557 atomic_fetch_or_explicit(&g_cpu_map[i - 1], (1UL << cpuid), memory_order_relaxed);
558 g_cpu_histogram[cpuid].current = 0;
559 }
560 }
561 }
562
563 /* Signal main thread and wait for start of iteration */
564
565 kr = semaphore_wait_signal(g_leadersem, g_main_sem);
566 mach_assert_zero_t(my_id, kr);
567
568 g_thread_endtimes_abs[my_id] = mach_absolute_time();
569
570 debug_log("%d Leader thread go\n", i);
571
572 assert_zero_t(my_id, atomic_load_explicit(&g_done_threads, memory_order_relaxed));
573
574 switch (g_waketype) {
575 case WAKE_BROADCAST_ONESEM:
576 kr = semaphore_signal_all(g_broadcastsem);
577 mach_assert_zero_t(my_id, kr);
578 break;
579 case WAKE_BROADCAST_PERTHREAD:
580 for (uint32_t j = 1; j < g_numthreads; j++) {
581 kr = semaphore_signal(g_semarr[j]);
582 mach_assert_zero_t(my_id, kr);
583 }
584 break;
585 case WAKE_CHAIN:
586 kr = semaphore_signal(g_semarr[my_id + 1]);
587 mach_assert_zero_t(my_id, kr);
588 break;
589 case WAKE_HOP:
590 kr = semaphore_wait_signal(g_donesem, g_semarr[my_id + 1]);
591 mach_assert_zero_t(my_id, kr);
592 break;
593 }
594 } else {
595 /*
596 * Everyone else waits to be woken up,
597 * records when she wakes up, and possibly
598 * wakes up a friend.
599 */
600 switch (g_waketype) {
601 case WAKE_BROADCAST_ONESEM:
602 kr = semaphore_wait_signal(g_broadcastsem, g_readysem);
603 mach_assert_zero_t(my_id, kr);
604
605 g_thread_endtimes_abs[my_id] = mach_absolute_time();
606 break;
607
608 case WAKE_BROADCAST_PERTHREAD:
609 kr = semaphore_wait_signal(g_semarr[my_id], g_readysem);
610 mach_assert_zero_t(my_id, kr);
611
612 g_thread_endtimes_abs[my_id] = mach_absolute_time();
613 break;
614
615 case WAKE_CHAIN:
616 kr = semaphore_wait_signal(g_semarr[my_id], g_readysem);
617 mach_assert_zero_t(my_id, kr);
618
619 /* Signal the next thread *after* recording wake time */
620
621 g_thread_endtimes_abs[my_id] = mach_absolute_time();
622
623 if (my_id < (g_numthreads - 1)) {
624 kr = semaphore_signal(g_semarr[my_id + 1]);
625 mach_assert_zero_t(my_id, kr);
626 }
627
628 break;
629
630 case WAKE_HOP:
631 kr = semaphore_wait_signal(g_semarr[my_id], g_readysem);
632 mach_assert_zero_t(my_id, kr);
633
634 /* Signal the next thread *after* recording wake time */
635
636 g_thread_endtimes_abs[my_id] = mach_absolute_time();
637
638 if (my_id < (g_numthreads - 1)) {
639 kr = semaphore_wait_signal(g_donesem, g_semarr[my_id + 1]);
640 mach_assert_zero_t(my_id, kr);
641 } else {
642 kr = semaphore_signal_all(g_donesem);
643 mach_assert_zero_t(my_id, kr);
644 }
645
646 break;
647 }
648 }
649
650 unsigned int cpuid = _os_cpu_number();
651 assert(cpuid < g_numcpus);
652 debug_log("Thread %p woke up on CPU %d for iteration %d.\n", pthread_self(), cpuid, i);
653 g_cpu_histogram[cpuid].current = 1;
654 g_cpu_histogram[cpuid].accum++;
655
656 if (g_do_one_long_spin && g_one_long_spin_id == my_id) {
657 /* One randomly chosen thread holds up the train for a while. */
658
659 uint64_t endspin = g_starttime_abs + g_one_long_spin_length_abs;
660 while (mach_absolute_time() < endspin) {
661 y = y + 1.5 + x;
662 x = sqrt(y);
663 }
664 }
665
666 if (g_do_each_spin) {
667 /* Each thread spins for a certain duration after waking up before blocking again. */
668
669 uint64_t endspin = mach_absolute_time() + g_each_spin_duration_abs;
670 while (mach_absolute_time() < endspin) {
671 y = y + 1.5 + x;
672 x = sqrt(y);
673 }
674 }
675
676 uint32_t done_threads;
677 done_threads = atomic_fetch_add_explicit(&g_done_threads, 1, memory_order_relaxed) + 1;
678
679 debug_log("Thread %p new value is %d, iteration %d\n", pthread_self(), done_threads, i);
680
681 if (g_drop_priority) {
682 /* Drop priority to BG momentarily */
683 errno_t ret = setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG);
684 if (ret) {
685 errc(EX_OSERR, ret, "setpriority PRIO_DARWIN_BG");
686 }
687 }
688
689 if (g_do_all_spin) {
690 /* Everyone spins until the last thread checks in. */
691
692 while (atomic_load_explicit(&g_done_threads, memory_order_relaxed) < g_numthreads) {
693 y = y + 1.5 + x;
694 x = sqrt(y);
695 }
696 }
697
698 if (g_drop_priority) {
699 /* Restore normal priority */
700 errno_t ret = setpriority(PRIO_DARWIN_THREAD, 0, 0);
701 if (ret) {
702 errc(EX_OSERR, ret, "setpriority 0");
703 }
704 }
705
706 debug_log("Thread %p done spinning, iteration %d\n", pthread_self(), i);
707 }
708
709 if (my_id == 0) {
710 /* Give the worker threads undisturbed time to finish before waiting on them */
711 if (g_do_sleep) {
712 usleep(g_iteration_sleeptime_us);
713 }
714
715 /* Wait for the worker threads to finish */
716 for (uint32_t i = 0; i < g_numthreads - 1; i++) {
717 kr = semaphore_wait(g_readysem);
718 mach_assert_zero_t(my_id, kr);
719 }
720
721 /* Tell everyone and the main thread that the last iteration is done */
722 debug_log("%d Leader thread done\n", g_iterations - 1);
723
724 for (int cpuid = 0; cpuid < g_numcpus; cpuid++) {
725 if (g_cpu_histogram[cpuid].current == 1) {
726 atomic_fetch_or_explicit(&g_cpu_map[g_iterations - 1], (1UL << cpuid), memory_order_relaxed);
727 g_cpu_histogram[cpuid].current = 0;
728 }
729 }
730
731 kr = semaphore_signal_all(g_main_sem);
732 mach_assert_zero_t(my_id, kr);
733 } else {
734 /* Hold up thread teardown so it doesn't affect the last iteration */
735 kr = semaphore_wait_signal(g_main_sem, g_readysem);
736 mach_assert_zero_t(my_id, kr);
737 }
738
739 return 0;
740}
741
742/*
743 * Given an array of uint64_t values, compute average, max, min, and standard deviation
744 */
745static void
746compute_stats(uint64_t *values, uint64_t count, float *averagep, uint64_t *maxp, uint64_t *minp, float *stddevp)
747{
748 uint32_t i;
749 uint64_t _sum = 0;
750 uint64_t _max = 0;
751 uint64_t _min = UINT64_MAX;
752 float _avg = 0;
753 float _dev = 0;
754
755 for (i = 0; i < count; i++) {
756 _sum += values[i];
757 _max = values[i] > _max ? values[i] : _max;
758 _min = values[i] < _min ? values[i] : _min;
759 }
760
761 _avg = ((float)_sum) / ((float)count);
762
763 _dev = 0;
764 for (i = 0; i < count; i++) {
765 _dev += powf((((float)values[i]) - _avg), 2);
766 }
767
768 _dev /= count;
769 _dev = sqrtf(_dev);
770
771 *averagep = _avg;
772 *maxp = _max;
773 *minp = _min;
774 *stddevp = _dev;
775}
776
777int
778main(int argc, char **argv)
779{
780 errno_t ret;
781 kern_return_t kr;
782
783 pthread_t *threads;
784 uint64_t *worst_latencies_ns;
785 uint64_t *worst_latencies_from_first_ns;
786 uint64_t max, min;
787 float avg, stddev;
788
789 bool test_fail = false;
790
791 for (int i = 0; i < argc; i++) {
792 if (strcmp(argv[i], "--switched_apptype") == 0) {
793 g_seen_apptype = TRUE;
794 }
795 }
796
797 if (!g_seen_apptype) {
798 selfexec_with_apptype(argc, argv);
799 }
800
801 parse_args(argc, argv);
802
803 srand((unsigned int)time(NULL));
804
805 mach_timebase_info(&g_mti);
806
807#if TARGET_OS_OSX
808 /* SKIP test if running on arm platform */
809 if (g_run_on_intel_only) {
810 int is_arm = 0;
811 size_t is_arm_size = sizeof(is_arm);
812 ret = sysctlbyname("hw.optional.arm64", &is_arm, &is_arm_size, NULL, 0);
813 if (ret == 0 && is_arm) {
814 printf("Unsupported platform. Skipping test.\n");
815 exit(0);
816 }
817 }
818#endif /* TARGET_OS_OSX */
819
820 size_t ncpu_size = sizeof(g_numcpus);
821 ret = sysctlbyname("hw.ncpu", &g_numcpus, &ncpu_size, NULL, 0);
822 if (ret) {
823 err(EX_OSERR, "Failed sysctlbyname(hw.ncpu)");
824 }
825 assert(g_numcpus <= 64); /* g_cpu_map needs to be extended for > 64 cpus */
826
827 size_t physicalcpu_size = sizeof(g_nphysicalcpu);
828 ret = sysctlbyname("hw.physicalcpu", &g_nphysicalcpu, &physicalcpu_size, NULL, 0);
829 if (ret) {
830 err(EX_OSERR, "Failed sysctlbyname(hw.physicalcpu)");
831 }
832
833 size_t logicalcpu_size = sizeof(g_nlogicalcpu);
834 ret = sysctlbyname("hw.logicalcpu", &g_nlogicalcpu, &logicalcpu_size, NULL, 0);
835 if (ret) {
836 err(EX_OSERR, "Failed sysctlbyname(hw.logicalcpu)");
837 }
838
839 if (g_test_rt) {
840 if (g_numthreads == 0) {
841 g_numthreads = g_numcpus;
842 }
843 g_policy = MY_POLICY_REALTIME;
844 g_do_all_spin = TRUE;
845 g_histogram = true;
846 /* Don't change g_traceworthy_latency_ns if it's explicity been set to something other than the default */
847 if (g_traceworthy_latency_ns == TRACEWORTHY_NANOS) {
848 g_traceworthy_latency_ns = TRACEWORTHY_NANOS_TEST;
849 }
850 } else if (g_test_rt_smt) {
851 if (g_nlogicalcpu != 2 * g_nphysicalcpu) {
852 /* Not SMT */
853 printf("Attempt to run --test-rt-smt on a non-SMT device\n");
854 exit(0);
855 }
856
857 if (g_numthreads == 0) {
858 g_numthreads = g_nphysicalcpu;
859 }
860 g_policy = MY_POLICY_REALTIME;
861 g_do_all_spin = TRUE;
862 g_histogram = true;
863 } else if (g_test_rt_avoid0) {
864#if defined(__x86_64__) || defined(__i386__)
865 if (g_numthreads == 0) {
866 g_numthreads = g_nphysicalcpu - 1;
867 }
868 if (g_numthreads == 0) {
869 printf("Attempt to run --test-rt-avoid0 on a uniprocessor\n");
870 exit(0);
871 }
872 g_policy = MY_POLICY_REALTIME;
873 g_do_all_spin = TRUE;
874 g_histogram = true;
875#else
876 printf("Attempt to run --test-rt-avoid0 on a non-Intel device\n");
877 exit(0);
878#endif
879 } else if (g_numthreads == 0) {
880 g_numthreads = g_numcpus;
881 }
882
883 if (g_do_each_spin) {
884 g_each_spin_duration_abs = nanos_to_abs(g_each_spin_duration_ns);
885 }
886
887 /* Configure the long-spin thread to take up half of its computation */
888 if (g_do_one_long_spin) {
889 g_one_long_spin_length_ns = COMPUTATION_NANOS / 2;
890 g_one_long_spin_length_abs = nanos_to_abs(g_one_long_spin_length_ns);
891 }
892
893 /* Estimate the amount of time the cleanup phase needs to back off */
894 g_iteration_sleeptime_us = g_numthreads * 20;
895
896 uint32_t threads_per_core = (g_numthreads / g_numcpus) + 1;
897 if (g_do_each_spin) {
898 g_iteration_sleeptime_us += threads_per_core * (g_each_spin_duration_ns / NSEC_PER_USEC);
899 }
900 if (g_do_one_long_spin) {
901 g_iteration_sleeptime_us += g_one_long_spin_length_ns / NSEC_PER_USEC;
902 }
903
904 /* Arrays for threads and their wakeup times */
905 threads = (pthread_t*) valloc(sizeof(pthread_t) * g_numthreads);
906 assert(threads);
907
908 size_t endtimes_size = sizeof(uint64_t) * g_numthreads;
909
910 g_thread_endtimes_abs = (uint64_t*) valloc(endtimes_size);
911 assert(g_thread_endtimes_abs);
912
913 /* Ensure the allocation is pre-faulted */
914 ret = memset_s(g_thread_endtimes_abs, endtimes_size, 0, endtimes_size);
915 if (ret) {
916 errc(EX_OSERR, ret, "memset_s endtimes");
917 }
918
919 size_t latencies_size = sizeof(uint64_t) * g_iterations;
920
921 worst_latencies_ns = (uint64_t*) valloc(latencies_size);
922 assert(worst_latencies_ns);
923
924 /* Ensure the allocation is pre-faulted */
925 ret = memset_s(worst_latencies_ns, latencies_size, 0, latencies_size);
926 if (ret) {
927 errc(EX_OSERR, ret, "memset_s latencies");
928 }
929
930 worst_latencies_from_first_ns = (uint64_t*) valloc(latencies_size);
931 assert(worst_latencies_from_first_ns);
932
933 /* Ensure the allocation is pre-faulted */
934 ret = memset_s(worst_latencies_from_first_ns, latencies_size, 0, latencies_size);
935 if (ret) {
936 errc(EX_OSERR, ret, "memset_s latencies_from_first");
937 }
938
939 size_t histogram_size = sizeof(histogram_t) * g_numcpus;
940 g_cpu_histogram = (histogram_t *)valloc(histogram_size);
941 assert(g_cpu_histogram);
942 /* Ensure the allocation is pre-faulted */
943 ret = memset_s(g_cpu_histogram, histogram_size, 0, histogram_size);
944 if (ret) {
945 errc(EX_OSERR, ret, "memset_s g_cpu_histogram");
946 }
947
948 size_t map_size = sizeof(uint64_t) * g_iterations;
949 g_cpu_map = (_Atomic uint64_t *)valloc(map_size);
950 assert(g_cpu_map);
951 /* Ensure the allocation is pre-faulted */
952 ret = memset_s(g_cpu_map, map_size, 0, map_size);
953 if (ret) {
954 errc(EX_OSERR, ret, "memset_s g_cpu_map");
955 }
956
957 kr = semaphore_create(mach_task_self(), &g_main_sem, SYNC_POLICY_FIFO, 0);
958 mach_assert_zero(kr);
959
960 /* Either one big semaphore or one per thread */
961 if (g_waketype == WAKE_CHAIN ||
962 g_waketype == WAKE_BROADCAST_PERTHREAD ||
963 g_waketype == WAKE_HOP) {
964 g_semarr = valloc(sizeof(semaphore_t) * g_numthreads);
965 assert(g_semarr);
966
967 for (uint32_t i = 0; i < g_numthreads; i++) {
968 kr = semaphore_create(mach_task_self(), &g_semarr[i], SYNC_POLICY_FIFO, 0);
969 mach_assert_zero(kr);
970 }
971
972 g_leadersem = g_semarr[0];
973 } else {
974 kr = semaphore_create(mach_task_self(), &g_broadcastsem, SYNC_POLICY_FIFO, 0);
975 mach_assert_zero(kr);
976 kr = semaphore_create(mach_task_self(), &g_leadersem, SYNC_POLICY_FIFO, 0);
977 mach_assert_zero(kr);
978 }
979
980 if (g_waketype == WAKE_HOP) {
981 kr = semaphore_create(mach_task_self(), &g_donesem, SYNC_POLICY_FIFO, 0);
982 mach_assert_zero(kr);
983 }
984
985 kr = semaphore_create(mach_task_self(), &g_readysem, SYNC_POLICY_FIFO, 0);
986 mach_assert_zero(kr);
987
988 kr = semaphore_create(mach_task_self(), &g_rt_churn_sem, SYNC_POLICY_FIFO, 0);
989 mach_assert_zero(kr);
990
991 kr = semaphore_create(mach_task_self(), &g_rt_churn_start_sem, SYNC_POLICY_FIFO, 0);
992 mach_assert_zero(kr);
993
994 atomic_store_explicit(&g_done_threads, 0, memory_order_relaxed);
995
996 /* Create the threads */
997 for (uint32_t i = 0; i < g_numthreads; i++) {
998 ret = pthread_create(&threads[i], NULL, worker_thread, (void*)(uintptr_t)i);
999 if (ret) {
1000 errc(EX_OSERR, ret, "pthread_create %d", i);
1001 }
1002 }
1003
1004 ret = setpriority(PRIO_DARWIN_ROLE, 0, PRIO_DARWIN_ROLE_UI_FOCAL);
1005 if (ret) {
1006 errc(EX_OSERR, ret, "setpriority");
1007 }
1008
1009 thread_setup(0);
1010
1011 g_starttime_abs = mach_absolute_time();
1012
1013 if (g_churn_pri) {
1014 create_churn_threads();
1015 }
1016 if (g_rt_churn) {
1017 create_rt_churn_threads();
1018 }
1019
1020 /* Let everyone get settled */
1021 kr = semaphore_wait(g_main_sem);
1022 mach_assert_zero(kr);
1023
1024 /* Give the system a bit more time to settle */
1025 if (g_do_sleep) {
1026 usleep(g_iteration_sleeptime_us);
1027 }
1028
1029 /* Go! */
1030 for (uint32_t i = 0; i < g_iterations; i++) {
1031 uint32_t j;
1032 uint64_t worst_abs = 0, best_abs = UINT64_MAX;
1033
1034 if (g_do_one_long_spin) {
1035 g_one_long_spin_id = (uint32_t)rand() % g_numthreads;
1036 }
1037
1038 if (g_rt_churn) {
1039 start_rt_churn_threads();
1040 usleep(100);
1041 }
1042
1043 debug_log("%d Main thread reset\n", i);
1044
1045 atomic_store_explicit(&g_done_threads, 0, memory_order_seq_cst);
1046
1047 g_starttime_abs = mach_absolute_time();
1048
1049 /* Fire them off and wait for worker threads to finish */
1050 kr = semaphore_wait_signal(g_main_sem, g_leadersem);
1051 mach_assert_zero(kr);
1052
1053 debug_log("%d Main thread return\n", i);
1054
1055 assert(atomic_load_explicit(&g_done_threads, memory_order_relaxed) == g_numthreads);
1056
1057 if (g_rt_churn) {
1058 wait_for_rt_churn_threads();
1059 }
1060
1061 /*
1062 * We report the worst latencies relative to start time
1063 * and relative to the lead worker thread.
1064 */
1065 for (j = 0; j < g_numthreads; j++) {
1066 uint64_t latency_abs;
1067
1068 latency_abs = g_thread_endtimes_abs[j] - g_starttime_abs;
1069 worst_abs = worst_abs < latency_abs ? latency_abs : worst_abs;
1070 }
1071
1072 worst_latencies_ns[i] = abs_to_nanos(worst_abs);
1073
1074 worst_abs = 0;
1075 for (j = 1; j < g_numthreads; j++) {
1076 uint64_t latency_abs;
1077
1078 latency_abs = g_thread_endtimes_abs[j] - g_thread_endtimes_abs[0];
1079 worst_abs = worst_abs < latency_abs ? latency_abs : worst_abs;
1080 best_abs = best_abs > latency_abs ? latency_abs : best_abs;
1081 }
1082
1083 worst_latencies_from_first_ns[i] = abs_to_nanos(worst_abs);
1084
1085 /*
1086 * In the event of a bad run, cut a trace point.
1087 */
1088 if (worst_latencies_from_first_ns[i] > g_traceworthy_latency_ns) {
1089 /* Ariadne's ad-hoc test signpost */
1090 kdebug_trace(ARIADNEDBG_CODE(0, 0), worst_latencies_from_first_ns[i], g_traceworthy_latency_ns, 0, 0);
1091
1092 if (g_verbose) {
1093 printf("Worst on this round was %.2f us.\n", ((float)worst_latencies_from_first_ns[i]) / 1000.0);
1094 }
1095 }
1096
1097 /* Give the system a bit more time to settle */
1098 if (g_do_sleep) {
1099 usleep(g_iteration_sleeptime_us);
1100 }
1101 }
1102
1103 /* Rejoin threads */
1104 for (uint32_t i = 0; i < g_numthreads; i++) {
1105 ret = pthread_join(threads[i], NULL);
1106 if (ret) {
1107 errc(EX_OSERR, ret, "pthread_join %d", i);
1108 }
1109 }
1110
1111 if (g_rt_churn) {
1112 join_rt_churn_threads();
1113 }
1114
1115 if (g_churn_pri) {
1116 join_churn_threads();
1117 }
1118
1119 compute_stats(worst_latencies_ns, g_iterations, &avg, &max, &min, &stddev);
1120 printf("Results (from a stop):\n");
1121 printf("Max:\t\t%.2f us\n", ((float)max) / 1000.0);
1122 printf("Min:\t\t%.2f us\n", ((float)min) / 1000.0);
1123 printf("Avg:\t\t%.2f us\n", avg / 1000.0);
1124 printf("Stddev:\t\t%.2f us\n", stddev / 1000.0);
1125
1126 putchar('\n');
1127
1128 compute_stats(worst_latencies_from_first_ns, g_iterations, &avg, &max, &min, &stddev);
1129 printf("Results (relative to first thread):\n");
1130 printf("Max:\t\t%.2f us\n", ((float)max) / 1000.0);
1131 printf("Min:\t\t%.2f us\n", ((float)min) / 1000.0);
1132 printf("Avg:\t\t%.2f us\n", avg / 1000.0);
1133 printf("Stddev:\t\t%.2f us\n", stddev / 1000.0);
1134
1135#if 0
1136 for (uint32_t i = 0; i < g_iterations; i++) {
1137 printf("Iteration %d: %f us\n", i, worst_latencies_ns[i] / 1000.0);
1138 }
1139#endif
1140
1141 if (g_histogram) {
1142 putchar('\n');
1143
1144 for (uint32_t i = 0; i < g_numcpus; i++) {
1145 printf("%d\t%d\n", i, g_cpu_histogram[i].accum);
1146 }
1147 }
1148
1149 if (g_test_rt || g_test_rt_smt || g_test_rt_avoid0) {
1150#define PRIMARY 0x5555555555555555ULL
1151#define SECONDARY 0xaaaaaaaaaaaaaaaaULL
1152
1153 int fail_count = 0;
1154
1155 for (uint32_t i = 0; i < g_iterations; i++) {
1156 bool secondary = false;
1157 bool fail = false;
1158 uint64_t map = g_cpu_map[i];
1159 if (g_test_rt_smt) {
1160 /* Test for one or more threads running on secondary cores unexpectedly (WARNING) */
1161 secondary = (map & SECONDARY);
1162 /* Test for threads running on both primary and secondary cpus of the same core (FAIL) */
1163 fail = ((map & PRIMARY) & ((map & SECONDARY) >> 1));
1164 } else if (g_test_rt) {
1165 fail = (__builtin_popcountll(map) != g_numthreads) && (worst_latencies_ns[i] > g_traceworthy_latency_ns);
1166 } else if (g_test_rt_avoid0) {
1167 fail = ((map & 0x1) == 0x1);
1168 }
1169 if (secondary || fail) {
1170 printf("Iteration %d: 0x%llx%s%s\n", i, map,
1171 secondary ? " SECONDARY" : "",
1172 fail ? " FAIL" : "");
1173 }
1174 test_fail |= fail;
1175 fail_count += fail;
1176 }
1177
1178 if (test_fail && (g_iterations >= 100) && (fail_count <= g_iterations / 100)) {
1179 printf("99%% or better success rate\n");
1180 test_fail = 0;
1181 }
1182 }
1183
1184 free(threads);
1185 free(g_thread_endtimes_abs);
1186 free(worst_latencies_ns);
1187 free(worst_latencies_from_first_ns);
1188 free(g_cpu_histogram);
1189 free(g_cpu_map);
1190
1191 return test_fail;
1192}
1193
1194/*
1195 * WARNING: This is SPI specifically intended for use by launchd to start UI
1196 * apps. We use it here for a test tool only to opt into QoS using the same
1197 * policies. Do not use this outside xnu or libxpc/launchd.
1198 */
1199static void
1200selfexec_with_apptype(int argc, char *argv[])
1201{
1202 int ret;
1203 posix_spawnattr_t attr;
1204 extern char **environ;
1205 char *new_argv[argc + 1 + 1 /* NULL */];
1206 int i;
1207 char prog[PATH_MAX];
1208 uint32_t prog_size = PATH_MAX;
1209
1210 ret = _NSGetExecutablePath(prog, &prog_size);
1211 if (ret) {
1212 err(EX_OSERR, "_NSGetExecutablePath");
1213 }
1214
1215 for (i = 0; i < argc; i++) {
1216 new_argv[i] = argv[i];
1217 }
1218
1219 new_argv[i] = "--switched_apptype";
1220 new_argv[i + 1] = NULL;
1221
1222 ret = posix_spawnattr_init(&attr);
1223 if (ret) {
1224 errc(EX_OSERR, ret, "posix_spawnattr_init");
1225 }
1226
1227 ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC);
1228 if (ret) {
1229 errc(EX_OSERR, ret, "posix_spawnattr_setflags");
1230 }
1231
1232 ret = posix_spawnattr_setprocesstype_np(&attr, POSIX_SPAWN_PROC_TYPE_APP_DEFAULT);
1233 if (ret) {
1234 errc(EX_OSERR, ret, "posix_spawnattr_setprocesstype_np");
1235 }
1236
1237 ret = posix_spawn(NULL, prog, NULL, &attr, new_argv, environ);
1238 if (ret) {
1239 errc(EX_OSERR, ret, "posix_spawn");
1240 }
1241}
1242
1243/*
1244 * Admittedly not very attractive.
1245 */
1246static void __attribute__((noreturn))
1247usage()
1248{
1249 errx(EX_USAGE, "Usage: %s <threads> <chain | hop | broadcast-single-sem | broadcast-per-thread> "
1250 "<realtime | timeshare | fixed> <iterations>\n\t\t"
1251 "[--trace <traceworthy latency in ns>] "
1252 "[--verbose] [--spin-one] [--spin-all] [--spin-time <nanos>] [--affinity]\n\t\t"
1253 "[--no-sleep] [--drop-priority] [--churn-pri <pri>] [--churn-count <n>]\n\t\t"
1254 "[--rt-churn] [--rt-churn-count <n>] [--rt-ll] [--test-rt] [--test-rt-smt] [--test-rt-avoid0]",
1255 getprogname());
1256}
1257
1258static struct option* g_longopts;
1259static int option_index;
1260
1261static uint32_t
1262read_dec_arg()
1263{
1264 char *cp;
1265 /* char* optarg is a magic global */
1266
1267 uint32_t arg_val = (uint32_t)strtoull(optarg, &cp, 10);
1268
1269 if (cp == optarg || *cp) {
1270 errx(EX_USAGE, "arg --%s requires a decimal number, found \"%s\"",
1271 g_longopts[option_index].name, optarg);
1272 }
1273
1274 return arg_val;
1275}
1276
1277static void
1278parse_args(int argc, char *argv[])
1279{
1280 enum {
1281 OPT_GETOPT = 0,
1282 OPT_SPIN_TIME,
1283 OPT_TRACE,
1284 OPT_PRIORITY,
1285 OPT_CHURN_PRI,
1286 OPT_CHURN_COUNT,
1287 OPT_RT_CHURN_COUNT,
1288 };
1289
1290 static struct option longopts[] = {
1291 /* BEGIN IGNORE CODESTYLE */
1292 { "spin-time", required_argument, NULL, OPT_SPIN_TIME },
1293 { "trace", required_argument, NULL, OPT_TRACE },
1294 { "priority", required_argument, NULL, OPT_PRIORITY },
1295 { "churn-pri", required_argument, NULL, OPT_CHURN_PRI },
1296 { "churn-count", required_argument, NULL, OPT_CHURN_COUNT },
1297 { "rt-churn-count", required_argument, NULL, OPT_RT_CHURN_COUNT },
1298 { "switched_apptype", no_argument, (int*)&g_seen_apptype, TRUE },
1299 { "spin-one", no_argument, (int*)&g_do_one_long_spin, TRUE },
1300 { "intel-only", no_argument, (int*)&g_run_on_intel_only, TRUE },
1301 { "spin-all", no_argument, (int*)&g_do_all_spin, TRUE },
1302 { "affinity", no_argument, (int*)&g_do_affinity, TRUE },
1303 { "no-sleep", no_argument, (int*)&g_do_sleep, FALSE },
1304 { "drop-priority", no_argument, (int*)&g_drop_priority, TRUE },
1305 { "test-rt", no_argument, (int*)&g_test_rt, TRUE },
1306 { "test-rt-smt", no_argument, (int*)&g_test_rt_smt, TRUE },
1307 { "test-rt-avoid0", no_argument, (int*)&g_test_rt_avoid0, TRUE },
1308 { "rt-churn", no_argument, (int*)&g_rt_churn, TRUE },
1309 { "rt-ll", no_argument, (int*)&g_rt_ll, TRUE },
1310 { "histogram", no_argument, (int*)&g_histogram, TRUE },
1311 { "verbose", no_argument, (int*)&g_verbose, TRUE },
1312 { "help", no_argument, NULL, 'h' },
1313 { NULL, 0, NULL, 0 }
1314 /* END IGNORE CODESTYLE */
1315 };
1316
1317 g_longopts = longopts;
1318 int ch = 0;
1319
1320 while ((ch = getopt_long(argc, argv, "h", longopts, &option_index)) != -1) {
1321 switch (ch) {
1322 case OPT_GETOPT:
1323 /* getopt_long set a variable */
1324 break;
1325 case OPT_SPIN_TIME:
1326 g_do_each_spin = TRUE;
1327 g_each_spin_duration_ns = read_dec_arg();
1328 break;
1329 case OPT_TRACE:
1330 g_traceworthy_latency_ns = read_dec_arg();
1331 break;
1332 case OPT_PRIORITY:
1333 g_priority = read_dec_arg();
1334 break;
1335 case OPT_CHURN_PRI:
1336 g_churn_pri = read_dec_arg();
1337 break;
1338 case OPT_CHURN_COUNT:
1339 g_churn_count = read_dec_arg();
1340 break;
1341 case OPT_RT_CHURN_COUNT:
1342 g_rt_churn_count = read_dec_arg();
1343 break;
1344 case '?':
1345 case 'h':
1346 default:
1347 usage();
1348 /* NORETURN */
1349 }
1350 }
1351
1352 /*
1353 * getopt_long reorders all the options to the beginning of the argv array.
1354 * Jump past them to the non-option arguments.
1355 */
1356
1357 argc -= optind;
1358 argv += optind;
1359
1360 if (argc > 4) {
1361 warnx("Too many non-option arguments passed");
1362 usage();
1363 }
1364
1365 if (argc != 4) {
1366 warnx("Missing required <threads> <waketype> <policy> <iterations> arguments");
1367 usage();
1368 }
1369
1370 char *cp;
1371
1372 /* How many threads? */
1373 g_numthreads = (uint32_t)strtoull(argv[0], &cp, 10);
1374
1375 if (cp == argv[0] || *cp) {
1376 errx(EX_USAGE, "numthreads requires a decimal number, found \"%s\"", argv[0]);
1377 }
1378
1379 /* What wakeup pattern? */
1380 g_waketype = parse_wakeup_pattern(argv[1]);
1381
1382 /* Policy */
1383 g_policy = parse_thread_policy(argv[2]);
1384
1385 /* Iterations */
1386 g_iterations = (uint32_t)strtoull(argv[3], &cp, 10);
1387
1388 if (cp == argv[3] || *cp) {
1389 errx(EX_USAGE, "numthreads requires a decimal number, found \"%s\"", argv[3]);
1390 }
1391
1392 if (g_iterations < 1) {
1393 errx(EX_USAGE, "Must have at least one iteration");
1394 }
1395
1396 if (g_numthreads == 1 && g_waketype == WAKE_CHAIN) {
1397 errx(EX_USAGE, "chain mode requires more than one thread");
1398 }
1399
1400 if (g_numthreads == 1 && g_waketype == WAKE_HOP) {
1401 errx(EX_USAGE, "hop mode requires more than one thread");
1402 }
1403}