xref: /xnu-11417.140.69/tests/ktrace/kperf_tests.c (revision 43a90889846e00bfb5cf1d255cdc0a701a1e05a4)
1*43a90889SApple OSS Distributions // Copyright (c) 2017-2021 Apple Inc.  All rights reserved.
2*43a90889SApple OSS Distributions 
3*43a90889SApple OSS Distributions #include <darwintest.h>
4*43a90889SApple OSS Distributions #include <darwintest_utils.h>
5*43a90889SApple OSS Distributions #include <dispatch/dispatch.h>
6*43a90889SApple OSS Distributions #include <inttypes.h>
7*43a90889SApple OSS Distributions #include <ktrace/session.h>
8*43a90889SApple OSS Distributions #include <ktrace/private.h>
9*43a90889SApple OSS Distributions #include <sys/kdebug.h>
10*43a90889SApple OSS Distributions #include <sys/syscall.h>
11*43a90889SApple OSS Distributions #include <kperf/kpc.h>
12*43a90889SApple OSS Distributions #include <kperf/kperf.h>
13*43a90889SApple OSS Distributions #include <kperfdata/kpdecode.h>
14*43a90889SApple OSS Distributions #include <os/assumes.h>
15*43a90889SApple OSS Distributions #include <stdint.h>
16*43a90889SApple OSS Distributions #include <sys/sysctl.h>
17*43a90889SApple OSS Distributions 
18*43a90889SApple OSS Distributions #include "kperf_helpers.h"
19*43a90889SApple OSS Distributions #include "ktrace_helpers.h"
20*43a90889SApple OSS Distributions #include "ktrace_meta.h"
21*43a90889SApple OSS Distributions #include "../drop_priv.h"
22*43a90889SApple OSS Distributions 
23*43a90889SApple OSS Distributions 
24*43a90889SApple OSS Distributions 
25*43a90889SApple OSS Distributions #define MAX_CPUS    64
26*43a90889SApple OSS Distributions #define MAX_THREADS 64
27*43a90889SApple OSS Distributions 
28*43a90889SApple OSS Distributions volatile static bool running_threads = true;
29*43a90889SApple OSS Distributions 
30*43a90889SApple OSS Distributions static void *
spinning_thread(void * semp)31*43a90889SApple OSS Distributions spinning_thread(void *semp)
32*43a90889SApple OSS Distributions {
33*43a90889SApple OSS Distributions 	T_QUIET;
34*43a90889SApple OSS Distributions 	T_ASSERT_NOTNULL(semp, "semaphore passed to thread should not be NULL");
35*43a90889SApple OSS Distributions 	dispatch_semaphore_signal(*(dispatch_semaphore_t *)semp);
36*43a90889SApple OSS Distributions 
37*43a90889SApple OSS Distributions 	while (running_threads) {
38*43a90889SApple OSS Distributions 		;
39*43a90889SApple OSS Distributions 	}
40*43a90889SApple OSS Distributions 	return NULL;
41*43a90889SApple OSS Distributions }
42*43a90889SApple OSS Distributions 
43*43a90889SApple OSS Distributions #define PERF_STK_KHDR   UINT32_C(0x25020014)
44*43a90889SApple OSS Distributions #define PERF_STK_UHDR   UINT32_C(0x25020018)
45*43a90889SApple OSS Distributions #define PERF_TMR_FIRE   KDBG_EVENTID(DBG_PERF, 3, 0)
46*43a90889SApple OSS Distributions #define PERF_TMR_HNDLR  KDBG_EVENTID(DBG_PERF, 3, 2)
47*43a90889SApple OSS Distributions #define PERF_TMR_PEND   KDBG_EVENTID(DBG_PERF, 3, 3)
48*43a90889SApple OSS Distributions #define PERF_TMR_SKIP   KDBG_EVENTID(DBG_PERF, 3, 4)
49*43a90889SApple OSS Distributions #define PERF_KPC_CONFIG KDBG_EVENTID(DBG_PERF, 6, 4)
50*43a90889SApple OSS Distributions #define PERF_KPC_REG    KDBG_EVENTID(DBG_PERF, 6, 5)
51*43a90889SApple OSS Distributions #define PERF_KPC_REG32  KDBG_EVENTID(DBG_PERF, 6, 7)
52*43a90889SApple OSS Distributions #define PERF_INSTR_DATA KDBG_EVENTID(DBG_PERF, 1, 17)
53*43a90889SApple OSS Distributions #define PERF_EVENT      KDBG_EVENTID(DBG_PERF, 0, 0)
54*43a90889SApple OSS Distributions #define PERF_DISPLABEL  KDBG_EVENTID(DBG_PERF, 1, 23)
55*43a90889SApple OSS Distributions #define PERF_DISPSAMPLE KDBG_EVENTID(DBG_PERF, 1, 10)
56*43a90889SApple OSS Distributions 
57*43a90889SApple OSS Distributions #define SCHED_DISPATCH KDBG_EVENTID(DBG_MACH, DBG_MACH_SCHED, MACH_DISPATCH)
58*43a90889SApple OSS Distributions #define SCHED_SWITCH KDBG_EVENTID(DBG_MACH, DBG_MACH_SCHED, MACH_SCHED)
59*43a90889SApple OSS Distributions #define SCHED_HANDOFF KDBG_EVENTID(DBG_MACH, DBG_MACH_SCHED, MACH_STACK_HANDOFF)
60*43a90889SApple OSS Distributions #define SCHED_IDLE KDBG_EVENTID(DBG_MACH, DBG_MACH_SCHED, MACH_IDLE)
61*43a90889SApple OSS Distributions 
62*43a90889SApple OSS Distributions #define MP_CPUS_CALL UINT32_C(0x1900004)
63*43a90889SApple OSS Distributions 
64*43a90889SApple OSS Distributions #define DISPATCH_AFTER_EVENT UINT32_C(0xfefffffc)
65*43a90889SApple OSS Distributions #define TIMEOUT_SECS 10
66*43a90889SApple OSS Distributions 
67*43a90889SApple OSS Distributions #define TIMER_PERIOD_NS (1 * NSEC_PER_MSEC)
68*43a90889SApple OSS Distributions 
69*43a90889SApple OSS Distributions static void
start_tracing_with_timeout(ktrace_session_t s,unsigned int timeout_secs)70*43a90889SApple OSS Distributions start_tracing_with_timeout(ktrace_session_t s, unsigned int timeout_secs)
71*43a90889SApple OSS Distributions {
72*43a90889SApple OSS Distributions 	// Only set the timeout after we've seen an event that was traced by us.
73*43a90889SApple OSS Distributions 	// This helps set a reasonable timeout after we're guaranteed to get a
74*43a90889SApple OSS Distributions 	// few events.
75*43a90889SApple OSS Distributions 	dispatch_queue_t q = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0);
76*43a90889SApple OSS Distributions 
77*43a90889SApple OSS Distributions 	ktrace_events_single(s, DISPATCH_AFTER_EVENT,
78*43a90889SApple OSS Distributions 	    ^(__unused struct trace_point *tp)
79*43a90889SApple OSS Distributions 	{
80*43a90889SApple OSS Distributions 		T_LOG("arming timer to stop tracing after %d seconds", timeout_secs);
81*43a90889SApple OSS Distributions 		dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
82*43a90889SApple OSS Distributions 		    timeout_secs * NSEC_PER_SEC), q, ^{
83*43a90889SApple OSS Distributions 			T_LOG("ending tracing due to timeout");
84*43a90889SApple OSS Distributions 			ktrace_end(s, 0);
85*43a90889SApple OSS Distributions 		});
86*43a90889SApple OSS Distributions 	});
87*43a90889SApple OSS Distributions 	ktrace_set_collection_interval(s, 100);
88*43a90889SApple OSS Distributions 
89*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(ktrace_start(s, q), "start ktrace");
90*43a90889SApple OSS Distributions 
91*43a90889SApple OSS Distributions 	kdebug_trace(DISPATCH_AFTER_EVENT, 0, 0, 0, 0);
92*43a90889SApple OSS Distributions 	T_LOG("trace point emitted");
93*43a90889SApple OSS Distributions }
94*43a90889SApple OSS Distributions 
95*43a90889SApple OSS Distributions static void
configure_kperf_timer_samplers(uint64_t period_ns,uint32_t samplers)96*43a90889SApple OSS Distributions configure_kperf_timer_samplers(uint64_t period_ns, uint32_t samplers)
97*43a90889SApple OSS Distributions {
98*43a90889SApple OSS Distributions 	T_SETUPBEGIN;
99*43a90889SApple OSS Distributions 
100*43a90889SApple OSS Distributions 	(void)kperf_action_count_set(1);
101*43a90889SApple OSS Distributions 	T_QUIET;
102*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1, samplers),
103*43a90889SApple OSS Distributions 	    NULL);
104*43a90889SApple OSS Distributions 	(void)kperf_timer_count_set(1);
105*43a90889SApple OSS Distributions 	T_QUIET;
106*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_timer_period_set(0,
107*43a90889SApple OSS Distributions 	    kperf_ns_to_ticks(period_ns)), NULL);
108*43a90889SApple OSS Distributions 	T_QUIET;
109*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_timer_action_set(0, 1), NULL);
110*43a90889SApple OSS Distributions 
111*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
112*43a90889SApple OSS Distributions 
113*43a90889SApple OSS Distributions 	T_SETUPEND;
114*43a90889SApple OSS Distributions }
115*43a90889SApple OSS Distributions 
116*43a90889SApple OSS Distributions static double
timestamp_secs(ktrace_session_t s,uint64_t timestamp)117*43a90889SApple OSS Distributions timestamp_secs(ktrace_session_t s, uint64_t timestamp)
118*43a90889SApple OSS Distributions {
119*43a90889SApple OSS Distributions 	uint64_t ns = 0;
120*43a90889SApple OSS Distributions 	T_QUIET;
121*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(ktrace_convert_timestamp_to_nanoseconds(s, timestamp,
122*43a90889SApple OSS Distributions 	    &ns), NULL);
123*43a90889SApple OSS Distributions 	return (double)ns / NSEC_PER_SEC;
124*43a90889SApple OSS Distributions }
125*43a90889SApple OSS Distributions 
126*43a90889SApple OSS Distributions #pragma mark - timers
127*43a90889SApple OSS Distributions 
128*43a90889SApple OSS Distributions // Ensure that kperf is correctly sampling CPUs that are actively scheduling by
129*43a90889SApple OSS Distributions // bringing up threads and ensuring that threads on-core are sampled by each
130*43a90889SApple OSS Distributions // timer fire.
131*43a90889SApple OSS Distributions 
132*43a90889SApple OSS Distributions T_DECL(kperf_sample_active_cpus,
133*43a90889SApple OSS Distributions     "make sure that kperf samples all active CPUs",
134*43a90889SApple OSS Distributions 	T_META_TAG_VM_NOT_ELIGIBLE)
135*43a90889SApple OSS Distributions {
136*43a90889SApple OSS Distributions 	start_controlling_ktrace();
137*43a90889SApple OSS Distributions 
138*43a90889SApple OSS Distributions 	T_SETUPBEGIN;
139*43a90889SApple OSS Distributions 
140*43a90889SApple OSS Distributions 	int ncpus = dt_ncpu();
141*43a90889SApple OSS Distributions 	T_QUIET;
142*43a90889SApple OSS Distributions 	T_ASSERT_LT(ncpus, MAX_CPUS,
143*43a90889SApple OSS Distributions 	    "only supports up to %d CPUs", MAX_CPUS);
144*43a90889SApple OSS Distributions 	T_LOG("found %d CPUs", ncpus);
145*43a90889SApple OSS Distributions 
146*43a90889SApple OSS Distributions 	int nthreads = ncpus - 1;
147*43a90889SApple OSS Distributions 	T_QUIET;
148*43a90889SApple OSS Distributions 	T_ASSERT_LT(nthreads, MAX_THREADS,
149*43a90889SApple OSS Distributions 	    "only supports up to %d threads", MAX_THREADS);
150*43a90889SApple OSS Distributions 
151*43a90889SApple OSS Distributions 	static pthread_t threads[MAX_THREADS];
152*43a90889SApple OSS Distributions 
153*43a90889SApple OSS Distributions 	ktrace_session_t s = ktrace_session_create();
154*43a90889SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(s, "ktrace_session_create");
155*43a90889SApple OSS Distributions 	ktrace_set_collection_interval(s, 100);
156*43a90889SApple OSS Distributions 
157*43a90889SApple OSS Distributions 	__block uint64_t nfires = 0;
158*43a90889SApple OSS Distributions 	__block uint64_t nsamples = 0;
159*43a90889SApple OSS Distributions 	static uint64_t idle_tids[MAX_CPUS] = { 0 };
160*43a90889SApple OSS Distributions 	__block double sum_saturation = 0;
161*43a90889SApple OSS Distributions 	__block uint64_t last_nsamples = 0;
162*43a90889SApple OSS Distributions 
163*43a90889SApple OSS Distributions 	// As a test debugging aid, take an additonal argument that specifies the
164*43a90889SApple OSS Distributions 	// number of fires to stop tracing after.  This also turns on additional
165*43a90889SApple OSS Distributions 	// logging of scheduler trace events.
166*43a90889SApple OSS Distributions 	int stopafter = 0;
167*43a90889SApple OSS Distributions 	if (argc > 0) {
168*43a90889SApple OSS Distributions 		stopafter = atoi(argv[0]);
169*43a90889SApple OSS Distributions 		if (stopafter < 0) {
170*43a90889SApple OSS Distributions 			T_ASSERT_FAIL("argument must be positive");
171*43a90889SApple OSS Distributions 		}
172*43a90889SApple OSS Distributions 	}
173*43a90889SApple OSS Distributions 
174*43a90889SApple OSS Distributions 	static uint64_t first_timestamp = 0;
175*43a90889SApple OSS Distributions 	static uint64_t last_timestamp = 0;
176*43a90889SApple OSS Distributions 	ktrace_events_any(s, ^(struct trace_point *tp) {
177*43a90889SApple OSS Distributions 		if (first_timestamp == 0) {
178*43a90889SApple OSS Distributions 			first_timestamp = tp->timestamp;
179*43a90889SApple OSS Distributions 		}
180*43a90889SApple OSS Distributions 		last_timestamp = tp->timestamp;
181*43a90889SApple OSS Distributions 	});
182*43a90889SApple OSS Distributions 
183*43a90889SApple OSS Distributions 	ktrace_set_completion_handler(s, ^{
184*43a90889SApple OSS Distributions 		T_LOG("stopping threads");
185*43a90889SApple OSS Distributions 
186*43a90889SApple OSS Distributions 		running_threads = false;
187*43a90889SApple OSS Distributions 
188*43a90889SApple OSS Distributions 		for (int i = 0; i < nthreads; i++) {
189*43a90889SApple OSS Distributions 		        T_QUIET;
190*43a90889SApple OSS Distributions 		        T_ASSERT_POSIX_ZERO(pthread_join(threads[i], NULL), NULL);
191*43a90889SApple OSS Distributions 		}
192*43a90889SApple OSS Distributions 
193*43a90889SApple OSS Distributions 		double saturation = sum_saturation / nfires * 100;
194*43a90889SApple OSS Distributions 
195*43a90889SApple OSS Distributions 		T_LOG("over %.1f seconds, saw %" PRIu64 " timer fires, %" PRIu64
196*43a90889SApple OSS Distributions 			" samples, %g samples/fire, %.2f%% saturation",
197*43a90889SApple OSS Distributions 			timestamp_secs(s, last_timestamp - first_timestamp), nfires,
198*43a90889SApple OSS Distributions 			nsamples, (double)nsamples / (double)nfires, saturation);
199*43a90889SApple OSS Distributions 		T_ASSERT_GT(saturation, 95.0,
200*43a90889SApple OSS Distributions 		    "saw reasonable percentage of full samples");
201*43a90889SApple OSS Distributions 
202*43a90889SApple OSS Distributions 		T_END;
203*43a90889SApple OSS Distributions 	});
204*43a90889SApple OSS Distributions 
205*43a90889SApple OSS Distributions 	// Track which threads are running on each CPU.
206*43a90889SApple OSS Distributions 	static uint64_t tids_on_cpu[MAX_CPUS] = { 0 };
207*43a90889SApple OSS Distributions 	void (^switch_cb)(struct trace_point *, const char *name) =
208*43a90889SApple OSS Distributions 	    ^(struct trace_point *tp, const char *name) {
209*43a90889SApple OSS Distributions 		uint64_t new_thread = tp->arg2;
210*43a90889SApple OSS Distributions 
211*43a90889SApple OSS Distributions 		if (idle_tids[tp->cpuid] != new_thread) {
212*43a90889SApple OSS Distributions 			tids_on_cpu[tp->cpuid] = new_thread;
213*43a90889SApple OSS Distributions 		}
214*43a90889SApple OSS Distributions 
215*43a90889SApple OSS Distributions 		if (stopafter) {
216*43a90889SApple OSS Distributions 			T_LOG("%.7g: %s on %d: %llx", timestamp_secs(s, tp->timestamp),
217*43a90889SApple OSS Distributions 			    name, tp->cpuid, tp->arg2);
218*43a90889SApple OSS Distributions 		}
219*43a90889SApple OSS Distributions 	};
220*43a90889SApple OSS Distributions 
221*43a90889SApple OSS Distributions 	ktrace_events_single(s, SCHED_SWITCH, ^(struct trace_point *tp) {
222*43a90889SApple OSS Distributions 		switch_cb(tp, "switch");
223*43a90889SApple OSS Distributions 	});
224*43a90889SApple OSS Distributions 	ktrace_events_single(s, SCHED_HANDOFF, ^(struct trace_point *tp) {
225*43a90889SApple OSS Distributions 		switch_cb(tp, "hndoff");
226*43a90889SApple OSS Distributions 	});
227*43a90889SApple OSS Distributions 
228*43a90889SApple OSS Distributions 	// Determine the thread IDs of the idle threads on each CPU.
229*43a90889SApple OSS Distributions 	ktrace_events_single(s, SCHED_IDLE, ^(struct trace_point *tp) {
230*43a90889SApple OSS Distributions 		if (tp->debugid & DBG_FUNC_END) {
231*43a90889SApple OSS Distributions 			return;
232*43a90889SApple OSS Distributions 		}
233*43a90889SApple OSS Distributions 		tids_on_cpu[tp->cpuid] = 0;
234*43a90889SApple OSS Distributions 		idle_tids[tp->cpuid] = tp->threadid;
235*43a90889SApple OSS Distributions 		if (stopafter) {
236*43a90889SApple OSS Distributions 			T_LOG("%.7g: idle on %d: %llx", timestamp_secs(s, tp->timestamp),
237*43a90889SApple OSS Distributions 			    tp->cpuid, tp->threadid);
238*43a90889SApple OSS Distributions 		}
239*43a90889SApple OSS Distributions 	});
240*43a90889SApple OSS Distributions 
241*43a90889SApple OSS Distributions 	// On each timer fire, go through all the cores and mark any threads
242*43a90889SApple OSS Distributions 	// that should be sampled.
243*43a90889SApple OSS Distributions 
244*43a90889SApple OSS Distributions 	__block int last_fire_cpu = -1;
245*43a90889SApple OSS Distributions 	static bool sample_missing[MAX_CPUS] = { false };
246*43a90889SApple OSS Distributions 	static uint64_t tids_snap[MAX_CPUS] = { 0 };
247*43a90889SApple OSS Distributions 	__block int nexpected = 0;
248*43a90889SApple OSS Distributions 	__block int nextra = 0;
249*43a90889SApple OSS Distributions 	__block int nidles = 0;
250*43a90889SApple OSS Distributions 
251*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_TMR_FIRE, ^(struct trace_point *tp) {
252*43a90889SApple OSS Distributions 		T_QUIET; T_ASSERT_EQ((tp->debugid & DBG_FUNC_START), 0,
253*43a90889SApple OSS Distributions 		    "no timer fire start events are allowed");
254*43a90889SApple OSS Distributions 		int last_expected = nexpected;
255*43a90889SApple OSS Distributions 		nfires++;
256*43a90889SApple OSS Distributions 
257*43a90889SApple OSS Distributions 		nexpected = 0;
258*43a90889SApple OSS Distributions 		for (int i = 0; i < ncpus; i++) {
259*43a90889SApple OSS Distributions 			if (sample_missing[i]) {
260*43a90889SApple OSS Distributions 				T_LOG("missed sample on CPU %d for thread %#llx from "
261*43a90889SApple OSS Distributions 				    "timer on CPU %d (expected %d samples)",
262*43a90889SApple OSS Distributions 				    tp->cpuid, tids_snap[i], last_fire_cpu, last_expected);
263*43a90889SApple OSS Distributions 				sample_missing[i] = false;
264*43a90889SApple OSS Distributions 			}
265*43a90889SApple OSS Distributions 
266*43a90889SApple OSS Distributions 			if (tids_on_cpu[i] != 0) {
267*43a90889SApple OSS Distributions 				tids_snap[i] = tids_on_cpu[i];
268*43a90889SApple OSS Distributions 				sample_missing[i] = true;
269*43a90889SApple OSS Distributions 				nexpected++;
270*43a90889SApple OSS Distributions 			}
271*43a90889SApple OSS Distributions 		}
272*43a90889SApple OSS Distributions 		if (stopafter) {
273*43a90889SApple OSS Distributions 			T_LOG("%.7g: FIRE on %d: %d extra, %d idles",
274*43a90889SApple OSS Distributions 			    timestamp_secs(s, tp->timestamp), tp->cpuid, nextra, nidles);
275*43a90889SApple OSS Distributions 		}
276*43a90889SApple OSS Distributions 
277*43a90889SApple OSS Distributions 		if (nfires == 1) {
278*43a90889SApple OSS Distributions 			return;
279*43a90889SApple OSS Distributions 		}
280*43a90889SApple OSS Distributions 
281*43a90889SApple OSS Distributions 		if (last_expected == 0) {
282*43a90889SApple OSS Distributions 			sum_saturation += 1;
283*43a90889SApple OSS Distributions 		} else {
284*43a90889SApple OSS Distributions 			sum_saturation += (double)(nsamples - last_nsamples) /
285*43a90889SApple OSS Distributions 			    last_expected;
286*43a90889SApple OSS Distributions 		}
287*43a90889SApple OSS Distributions 		last_nsamples = nsamples;
288*43a90889SApple OSS Distributions 		nextra = 0;
289*43a90889SApple OSS Distributions 		nidles = 0;
290*43a90889SApple OSS Distributions 
291*43a90889SApple OSS Distributions 		T_QUIET;
292*43a90889SApple OSS Distributions 		T_ASSERT_LT((int)tp->cpuid, ncpus,
293*43a90889SApple OSS Distributions 		    "timer fire should not occur on an IOP");
294*43a90889SApple OSS Distributions 		last_fire_cpu = (int)tp->cpuid;
295*43a90889SApple OSS Distributions 
296*43a90889SApple OSS Distributions 		if (stopafter && (uint64_t)stopafter == nfires) {
297*43a90889SApple OSS Distributions 			ktrace_end(s, 1);
298*43a90889SApple OSS Distributions 		}
299*43a90889SApple OSS Distributions 	});
300*43a90889SApple OSS Distributions 
301*43a90889SApple OSS Distributions 	// On the timer handler for each CPU, unset the missing sample bitmap.
302*43a90889SApple OSS Distributions 
303*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_TMR_HNDLR, ^(struct trace_point *tp) {
304*43a90889SApple OSS Distributions 		nsamples++;
305*43a90889SApple OSS Distributions 		if ((int)tp->cpuid > ncpus) {
306*43a90889SApple OSS Distributions 		        // Skip IOPs; they're not scheduling any relevant threads.
307*43a90889SApple OSS Distributions 		        return;
308*43a90889SApple OSS Distributions 		}
309*43a90889SApple OSS Distributions 
310*43a90889SApple OSS Distributions 		if (!sample_missing[tp->cpuid] && idle_tids[tp->cpuid] != 0) {
311*43a90889SApple OSS Distributions 			T_LOG("sampled additional thread %llx on CPU %d", tp->threadid,
312*43a90889SApple OSS Distributions 			    tp->cpuid);
313*43a90889SApple OSS Distributions 			nextra++;
314*43a90889SApple OSS Distributions 		}
315*43a90889SApple OSS Distributions 		if (tp->threadid == idle_tids[tp->cpuid]) {
316*43a90889SApple OSS Distributions 			T_LOG("sampled idle thread on CPU %d", tp->cpuid);
317*43a90889SApple OSS Distributions 			nidles++;
318*43a90889SApple OSS Distributions 		}
319*43a90889SApple OSS Distributions 		sample_missing[tp->cpuid] = false;
320*43a90889SApple OSS Distributions 	});
321*43a90889SApple OSS Distributions 
322*43a90889SApple OSS Distributions 	configure_kperf_timer_samplers(TIMER_PERIOD_NS, KPERF_SAMPLER_KSTACK);
323*43a90889SApple OSS Distributions 
324*43a90889SApple OSS Distributions 	T_SETUPEND;
325*43a90889SApple OSS Distributions 
326*43a90889SApple OSS Distributions 	start_tracing_with_timeout(s, TIMEOUT_SECS);
327*43a90889SApple OSS Distributions 
328*43a90889SApple OSS Distributions 	// Create threads to bring up all of the CPUs.
329*43a90889SApple OSS Distributions 
330*43a90889SApple OSS Distributions 	dispatch_semaphore_t thread_spinning = dispatch_semaphore_create(0);
331*43a90889SApple OSS Distributions 
332*43a90889SApple OSS Distributions 	for (int i = 0; i < nthreads; i++) {
333*43a90889SApple OSS Distributions 		T_QUIET;
334*43a90889SApple OSS Distributions 		T_ASSERT_POSIX_ZERO(
335*43a90889SApple OSS Distributions 			pthread_create(&threads[i], NULL, &spinning_thread,
336*43a90889SApple OSS Distributions 			&thread_spinning), NULL);
337*43a90889SApple OSS Distributions 		dispatch_semaphore_wait(thread_spinning, DISPATCH_TIME_FOREVER);
338*43a90889SApple OSS Distributions 	}
339*43a90889SApple OSS Distributions 
340*43a90889SApple OSS Distributions 	T_LOG("spun up %d thread%s", nthreads, nthreads == 1 ? "" : "s");
341*43a90889SApple OSS Distributions 
342*43a90889SApple OSS Distributions 	dispatch_main();
343*43a90889SApple OSS Distributions }
344*43a90889SApple OSS Distributions 
345*43a90889SApple OSS Distributions #define FIRES_THRESHOLD (5000)
346*43a90889SApple OSS Distributions 
347*43a90889SApple OSS Distributions T_DECL(kperf_timer_fires_enough_times,
348*43a90889SApple OSS Distributions     "ensure the correct number of timers fire in a period of time",
349*43a90889SApple OSS Distributions 	T_META_TAG_VM_NOT_ELIGIBLE)
350*43a90889SApple OSS Distributions {
351*43a90889SApple OSS Distributions 	start_controlling_ktrace();
352*43a90889SApple OSS Distributions 
353*43a90889SApple OSS Distributions 	dispatch_semaphore_t thread_spinning = dispatch_semaphore_create(0);
354*43a90889SApple OSS Distributions 
355*43a90889SApple OSS Distributions 	ktrace_session_t s = ktrace_session_create();
356*43a90889SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(s, "ktrace_session_create");
357*43a90889SApple OSS Distributions 	ktrace_set_collection_interval(s, 100);
358*43a90889SApple OSS Distributions 
359*43a90889SApple OSS Distributions 	__block uint64_t nfires = 0;
360*43a90889SApple OSS Distributions 	__block uint64_t first_fire_ns = 0;
361*43a90889SApple OSS Distributions 	__block uint64_t last_fire_ns = 0;
362*43a90889SApple OSS Distributions 
363*43a90889SApple OSS Distributions 	int ncpus = dt_ncpu();
364*43a90889SApple OSS Distributions 
365*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_TMR_FIRE, ^(struct trace_point *tp) {
366*43a90889SApple OSS Distributions 		nfires++;
367*43a90889SApple OSS Distributions 		if (first_fire_ns == 0) {
368*43a90889SApple OSS Distributions 			ktrace_convert_timestamp_to_nanoseconds(s, tp->timestamp,
369*43a90889SApple OSS Distributions 			    &first_fire_ns);
370*43a90889SApple OSS Distributions 		}
371*43a90889SApple OSS Distributions 		ktrace_convert_timestamp_to_nanoseconds(s, tp->timestamp,
372*43a90889SApple OSS Distributions 		    &last_fire_ns);
373*43a90889SApple OSS Distributions 
374*43a90889SApple OSS Distributions 		T_QUIET; T_ASSERT_LT((int)tp->cpuid, ncpus,
375*43a90889SApple OSS Distributions 		    "timer fire should not occur on an IOP");
376*43a90889SApple OSS Distributions 		if (nfires >= FIRES_THRESHOLD) {
377*43a90889SApple OSS Distributions 			ktrace_end(s, 1);
378*43a90889SApple OSS Distributions 		}
379*43a90889SApple OSS Distributions 	});
380*43a90889SApple OSS Distributions 
381*43a90889SApple OSS Distributions 	configure_kperf_timer_samplers(TIMER_PERIOD_NS, KPERF_SAMPLER_KSTACK);
382*43a90889SApple OSS Distributions 
383*43a90889SApple OSS Distributions 	pthread_t thread;
384*43a90889SApple OSS Distributions  	T_QUIET;
385*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(pthread_create(&thread, NULL, &spinning_thread,
386*43a90889SApple OSS Distributions 	    &thread_spinning), NULL);
387*43a90889SApple OSS Distributions 	dispatch_semaphore_wait(thread_spinning, DISPATCH_TIME_FOREVER);
388*43a90889SApple OSS Distributions 
389*43a90889SApple OSS Distributions 	ktrace_set_completion_handler(s, ^{
390*43a90889SApple OSS Distributions 		running_threads = false;
391*43a90889SApple OSS Distributions 
392*43a90889SApple OSS Distributions 		double duration_secs = (double)(last_fire_ns - first_fire_ns) /
393*43a90889SApple OSS Distributions 		    NSEC_PER_SEC;
394*43a90889SApple OSS Distributions 		T_LOG("stopping thread after %.2f seconds", duration_secs);
395*43a90889SApple OSS Distributions 
396*43a90889SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_ZERO(pthread_join(thread, NULL), NULL);
397*43a90889SApple OSS Distributions 
398*43a90889SApple OSS Distributions 		T_LOG("saw %" PRIu64 " timer fires (%g fires/second)", nfires,
399*43a90889SApple OSS Distributions 		    (double)nfires / (double)duration_secs);
400*43a90889SApple OSS Distributions 		double expected_nfires = duration_secs * NSEC_PER_SEC / TIMER_PERIOD_NS;
401*43a90889SApple OSS Distributions 		T_LOG("expecting %g timer fires", expected_nfires);
402*43a90889SApple OSS Distributions 		double nfires_seen_pct = expected_nfires / nfires * 100;
403*43a90889SApple OSS Distributions 		T_ASSERT_GT(nfires_seen_pct, 95.0,
404*43a90889SApple OSS Distributions 		    "saw reasonable number of missed timer fires");
405*43a90889SApple OSS Distributions 		T_ASSERT_LT(nfires_seen_pct, 105.0,
406*43a90889SApple OSS Distributions 			"saw reasonable number of extra timer fires");
407*43a90889SApple OSS Distributions 
408*43a90889SApple OSS Distributions 		T_END;
409*43a90889SApple OSS Distributions 	});
410*43a90889SApple OSS Distributions 
411*43a90889SApple OSS Distributions 	start_tracing_with_timeout(s, TIMEOUT_SECS);
412*43a90889SApple OSS Distributions 
413*43a90889SApple OSS Distributions 	dispatch_main();
414*43a90889SApple OSS Distributions }
415*43a90889SApple OSS Distributions 
416*43a90889SApple OSS Distributions // kperf_timer_not_oversampling ensures that the profiling timer fires are
417*43a90889SApple OSS Distributions // spaced apart by the programmed timer period.  Otherwise, tools that rely on
418*43a90889SApple OSS Distributions // sample count as a proxy for CPU usage will over-estimate.
419*43a90889SApple OSS Distributions 
420*43a90889SApple OSS Distributions #define FIRE_PERIOD_THRESHOLD_NS \
421*43a90889SApple OSS Distributions 		(TIMER_PERIOD_NS - (uint64_t)(TIMER_PERIOD_NS * 0.05))
422*43a90889SApple OSS Distributions 
423*43a90889SApple OSS Distributions struct cirq {
424*43a90889SApple OSS Distributions 	unsigned int nslots;
425*43a90889SApple OSS Distributions 	unsigned int tail_slot;
426*43a90889SApple OSS Distributions 	unsigned int slot_size;
427*43a90889SApple OSS Distributions };
428*43a90889SApple OSS Distributions 
429*43a90889SApple OSS Distributions #define CIRQ_INIT(TYPE, NSLOTS) \
430*43a90889SApple OSS Distributions 	(struct cirq){ \
431*43a90889SApple OSS Distributions 		.nslots = NSLOTS, .tail_slot = 0, .slot_size = sizeof(TYPE), \
432*43a90889SApple OSS Distributions 	}
433*43a90889SApple OSS Distributions 
434*43a90889SApple OSS Distributions static inline void *
cirq_get(struct cirq * cq,unsigned int i)435*43a90889SApple OSS Distributions cirq_get(struct cirq *cq, unsigned int i)
436*43a90889SApple OSS Distributions {
437*43a90889SApple OSS Distributions 	return (char *)cq + sizeof(*cq) + (cq->slot_size * i);
438*43a90889SApple OSS Distributions }
439*43a90889SApple OSS Distributions 
440*43a90889SApple OSS Distributions static void *
cirq_top(void * vcq)441*43a90889SApple OSS Distributions cirq_top(void *vcq)
442*43a90889SApple OSS Distributions {
443*43a90889SApple OSS Distributions 	struct cirq *cq = vcq;
444*43a90889SApple OSS Distributions 	unsigned int tail_slot = cq->tail_slot;
445*43a90889SApple OSS Distributions 	unsigned int top_slot = (tail_slot > 0 ? tail_slot : cq->nslots) - 1;
446*43a90889SApple OSS Distributions 	return cirq_get(cq, top_slot);
447*43a90889SApple OSS Distributions }
448*43a90889SApple OSS Distributions 
449*43a90889SApple OSS Distributions static void *
cirq_push(void * vcq)450*43a90889SApple OSS Distributions cirq_push(void *vcq)
451*43a90889SApple OSS Distributions {
452*43a90889SApple OSS Distributions 	struct cirq *cq = vcq;
453*43a90889SApple OSS Distributions 	unsigned int tail_slot = cq->tail_slot;
454*43a90889SApple OSS Distributions 	unsigned int next_slot = tail_slot == cq->nslots - 1 ? 0 : tail_slot + 1;
455*43a90889SApple OSS Distributions 	cq->tail_slot = next_slot;
456*43a90889SApple OSS Distributions 	return cirq_get(cq, tail_slot);
457*43a90889SApple OSS Distributions }
458*43a90889SApple OSS Distributions 
459*43a90889SApple OSS Distributions static void
460*43a90889SApple OSS Distributions cirq_for(void *vcq, void (^iter)(void *elt))
461*43a90889SApple OSS Distributions {
462*43a90889SApple OSS Distributions 	struct cirq *cq = vcq;
463*43a90889SApple OSS Distributions 	for (unsigned int i = cq->tail_slot; i < cq->nslots; i++) {
464*43a90889SApple OSS Distributions 		iter(cirq_get(cq, i));
465*43a90889SApple OSS Distributions 	}
466*43a90889SApple OSS Distributions 	for (unsigned int i = 0; i < cq->tail_slot; i++) {
467*43a90889SApple OSS Distributions 		iter(cirq_get(cq, i));
468*43a90889SApple OSS Distributions 	}
469*43a90889SApple OSS Distributions }
470*43a90889SApple OSS Distributions 
471*43a90889SApple OSS Distributions #define HISTORY_LEN 5
472*43a90889SApple OSS Distributions 
473*43a90889SApple OSS Distributions struct instval {
474*43a90889SApple OSS Distributions 	uint64_t iv_instant_ns;
475*43a90889SApple OSS Distributions 	uint64_t iv_val;
476*43a90889SApple OSS Distributions };
477*43a90889SApple OSS Distributions 
478*43a90889SApple OSS Distributions struct cirq_instval {
479*43a90889SApple OSS Distributions 	struct cirq cq;
480*43a90889SApple OSS Distributions 	struct instval elts[HISTORY_LEN];
481*43a90889SApple OSS Distributions };
482*43a90889SApple OSS Distributions 
483*43a90889SApple OSS Distributions struct cirq_u64 {
484*43a90889SApple OSS Distributions 	struct cirq cq;
485*43a90889SApple OSS Distributions 	uint64_t elts[HISTORY_LEN];
486*43a90889SApple OSS Distributions };
487*43a90889SApple OSS Distributions 
488*43a90889SApple OSS Distributions struct cpu_oversample {
489*43a90889SApple OSS Distributions 	struct cirq_instval timer_latencies;
490*43a90889SApple OSS Distributions 	struct cirq_instval fire_latencies;
491*43a90889SApple OSS Distributions };
492*43a90889SApple OSS Distributions 
493*43a90889SApple OSS Distributions static void
cpu_oversample_log(struct cpu_oversample * cpu,unsigned int cpuid)494*43a90889SApple OSS Distributions cpu_oversample_log(struct cpu_oversample *cpu, unsigned int cpuid)
495*43a90889SApple OSS Distributions {
496*43a90889SApple OSS Distributions 	T_LOG("CPU %d timer latencies:", cpuid);
497*43a90889SApple OSS Distributions 	__block int i = -HISTORY_LEN;
498*43a90889SApple OSS Distributions 	cirq_for(&cpu->timer_latencies, ^(void *viv) {
499*43a90889SApple OSS Distributions 		struct instval *iv = viv;
500*43a90889SApple OSS Distributions 		T_LOG("\t%llu timer latency %d: %llu", iv->iv_instant_ns, i,
501*43a90889SApple OSS Distributions 		    iv->iv_val);
502*43a90889SApple OSS Distributions 		i++;
503*43a90889SApple OSS Distributions 	});
504*43a90889SApple OSS Distributions 
505*43a90889SApple OSS Distributions 	T_LOG("CPU %d fire latencies:", cpuid);
506*43a90889SApple OSS Distributions 	i = -HISTORY_LEN;
507*43a90889SApple OSS Distributions 	cirq_for(&cpu->fire_latencies, ^(void *viv) {
508*43a90889SApple OSS Distributions 		struct instval *iv = viv;
509*43a90889SApple OSS Distributions 		T_LOG("\t%llu fire latency %d: %llu", iv->iv_instant_ns, i, iv->iv_val);
510*43a90889SApple OSS Distributions 		i++;
511*43a90889SApple OSS Distributions 	});
512*43a90889SApple OSS Distributions }
513*43a90889SApple OSS Distributions 
514*43a90889SApple OSS Distributions T_DECL(kperf_timer_not_oversampling,
515*43a90889SApple OSS Distributions     "ensure that time between fires is long enough",
516*43a90889SApple OSS Distributions 	T_META_TAG_VM_NOT_ELIGIBLE)
517*43a90889SApple OSS Distributions {
518*43a90889SApple OSS Distributions 	start_controlling_ktrace();
519*43a90889SApple OSS Distributions 
520*43a90889SApple OSS Distributions 	ktrace_session_t s = ktrace_session_create();
521*43a90889SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(s, "ktrace_session_create");
522*43a90889SApple OSS Distributions 	// Try not to perturb the system with more work.
523*43a90889SApple OSS Distributions 	ktrace_set_collection_interval(s, 1000);
524*43a90889SApple OSS Distributions 	__block uint64_t nfires = 0;
525*43a90889SApple OSS Distributions 	__block uint64_t first_fire_ns = 0;
526*43a90889SApple OSS Distributions 	__block uint64_t last_fire_ns = 0;
527*43a90889SApple OSS Distributions 	__block unsigned int last_fire_cpuid = 0;
528*43a90889SApple OSS Distributions 
529*43a90889SApple OSS Distributions 	int ncpus = dt_ncpu();
530*43a90889SApple OSS Distributions 	T_QUIET; T_ASSERT_GT(ncpus, 0, "should see positive number of CPUs");
531*43a90889SApple OSS Distributions 
532*43a90889SApple OSS Distributions 	struct cpu_oversample *per_cpu = calloc((unsigned int)ncpus,
533*43a90889SApple OSS Distributions 			sizeof(per_cpu[0]));
534*43a90889SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO;
535*43a90889SApple OSS Distributions 	T_ASSERT_NOTNULL(per_cpu, "allocated timer latency tracking");
536*43a90889SApple OSS Distributions 	for (int i = 0; i < ncpus; i++) {
537*43a90889SApple OSS Distributions 		per_cpu[i].timer_latencies.cq = CIRQ_INIT(struct instval, HISTORY_LEN);
538*43a90889SApple OSS Distributions 		per_cpu[i].fire_latencies.cq = CIRQ_INIT(struct instval, HISTORY_LEN);
539*43a90889SApple OSS Distributions 	}
540*43a90889SApple OSS Distributions 
541*43a90889SApple OSS Distributions 	__block bool in_stackshot = false;
542*43a90889SApple OSS Distributions 	__block uint64_t last_stackshot_ns = 0;
543*43a90889SApple OSS Distributions 
544*43a90889SApple OSS Distributions 	// Stackshots are the primary source of interrupt latency on the system.
545*43a90889SApple OSS Distributions 	ktrace_events_single(s, KDBG_EVENTID(DBG_BSD, DBG_BSD_EXCP_SC,
546*43a90889SApple OSS Distributions 			SYS_stack_snapshot_with_config), ^(struct trace_point *tp) {
547*43a90889SApple OSS Distributions 		bool start = tp->debugid & DBG_FUNC_START;
548*43a90889SApple OSS Distributions 		uint64_t cur_ns = relns_from_abs(s, tp->timestamp);
549*43a90889SApple OSS Distributions 		T_LOG("%llu: %s stackshot syscall from process %s",
550*43a90889SApple OSS Distributions 				cur_ns, start ? "start" : "finish", tp->command);
551*43a90889SApple OSS Distributions 		in_stackshot = start;
552*43a90889SApple OSS Distributions 		if (!start) {
553*43a90889SApple OSS Distributions 			last_stackshot_ns = cur_ns;
554*43a90889SApple OSS Distributions 		}
555*43a90889SApple OSS Distributions 	});
556*43a90889SApple OSS Distributions 
557*43a90889SApple OSS Distributions 	struct cirq_u64 *fire_times = calloc(1, sizeof(*fire_times));
558*43a90889SApple OSS Distributions 	T_ASSERT_NOTNULL(fire_times, "allocated fire time tracking");
559*43a90889SApple OSS Distributions 	fire_times->cq = CIRQ_INIT(uint64_t, HISTORY_LEN);
560*43a90889SApple OSS Distributions 
561*43a90889SApple OSS Distributions 	// Track the decrementer's latency values to find any unexpectedly long
562*43a90889SApple OSS Distributions 	// interrupt latencies that could affect the firing cadence.
563*43a90889SApple OSS Distributions 	ktrace_events_single(s, DECR_TRAP_LATENCY,
564*43a90889SApple OSS Distributions 			^(struct trace_point *tp) {
565*43a90889SApple OSS Distributions 		uint64_t cur_ns = relns_from_abs(s, tp->timestamp);
566*43a90889SApple OSS Distributions 		uint64_t latency_ns = ns_from_abs(s, 0 - tp->arg1);
567*43a90889SApple OSS Distributions 		struct instval *latency = cirq_push(&per_cpu[tp->cpuid].timer_latencies);
568*43a90889SApple OSS Distributions 		latency->iv_instant_ns = cur_ns;
569*43a90889SApple OSS Distributions 		latency->iv_val = latency_ns;
570*43a90889SApple OSS Distributions 	});
571*43a90889SApple OSS Distributions 
572*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_TMR_FIRE, ^(struct trace_point *tp) {
573*43a90889SApple OSS Distributions 		T_QUIET; T_ASSERT_LT((int)tp->cpuid, ncpus,
574*43a90889SApple OSS Distributions 				"timer fire should not occur on an IOP");
575*43a90889SApple OSS Distributions 
576*43a90889SApple OSS Distributions 		nfires++;
577*43a90889SApple OSS Distributions 		uint64_t cur_ns = relns_from_abs(s, tp->timestamp);
578*43a90889SApple OSS Distributions 		uint64_t *fire_ns = cirq_push(fire_times);
579*43a90889SApple OSS Distributions 		*fire_ns = cur_ns;
580*43a90889SApple OSS Distributions 
581*43a90889SApple OSS Distributions 		struct cpu_oversample *cur_cpu = &per_cpu[tp->cpuid];
582*43a90889SApple OSS Distributions 		struct instval *last_timer_latency = cirq_top(
583*43a90889SApple OSS Distributions 				&cur_cpu->timer_latencies);
584*43a90889SApple OSS Distributions 		uint64_t timer_latency_ns = last_timer_latency->iv_val;
585*43a90889SApple OSS Distributions 
586*43a90889SApple OSS Distributions 		if (first_fire_ns == 0) {
587*43a90889SApple OSS Distributions 			first_fire_ns = cur_ns;
588*43a90889SApple OSS Distributions 		} else {
589*43a90889SApple OSS Distributions 			struct cpu_oversample *last_cpu = &per_cpu[last_fire_cpuid];
590*43a90889SApple OSS Distributions 			struct instval *last_latency = cirq_top(&last_cpu->fire_latencies);
591*43a90889SApple OSS Distributions 			uint64_t last_fire_latency_ns = last_latency->iv_val;
592*43a90889SApple OSS Distributions 
593*43a90889SApple OSS Distributions 			if (timer_latency_ns > TIMER_PERIOD_NS / 4) {
594*43a90889SApple OSS Distributions 				T_LOG("%llu: long timer latency at fire: %llu", cur_ns,
595*43a90889SApple OSS Distributions 						timer_latency_ns);
596*43a90889SApple OSS Distributions 			}
597*43a90889SApple OSS Distributions 
598*43a90889SApple OSS Distributions 			// Long interrupt latencies will cause the timer to miss its fire
599*43a90889SApple OSS Distributions 			// time and report a fire past when it should have, making the next
600*43a90889SApple OSS Distributions 			// period too short.  Keep track of the latency as a leeway
601*43a90889SApple OSS Distributions 			// adjustment for the subsequent fire.
602*43a90889SApple OSS Distributions 			uint64_t fire_period_ns = cur_ns - last_fire_ns;
603*43a90889SApple OSS Distributions 			uint64_t fire_period_adj_ns = fire_period_ns +
604*43a90889SApple OSS Distributions 			    last_fire_latency_ns + timer_latency_ns;
605*43a90889SApple OSS Distributions 			// Within 5% is still a valid period -- adjust this for running in
606*43a90889SApple OSS Distributions 			// potentially-noisy automation.
607*43a90889SApple OSS Distributions 			uint64_t fire_period_pct_adj_ns = fire_period_adj_ns +
608*43a90889SApple OSS Distributions 			    FIRE_PERIOD_THRESHOLD_NS / 20;
609*43a90889SApple OSS Distributions 			if (fire_period_adj_ns < FIRE_PERIOD_THRESHOLD_NS &&
610*43a90889SApple OSS Distributions 			    fire_period_pct_adj_ns >= FIRE_PERIOD_THRESHOLD_NS) {
611*43a90889SApple OSS Distributions 				T_LOG("ignoring period of %llu within 5%% of expected %llu",
612*43a90889SApple OSS Distributions 				    fire_period_adj_ns, fire_period_pct_adj_ns);
613*43a90889SApple OSS Distributions 			}
614*43a90889SApple OSS Distributions 			bool too_short = fire_period_pct_adj_ns < FIRE_PERIOD_THRESHOLD_NS;
615*43a90889SApple OSS Distributions 			if (too_short) {
616*43a90889SApple OSS Distributions 				T_LOG("%llu: period of timer fire %llu is %llu + %llu + %llu = "
617*43a90889SApple OSS Distributions 						"%llu < %llu",
618*43a90889SApple OSS Distributions 						cur_ns, nfires, fire_period_ns, last_fire_latency_ns,
619*43a90889SApple OSS Distributions 						timer_latency_ns, fire_period_adj_ns,
620*43a90889SApple OSS Distributions 						FIRE_PERIOD_THRESHOLD_NS);
621*43a90889SApple OSS Distributions 
622*43a90889SApple OSS Distributions 				T_LOG("short profile timer fired on CPU %d", tp->cpuid);
623*43a90889SApple OSS Distributions 				cpu_oversample_log(cur_cpu, tp->cpuid);
624*43a90889SApple OSS Distributions 
625*43a90889SApple OSS Distributions 				if (cur_cpu == last_cpu) {
626*43a90889SApple OSS Distributions 					T_LOG("fired back-to-back on CPU %d", tp->cpuid);
627*43a90889SApple OSS Distributions 				} else {
628*43a90889SApple OSS Distributions 					T_LOG("previous profile timer fired on CPU %d",
629*43a90889SApple OSS Distributions 							last_fire_cpuid);
630*43a90889SApple OSS Distributions 					cpu_oversample_log(last_cpu, last_fire_cpuid);
631*43a90889SApple OSS Distributions 				}
632*43a90889SApple OSS Distributions 
633*43a90889SApple OSS Distributions 				T_LOG("profile timer fires:");
634*43a90889SApple OSS Distributions 				cirq_for(fire_times, ^(void *vu64) {
635*43a90889SApple OSS Distributions 					T_LOG("\tfire: %llu", *(uint64_t *)vu64);
636*43a90889SApple OSS Distributions 				});
637*43a90889SApple OSS Distributions 				if (nfires < (unsigned int)ncpus) {
638*43a90889SApple OSS Distributions 					T_LOG("ignoring timer fire %llu as context may be missing",
639*43a90889SApple OSS Distributions 							nfires);
640*43a90889SApple OSS Distributions 				} else {
641*43a90889SApple OSS Distributions 					if (in_stackshot) {
642*43a90889SApple OSS Distributions 						T_LOG("skipping assertion because stackshot is "
643*43a90889SApple OSS Distributions 								"happening");
644*43a90889SApple OSS Distributions 					} else if (last_stackshot_ns != 0 &&
645*43a90889SApple OSS Distributions 							cur_ns > last_stackshot_ns &&
646*43a90889SApple OSS Distributions 							cur_ns - last_stackshot_ns < TIMER_PERIOD_NS) {
647*43a90889SApple OSS Distributions 						T_LOG("skipping assertion because stackshot happened "
648*43a90889SApple OSS Distributions 								"%" PRIu64 "ns ago",
649*43a90889SApple OSS Distributions 								cur_ns - last_stackshot_ns);
650*43a90889SApple OSS Distributions 					} else {
651*43a90889SApple OSS Distributions 						T_ASSERT_FAIL("profiling period is shorter than "
652*43a90889SApple OSS Distributions 								"expected with no stackshot interference");
653*43a90889SApple OSS Distributions 					}
654*43a90889SApple OSS Distributions 				}
655*43a90889SApple OSS Distributions 			}
656*43a90889SApple OSS Distributions 
657*43a90889SApple OSS Distributions 			struct instval *latency = cirq_push(&cur_cpu->fire_latencies);
658*43a90889SApple OSS Distributions 			latency->iv_instant_ns = cur_ns;
659*43a90889SApple OSS Distributions 			latency->iv_val = timer_latency_ns;
660*43a90889SApple OSS Distributions 
661*43a90889SApple OSS Distributions 			// Snapshot this timer fire's interrupt latency, so the next one
662*43a90889SApple OSS Distributions 			// can make an adjustment to the period.
663*43a90889SApple OSS Distributions 			last_fire_latency_ns = timer_latency_ns;
664*43a90889SApple OSS Distributions 		}
665*43a90889SApple OSS Distributions 		last_fire_ns = cur_ns;
666*43a90889SApple OSS Distributions 		last_fire_cpuid = tp->cpuid;
667*43a90889SApple OSS Distributions 
668*43a90889SApple OSS Distributions 		if (nfires >= FIRES_THRESHOLD) {
669*43a90889SApple OSS Distributions 			ktrace_end(s, 1);
670*43a90889SApple OSS Distributions 		}
671*43a90889SApple OSS Distributions 	});
672*43a90889SApple OSS Distributions 
673*43a90889SApple OSS Distributions 	configure_kperf_timer_samplers(TIMER_PERIOD_NS, KPERF_SAMPLER_TINFO);
674*43a90889SApple OSS Distributions 
675*43a90889SApple OSS Distributions 	ktrace_set_completion_handler(s, ^{
676*43a90889SApple OSS Distributions 		double duration_secs = (double)(last_fire_ns - first_fire_ns) /
677*43a90889SApple OSS Distributions 		    NSEC_PER_SEC;
678*43a90889SApple OSS Distributions 		T_LOG("stopping trace after %.2f seconds", duration_secs);
679*43a90889SApple OSS Distributions 
680*43a90889SApple OSS Distributions 		T_PASS("saw %" PRIu64 " timer fires (%g fires/second) without "
681*43a90889SApple OSS Distributions 			"oversampling", nfires, (double)nfires / (double)duration_secs);
682*43a90889SApple OSS Distributions 
683*43a90889SApple OSS Distributions 		T_END;
684*43a90889SApple OSS Distributions 	});
685*43a90889SApple OSS Distributions 
686*43a90889SApple OSS Distributions 	start_tracing_with_timeout(s, 5);
687*43a90889SApple OSS Distributions 
688*43a90889SApple OSS Distributions 	// Get all CPUs out of idle.
689*43a90889SApple OSS Distributions 	uint64_t *counts = kpc_counterbuf_alloc();
690*43a90889SApple OSS Distributions 	(void)kpc_get_cpu_counters(true,KPC_CLASS_CONFIGURABLE_MASK, NULL, counts);
691*43a90889SApple OSS Distributions 	free(counts);
692*43a90889SApple OSS Distributions 
693*43a90889SApple OSS Distributions 	dispatch_main();
694*43a90889SApple OSS Distributions }
695*43a90889SApple OSS Distributions 
696*43a90889SApple OSS Distributions T_DECL(kperf_timer_stress, "repeatedly enable and disable timers",
697*43a90889SApple OSS Distributions 	T_META_TAG_VM_NOT_ELIGIBLE)
698*43a90889SApple OSS Distributions {
699*43a90889SApple OSS Distributions 	start_controlling_ktrace();
700*43a90889SApple OSS Distributions 
701*43a90889SApple OSS Distributions 	const int niters = 500;
702*43a90889SApple OSS Distributions 	for (int i = 0; i < niters; i++) {
703*43a90889SApple OSS Distributions 		configure_kperf_stacks_timer(-1, 1, true);
704*43a90889SApple OSS Distributions 		T_QUIET;
705*43a90889SApple OSS Distributions 		T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
706*43a90889SApple OSS Distributions 		usleep(2000);
707*43a90889SApple OSS Distributions 		kperf_reset();
708*43a90889SApple OSS Distributions 	}
709*43a90889SApple OSS Distributions 	T_LOG("configured kperf with a timer %d times", niters);
710*43a90889SApple OSS Distributions }
711*43a90889SApple OSS Distributions 
712*43a90889SApple OSS Distributions #pragma mark - kdebug triggers
713*43a90889SApple OSS Distributions 
714*43a90889SApple OSS Distributions #define KDEBUG_TRIGGER_TIMEOUT_NS (10 * NSEC_PER_SEC)
715*43a90889SApple OSS Distributions 
716*43a90889SApple OSS Distributions #define NON_TRIGGER_CLASS    UINT32_C(0xfd)
717*43a90889SApple OSS Distributions #define NON_TRIGGER_SUBCLASS UINT32_C(0xff)
718*43a90889SApple OSS Distributions #define NON_TRIGGER_CODE     UINT32_C(0xff)
719*43a90889SApple OSS Distributions 
720*43a90889SApple OSS Distributions #define NON_TRIGGER_EVENT \
721*43a90889SApple OSS Distributions 	        (KDBG_EVENTID(NON_TRIGGER_CLASS, NON_TRIGGER_SUBCLASS, \
722*43a90889SApple OSS Distributions 	        NON_TRIGGER_CODE))
723*43a90889SApple OSS Distributions 
724*43a90889SApple OSS Distributions static void
expect_kdebug_trigger(const char * filter_desc,const uint32_t * debugids,unsigned int n_debugids)725*43a90889SApple OSS Distributions expect_kdebug_trigger(const char *filter_desc, const uint32_t *debugids,
726*43a90889SApple OSS Distributions     unsigned int n_debugids)
727*43a90889SApple OSS Distributions {
728*43a90889SApple OSS Distributions 	__block int missing_kernel_stacks = 0;
729*43a90889SApple OSS Distributions 	__block int missing_user_stacks = 0;
730*43a90889SApple OSS Distributions 	ktrace_session_t s;
731*43a90889SApple OSS Distributions 	kperf_kdebug_filter_t filter;
732*43a90889SApple OSS Distributions 
733*43a90889SApple OSS Distributions 	s = ktrace_session_create();
734*43a90889SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(s, "ktrace_session_create");
735*43a90889SApple OSS Distributions 	ktrace_set_collection_interval(s, 100);
736*43a90889SApple OSS Distributions 
737*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_STK_KHDR, ^(struct trace_point *tp) {
738*43a90889SApple OSS Distributions 		missing_kernel_stacks--;
739*43a90889SApple OSS Distributions 		T_LOG("saw kernel stack with %" PRIu64 " frames, flags = %#"
740*43a90889SApple OSS Distributions 		PRIx64, tp->arg2, tp->arg1);
741*43a90889SApple OSS Distributions 	});
742*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_STK_UHDR, ^(struct trace_point *tp) {
743*43a90889SApple OSS Distributions 		missing_user_stacks--;
744*43a90889SApple OSS Distributions 		T_LOG("saw user stack with %" PRIu64 " frames, flags = %#"
745*43a90889SApple OSS Distributions 		PRIx64, tp->arg2, tp->arg1);
746*43a90889SApple OSS Distributions 	});
747*43a90889SApple OSS Distributions 
748*43a90889SApple OSS Distributions 	for (unsigned int i = 0; i < n_debugids; i++) {
749*43a90889SApple OSS Distributions 		ktrace_events_single(s, debugids[i], ^(struct trace_point *tp) {
750*43a90889SApple OSS Distributions 			missing_kernel_stacks++;
751*43a90889SApple OSS Distributions 			missing_user_stacks++;
752*43a90889SApple OSS Distributions 			T_LOG("saw event with debugid 0x%" PRIx32, tp->debugid);
753*43a90889SApple OSS Distributions 		});
754*43a90889SApple OSS Distributions 	}
755*43a90889SApple OSS Distributions 
756*43a90889SApple OSS Distributions 	ktrace_events_single(s, NON_TRIGGER_EVENT,
757*43a90889SApple OSS Distributions 	    ^(__unused struct trace_point *tp)
758*43a90889SApple OSS Distributions 	{
759*43a90889SApple OSS Distributions 		ktrace_end(s, 0);
760*43a90889SApple OSS Distributions 	});
761*43a90889SApple OSS Distributions 
762*43a90889SApple OSS Distributions 	ktrace_set_completion_handler(s, ^{
763*43a90889SApple OSS Distributions 		T_EXPECT_LE(missing_kernel_stacks, 0, NULL);
764*43a90889SApple OSS Distributions 		T_EXPECT_LE(missing_user_stacks, 0, NULL);
765*43a90889SApple OSS Distributions 
766*43a90889SApple OSS Distributions 		ktrace_session_destroy(s);
767*43a90889SApple OSS Distributions 		T_END;
768*43a90889SApple OSS Distributions 	});
769*43a90889SApple OSS Distributions 
770*43a90889SApple OSS Distributions 	kperf_reset();
771*43a90889SApple OSS Distributions 
772*43a90889SApple OSS Distributions 	(void)kperf_action_count_set(1);
773*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1,
774*43a90889SApple OSS Distributions 	    KPERF_SAMPLER_KSTACK | KPERF_SAMPLER_USTACK), NULL);
775*43a90889SApple OSS Distributions 
776*43a90889SApple OSS Distributions 	filter = kperf_kdebug_filter_create();
777*43a90889SApple OSS Distributions 	T_ASSERT_NOTNULL(filter, NULL);
778*43a90889SApple OSS Distributions 
779*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_kdebug_action_set(1), NULL);
780*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_add_desc(filter, filter_desc),
781*43a90889SApple OSS Distributions 	    NULL);
782*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_set(filter), NULL);
783*43a90889SApple OSS Distributions 	kperf_kdebug_filter_destroy(filter);
784*43a90889SApple OSS Distributions 
785*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL);
786*43a90889SApple OSS Distributions 
787*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(ktrace_start(s, dispatch_get_main_queue()), NULL);
788*43a90889SApple OSS Distributions 
789*43a90889SApple OSS Distributions 	// Trace the triggering events.
790*43a90889SApple OSS Distributions 
791*43a90889SApple OSS Distributions 	for (unsigned int i = 0; i < n_debugids; i++) {
792*43a90889SApple OSS Distributions 		T_ASSERT_POSIX_SUCCESS(kdebug_trace(debugids[i], 0, 0, 0, 0), NULL);
793*43a90889SApple OSS Distributions 	}
794*43a90889SApple OSS Distributions 
795*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kdebug_trace(NON_TRIGGER_EVENT, 0, 0, 0, 0), NULL);
796*43a90889SApple OSS Distributions 
797*43a90889SApple OSS Distributions 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, KDEBUG_TRIGGER_TIMEOUT_NS),
798*43a90889SApple OSS Distributions 	    dispatch_get_main_queue(), ^(void)
799*43a90889SApple OSS Distributions 	{
800*43a90889SApple OSS Distributions 		ktrace_end(s, 1);
801*43a90889SApple OSS Distributions 	});
802*43a90889SApple OSS Distributions }
803*43a90889SApple OSS Distributions 
804*43a90889SApple OSS Distributions #define TRIGGER_CLASS     UINT32_C(0xfe)
805*43a90889SApple OSS Distributions #define TRIGGER_CLASS_END UINT32_C(0xfd)
806*43a90889SApple OSS Distributions #define TRIGGER_SUBCLASS  UINT32_C(0xff)
807*43a90889SApple OSS Distributions #define TRIGGER_CODE      UINT32_C(0)
808*43a90889SApple OSS Distributions #define TRIGGER_DEBUGID \
809*43a90889SApple OSS Distributions 	        (KDBG_EVENTID(TRIGGER_CLASS, TRIGGER_SUBCLASS, TRIGGER_CODE))
810*43a90889SApple OSS Distributions 
811*43a90889SApple OSS Distributions T_DECL(kperf_kdebug_trigger_classes,
812*43a90889SApple OSS Distributions     "test that kdebug trigger samples on classes",
813*43a90889SApple OSS Distributions 	T_META_TAG_VM_PREFERRED)
814*43a90889SApple OSS Distributions {
815*43a90889SApple OSS Distributions 	start_controlling_ktrace();
816*43a90889SApple OSS Distributions 
817*43a90889SApple OSS Distributions 	const uint32_t class_debugids[] = {
818*43a90889SApple OSS Distributions 		KDBG_EVENTID(TRIGGER_CLASS, 1, 1),
819*43a90889SApple OSS Distributions 		KDBG_EVENTID(TRIGGER_CLASS, 2, 1),
820*43a90889SApple OSS Distributions 		KDBG_EVENTID(TRIGGER_CLASS_END, 1, 1) | DBG_FUNC_END,
821*43a90889SApple OSS Distributions 		KDBG_EVENTID(TRIGGER_CLASS_END, 2, 1) | DBG_FUNC_END,
822*43a90889SApple OSS Distributions 	};
823*43a90889SApple OSS Distributions 
824*43a90889SApple OSS Distributions 	expect_kdebug_trigger("C0xfe,C0xfdr", class_debugids,
825*43a90889SApple OSS Distributions 	    sizeof(class_debugids) / sizeof(class_debugids[0]));
826*43a90889SApple OSS Distributions 	dispatch_main();
827*43a90889SApple OSS Distributions }
828*43a90889SApple OSS Distributions 
829*43a90889SApple OSS Distributions T_DECL(kperf_kdebug_trigger_subclasses,
830*43a90889SApple OSS Distributions     "test that kdebug trigger samples on subclasses",
831*43a90889SApple OSS Distributions 	T_META_TAG_VM_PREFERRED)
832*43a90889SApple OSS Distributions {
833*43a90889SApple OSS Distributions 	start_controlling_ktrace();
834*43a90889SApple OSS Distributions 
835*43a90889SApple OSS Distributions 	const uint32_t subclass_debugids[] = {
836*43a90889SApple OSS Distributions 		KDBG_EVENTID(TRIGGER_CLASS, TRIGGER_SUBCLASS, 0),
837*43a90889SApple OSS Distributions 		KDBG_EVENTID(TRIGGER_CLASS, TRIGGER_SUBCLASS, 1),
838*43a90889SApple OSS Distributions 		KDBG_EVENTID(TRIGGER_CLASS_END, TRIGGER_SUBCLASS, 0) | DBG_FUNC_END,
839*43a90889SApple OSS Distributions 		KDBG_EVENTID(TRIGGER_CLASS_END, TRIGGER_SUBCLASS, 1) | DBG_FUNC_END
840*43a90889SApple OSS Distributions 	};
841*43a90889SApple OSS Distributions 
842*43a90889SApple OSS Distributions 	expect_kdebug_trigger("S0xfeff,S0xfdffr", subclass_debugids,
843*43a90889SApple OSS Distributions 	    sizeof(subclass_debugids) / sizeof(subclass_debugids[0]));
844*43a90889SApple OSS Distributions 	dispatch_main();
845*43a90889SApple OSS Distributions }
846*43a90889SApple OSS Distributions 
847*43a90889SApple OSS Distributions T_DECL(kperf_kdebug_trigger_debugids,
848*43a90889SApple OSS Distributions     "test that kdebug trigger samples on debugids",
849*43a90889SApple OSS Distributions 	T_META_TAG_VM_PREFERRED)
850*43a90889SApple OSS Distributions {
851*43a90889SApple OSS Distributions 	start_controlling_ktrace();
852*43a90889SApple OSS Distributions 
853*43a90889SApple OSS Distributions 	const uint32_t debugids[] = {
854*43a90889SApple OSS Distributions 		TRIGGER_DEBUGID
855*43a90889SApple OSS Distributions 	};
856*43a90889SApple OSS Distributions 
857*43a90889SApple OSS Distributions 	expect_kdebug_trigger("D0xfeff0000", debugids,
858*43a90889SApple OSS Distributions 	    sizeof(debugids) / sizeof(debugids[0]));
859*43a90889SApple OSS Distributions 	dispatch_main();
860*43a90889SApple OSS Distributions }
861*43a90889SApple OSS Distributions 
862*43a90889SApple OSS Distributions // TODO Set a single function specifier filter, expect not to trigger of all
863*43a90889SApple OSS Distributions // events from that class.
864*43a90889SApple OSS Distributions 
865*43a90889SApple OSS Distributions static void
reset_kperf(void)866*43a90889SApple OSS Distributions reset_kperf(void)
867*43a90889SApple OSS Distributions {
868*43a90889SApple OSS Distributions 	(void)kperf_reset();
869*43a90889SApple OSS Distributions 	(void)sysctlbyname("kperf.debug_level", NULL, NULL, &(int){ 0 },
870*43a90889SApple OSS Distributions 	    sizeof(int));
871*43a90889SApple OSS Distributions }
872*43a90889SApple OSS Distributions 
873*43a90889SApple OSS Distributions T_DECL(kperf_kdbg_callstacks,
874*43a90889SApple OSS Distributions     "test that the kdbg_callstacks samples on syscalls",
875*43a90889SApple OSS Distributions 	T_META_TAG_VM_PREFERRED)
876*43a90889SApple OSS Distributions {
877*43a90889SApple OSS Distributions 	start_controlling_ktrace();
878*43a90889SApple OSS Distributions 
879*43a90889SApple OSS Distributions 	ktrace_session_t s;
880*43a90889SApple OSS Distributions 	__block bool saw_user_stack = false;
881*43a90889SApple OSS Distributions 
882*43a90889SApple OSS Distributions 	s = ktrace_session_create();
883*43a90889SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(s, "ktrace_session_create");
884*43a90889SApple OSS Distributions 	ktrace_set_collection_interval(s, 100);
885*43a90889SApple OSS Distributions 
886*43a90889SApple OSS Distributions 	// Make sure BSD events are traced in order to trigger samples on syscalls.
887*43a90889SApple OSS Distributions 	ktrace_events_class(s, DBG_BSD, ^void (__unused struct trace_point *tp) {});
888*43a90889SApple OSS Distributions 
889*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_STK_UHDR, ^(__unused struct trace_point *tp) {
890*43a90889SApple OSS Distributions 		saw_user_stack = true;
891*43a90889SApple OSS Distributions 		ktrace_end(s, 1);
892*43a90889SApple OSS Distributions 	});
893*43a90889SApple OSS Distributions 
894*43a90889SApple OSS Distributions 	ktrace_set_completion_handler(s, ^{
895*43a90889SApple OSS Distributions 		ktrace_session_destroy(s);
896*43a90889SApple OSS Distributions 
897*43a90889SApple OSS Distributions 		T_EXPECT_TRUE(saw_user_stack,
898*43a90889SApple OSS Distributions 		"saw user stack after configuring kdbg_callstacks");
899*43a90889SApple OSS Distributions 		T_END;
900*43a90889SApple OSS Distributions 	});
901*43a90889SApple OSS Distributions 
902*43a90889SApple OSS Distributions #pragma clang diagnostic push
903*43a90889SApple OSS Distributions #pragma clang diagnostic ignored "-Wdeprecated-declarations"
904*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_kdbg_callstacks_set(1), NULL);
905*43a90889SApple OSS Distributions #pragma clang diagnostic pop
906*43a90889SApple OSS Distributions 	T_ATEND(reset_kperf);
907*43a90889SApple OSS Distributions 
908*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(ktrace_start(s, dispatch_get_main_queue()), NULL);
909*43a90889SApple OSS Distributions 
910*43a90889SApple OSS Distributions 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC),
911*43a90889SApple OSS Distributions 	    dispatch_get_main_queue(), ^(void) {
912*43a90889SApple OSS Distributions 		ktrace_end(s, 1);
913*43a90889SApple OSS Distributions 	});
914*43a90889SApple OSS Distributions 
915*43a90889SApple OSS Distributions 	dispatch_main();
916*43a90889SApple OSS Distributions }
917*43a90889SApple OSS Distributions 
918*43a90889SApple OSS Distributions #pragma mark - PET
919*43a90889SApple OSS Distributions 
920*43a90889SApple OSS Distributions #define STACKS_WAIT_DURATION_NS (3 * NSEC_PER_SEC)
921*43a90889SApple OSS Distributions 
922*43a90889SApple OSS Distributions static void
923*43a90889SApple OSS Distributions expect_stacks_traced(void (^setup)(ktrace_session_t s), void (^complete)(void))
924*43a90889SApple OSS Distributions {
925*43a90889SApple OSS Distributions 	ktrace_session_t s;
926*43a90889SApple OSS Distributions 
927*43a90889SApple OSS Distributions 	s = ktrace_session_create();
928*43a90889SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(s, "ktrace_session_create");
929*43a90889SApple OSS Distributions 	ktrace_set_collection_interval(s, 100);
930*43a90889SApple OSS Distributions 	if (setup) {
931*43a90889SApple OSS Distributions 		setup(s);
932*43a90889SApple OSS Distributions 	}
933*43a90889SApple OSS Distributions 
934*43a90889SApple OSS Distributions 	__block unsigned int user_stacks = 0;
935*43a90889SApple OSS Distributions 	__block unsigned int kernel_stacks = 0;
936*43a90889SApple OSS Distributions 
937*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_STK_UHDR, ^(__unused struct trace_point *tp) {
938*43a90889SApple OSS Distributions 		user_stacks++;
939*43a90889SApple OSS Distributions 	});
940*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_STK_KHDR, ^(__unused struct trace_point *tp) {
941*43a90889SApple OSS Distributions 		kernel_stacks++;
942*43a90889SApple OSS Distributions 	});
943*43a90889SApple OSS Distributions 
944*43a90889SApple OSS Distributions 	ktrace_set_completion_handler(s, ^(void) {
945*43a90889SApple OSS Distributions 		ktrace_session_destroy(s);
946*43a90889SApple OSS Distributions 		T_EXPECT_GT(user_stacks, 0U, NULL);
947*43a90889SApple OSS Distributions 		T_EXPECT_GT(kernel_stacks, 0U, NULL);
948*43a90889SApple OSS Distributions 		complete();
949*43a90889SApple OSS Distributions 	});
950*43a90889SApple OSS Distributions 
951*43a90889SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL);
952*43a90889SApple OSS Distributions 
953*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(ktrace_start(s, dispatch_get_main_queue()), NULL);
954*43a90889SApple OSS Distributions 
955*43a90889SApple OSS Distributions 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, STACKS_WAIT_DURATION_NS),
956*43a90889SApple OSS Distributions 	    dispatch_get_main_queue(), ^(void)
957*43a90889SApple OSS Distributions 	{
958*43a90889SApple OSS Distributions 		kperf_reset();
959*43a90889SApple OSS Distributions 		ktrace_end(s, 0);
960*43a90889SApple OSS Distributions 	});
961*43a90889SApple OSS Distributions }
962*43a90889SApple OSS Distributions 
963*43a90889SApple OSS Distributions T_DECL(kperf_pet, "test that PET mode samples kernel and user stacks",
964*43a90889SApple OSS Distributions 	T_META_TAG_VM_PREFERRED)
965*43a90889SApple OSS Distributions {
966*43a90889SApple OSS Distributions 	start_controlling_ktrace();
967*43a90889SApple OSS Distributions 
968*43a90889SApple OSS Distributions 	configure_kperf_stacks_timer(-1, 10, false);
969*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_timer_pet_set(0), NULL);
970*43a90889SApple OSS Distributions 
971*43a90889SApple OSS Distributions 	expect_stacks_traced(NULL, ^(void) {
972*43a90889SApple OSS Distributions 		T_END;
973*43a90889SApple OSS Distributions 	});
974*43a90889SApple OSS Distributions 
975*43a90889SApple OSS Distributions 	dispatch_main();
976*43a90889SApple OSS Distributions }
977*43a90889SApple OSS Distributions 
978*43a90889SApple OSS Distributions T_DECL(kperf_lightweight_pet,
979*43a90889SApple OSS Distributions     "test that lightweight PET mode samples kernel and user stacks",
980*43a90889SApple OSS Distributions     T_META_ASROOT(true),
981*43a90889SApple OSS Distributions 	T_META_TAG_VM_PREFERRED)
982*43a90889SApple OSS Distributions {
983*43a90889SApple OSS Distributions 	start_controlling_ktrace();
984*43a90889SApple OSS Distributions 
985*43a90889SApple OSS Distributions 	int set = 1;
986*43a90889SApple OSS Distributions 
987*43a90889SApple OSS Distributions 	configure_kperf_stacks_timer(-1, 10, false);
988*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname("kperf.lightweight_pet", NULL, NULL,
989*43a90889SApple OSS Distributions 	    &set, sizeof(set)), NULL);
990*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_timer_pet_set(0), NULL);
991*43a90889SApple OSS Distributions 
992*43a90889SApple OSS Distributions 	expect_stacks_traced(^(ktrace_session_t s) {}, ^(void) {
993*43a90889SApple OSS Distributions 		T_END;
994*43a90889SApple OSS Distributions 	});
995*43a90889SApple OSS Distributions 
996*43a90889SApple OSS Distributions 	dispatch_main();
997*43a90889SApple OSS Distributions }
998*43a90889SApple OSS Distributions 
999*43a90889SApple OSS Distributions T_DECL(kperf_pet_stress, "repeatedly enable and disable PET mode",
1000*43a90889SApple OSS Distributions 	T_META_TAG_VM_NOT_ELIGIBLE)
1001*43a90889SApple OSS Distributions {
1002*43a90889SApple OSS Distributions 	start_controlling_ktrace();
1003*43a90889SApple OSS Distributions 
1004*43a90889SApple OSS Distributions 	const int niters = 500;
1005*43a90889SApple OSS Distributions 	for (int i = 0; i < niters; i++) {
1006*43a90889SApple OSS Distributions 		configure_kperf_stacks_timer(-1, 1, true);
1007*43a90889SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_SUCCESS(kperf_timer_pet_set(0), NULL);
1008*43a90889SApple OSS Distributions 		T_QUIET;
1009*43a90889SApple OSS Distributions 		T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
1010*43a90889SApple OSS Distributions 		usleep(2000);
1011*43a90889SApple OSS Distributions 		kperf_reset();
1012*43a90889SApple OSS Distributions 	}
1013*43a90889SApple OSS Distributions 
1014*43a90889SApple OSS Distributions 	T_PASS("configured kperf PET %d times", niters);
1015*43a90889SApple OSS Distributions }
1016*43a90889SApple OSS Distributions 
1017*43a90889SApple OSS Distributions #pragma mark - PMCs
1018*43a90889SApple OSS Distributions 
1019*43a90889SApple OSS Distributions T_DECL(kperf_pmc_config_only,
1020*43a90889SApple OSS Distributions     "shouldn't show PMC config events unless requested",
1021*43a90889SApple OSS Distributions 	T_META_TAG_VM_NOT_ELIGIBLE)
1022*43a90889SApple OSS Distributions {
1023*43a90889SApple OSS Distributions 	start_controlling_ktrace();
1024*43a90889SApple OSS Distributions 
1025*43a90889SApple OSS Distributions 	__block bool saw_kpc_config = false;
1026*43a90889SApple OSS Distributions 	__block bool saw_kpc_reg = false;
1027*43a90889SApple OSS Distributions 
1028*43a90889SApple OSS Distributions 	ktrace_session_t s = ktrace_session_create();
1029*43a90889SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(s, "ktrace_session_create");
1030*43a90889SApple OSS Distributions 	ktrace_set_collection_interval(s, 100);
1031*43a90889SApple OSS Distributions 
1032*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_KPC_CONFIG,
1033*43a90889SApple OSS Distributions 	    ^(__unused struct trace_point *tp) {
1034*43a90889SApple OSS Distributions 		saw_kpc_config = true;
1035*43a90889SApple OSS Distributions 	});
1036*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_KPC_REG,
1037*43a90889SApple OSS Distributions 	    ^(__unused struct trace_point *tp) {
1038*43a90889SApple OSS Distributions 		saw_kpc_reg = true;
1039*43a90889SApple OSS Distributions 	});
1040*43a90889SApple OSS Distributions 	ktrace_events_single(s, PERF_KPC_REG32,
1041*43a90889SApple OSS Distributions 	    ^(__unused struct trace_point *tp) {
1042*43a90889SApple OSS Distributions 		saw_kpc_reg = true;
1043*43a90889SApple OSS Distributions 	});
1044*43a90889SApple OSS Distributions 
1045*43a90889SApple OSS Distributions 	ktrace_set_completion_handler(s, ^{
1046*43a90889SApple OSS Distributions 		ktrace_session_destroy(s);
1047*43a90889SApple OSS Distributions 		T_EXPECT_FALSE(saw_kpc_config,
1048*43a90889SApple OSS Distributions 		"should see no KPC configs without sampler enabled");
1049*43a90889SApple OSS Distributions 		T_EXPECT_FALSE(saw_kpc_reg,
1050*43a90889SApple OSS Distributions 		"should see no KPC registers without sampler enabled");
1051*43a90889SApple OSS Distributions 		T_END;
1052*43a90889SApple OSS Distributions 	});
1053*43a90889SApple OSS Distributions 
1054*43a90889SApple OSS Distributions 	uint32_t nconfigs = kpc_get_config_count(KPC_CLASS_CONFIGURABLE_MASK);
1055*43a90889SApple OSS Distributions 	uint64_t *config = calloc(nconfigs, sizeof(*config));
1056*43a90889SApple OSS Distributions 	config[0] = 0x02;
1057*43a90889SApple OSS Distributions 	int ret = kpc_set_config(KPC_CLASS_CONFIGURABLE_MASK, config);
1058*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "configured kpc");
1059*43a90889SApple OSS Distributions 	T_QUIET;
1060*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kpc_set_counting(KPC_CLASS_CONFIGURABLE_MASK),
1061*43a90889SApple OSS Distributions 	    "kpc_set_counting");
1062*43a90889SApple OSS Distributions 
1063*43a90889SApple OSS Distributions 	(void)kperf_action_count_set(1);
1064*43a90889SApple OSS Distributions 	T_ATEND(reset_kperf);
1065*43a90889SApple OSS Distributions 	T_QUIET;
1066*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1, KPERF_SAMPLER_PMC_CPU),
1067*43a90889SApple OSS Distributions 	    NULL);
1068*43a90889SApple OSS Distributions 
1069*43a90889SApple OSS Distributions 	(void)kperf_timer_count_set(1);
1070*43a90889SApple OSS Distributions 	T_QUIET;
1071*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_timer_period_set(0,
1072*43a90889SApple OSS Distributions 	    kperf_ns_to_ticks(TIMER_PERIOD_NS)), NULL);
1073*43a90889SApple OSS Distributions 	T_QUIET;
1074*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_timer_action_set(0, 1), NULL);
1075*43a90889SApple OSS Distributions 
1076*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
1077*43a90889SApple OSS Distributions 
1078*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(ktrace_start(s, dispatch_get_main_queue()), NULL);
1079*43a90889SApple OSS Distributions 
1080*43a90889SApple OSS Distributions 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC),
1081*43a90889SApple OSS Distributions 	    dispatch_get_main_queue(), ^(void) {
1082*43a90889SApple OSS Distributions 		ktrace_end(s, 1);
1083*43a90889SApple OSS Distributions 	});
1084*43a90889SApple OSS Distributions 
1085*43a90889SApple OSS Distributions 	dispatch_main();
1086*43a90889SApple OSS Distributions }
1087*43a90889SApple OSS Distributions 
1088*43a90889SApple OSS Distributions static void
skip_if_monotonic_unsupported(void)1089*43a90889SApple OSS Distributions skip_if_monotonic_unsupported(void)
1090*43a90889SApple OSS Distributions {
1091*43a90889SApple OSS Distributions 	int r;
1092*43a90889SApple OSS Distributions 	int supported = 0;
1093*43a90889SApple OSS Distributions 	size_t supported_size = sizeof(supported);
1094*43a90889SApple OSS Distributions 
1095*43a90889SApple OSS Distributions 	r = sysctlbyname("kern.monotonic.supported", &supported, &supported_size,
1096*43a90889SApple OSS Distributions 	    NULL, 0);
1097*43a90889SApple OSS Distributions 	if (r < 0) {
1098*43a90889SApple OSS Distributions 		T_WITH_ERRNO;
1099*43a90889SApple OSS Distributions 		T_SKIP("could not find \"kern.monotonic.supported\" sysctl");
1100*43a90889SApple OSS Distributions 	}
1101*43a90889SApple OSS Distributions 
1102*43a90889SApple OSS Distributions 	if (!supported) {
1103*43a90889SApple OSS Distributions 		T_SKIP("monotonic is not supported on this platform");
1104*43a90889SApple OSS Distributions 	}
1105*43a90889SApple OSS Distributions }
1106*43a90889SApple OSS Distributions 
1107*43a90889SApple OSS Distributions #define INSTRS_CYCLES_UPPER 500
1108*43a90889SApple OSS Distributions #define INSTRS_CYCLES_LOWER 50
1109*43a90889SApple OSS Distributions 
1110*43a90889SApple OSS Distributions T_DECL(kperf_sample_instrs_cycles,
1111*43a90889SApple OSS Distributions     "ensure instructions and cycles are sampled",
1112*43a90889SApple OSS Distributions 	T_META_TAG_VM_NOT_ELIGIBLE)
1113*43a90889SApple OSS Distributions {
1114*43a90889SApple OSS Distributions 	T_SETUPBEGIN;
1115*43a90889SApple OSS Distributions 
1116*43a90889SApple OSS Distributions 	skip_if_monotonic_unsupported();
1117*43a90889SApple OSS Distributions 	start_controlling_ktrace();
1118*43a90889SApple OSS Distributions 
1119*43a90889SApple OSS Distributions 	ktrace_session_t sess = ktrace_session_create();
1120*43a90889SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(sess, "ktrace_session_create");
1121*43a90889SApple OSS Distributions 	ktrace_set_collection_interval(sess, 100);
1122*43a90889SApple OSS Distributions 
1123*43a90889SApple OSS Distributions 	__block uint64_t ninstrs_cycles = 0;
1124*43a90889SApple OSS Distributions 	__block uint64_t nzeroes = 0;
1125*43a90889SApple OSS Distributions 	ktrace_events_single(sess, PERF_INSTR_DATA,
1126*43a90889SApple OSS Distributions 	    ^(__unused struct trace_point *tp) {
1127*43a90889SApple OSS Distributions 		ninstrs_cycles++;
1128*43a90889SApple OSS Distributions 		if (tp->arg1 == 0) {
1129*43a90889SApple OSS Distributions 			T_LOG("%llx (%s)\n", tp->threadid, tp->command);
1130*43a90889SApple OSS Distributions 			nzeroes++;
1131*43a90889SApple OSS Distributions 		}
1132*43a90889SApple OSS Distributions 		if (ninstrs_cycles >= INSTRS_CYCLES_UPPER) {
1133*43a90889SApple OSS Distributions 			ktrace_end(sess, 1);
1134*43a90889SApple OSS Distributions 		}
1135*43a90889SApple OSS Distributions 	});
1136*43a90889SApple OSS Distributions 
1137*43a90889SApple OSS Distributions 	ktrace_set_collection_interval(sess, 200);
1138*43a90889SApple OSS Distributions 
1139*43a90889SApple OSS Distributions 	ktrace_set_completion_handler(sess, ^{
1140*43a90889SApple OSS Distributions 		T_EXPECT_GE(ninstrs_cycles, (uint64_t)INSTRS_CYCLES_LOWER,
1141*43a90889SApple OSS Distributions 		    "saw enough instructions and cycles events");
1142*43a90889SApple OSS Distributions 		T_EXPECT_EQ(nzeroes, UINT64_C(0),
1143*43a90889SApple OSS Distributions 		    "saw no events with 0 instructions");
1144*43a90889SApple OSS Distributions 		T_END;
1145*43a90889SApple OSS Distributions 	});
1146*43a90889SApple OSS Distributions 
1147*43a90889SApple OSS Distributions 	(void)kperf_action_count_set(1);
1148*43a90889SApple OSS Distributions 	T_ATEND(reset_kperf);
1149*43a90889SApple OSS Distributions 	T_QUIET;
1150*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1,
1151*43a90889SApple OSS Distributions 	    KPERF_SAMPLER_TH_INSTRS_CYCLES), NULL);
1152*43a90889SApple OSS Distributions 
1153*43a90889SApple OSS Distributions 	(void)kperf_timer_count_set(1);
1154*43a90889SApple OSS Distributions 	T_QUIET;
1155*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_timer_period_set(0,
1156*43a90889SApple OSS Distributions 	    kperf_ns_to_ticks(TIMER_PERIOD_NS)), NULL);
1157*43a90889SApple OSS Distributions 	T_QUIET;
1158*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_timer_action_set(0, 1), NULL);
1159*43a90889SApple OSS Distributions 
1160*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
1161*43a90889SApple OSS Distributions 
1162*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(ktrace_start(sess, dispatch_get_main_queue()),
1163*43a90889SApple OSS Distributions 	    NULL);
1164*43a90889SApple OSS Distributions 
1165*43a90889SApple OSS Distributions 	T_SETUPEND;
1166*43a90889SApple OSS Distributions 
1167*43a90889SApple OSS Distributions 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC),
1168*43a90889SApple OSS Distributions 	    dispatch_get_main_queue(), ^(void) {
1169*43a90889SApple OSS Distributions 		ktrace_end(sess, 1);
1170*43a90889SApple OSS Distributions 	});
1171*43a90889SApple OSS Distributions 
1172*43a90889SApple OSS Distributions 	dispatch_main();
1173*43a90889SApple OSS Distributions }
1174*43a90889SApple OSS Distributions 
1175*43a90889SApple OSS Distributions #define UNCOMMON_EVENTID 0xfeedfac0
1176*43a90889SApple OSS Distributions #define LABEL "this_is_a_test_label_that_is_very_long_and_will_be_truncated" \
1177*43a90889SApple OSS Distributions 		"_but_needs_some_more_length_for_good_measure"
1178*43a90889SApple OSS Distributions #define MAX_LABEL_LEN 64
1179*43a90889SApple OSS Distributions static_assert(sizeof(LABEL) > MAX_LABEL_LEN,
1180*43a90889SApple OSS Distributions     "should be larger than the max length of a label");
1181*43a90889SApple OSS Distributions 
1182*43a90889SApple OSS Distributions T_DECL(kperf_dispatch_labels, "ensure dispatch queue labels are sampled",
1183*43a90889SApple OSS Distributions 	T_META_TAG_VM_PREFERRED)
1184*43a90889SApple OSS Distributions {
1185*43a90889SApple OSS Distributions 	T_SETUPBEGIN;
1186*43a90889SApple OSS Distributions 	start_controlling_ktrace();
1187*43a90889SApple OSS Distributions 	ktrace_session_t sess = ktrace_session_create();
1188*43a90889SApple OSS Distributions 	ktrace_set_collection_interval(sess, 200);
1189*43a90889SApple OSS Distributions 
1190*43a90889SApple OSS Distributions 	/*
1191*43a90889SApple OSS Distributions 	 * Expect to see the label that was sampled by kperf.
1192*43a90889SApple OSS Distributions 	 */
1193*43a90889SApple OSS Distributions 	__block uint64_t nlabels = 0;
1194*43a90889SApple OSS Distributions 	char obs_label[MAX_LABEL_LEN + 1] = {};
1195*43a90889SApple OSS Distributions 	char *obs_label_b = obs_label;
1196*43a90889SApple OSS Distributions 	char exp_label[MAX_LABEL_LEN + 1] = {};
1197*43a90889SApple OSS Distributions 	strlcpy(exp_label, LABEL, sizeof(exp_label));
1198*43a90889SApple OSS Distributions 	char *exp_label_b = exp_label;
1199*43a90889SApple OSS Distributions 
1200*43a90889SApple OSS Distributions 	ktrace_events_single(sess, PERF_DISPLABEL, ^(struct trace_point *tp) {
1201*43a90889SApple OSS Distributions 		nlabels++;
1202*43a90889SApple OSS Distributions 		size_t argsize = ktrace_is_kernel_64_bit(sess) ? 8 : 4;
1203*43a90889SApple OSS Distributions 		strncat(obs_label_b, (char *)&tp->arg1, argsize);
1204*43a90889SApple OSS Distributions 		strncat(obs_label_b, (char *)&tp->arg2, argsize);
1205*43a90889SApple OSS Distributions 		strncat(obs_label_b, (char *)&tp->arg3, argsize);
1206*43a90889SApple OSS Distributions 		strncat(obs_label_b, (char *)&tp->arg4, argsize);
1207*43a90889SApple OSS Distributions 		if (tp->debugid & DBG_FUNC_END) {
1208*43a90889SApple OSS Distributions 			ktrace_end(sess, 1);
1209*43a90889SApple OSS Distributions 		}
1210*43a90889SApple OSS Distributions 	});
1211*43a90889SApple OSS Distributions 	ktrace_events_single(sess, PERF_DISPSAMPLE, ^(struct trace_point *tp) {
1212*43a90889SApple OSS Distributions 		if (tp->debugid & DBG_FUNC_END) {
1213*43a90889SApple OSS Distributions 			T_LOG("ending sample event returned %d (%s)",
1214*43a90889SApple OSS Distributions 			    (int)tp->arg1, strerror((int)tp->arg1));
1215*43a90889SApple OSS Distributions 		}
1216*43a90889SApple OSS Distributions 	});
1217*43a90889SApple OSS Distributions 	ktrace_events_single(sess, UNCOMMON_EVENTID,
1218*43a90889SApple OSS Distributions 	    ^(__unused struct trace_point *tp) {
1219*43a90889SApple OSS Distributions 		T_LOG("saw triggering event");
1220*43a90889SApple OSS Distributions 	});
1221*43a90889SApple OSS Distributions 	ktrace_set_completion_handler(sess, ^{
1222*43a90889SApple OSS Distributions 		T_EXPECT_GT(nlabels, 0ULL, "saw %" PRIu64
1223*43a90889SApple OSS Distributions 		    " dispatch queue label events", nlabels);
1224*43a90889SApple OSS Distributions 		T_EXPECT_EQ_STR(obs_label_b, exp_label_b, "label string is correct");
1225*43a90889SApple OSS Distributions 		T_END;
1226*43a90889SApple OSS Distributions 	});
1227*43a90889SApple OSS Distributions 
1228*43a90889SApple OSS Distributions 	/*
1229*43a90889SApple OSS Distributions 	 * Set up kperf to log a dispatch queue label when an uncommon event ID
1230*43a90889SApple OSS Distributions 	 * is traced.
1231*43a90889SApple OSS Distributions 	 */
1232*43a90889SApple OSS Distributions 	(void)sysctlbyname("kperf.debug_level", NULL, NULL, &(int){ 2 }, sizeof(int));
1233*43a90889SApple OSS Distributions 	(void)kperf_action_count_set(1);
1234*43a90889SApple OSS Distributions 	T_ATEND(reset_kperf);
1235*43a90889SApple OSS Distributions 	T_QUIET;
1236*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1,
1237*43a90889SApple OSS Distributions 	    KPERF_SAMPLER_TH_DISPATCH), NULL);
1238*43a90889SApple OSS Distributions 	(void)kperf_kdebug_action_set(1);
1239*43a90889SApple OSS Distributions 	kperf_kdebug_filter_t kdfilt = kperf_kdebug_filter_create();
1240*43a90889SApple OSS Distributions 	T_QUIET;
1241*43a90889SApple OSS Distributions 	T_ASSERT_NOTNULL(kdfilt, "create kdebug filter for kperf");
1242*43a90889SApple OSS Distributions 	T_QUIET;
1243*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(
1244*43a90889SApple OSS Distributions 	    kperf_kdebug_filter_add_debugid(kdfilt, UNCOMMON_EVENTID),
1245*43a90889SApple OSS Distributions 	    "add debugid to filter on");
1246*43a90889SApple OSS Distributions 	T_QUIET;
1247*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(
1248*43a90889SApple OSS Distributions 	    kperf_kdebug_filter_set(kdfilt), "set filter for kperf");
1249*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
1250*43a90889SApple OSS Distributions 
1251*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(ktrace_start(sess, dispatch_get_main_queue()),
1252*43a90889SApple OSS Distributions 	    NULL);
1253*43a90889SApple OSS Distributions 
1254*43a90889SApple OSS Distributions 	/*
1255*43a90889SApple OSS Distributions 	 * Create the queue and emit the sampling event from it.
1256*43a90889SApple OSS Distributions 	 */
1257*43a90889SApple OSS Distributions 	dispatch_queue_t testq = dispatch_queue_create(LABEL, 0);
1258*43a90889SApple OSS Distributions 	T_ASSERT_NOTNULL(testq, "created queue with label: " LABEL);
1259*43a90889SApple OSS Distributions 	dispatch_async(testq, ^{
1260*43a90889SApple OSS Distributions 		kdebug_trace(UNCOMMON_EVENTID, 0, 0, 0, 0);
1261*43a90889SApple OSS Distributions 		sleep(10);
1262*43a90889SApple OSS Distributions 	});
1263*43a90889SApple OSS Distributions 
1264*43a90889SApple OSS Distributions 	T_SETUPEND;
1265*43a90889SApple OSS Distributions 
1266*43a90889SApple OSS Distributions 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC),
1267*43a90889SApple OSS Distributions 	    dispatch_get_main_queue(), ^(void) {
1268*43a90889SApple OSS Distributions 		ktrace_end(sess, 1);
1269*43a90889SApple OSS Distributions 	});
1270*43a90889SApple OSS Distributions 
1271*43a90889SApple OSS Distributions 	dispatch_main();
1272*43a90889SApple OSS Distributions }
1273*43a90889SApple OSS Distributions 
1274*43a90889SApple OSS Distributions T_HELPER_DECL(kperf_blessed_helper,
1275*43a90889SApple OSS Distributions     "drop priviliges and attempt to configure kperf")
1276*43a90889SApple OSS Distributions {
1277*43a90889SApple OSS Distributions 	drop_priv();
1278*43a90889SApple OSS Distributions 	(void)kperf_action_count_set(1);
1279*43a90889SApple OSS Distributions 	errno = 0;
1280*43a90889SApple OSS Distributions 	int ret = kperf_lazy_wait_action_set(1);
1281*43a90889SApple OSS Distributions 	int error = errno;
1282*43a90889SApple OSS Distributions 	exit(ret != 0 ? (error == 0) ? EINVAL : error : 0);
1283*43a90889SApple OSS Distributions }
1284*43a90889SApple OSS Distributions 
1285*43a90889SApple OSS Distributions static pid_t
spawn_bless_helper(void)1286*43a90889SApple OSS Distributions spawn_bless_helper(void)
1287*43a90889SApple OSS Distributions {
1288*43a90889SApple OSS Distributions 	char test_path[MAXPATHLEN] = { 0 };
1289*43a90889SApple OSS Distributions 	int ret = proc_pidpath(getpid(), test_path, MAXPATHLEN);
1290*43a90889SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "query path for current process");
1291*43a90889SApple OSS Distributions 	T_LOG("spawning helper tool at %s", test_path);
1292*43a90889SApple OSS Distributions 	char *child_argv[] = { test_path, "-n", "kperf_blessed_helper", NULL };
1293*43a90889SApple OSS Distributions 	pid_t child_pid = -1;
1294*43a90889SApple OSS Distributions 	int error = dt_launch_tool(&child_pid, child_argv, true, NULL, NULL);
1295*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(error, "spawned child with pid %d", child_pid);
1296*43a90889SApple OSS Distributions 	T_QUIET; T_ASSERT_GT(child_pid, 0, "child pid is valid");
1297*43a90889SApple OSS Distributions 	return child_pid;
1298*43a90889SApple OSS Distributions }
1299*43a90889SApple OSS Distributions 
1300*43a90889SApple OSS Distributions static int
test_child_process(pid_t child_pid)1301*43a90889SApple OSS Distributions test_child_process(pid_t child_pid)
1302*43a90889SApple OSS Distributions {
1303*43a90889SApple OSS Distributions 	int ret = kill(child_pid, SIGCONT);
1304*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "continuing child process");
1305*43a90889SApple OSS Distributions 	int status = 0;
1306*43a90889SApple OSS Distributions 	ret = waitpid(child_pid, &status, 0);
1307*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "waited on child process");
1308*43a90889SApple OSS Distributions 	return status;
1309*43a90889SApple OSS Distributions }
1310*43a90889SApple OSS Distributions 
1311*43a90889SApple OSS Distributions T_DECL(kperf_unblessed_refusal,
1312*43a90889SApple OSS Distributions     "check that an unblessed child cannot access ktrace",
1313*43a90889SApple OSS Distributions 	T_META_TAG_VM_PREFERRED)
1314*43a90889SApple OSS Distributions {
1315*43a90889SApple OSS Distributions 	T_SETUPBEGIN;
1316*43a90889SApple OSS Distributions 	pid_t child_pid = spawn_bless_helper();
1317*43a90889SApple OSS Distributions 	T_SETUPEND;
1318*43a90889SApple OSS Distributions 	int status = test_child_process(child_pid);
1319*43a90889SApple OSS Distributions 	int exit_status = WEXITSTATUS(status);
1320*43a90889SApple OSS Distributions 	T_EXPECT_NE(exit_status, 0, "child failed to access ktrace with %d (%s)",
1321*43a90889SApple OSS Distributions 		exit_status, exit_status == 0 ? "ok" : strerror(exit_status));
1322*43a90889SApple OSS Distributions }
1323*43a90889SApple OSS Distributions 
1324*43a90889SApple OSS Distributions T_DECL(kperf_blessed_ownership, "check that a blessed child can access ktrace",
1325*43a90889SApple OSS Distributions 	T_META_TAG_VM_PREFERRED)
1326*43a90889SApple OSS Distributions {
1327*43a90889SApple OSS Distributions 	T_LOG("parent pid is %d\n", getpid());
1328*43a90889SApple OSS Distributions 	T_SETUPBEGIN;
1329*43a90889SApple OSS Distributions 	pid_t child_pid = spawn_bless_helper();
1330*43a90889SApple OSS Distributions 	T_SETUPEND;
1331*43a90889SApple OSS Distributions 	int ret = kperf_bless_set(child_pid);
1332*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "blessed child with pid %d", child_pid);
1333*43a90889SApple OSS Distributions 	int status = test_child_process(child_pid);
1334*43a90889SApple OSS Distributions 	int exit_status = WEXITSTATUS(status);
1335*43a90889SApple OSS Distributions 	T_EXPECT_EQ(exit_status, 0, "child status was %d (%s)", exit_status,
1336*43a90889SApple OSS Distributions 	    exit_status == 0 ? "ok" : strerror(exit_status));
1337*43a90889SApple OSS Distributions 	ret = kperf_bless_set(-1);
1338*43a90889SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "removed blessing from child");
1339*43a90889SApple OSS Distributions }
1340*43a90889SApple OSS Distributions 
1341