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