1*4f1223e8SApple OSS Distributions #include <darwintest.h>
2*4f1223e8SApple OSS Distributions
3*4f1223e8SApple OSS Distributions #include <assert.h>
4*4f1223e8SApple OSS Distributions #include <mach/clock_types.h>
5*4f1223e8SApple OSS Distributions #include <unistd.h>
6*4f1223e8SApple OSS Distributions #include <stdlib.h>
7*4f1223e8SApple OSS Distributions #include <stdio.h>
8*4f1223e8SApple OSS Distributions #include <errno.h>
9*4f1223e8SApple OSS Distributions #include <err.h>
10*4f1223e8SApple OSS Distributions #include <sys/time.h>
11*4f1223e8SApple OSS Distributions #include <mach/mach.h>
12*4f1223e8SApple OSS Distributions #include <mach/mach_time.h>
13*4f1223e8SApple OSS Distributions #include <pthread.h>
14*4f1223e8SApple OSS Distributions #include <perfdata/perfdata.h>
15*4f1223e8SApple OSS Distributions #include <sys/sysctl.h>
16*4f1223e8SApple OSS Distributions #include <sys/stat.h>
17*4f1223e8SApple OSS Distributions #include <sys/mount.h>
18*4f1223e8SApple OSS Distributions #include <stdbool.h>
19*4f1223e8SApple OSS Distributions #include <signal.h>
20*4f1223e8SApple OSS Distributions #include <sys/resource.h>
21*4f1223e8SApple OSS Distributions #include <sys/resource_private.h>
22*4f1223e8SApple OSS Distributions #include <time.h>
23*4f1223e8SApple OSS Distributions #include <os/atomic_private.h>
24*4f1223e8SApple OSS Distributions #include <libproc.h>
25*4f1223e8SApple OSS Distributions #include <TargetConditionals.h>
26*4f1223e8SApple OSS Distributions
27*4f1223e8SApple OSS Distributions #if __has_include(<mach/mach_time_private.h>)
28*4f1223e8SApple OSS Distributions #include <mach/mach_time_private.h>
29*4f1223e8SApple OSS Distributions #else
30*4f1223e8SApple OSS Distributions kern_return_t mach_get_times(uint64_t* absolute_time,
31*4f1223e8SApple OSS Distributions uint64_t* continuous_time,
32*4f1223e8SApple OSS Distributions struct timespec *tp);
33*4f1223e8SApple OSS Distributions #endif
34*4f1223e8SApple OSS Distributions
35*4f1223e8SApple OSS Distributions /*
36*4f1223e8SApple OSS Distributions * This test program creates up to 8 worker threads performing
37*4f1223e8SApple OSS Distributions * mixed workloads of system calls (which contribute to both
38*4f1223e8SApple OSS Distributions * user and system time), as well as spins in userspace (which
39*4f1223e8SApple OSS Distributions * only contribute to user time).
40*4f1223e8SApple OSS Distributions *
41*4f1223e8SApple OSS Distributions * setitimer(2) is used to program timers that fire signals
42*4f1223e8SApple OSS Distributions * after various thresholds. The signal handler detects
43*4f1223e8SApple OSS Distributions * which thread the signal was delivered on by matching the
44*4f1223e8SApple OSS Distributions * stack pointer to ranges for each thread.
45*4f1223e8SApple OSS Distributions *
46*4f1223e8SApple OSS Distributions * After the test scenario is complete, the distribution of
47*4f1223e8SApple OSS Distributions * threads which received interrupts is evaluated to match
48*4f1223e8SApple OSS Distributions * expected heuristics.
49*4f1223e8SApple OSS Distributions */
50*4f1223e8SApple OSS Distributions
51*4f1223e8SApple OSS Distributions T_GLOBAL_META(
52*4f1223e8SApple OSS Distributions T_META_RUN_CONCURRENTLY(false),
53*4f1223e8SApple OSS Distributions T_META_CHECK_LEAKS(false),
54*4f1223e8SApple OSS Distributions T_META_ALL_VALID_ARCHS(true),
55*4f1223e8SApple OSS Distributions T_META_RADAR_COMPONENT_NAME("xnu"),
56*4f1223e8SApple OSS Distributions T_META_RADAR_COMPONENT_VERSION("scheduler"),
57*4f1223e8SApple OSS Distributions T_META_OWNER("chimene"),
58*4f1223e8SApple OSS Distributions T_META_ENABLED(TARGET_OS_OSX),
59*4f1223e8SApple OSS Distributions T_META_TAG_VM_NOT_ELIGIBLE
60*4f1223e8SApple OSS Distributions );
61*4f1223e8SApple OSS Distributions
62*4f1223e8SApple OSS Distributions static void *stat_thread(void *arg);
63*4f1223e8SApple OSS Distributions static void *statfs_thread(void *arg);
64*4f1223e8SApple OSS Distributions
65*4f1223e8SApple OSS Distributions static void alrm_handler(int, struct __siginfo *, void *);
66*4f1223e8SApple OSS Distributions
67*4f1223e8SApple OSS Distributions static semaphore_t gMainWaitForWorkers;
68*4f1223e8SApple OSS Distributions static semaphore_t gWorkersStart;
69*4f1223e8SApple OSS Distributions
70*4f1223e8SApple OSS Distributions static pthread_mutex_t gShouldExitMutex = PTHREAD_MUTEX_INITIALIZER;
71*4f1223e8SApple OSS Distributions static pthread_cond_t gShouldExitCondition = PTHREAD_COND_INITIALIZER;
72*4f1223e8SApple OSS Distributions
73*4f1223e8SApple OSS Distributions static _Atomic bool gShouldExit = false;
74*4f1223e8SApple OSS Distributions
75*4f1223e8SApple OSS Distributions static const uint32_t max_threads = 9;
76*4f1223e8SApple OSS Distributions
77*4f1223e8SApple OSS Distributions static struct threadentry {
78*4f1223e8SApple OSS Distributions pthread_t thread;
79*4f1223e8SApple OSS Distributions uint64_t tid;
80*4f1223e8SApple OSS Distributions void* stack_addr;
81*4f1223e8SApple OSS Distributions size_t stack_size;
82*4f1223e8SApple OSS Distributions bool expect_cpu_usage;
83*4f1223e8SApple OSS Distributions uint32_t alrm_count;
84*4f1223e8SApple OSS Distributions uint32_t vtalrm_count;
85*4f1223e8SApple OSS Distributions uint32_t prof_count;
86*4f1223e8SApple OSS Distributions uint32_t xcpu_count;
87*4f1223e8SApple OSS Distributions struct thsc_time_cpi self_stats;
88*4f1223e8SApple OSS Distributions } __attribute__((aligned(128))) gThreadList[max_threads];
89*4f1223e8SApple OSS Distributions
90*4f1223e8SApple OSS Distributions static uint32_t nworkers;
91*4f1223e8SApple OSS Distributions static uint32_t nthreads;
92*4f1223e8SApple OSS Distributions
93*4f1223e8SApple OSS Distributions static double offcore_time_percent_threshold = 75.0;
94*4f1223e8SApple OSS Distributions
95*4f1223e8SApple OSS Distributions static bool is_rosetta = false;
96*4f1223e8SApple OSS Distributions
97*4f1223e8SApple OSS Distributions static mach_timebase_info_data_t timebase_info;
98*4f1223e8SApple OSS Distributions
99*4f1223e8SApple OSS Distributions static uint64_t
abs_to_nanos(uint64_t abs)100*4f1223e8SApple OSS Distributions abs_to_nanos(uint64_t abs)
101*4f1223e8SApple OSS Distributions {
102*4f1223e8SApple OSS Distributions return abs * timebase_info.numer / timebase_info.denom;
103*4f1223e8SApple OSS Distributions }
104*4f1223e8SApple OSS Distributions
105*4f1223e8SApple OSS Distributions /* Some statistics APIs return host abstime instead of Rosetta-translated abstime */
106*4f1223e8SApple OSS Distributions static uint64_t
abs_to_nanos_host(uint64_t abstime)107*4f1223e8SApple OSS Distributions abs_to_nanos_host(uint64_t abstime)
108*4f1223e8SApple OSS Distributions {
109*4f1223e8SApple OSS Distributions if (is_rosetta) {
110*4f1223e8SApple OSS Distributions return abstime * 125 / 3;
111*4f1223e8SApple OSS Distributions } else {
112*4f1223e8SApple OSS Distributions return abs_to_nanos(abstime);
113*4f1223e8SApple OSS Distributions }
114*4f1223e8SApple OSS Distributions }
115*4f1223e8SApple OSS Distributions
116*4f1223e8SApple OSS Distributions static int
processIsTranslated(void)117*4f1223e8SApple OSS Distributions processIsTranslated(void)
118*4f1223e8SApple OSS Distributions {
119*4f1223e8SApple OSS Distributions int ret = 0;
120*4f1223e8SApple OSS Distributions size_t size = sizeof(ret);
121*4f1223e8SApple OSS Distributions if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
122*4f1223e8SApple OSS Distributions if (errno == ENOENT) {
123*4f1223e8SApple OSS Distributions return 0;
124*4f1223e8SApple OSS Distributions } else {
125*4f1223e8SApple OSS Distributions return -1;
126*4f1223e8SApple OSS Distributions }
127*4f1223e8SApple OSS Distributions }
128*4f1223e8SApple OSS Distributions return ret;
129*4f1223e8SApple OSS Distributions }
130*4f1223e8SApple OSS Distributions
131*4f1223e8SApple OSS Distributions static void
fill_thread_stats(uint32_t i)132*4f1223e8SApple OSS Distributions fill_thread_stats(uint32_t i)
133*4f1223e8SApple OSS Distributions {
134*4f1223e8SApple OSS Distributions struct threadentry *entry = &gThreadList[i];
135*4f1223e8SApple OSS Distributions
136*4f1223e8SApple OSS Distributions int rv = thread_selfcounts(THSC_TIME_CPI, &entry->self_stats, sizeof(entry->self_stats));
137*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "thread_selfcounts(THSC_TIME_CPI)");
138*4f1223e8SApple OSS Distributions }
139*4f1223e8SApple OSS Distributions
140*4f1223e8SApple OSS Distributions T_DECL(setitimer,
141*4f1223e8SApple OSS Distributions "Test various setitimer delivered signals to CPU-burning threads")
142*4f1223e8SApple OSS Distributions {
143*4f1223e8SApple OSS Distributions int rv;
144*4f1223e8SApple OSS Distributions kern_return_t kr;
145*4f1223e8SApple OSS Distributions uint32_t ncpu;
146*4f1223e8SApple OSS Distributions size_t ncpu_size = sizeof(ncpu);
147*4f1223e8SApple OSS Distributions
148*4f1223e8SApple OSS Distributions struct sched_param self_param = {.sched_priority = 47};
149*4f1223e8SApple OSS Distributions
150*4f1223e8SApple OSS Distributions rv = pthread_setschedparam(pthread_self(), SCHED_FIFO, &self_param);
151*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_setschedparam");
152*4f1223e8SApple OSS Distributions
153*4f1223e8SApple OSS Distributions kr = mach_timebase_info(&timebase_info);
154*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_timebase_info");
155*4f1223e8SApple OSS Distributions
156*4f1223e8SApple OSS Distributions is_rosetta = processIsTranslated();
157*4f1223e8SApple OSS Distributions
158*4f1223e8SApple OSS Distributions rv = sysctlbyname("hw.ncpu", &ncpu, &ncpu_size, NULL, 0);
159*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "sysctlbyname(hw.ncpu)");
160*4f1223e8SApple OSS Distributions
161*4f1223e8SApple OSS Distributions if (ncpu < 2) {
162*4f1223e8SApple OSS Distributions T_SKIP("%d CPUs not supported for test, returning success", ncpu);
163*4f1223e8SApple OSS Distributions }
164*4f1223e8SApple OSS Distributions
165*4f1223e8SApple OSS Distributions nworkers = MIN(max_threads - 1, ncpu);
166*4f1223e8SApple OSS Distributions nthreads = nworkers + 1;
167*4f1223e8SApple OSS Distributions
168*4f1223e8SApple OSS Distributions T_LOG("rosetta = %d\n", is_rosetta);
169*4f1223e8SApple OSS Distributions T_LOG("hw.ncpu = %d\n", ncpu);
170*4f1223e8SApple OSS Distributions T_LOG("nworkers = %d\n", nworkers);
171*4f1223e8SApple OSS Distributions T_LOG("nthreads = %d\n", nthreads);
172*4f1223e8SApple OSS Distributions
173*4f1223e8SApple OSS Distributions kr = semaphore_create(mach_task_self(), &gMainWaitForWorkers, SYNC_POLICY_FIFO, 0);
174*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "semaphore_create()");
175*4f1223e8SApple OSS Distributions
176*4f1223e8SApple OSS Distributions kr = semaphore_create(mach_task_self(), &gWorkersStart, SYNC_POLICY_FIFO, 0);
177*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "semaphore_create()");
178*4f1223e8SApple OSS Distributions
179*4f1223e8SApple OSS Distributions pthread_attr_t attr;
180*4f1223e8SApple OSS Distributions
181*4f1223e8SApple OSS Distributions rv = pthread_attr_init(&attr);
182*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_attr_init");
183*4f1223e8SApple OSS Distributions
184*4f1223e8SApple OSS Distributions struct sched_param child_param = {.sched_priority = 37};
185*4f1223e8SApple OSS Distributions
186*4f1223e8SApple OSS Distributions rv = pthread_attr_setschedparam(&attr, &child_param);
187*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_attr_set_qos_class_np");
188*4f1223e8SApple OSS Distributions
189*4f1223e8SApple OSS Distributions for (uint32_t i = 0; i < nthreads; i++) {
190*4f1223e8SApple OSS Distributions if (i == 0) {
191*4f1223e8SApple OSS Distributions gThreadList[i].thread = pthread_self();
192*4f1223e8SApple OSS Distributions } else {
193*4f1223e8SApple OSS Distributions rv = pthread_create(&gThreadList[i].thread, &attr,
194*4f1223e8SApple OSS Distributions i % 2 ? stat_thread : statfs_thread,
195*4f1223e8SApple OSS Distributions (void *)(uintptr_t)i);
196*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_create");
197*4f1223e8SApple OSS Distributions gThreadList[i].expect_cpu_usage = i % 2 == 0 ? true : false;
198*4f1223e8SApple OSS Distributions }
199*4f1223e8SApple OSS Distributions
200*4f1223e8SApple OSS Distributions rv = pthread_threadid_np(gThreadList[i].thread, &gThreadList[i].tid);
201*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_threadid_np");
202*4f1223e8SApple OSS Distributions
203*4f1223e8SApple OSS Distributions gThreadList[i].stack_addr = pthread_get_stackaddr_np(gThreadList[i].thread);
204*4f1223e8SApple OSS Distributions gThreadList[i].stack_size = pthread_get_stacksize_np(gThreadList[i].thread);
205*4f1223e8SApple OSS Distributions }
206*4f1223e8SApple OSS Distributions
207*4f1223e8SApple OSS Distributions rv = pthread_attr_destroy(&attr);
208*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_attr_destroy");
209*4f1223e8SApple OSS Distributions
210*4f1223e8SApple OSS Distributions for (uint32_t i = 1; i < nthreads; i++) {
211*4f1223e8SApple OSS Distributions kr = semaphore_wait(gMainWaitForWorkers);
212*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "semaphore_wait()");
213*4f1223e8SApple OSS Distributions }
214*4f1223e8SApple OSS Distributions
215*4f1223e8SApple OSS Distributions for (uint32_t i = 0; i < nthreads; i++) {
216*4f1223e8SApple OSS Distributions T_LOG("Thread %p (0x%llx) checked in, stack %p/%p\n",
217*4f1223e8SApple OSS Distributions (void*)gThreadList[i].thread,
218*4f1223e8SApple OSS Distributions gThreadList[i].tid,
219*4f1223e8SApple OSS Distributions gThreadList[i].stack_addr,
220*4f1223e8SApple OSS Distributions (void *)gThreadList[i].stack_size);
221*4f1223e8SApple OSS Distributions }
222*4f1223e8SApple OSS Distributions
223*4f1223e8SApple OSS Distributions sigset_t sigmk;
224*4f1223e8SApple OSS Distributions sigemptyset(&sigmk);
225*4f1223e8SApple OSS Distributions
226*4f1223e8SApple OSS Distributions struct sigaction sigact = {
227*4f1223e8SApple OSS Distributions .sa_sigaction = alrm_handler,
228*4f1223e8SApple OSS Distributions .sa_mask = sigmk,
229*4f1223e8SApple OSS Distributions .sa_flags = SA_SIGINFO,
230*4f1223e8SApple OSS Distributions };
231*4f1223e8SApple OSS Distributions
232*4f1223e8SApple OSS Distributions rv = sigaction(SIGALRM, &sigact, NULL);
233*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "sigaction(SIGALRM)");
234*4f1223e8SApple OSS Distributions
235*4f1223e8SApple OSS Distributions rv = sigaction(SIGVTALRM, &sigact, NULL);
236*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "sigaction(SIGVTALRM)");
237*4f1223e8SApple OSS Distributions
238*4f1223e8SApple OSS Distributions rv = sigaction(SIGPROF, &sigact, NULL);
239*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "sigaction(SIGPROF)");
240*4f1223e8SApple OSS Distributions
241*4f1223e8SApple OSS Distributions rv = sigaction(SIGXCPU, &sigact, NULL);
242*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "sigaction(SIGXCPU)");
243*4f1223e8SApple OSS Distributions
244*4f1223e8SApple OSS Distributions struct itimerval itime = {
245*4f1223e8SApple OSS Distributions .it_interval.tv_sec = 0,
246*4f1223e8SApple OSS Distributions .it_interval.tv_usec = 10000,
247*4f1223e8SApple OSS Distributions .it_value.tv_sec = 0,
248*4f1223e8SApple OSS Distributions .it_value.tv_usec = 10, /* immediately */
249*4f1223e8SApple OSS Distributions };
250*4f1223e8SApple OSS Distributions
251*4f1223e8SApple OSS Distributions rv = setitimer(ITIMER_REAL, &itime, NULL);
252*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "setitimer(ITIMER_REAL)");
253*4f1223e8SApple OSS Distributions
254*4f1223e8SApple OSS Distributions rv = setitimer(ITIMER_VIRTUAL, &itime, NULL);
255*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "setitimer(ITIMER_REAL)");
256*4f1223e8SApple OSS Distributions
257*4f1223e8SApple OSS Distributions rv = setitimer(ITIMER_PROF, &itime, NULL);
258*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "setitimer(ITIMER_REAL)");
259*4f1223e8SApple OSS Distributions
260*4f1223e8SApple OSS Distributions struct rlimit rlim = {};
261*4f1223e8SApple OSS Distributions
262*4f1223e8SApple OSS Distributions rv = getrlimit(RLIMIT_CPU, &rlim);
263*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "getrlimit(RLIMIT_CPU)");
264*4f1223e8SApple OSS Distributions
265*4f1223e8SApple OSS Distributions rlim.rlim_cur = 1;
266*4f1223e8SApple OSS Distributions rv = setrlimit(RLIMIT_CPU, &rlim);
267*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "setrlimit(RLIMIT_CPU)");
268*4f1223e8SApple OSS Distributions
269*4f1223e8SApple OSS Distributions rv = pthread_mutex_lock(&gShouldExitMutex);
270*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_mutex_lock(&gShouldExitMutex)");
271*4f1223e8SApple OSS Distributions
272*4f1223e8SApple OSS Distributions kr = semaphore_signal_all(gWorkersStart);
273*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "semaphore_signal_all()");
274*4f1223e8SApple OSS Distributions
275*4f1223e8SApple OSS Distributions struct timespec timenow = {};
276*4f1223e8SApple OSS Distributions uint64_t time_start;
277*4f1223e8SApple OSS Distributions
278*4f1223e8SApple OSS Distributions kr = mach_get_times(&time_start, NULL, &timenow);
279*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_get_times()");
280*4f1223e8SApple OSS Distributions
281*4f1223e8SApple OSS Distributions struct timespec timeout = {
282*4f1223e8SApple OSS Distributions .tv_sec = timenow.tv_sec + 10,
283*4f1223e8SApple OSS Distributions .tv_nsec = timenow.tv_nsec,
284*4f1223e8SApple OSS Distributions };
285*4f1223e8SApple OSS Distributions
286*4f1223e8SApple OSS Distributions uint64_t time_end = 0;
287*4f1223e8SApple OSS Distributions
288*4f1223e8SApple OSS Distributions do {
289*4f1223e8SApple OSS Distributions assert(os_atomic_load(&gShouldExit, relaxed) == false);
290*4f1223e8SApple OSS Distributions
291*4f1223e8SApple OSS Distributions rv = pthread_cond_timedwait(&gShouldExitCondition, &gShouldExitMutex, &timeout);
292*4f1223e8SApple OSS Distributions if (rv == ETIMEDOUT) {
293*4f1223e8SApple OSS Distributions os_atomic_store(&gShouldExit, true, relaxed);
294*4f1223e8SApple OSS Distributions
295*4f1223e8SApple OSS Distributions time_end = mach_absolute_time();
296*4f1223e8SApple OSS Distributions
297*4f1223e8SApple OSS Distributions struct itimerval itime_stop = {
298*4f1223e8SApple OSS Distributions .it_interval.tv_sec = 0,
299*4f1223e8SApple OSS Distributions .it_interval.tv_usec = 0,
300*4f1223e8SApple OSS Distributions .it_value.tv_sec = 0,
301*4f1223e8SApple OSS Distributions .it_value.tv_usec = 0, /* stop immediately */
302*4f1223e8SApple OSS Distributions };
303*4f1223e8SApple OSS Distributions
304*4f1223e8SApple OSS Distributions rv = setitimer(ITIMER_REAL, &itime_stop, NULL);
305*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "setitimer(ITIMER_REAL)");
306*4f1223e8SApple OSS Distributions
307*4f1223e8SApple OSS Distributions rv = setitimer(ITIMER_VIRTUAL, &itime_stop, NULL);
308*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "setitimer(ITIMER_VIRTUAL)");
309*4f1223e8SApple OSS Distributions
310*4f1223e8SApple OSS Distributions rv = setitimer(ITIMER_PROF, &itime_stop, NULL);
311*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "setitimer(ITIMER_PROF)");
312*4f1223e8SApple OSS Distributions
313*4f1223e8SApple OSS Distributions break;
314*4f1223e8SApple OSS Distributions } else {
315*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_cond_timedwait(&gShouldExitCondition, ...)");
316*4f1223e8SApple OSS Distributions }
317*4f1223e8SApple OSS Distributions } while (true);
318*4f1223e8SApple OSS Distributions
319*4f1223e8SApple OSS Distributions rv = pthread_mutex_unlock(&gShouldExitMutex);
320*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_mutex_unlock(&gShouldExitMutex)");
321*4f1223e8SApple OSS Distributions
322*4f1223e8SApple OSS Distributions for (uint32_t i = 1; i < nthreads; i++) {
323*4f1223e8SApple OSS Distributions rv = pthread_join(gThreadList[i].thread, NULL);
324*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_join");
325*4f1223e8SApple OSS Distributions }
326*4f1223e8SApple OSS Distributions
327*4f1223e8SApple OSS Distributions uint64_t test_duration = time_end - time_start;
328*4f1223e8SApple OSS Distributions uint64_t test_duration_ns = abs_to_nanos(test_duration);
329*4f1223e8SApple OSS Distributions
330*4f1223e8SApple OSS Distributions double elapsed_secs = (double) test_duration_ns / (uint64_t)NSEC_PER_SEC;
331*4f1223e8SApple OSS Distributions
332*4f1223e8SApple OSS Distributions T_LOG("test duration %3.3f seconds\n", elapsed_secs);
333*4f1223e8SApple OSS Distributions
334*4f1223e8SApple OSS Distributions fill_thread_stats(0);
335*4f1223e8SApple OSS Distributions
336*4f1223e8SApple OSS Distributions struct rusage_info_v6 ru = {};
337*4f1223e8SApple OSS Distributions rv = proc_pid_rusage(getpid(), RUSAGE_INFO_V6, (rusage_info_t *)&ru);
338*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "proc_pid_rusage");
339*4f1223e8SApple OSS Distributions
340*4f1223e8SApple OSS Distributions uint64_t total_user_time_ns = abs_to_nanos_host(ru.ri_user_time);
341*4f1223e8SApple OSS Distributions double total_user_time_s = (double)total_user_time_ns / (uint64_t)NSEC_PER_SEC;
342*4f1223e8SApple OSS Distributions
343*4f1223e8SApple OSS Distributions uint64_t total_system_time_ns = abs_to_nanos_host(ru.ri_system_time);
344*4f1223e8SApple OSS Distributions double total_system_time_s = (double)total_system_time_ns / (uint64_t)NSEC_PER_SEC;
345*4f1223e8SApple OSS Distributions
346*4f1223e8SApple OSS Distributions uint64_t total_time_ns = (total_user_time_ns + total_system_time_ns);
347*4f1223e8SApple OSS Distributions double total_time_s = (double)total_time_ns / (uint64_t)NSEC_PER_SEC;
348*4f1223e8SApple OSS Distributions
349*4f1223e8SApple OSS Distributions uint64_t total_runnable_time_ns = abs_to_nanos_host(ru.ri_runnable_time);
350*4f1223e8SApple OSS Distributions double total_runnable_time_s = (double)total_runnable_time_ns / (uint64_t)NSEC_PER_SEC;
351*4f1223e8SApple OSS Distributions
352*4f1223e8SApple OSS Distributions uint64_t total_pending_time_ns = total_runnable_time_ns - (total_time_ns);
353*4f1223e8SApple OSS Distributions double total_pending_time_s = (double)total_pending_time_ns / (uint64_t)NSEC_PER_SEC;
354*4f1223e8SApple OSS Distributions
355*4f1223e8SApple OSS Distributions uint64_t total_p_time_ns = abs_to_nanos_host(ru.ri_user_ptime + ru.ri_system_ptime);
356*4f1223e8SApple OSS Distributions double total_p_time_s = (double)total_p_time_ns / (uint64_t)NSEC_PER_SEC;
357*4f1223e8SApple OSS Distributions
358*4f1223e8SApple OSS Distributions T_LOG("total usage: time: %3.3f user: %3.3f kernel: %3.3f runnable: %3.3f pending: %3.3f pcore: %3.3f\n",
359*4f1223e8SApple OSS Distributions total_time_s, total_user_time_s, total_system_time_s,
360*4f1223e8SApple OSS Distributions total_runnable_time_s, total_pending_time_s,
361*4f1223e8SApple OSS Distributions total_p_time_s);
362*4f1223e8SApple OSS Distributions
363*4f1223e8SApple OSS Distributions /*
364*4f1223e8SApple OSS Distributions * "Good" data looks like:
365*4f1223e8SApple OSS Distributions *
366*4f1223e8SApple OSS Distributions * total usage: time: 77.696 user: 16.570 kernel: 61.126 runnable: 79.951 pending: 2.255 pcore: 72.719
367*4f1223e8SApple OSS Distributions * Thread ALRM VTALRM PROF XCPU inst cycle user kernel offcore type
368*4f1223e8SApple OSS Distributions * 0x16f78f000 0 251 811 0 27680301973 28913501188 3706622958 ( 38.14%) 6012631083 ( 61.86%) 2.81% statfs
369*4f1223e8SApple OSS Distributions * 0x16f81b000 0 2 889 0 27962710058 28780576123 439297291 ( 4.53%) 9259942583 ( 95.47%) 3.01% stat
370*4f1223e8SApple OSS Distributions * 0x16f8a7000 0 251 836 0 27558331077 28889228535 3699010000 ( 38.08%) 6016015083 ( 61.92%) 2.85% statfs
371*4f1223e8SApple OSS Distributions * 0x16f933000 0 0 939 0 28078084696 28880195679 443067500 ( 4.56%) 9269807666 ( 95.44%) 2.87% stat
372*4f1223e8SApple OSS Distributions * 0x16f9bf000 0 283 874 0 27691851016 28969873070 3710916750 ( 38.16%) 6012783541 ( 61.84%) 2.76% statfs
373*4f1223e8SApple OSS Distributions * 0x16fa4b000 0 2 908 1 27945063330 28769971396 438583000 ( 4.53%) 9252694291 ( 95.47%) 3.09% stat
374*4f1223e8SApple OSS Distributions * 0x16fad7000 0 262 889 0 27328496429 28772748055 3689245375 ( 38.03%) 6011061458 ( 61.97%) 3.00% statfs
375*4f1223e8SApple OSS Distributions * 0x16fb63000 0 0 914 0 27942195343 28757254100 439690166 ( 4.53%) 9256659500 ( 95.47%) 3.04% stat
376*4f1223e8SApple OSS Distributions * 0x1fe2bb400 1001 0 3 0 72144372 102339334 3532125 ( 9.35%) 34249208 ( 90.65%) 99.62% main
377*4f1223e8SApple OSS Distributions */
378*4f1223e8SApple OSS Distributions uint32_t total_alrm = 0;
379*4f1223e8SApple OSS Distributions uint32_t total_vtalrm = 0;
380*4f1223e8SApple OSS Distributions uint32_t total_prof = 0;
381*4f1223e8SApple OSS Distributions uint32_t total_xcpu = 0;
382*4f1223e8SApple OSS Distributions uint32_t total_vtalrm_in_cpubound = 0;
383*4f1223e8SApple OSS Distributions
384*4f1223e8SApple OSS Distributions uint32_t total_threads_not_finding_cpus = 0;
385*4f1223e8SApple OSS Distributions
386*4f1223e8SApple OSS Distributions T_LOG("Thread ALRM VTALRM PROF XCPU "
387*4f1223e8SApple OSS Distributions "inst cycle user kernel "
388*4f1223e8SApple OSS Distributions "offcore type\n");
389*4f1223e8SApple OSS Distributions
390*4f1223e8SApple OSS Distributions for (uint32_t i = 0; i < nthreads; i++) {
391*4f1223e8SApple OSS Distributions uint64_t user_time = abs_to_nanos_host(gThreadList[i].self_stats.ttci_user_time_mach);
392*4f1223e8SApple OSS Distributions uint64_t system_time = abs_to_nanos_host(gThreadList[i].self_stats.ttci_system_time_mach);
393*4f1223e8SApple OSS Distributions
394*4f1223e8SApple OSS Distributions
395*4f1223e8SApple OSS Distributions uint64_t total_time = user_time + system_time;
396*4f1223e8SApple OSS Distributions
397*4f1223e8SApple OSS Distributions double percentage_user = (double)user_time / (double) total_time * 100;
398*4f1223e8SApple OSS Distributions double percentage_system = (double)system_time / (double) total_time * 100;
399*4f1223e8SApple OSS Distributions double percentage_not_running = (double)(test_duration_ns - total_time) / (double) test_duration_ns * 100;
400*4f1223e8SApple OSS Distributions
401*4f1223e8SApple OSS Distributions char* thread_type_str = "";
402*4f1223e8SApple OSS Distributions char* warning_str = "";
403*4f1223e8SApple OSS Distributions
404*4f1223e8SApple OSS Distributions if (i == 0) {
405*4f1223e8SApple OSS Distributions thread_type_str = "main ";
406*4f1223e8SApple OSS Distributions } else {
407*4f1223e8SApple OSS Distributions thread_type_str = i % 2 ? "stat " : "statfs ";
408*4f1223e8SApple OSS Distributions
409*4f1223e8SApple OSS Distributions if (percentage_not_running > offcore_time_percent_threshold) {
410*4f1223e8SApple OSS Distributions total_threads_not_finding_cpus++;
411*4f1223e8SApple OSS Distributions warning_str = "** too much offcore time **";
412*4f1223e8SApple OSS Distributions }
413*4f1223e8SApple OSS Distributions }
414*4f1223e8SApple OSS Distributions
415*4f1223e8SApple OSS Distributions T_LOG("0x%010llx %6d %6d %6d %6d %12lld %12lld %12lld (%7.2f%%) %12lld (%7.2f%%) %7.2f%% %s%s\n",
416*4f1223e8SApple OSS Distributions gThreadList[i].tid,
417*4f1223e8SApple OSS Distributions gThreadList[i].alrm_count,
418*4f1223e8SApple OSS Distributions gThreadList[i].vtalrm_count,
419*4f1223e8SApple OSS Distributions gThreadList[i].prof_count,
420*4f1223e8SApple OSS Distributions gThreadList[i].xcpu_count,
421*4f1223e8SApple OSS Distributions gThreadList[i].self_stats.ttci_instructions,
422*4f1223e8SApple OSS Distributions gThreadList[i].self_stats.ttci_cycles,
423*4f1223e8SApple OSS Distributions user_time, percentage_user,
424*4f1223e8SApple OSS Distributions system_time, percentage_system,
425*4f1223e8SApple OSS Distributions percentage_not_running,
426*4f1223e8SApple OSS Distributions thread_type_str, warning_str);
427*4f1223e8SApple OSS Distributions
428*4f1223e8SApple OSS Distributions total_alrm += gThreadList[i].alrm_count;
429*4f1223e8SApple OSS Distributions total_vtalrm += gThreadList[i].vtalrm_count;
430*4f1223e8SApple OSS Distributions total_prof += gThreadList[i].prof_count;
431*4f1223e8SApple OSS Distributions total_xcpu += gThreadList[i].xcpu_count;
432*4f1223e8SApple OSS Distributions
433*4f1223e8SApple OSS Distributions if (gThreadList[i].expect_cpu_usage) {
434*4f1223e8SApple OSS Distributions total_vtalrm_in_cpubound += gThreadList[i].vtalrm_count;
435*4f1223e8SApple OSS Distributions }
436*4f1223e8SApple OSS Distributions }
437*4f1223e8SApple OSS Distributions
438*4f1223e8SApple OSS Distributions /*
439*4f1223e8SApple OSS Distributions * We expect all SIGALRM to go to the main thread, because it is the
440*4f1223e8SApple OSS Distributions * first thread in the process with the signal unmasked, and we
441*4f1223e8SApple OSS Distributions * never expect the signal handler itself to take >10ms
442*4f1223e8SApple OSS Distributions *
443*4f1223e8SApple OSS Distributions * This can happen if the main thread is preempted for the entire 10ms duration, though.
444*4f1223e8SApple OSS Distributions * Being high priority, it shouldn't be delayed for more than 10ms too often.
445*4f1223e8SApple OSS Distributions * Allow up to 10% to deliver to other threads.
446*4f1223e8SApple OSS Distributions */
447*4f1223e8SApple OSS Distributions if ((double)gThreadList[0].alrm_count * 100 / total_alrm < 90.0) {
448*4f1223e8SApple OSS Distributions T_FAIL("SIGALRM delivered to non-main thread more than 10%% of the time (%d of %d)",
449*4f1223e8SApple OSS Distributions gThreadList[0].alrm_count,
450*4f1223e8SApple OSS Distributions total_alrm);
451*4f1223e8SApple OSS Distributions }
452*4f1223e8SApple OSS Distributions
453*4f1223e8SApple OSS Distributions /* We expect all worker threads to find CPUs of their own for most of the test */
454*4f1223e8SApple OSS Distributions if (total_threads_not_finding_cpus != 0) {
455*4f1223e8SApple OSS Distributions T_FAIL("%d worker threads spent more than %2.0f%% of time off-core",
456*4f1223e8SApple OSS Distributions total_threads_not_finding_cpus, offcore_time_percent_threshold);
457*4f1223e8SApple OSS Distributions }
458*4f1223e8SApple OSS Distributions
459*4f1223e8SApple OSS Distributions /*
460*4f1223e8SApple OSS Distributions * SIGVTALRM is delivered based on user time, and we expect the busy
461*4f1223e8SApple OSS Distributions * threads to have an advantage and account for 80% (non-scientific) of events,
462*4f1223e8SApple OSS Distributions * since the other threads will spend more time in kernel mode.
463*4f1223e8SApple OSS Distributions */
464*4f1223e8SApple OSS Distributions if (total_vtalrm_in_cpubound * 100 / total_vtalrm < 80) {
465*4f1223e8SApple OSS Distributions T_FAIL("SIGVTALRM delivered to threads without extra userspace spin (only %d of %d)",
466*4f1223e8SApple OSS Distributions total_vtalrm_in_cpubound, total_vtalrm);
467*4f1223e8SApple OSS Distributions }
468*4f1223e8SApple OSS Distributions
469*4f1223e8SApple OSS Distributions /*
470*4f1223e8SApple OSS Distributions * SIGPROF is delivered based on user+system time, and we expect it to be distributed
471*4f1223e8SApple OSS Distributions * among non-blocked threads (so not the main thread, which only handles SIGALRM).
472*4f1223e8SApple OSS Distributions */
473*4f1223e8SApple OSS Distributions if (gThreadList[0].prof_count * 100 / total_prof > 1) {
474*4f1223e8SApple OSS Distributions T_FAIL("SIGPROF delivered to main thread more than 1%% (%d of %d)",
475*4f1223e8SApple OSS Distributions gThreadList[0].prof_count,
476*4f1223e8SApple OSS Distributions total_prof);
477*4f1223e8SApple OSS Distributions }
478*4f1223e8SApple OSS Distributions
479*4f1223e8SApple OSS Distributions /*
480*4f1223e8SApple OSS Distributions * SIGXCPU should be delivered exactly once.
481*4f1223e8SApple OSS Distributions */
482*4f1223e8SApple OSS Distributions if (total_xcpu == 0) {
483*4f1223e8SApple OSS Distributions T_FAIL("SIGXCPU delivered %d times (expected at least once)", total_xcpu);
484*4f1223e8SApple OSS Distributions }
485*4f1223e8SApple OSS Distributions }
486*4f1223e8SApple OSS Distributions
487*4f1223e8SApple OSS Distributions static void *
stat_thread(void * arg)488*4f1223e8SApple OSS Distributions stat_thread(void *arg)
489*4f1223e8SApple OSS Distributions {
490*4f1223e8SApple OSS Distributions kern_return_t kr;
491*4f1223e8SApple OSS Distributions int rv;
492*4f1223e8SApple OSS Distributions
493*4f1223e8SApple OSS Distributions /* This wait can be aborted by one of the signals, so we make sure to wait for the first iteration of main */
494*4f1223e8SApple OSS Distributions kr = semaphore_wait_signal(gWorkersStart, gMainWaitForWorkers);
495*4f1223e8SApple OSS Distributions if (kr != KERN_ABORTED) {
496*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "semaphore_wait_signal()");
497*4f1223e8SApple OSS Distributions }
498*4f1223e8SApple OSS Distributions
499*4f1223e8SApple OSS Distributions rv = pthread_mutex_lock(&gShouldExitMutex);
500*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_mutex_lock(&gShouldExitMutex)");
501*4f1223e8SApple OSS Distributions rv = pthread_mutex_unlock(&gShouldExitMutex);
502*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_mutex_unlock(&gShouldExitMutex)");
503*4f1223e8SApple OSS Distributions
504*4f1223e8SApple OSS Distributions do {
505*4f1223e8SApple OSS Distributions struct stat sb;
506*4f1223e8SApple OSS Distributions
507*4f1223e8SApple OSS Distributions rv = stat("/", &sb);
508*4f1223e8SApple OSS Distributions if (rv != 0) {
509*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "stat");
510*4f1223e8SApple OSS Distributions }
511*4f1223e8SApple OSS Distributions } while (os_atomic_load(&gShouldExit, relaxed) == false);
512*4f1223e8SApple OSS Distributions
513*4f1223e8SApple OSS Distributions fill_thread_stats((uint32_t)(uintptr_t)arg);
514*4f1223e8SApple OSS Distributions
515*4f1223e8SApple OSS Distributions return NULL;
516*4f1223e8SApple OSS Distributions }
517*4f1223e8SApple OSS Distributions
518*4f1223e8SApple OSS Distributions static void *
statfs_thread(void * arg)519*4f1223e8SApple OSS Distributions statfs_thread(void *arg)
520*4f1223e8SApple OSS Distributions {
521*4f1223e8SApple OSS Distributions kern_return_t kr;
522*4f1223e8SApple OSS Distributions uint64_t previous_spin_timestamp;
523*4f1223e8SApple OSS Distributions int iteration = 0;
524*4f1223e8SApple OSS Distributions int rv;
525*4f1223e8SApple OSS Distributions
526*4f1223e8SApple OSS Distributions /* This wait can be aborted by one of the signals, so we make sure to wait for the first iteration of main */
527*4f1223e8SApple OSS Distributions kr = semaphore_wait_signal(gWorkersStart, gMainWaitForWorkers);
528*4f1223e8SApple OSS Distributions if (kr != KERN_ABORTED) {
529*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "semaphore_wait_signal()");
530*4f1223e8SApple OSS Distributions }
531*4f1223e8SApple OSS Distributions
532*4f1223e8SApple OSS Distributions rv = pthread_mutex_lock(&gShouldExitMutex);
533*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_mutex_lock(&gShouldExitMutex)");
534*4f1223e8SApple OSS Distributions rv = pthread_mutex_unlock(&gShouldExitMutex);
535*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pthread_mutex_unlock(&gShouldExitMutex)");
536*4f1223e8SApple OSS Distributions
537*4f1223e8SApple OSS Distributions previous_spin_timestamp = mach_absolute_time();
538*4f1223e8SApple OSS Distributions
539*4f1223e8SApple OSS Distributions do {
540*4f1223e8SApple OSS Distributions struct statfs sf;
541*4f1223e8SApple OSS Distributions
542*4f1223e8SApple OSS Distributions /*
543*4f1223e8SApple OSS Distributions * Every so many system calls, inject a spin in userspace
544*4f1223e8SApple OSS Distributions * proportional to how much time was spent performing the
545*4f1223e8SApple OSS Distributions * system calls.
546*4f1223e8SApple OSS Distributions */
547*4f1223e8SApple OSS Distributions #define SYSCALL_ITERATIONS_BETWEEN_SPINS (10000)
548*4f1223e8SApple OSS Distributions if (++iteration % SYSCALL_ITERATIONS_BETWEEN_SPINS == 0) {
549*4f1223e8SApple OSS Distributions uint64_t now = mach_absolute_time();
550*4f1223e8SApple OSS Distributions uint64_t spin_deadline = now + (now - previous_spin_timestamp) / 2;
551*4f1223e8SApple OSS Distributions
552*4f1223e8SApple OSS Distributions while (mach_absolute_time() < spin_deadline) {
553*4f1223e8SApple OSS Distributions ;
554*4f1223e8SApple OSS Distributions }
555*4f1223e8SApple OSS Distributions
556*4f1223e8SApple OSS Distributions previous_spin_timestamp = mach_absolute_time();
557*4f1223e8SApple OSS Distributions }
558*4f1223e8SApple OSS Distributions
559*4f1223e8SApple OSS Distributions rv = statfs("/", &sf);
560*4f1223e8SApple OSS Distributions if (rv != 0) {
561*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "statfs");
562*4f1223e8SApple OSS Distributions }
563*4f1223e8SApple OSS Distributions } while (os_atomic_load(&gShouldExit, relaxed) == false);
564*4f1223e8SApple OSS Distributions
565*4f1223e8SApple OSS Distributions fill_thread_stats((uint32_t)(uintptr_t)arg);
566*4f1223e8SApple OSS Distributions
567*4f1223e8SApple OSS Distributions return NULL;
568*4f1223e8SApple OSS Distributions }
569*4f1223e8SApple OSS Distributions
570*4f1223e8SApple OSS Distributions static void
alrm_handler(int signum,struct __siginfo * info __unused,void * uap)571*4f1223e8SApple OSS Distributions alrm_handler(int signum, struct __siginfo *info __unused, void *uap)
572*4f1223e8SApple OSS Distributions {
573*4f1223e8SApple OSS Distributions ucontext_t *context = (ucontext_t *)uap;
574*4f1223e8SApple OSS Distributions struct threadentry *entry = NULL;
575*4f1223e8SApple OSS Distributions void *sp;
576*4f1223e8SApple OSS Distributions
577*4f1223e8SApple OSS Distributions #if defined(__arm64__)
578*4f1223e8SApple OSS Distributions sp = (void *)__darwin_arm_thread_state64_get_sp((context->uc_mcontext)->__ss);
579*4f1223e8SApple OSS Distributions #elif defined(__i386__)
580*4f1223e8SApple OSS Distributions sp = (void *)(context->uc_mcontext)->__ss.__esp;
581*4f1223e8SApple OSS Distributions #elif defined(__x86_64__)
582*4f1223e8SApple OSS Distributions sp = (void *)(context->uc_mcontext)->__ss.__rsp;
583*4f1223e8SApple OSS Distributions #else
584*4f1223e8SApple OSS Distributions #error Unrecognized architecture
585*4f1223e8SApple OSS Distributions #endif
586*4f1223e8SApple OSS Distributions
587*4f1223e8SApple OSS Distributions for (uint32_t i = 0; i < nworkers + 1; i++) {
588*4f1223e8SApple OSS Distributions struct threadentry *t = &gThreadList[i];
589*4f1223e8SApple OSS Distributions if (((uintptr_t)sp >= ((uintptr_t)t->stack_addr - t->stack_size) &&
590*4f1223e8SApple OSS Distributions ((uintptr_t)sp < (uintptr_t)t->stack_addr))) {
591*4f1223e8SApple OSS Distributions entry = t;
592*4f1223e8SApple OSS Distributions break;
593*4f1223e8SApple OSS Distributions }
594*4f1223e8SApple OSS Distributions }
595*4f1223e8SApple OSS Distributions
596*4f1223e8SApple OSS Distributions if (entry == NULL) {
597*4f1223e8SApple OSS Distributions T_ASSERT_FAIL("Signal %d delivered to unknown thread, SP=%p", signum, sp);
598*4f1223e8SApple OSS Distributions }
599*4f1223e8SApple OSS Distributions
600*4f1223e8SApple OSS Distributions switch (signum) {
601*4f1223e8SApple OSS Distributions case SIGALRM:
602*4f1223e8SApple OSS Distributions os_atomic_inc(&entry->alrm_count, relaxed);
603*4f1223e8SApple OSS Distributions break;
604*4f1223e8SApple OSS Distributions case SIGVTALRM:
605*4f1223e8SApple OSS Distributions os_atomic_inc(&entry->vtalrm_count, relaxed);
606*4f1223e8SApple OSS Distributions break;
607*4f1223e8SApple OSS Distributions case SIGPROF:
608*4f1223e8SApple OSS Distributions os_atomic_inc(&entry->prof_count, relaxed);
609*4f1223e8SApple OSS Distributions break;
610*4f1223e8SApple OSS Distributions case SIGXCPU:
611*4f1223e8SApple OSS Distributions os_atomic_inc(&entry->xcpu_count, relaxed);
612*4f1223e8SApple OSS Distributions break;
613*4f1223e8SApple OSS Distributions }
614*4f1223e8SApple OSS Distributions }
615*4f1223e8SApple OSS Distributions
616*4f1223e8SApple OSS Distributions // When the SIGPROF signal was received.
617*4f1223e8SApple OSS Distributions static uint64_t sigprof_received_ns = 0;
618*4f1223e8SApple OSS Distributions
619*4f1223e8SApple OSS Distributions static const uint64_t ITIMER_PROF_SECS = 2;
620*4f1223e8SApple OSS Distributions #define PROF_EXTRA_THREAD_COUNT (1)
621*4f1223e8SApple OSS Distributions // Include the main thread as participating in consuming CPU.
622*4f1223e8SApple OSS Distributions static const uint64_t EXPECTED_PROF_DURATION_SECS =
623*4f1223e8SApple OSS Distributions ITIMER_PROF_SECS / (PROF_EXTRA_THREAD_COUNT + 1);
624*4f1223e8SApple OSS Distributions static const uint64_t EXTRA_TIMEOUT_SECS = 1;
625*4f1223e8SApple OSS Distributions
626*4f1223e8SApple OSS Distributions struct cpu_spin_args {
627*4f1223e8SApple OSS Distributions bool spin_while_true;
628*4f1223e8SApple OSS Distributions };
629*4f1223e8SApple OSS Distributions
630*4f1223e8SApple OSS Distributions static void *
cpu_thread_main(void * arg)631*4f1223e8SApple OSS Distributions cpu_thread_main(void *arg)
632*4f1223e8SApple OSS Distributions {
633*4f1223e8SApple OSS Distributions bool *spin = arg;
634*4f1223e8SApple OSS Distributions while (*spin) {
635*4f1223e8SApple OSS Distributions }
636*4f1223e8SApple OSS Distributions return NULL;
637*4f1223e8SApple OSS Distributions }
638*4f1223e8SApple OSS Distributions
639*4f1223e8SApple OSS Distributions static void
sigprof_received(int __unused sig)640*4f1223e8SApple OSS Distributions sigprof_received(int __unused sig)
641*4f1223e8SApple OSS Distributions {
642*4f1223e8SApple OSS Distributions sigprof_received_ns = clock_gettime_nsec_np(CLOCK_MONOTONIC);
643*4f1223e8SApple OSS Distributions }
644*4f1223e8SApple OSS Distributions
645*4f1223e8SApple OSS Distributions T_DECL(setitimer_prof,
646*4f1223e8SApple OSS Distributions "ensure a single-threaded process doesn't receive early signals",
647*4f1223e8SApple OSS Distributions T_META_TAG_VM_PREFERRED)
648*4f1223e8SApple OSS Distributions {
649*4f1223e8SApple OSS Distributions T_SETUPBEGIN;
650*4f1223e8SApple OSS Distributions (void)signal(SIGPROF, sigprof_received);
651*4f1223e8SApple OSS Distributions
652*4f1223e8SApple OSS Distributions uint64_t start_ns = clock_gettime_nsec_np(CLOCK_MONOTONIC);
653*4f1223e8SApple OSS Distributions uint64_t expected_end_ns = start_ns + (ITIMER_PROF_SECS * NSEC_PER_SEC);
654*4f1223e8SApple OSS Distributions uint64_t end_timeout_ns = expected_end_ns + (EXTRA_TIMEOUT_SECS * NSEC_PER_SEC);
655*4f1223e8SApple OSS Distributions
656*4f1223e8SApple OSS Distributions struct itimerval prof_timer = {
657*4f1223e8SApple OSS Distributions .it_value.tv_sec = ITIMER_PROF_SECS,
658*4f1223e8SApple OSS Distributions };
659*4f1223e8SApple OSS Distributions
660*4f1223e8SApple OSS Distributions int ret = setitimer(ITIMER_PROF, &prof_timer, NULL);
661*4f1223e8SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(ret, "setitimer(ITIMER_PROF, %llus)",
662*4f1223e8SApple OSS Distributions ITIMER_PROF_SECS);
663*4f1223e8SApple OSS Distributions T_SETUPEND;
664*4f1223e8SApple OSS Distributions
665*4f1223e8SApple OSS Distributions uint64_t last_ns = 0;
666*4f1223e8SApple OSS Distributions while ((last_ns = clock_gettime_nsec_np(CLOCK_MONOTONIC)) < end_timeout_ns) {
667*4f1223e8SApple OSS Distributions if (sigprof_received_ns) {
668*4f1223e8SApple OSS Distributions break;
669*4f1223e8SApple OSS Distributions }
670*4f1223e8SApple OSS Distributions }
671*4f1223e8SApple OSS Distributions
672*4f1223e8SApple OSS Distributions T_EXPECT_LE(last_ns, end_timeout_ns, "received SIGPROF within the timeout (%.9gs < %.9gs)",
673*4f1223e8SApple OSS Distributions (double)(last_ns - start_ns) / 1e9, (double)(end_timeout_ns - start_ns) / 1e9);
674*4f1223e8SApple OSS Distributions T_LOG("SIGPROF was delivered %+.6gms after expected duration",
675*4f1223e8SApple OSS Distributions (double)(last_ns - expected_end_ns) / 1e6);
676*4f1223e8SApple OSS Distributions T_EXPECT_GE(last_ns, expected_end_ns, "received SIGPROF after enough time (%.9gs > %.9gs)",
677*4f1223e8SApple OSS Distributions (double)(last_ns - start_ns) / 1e9, (double)(expected_end_ns - start_ns) / 1e9);
678*4f1223e8SApple OSS Distributions }
679*4f1223e8SApple OSS Distributions
680*4f1223e8SApple OSS Distributions T_DECL(setitimer_prof_multi_threaded,
681*4f1223e8SApple OSS Distributions "ensure a multi-threaded process doesn't receive early signals",
682*4f1223e8SApple OSS Distributions T_META_TAG_VM_PREFERRED)
683*4f1223e8SApple OSS Distributions {
684*4f1223e8SApple OSS Distributions T_SETUPBEGIN;
685*4f1223e8SApple OSS Distributions (void)signal(SIGPROF, sigprof_received);
686*4f1223e8SApple OSS Distributions
687*4f1223e8SApple OSS Distributions pthread_t cpu_threads[PROF_EXTRA_THREAD_COUNT] = { 0 };
688*4f1223e8SApple OSS Distributions bool spin_while_true = true;
689*4f1223e8SApple OSS Distributions for (unsigned int i = 0; i < PROF_EXTRA_THREAD_COUNT; i++) {
690*4f1223e8SApple OSS Distributions int error = pthread_create(&cpu_threads[i], NULL, cpu_thread_main, &spin_while_true);
691*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(error, "create thread %d", i);
692*4f1223e8SApple OSS Distributions }
693*4f1223e8SApple OSS Distributions T_LOG("spinning %d threads on CPU", PROF_EXTRA_THREAD_COUNT + 1);
694*4f1223e8SApple OSS Distributions
695*4f1223e8SApple OSS Distributions uint64_t start_ns = clock_gettime_nsec_np(CLOCK_MONOTONIC);
696*4f1223e8SApple OSS Distributions uint64_t expected_end_ns = start_ns + (EXPECTED_PROF_DURATION_SECS * NSEC_PER_SEC);
697*4f1223e8SApple OSS Distributions uint64_t end_timeout_ns = expected_end_ns + (EXTRA_TIMEOUT_SECS * NSEC_PER_SEC);
698*4f1223e8SApple OSS Distributions
699*4f1223e8SApple OSS Distributions struct itimerval prof_timer = {
700*4f1223e8SApple OSS Distributions .it_value.tv_sec = ITIMER_PROF_SECS,
701*4f1223e8SApple OSS Distributions };
702*4f1223e8SApple OSS Distributions
703*4f1223e8SApple OSS Distributions int ret = setitimer(ITIMER_PROF, &prof_timer, NULL);
704*4f1223e8SApple OSS Distributions T_ASSERT_POSIX_SUCCESS(ret, "setitimer(ITIMER_PROF, %llus)",
705*4f1223e8SApple OSS Distributions ITIMER_PROF_SECS);
706*4f1223e8SApple OSS Distributions T_SETUPEND;
707*4f1223e8SApple OSS Distributions
708*4f1223e8SApple OSS Distributions uint64_t last_ns = 0;
709*4f1223e8SApple OSS Distributions while ((last_ns = clock_gettime_nsec_np(CLOCK_MONOTONIC)) < end_timeout_ns) {
710*4f1223e8SApple OSS Distributions if (sigprof_received_ns) {
711*4f1223e8SApple OSS Distributions break;
712*4f1223e8SApple OSS Distributions }
713*4f1223e8SApple OSS Distributions }
714*4f1223e8SApple OSS Distributions
715*4f1223e8SApple OSS Distributions spin_while_true = false;
716*4f1223e8SApple OSS Distributions T_EXPECT_LE(last_ns, end_timeout_ns, "received SIGPROF within the timeout (%.9gs < %.9gs)",
717*4f1223e8SApple OSS Distributions (double)(last_ns - start_ns) / 1e9, (double)(end_timeout_ns - start_ns) / 1e9);
718*4f1223e8SApple OSS Distributions T_LOG("SIGPROF was delivered %+.6gms after expected duration",
719*4f1223e8SApple OSS Distributions (double)(last_ns - expected_end_ns) / 1e6);
720*4f1223e8SApple OSS Distributions T_EXPECT_GE(last_ns, expected_end_ns, "received SIGPROF after enough time (%.9gs > %.9gs)",
721*4f1223e8SApple OSS Distributions (double)(last_ns - start_ns) / 1e9, (double)(expected_end_ns - start_ns) / 1e9);
722*4f1223e8SApple OSS Distributions }
723*4f1223e8SApple OSS Distributions
724*4f1223e8SApple OSS Distributions static uint64_t sigprof_received_usage_mach = 0;
725*4f1223e8SApple OSS Distributions extern uint64_t __thread_selfusage(void);
726*4f1223e8SApple OSS Distributions static int const ITERATION_COUNT = 50;
727*4f1223e8SApple OSS Distributions static uint64_t const ITIMER_PERF_PROF_US = 50000;
728*4f1223e8SApple OSS Distributions
729*4f1223e8SApple OSS Distributions static void
sigprof_received_usage(int __unused sig)730*4f1223e8SApple OSS Distributions sigprof_received_usage(int __unused sig)
731*4f1223e8SApple OSS Distributions {
732*4f1223e8SApple OSS Distributions sigprof_received_usage_mach = __thread_selfusage();
733*4f1223e8SApple OSS Distributions }
734*4f1223e8SApple OSS Distributions
735*4f1223e8SApple OSS Distributions T_DECL(setitimer_prof_latency,
736*4f1223e8SApple OSS Distributions "measure the latency of delivering SIGPROF",
737*4f1223e8SApple OSS Distributions T_META_TAG_PERF, T_META_TAG_VM_NOT_ELIGIBLE,
738*4f1223e8SApple OSS Distributions T_META_TIMEOUT(10))
739*4f1223e8SApple OSS Distributions {
740*4f1223e8SApple OSS Distributions T_SETUPBEGIN;
741*4f1223e8SApple OSS Distributions (void)signal(SIGPROF, sigprof_received_usage);
742*4f1223e8SApple OSS Distributions
743*4f1223e8SApple OSS Distributions struct itimerval prof_timer = {
744*4f1223e8SApple OSS Distributions .it_value.tv_usec = ITIMER_PERF_PROF_US,
745*4f1223e8SApple OSS Distributions };
746*4f1223e8SApple OSS Distributions mach_timebase_info_data_t timebase = { 0 };
747*4f1223e8SApple OSS Distributions int ret = mach_timebase_info(&timebase);
748*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "mach_timebase_info");
749*4f1223e8SApple OSS Distributions
750*4f1223e8SApple OSS Distributions char pd_path[PATH_MAX] = "";
751*4f1223e8SApple OSS Distributions pdwriter_t pd = pdwriter_open_tmp("xnu", "setitimer_sigprof_latency", 1, 0, pd_path, sizeof(pd_path));
752*4f1223e8SApple OSS Distributions T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(pd, "opened perfdata file");
753*4f1223e8SApple OSS Distributions T_SETUPEND;
754*4f1223e8SApple OSS Distributions
755*4f1223e8SApple OSS Distributions T_LOG("running %d iterations, %lluus each", ITERATION_COUNT, ITIMER_PERF_PROF_US);
756*4f1223e8SApple OSS Distributions for (int i = 0; i < ITERATION_COUNT; i++) {
757*4f1223e8SApple OSS Distributions sigprof_received_usage_mach = 0;
758*4f1223e8SApple OSS Distributions
759*4f1223e8SApple OSS Distributions uint64_t const start_usage_mach = __thread_selfusage();
760*4f1223e8SApple OSS Distributions
761*4f1223e8SApple OSS Distributions ret = setitimer(ITIMER_PROF, &prof_timer, NULL);
762*4f1223e8SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "setitimer(ITIMER_PROF, %llus)",
763*4f1223e8SApple OSS Distributions ITIMER_PROF_SECS);
764*4f1223e8SApple OSS Distributions
765*4f1223e8SApple OSS Distributions while (sigprof_received_usage_mach == 0) {
766*4f1223e8SApple OSS Distributions // XXX ITIMER_PROF only re-evaluates the timers at user/kernel boundaries.
767*4f1223e8SApple OSS Distributions // Issue a trivial syscall (getpid has a fast path without making a syscall)
768*4f1223e8SApple OSS Distributions // to ensure the system checks the timer.
769*4f1223e8SApple OSS Distributions (void)getppid();
770*4f1223e8SApple OSS Distributions }
771*4f1223e8SApple OSS Distributions
772*4f1223e8SApple OSS Distributions uint64_t const end_usage_mach = start_usage_mach +
773*4f1223e8SApple OSS Distributions (ITIMER_PERF_PROF_US * NSEC_PER_USEC) * timebase.denom / timebase.numer;
774*4f1223e8SApple OSS Distributions uint64_t const latency_mach = sigprof_received_usage_mach - end_usage_mach;
775*4f1223e8SApple OSS Distributions uint64_t const latency_ns = latency_mach * timebase.denom / timebase.numer;
776*4f1223e8SApple OSS Distributions
777*4f1223e8SApple OSS Distributions pdwriter_new_value(pd, "sigprof_latency", pdunit_ns, latency_ns);
778*4f1223e8SApple OSS Distributions }
779*4f1223e8SApple OSS Distributions
780*4f1223e8SApple OSS Distributions pdwriter_close(pd);
781*4f1223e8SApple OSS Distributions T_LOG("wrote perfdata to %s", pd_path);
782*4f1223e8SApple OSS Distributions }
783