xref: /xnu-11215/tests/microstackshot_tests.c (revision 8d741a5d)
1e7776783SApple OSS Distributions /* Copyright (c) 2018-2021 Apple Inc.  All rights reserved. */
2e7776783SApple OSS Distributions 
3e7776783SApple OSS Distributions #include <CoreFoundation/CoreFoundation.h>
4e7776783SApple OSS Distributions #include <darwintest.h>
5e7776783SApple OSS Distributions #include <darwintest_utils.h>
6e7776783SApple OSS Distributions #include <dispatch/dispatch.h>
7e7776783SApple OSS Distributions #include <ktrace/ktrace.h>
8e7776783SApple OSS Distributions #include <kperf/kperf.h>
9e7776783SApple OSS Distributions #include <kern/debug.h>
10e7776783SApple OSS Distributions #include <notify.h>
11e7776783SApple OSS Distributions #include <sys/kdebug.h>
12e7776783SApple OSS Distributions #include <sys/sysctl.h>
13e7776783SApple OSS Distributions #include <TargetConditionals.h>
14e7776783SApple OSS Distributions 
15e7776783SApple OSS Distributions #include "ktrace/ktrace_helpers.h"
16e7776783SApple OSS Distributions 
17e7776783SApple OSS Distributions enum telemetry_pmi {
18e7776783SApple OSS Distributions 	TELEMETRY_PMI_NONE,
19e7776783SApple OSS Distributions 	TELEMETRY_PMI_INSTRS,
20e7776783SApple OSS Distributions 	TELEMETRY_PMI_CYCLES,
21e7776783SApple OSS Distributions };
22e7776783SApple OSS Distributions #define TELEMETRY_CMD_PMI_SETUP 3
23e7776783SApple OSS Distributions 
24e7776783SApple OSS Distributions T_GLOBAL_META(T_META_NAMESPACE("xnu.stackshot.microstackshot"),
25e7776783SApple OSS Distributions     T_META_RADAR_COMPONENT_NAME("xnu"),
26e7776783SApple OSS Distributions     T_META_RADAR_COMPONENT_VERSION("stackshot"),
27e7776783SApple OSS Distributions     T_META_OWNER("mwidmann"),
28e7776783SApple OSS Distributions     T_META_CHECK_LEAKS(false),
29e7776783SApple OSS Distributions     T_META_ASROOT(true));
30e7776783SApple OSS Distributions 
31e7776783SApple OSS Distributions extern int __telemetry(uint64_t cmd, uint64_t deadline, uint64_t interval,
32e7776783SApple OSS Distributions     uint64_t leeway, uint64_t arg4, uint64_t arg5);
33e7776783SApple OSS Distributions 
34e7776783SApple OSS Distributions /*
35e7776783SApple OSS Distributions  * Data Analytics (da) also has a microstackshot configuration -- set a PMI
36e7776783SApple OSS Distributions  * cycle interval of 0 to force it to disable microstackshot on PMI.
37e7776783SApple OSS Distributions  */
38e7776783SApple OSS Distributions 
39e7776783SApple OSS Distributions static void
set_da_microstackshot_period(CFNumberRef num)40e7776783SApple OSS Distributions set_da_microstackshot_period(CFNumberRef num)
41e7776783SApple OSS Distributions {
42e7776783SApple OSS Distributions 	CFPreferencesSetValue(CFSTR("microstackshotPMICycleInterval"), num,
43e7776783SApple OSS Distributions 	    CFSTR("com.apple.da"),
44e7776783SApple OSS Distributions #if TARGET_OS_IPHONE
45e7776783SApple OSS Distributions 	    CFSTR("mobile"),
46e7776783SApple OSS Distributions #else // TARGET_OS_IPHONE
47e7776783SApple OSS Distributions 	    CFSTR("root"),
48e7776783SApple OSS Distributions #endif // !TARGET_OS_IPHONE
49e7776783SApple OSS Distributions 	    kCFPreferencesCurrentHost);
50e7776783SApple OSS Distributions 
51e7776783SApple OSS Distributions 	notify_post("com.apple.da.tasking_changed");
52e7776783SApple OSS Distributions }
53e7776783SApple OSS Distributions 
54e7776783SApple OSS Distributions static void
disable_da_microstackshots(void)55e7776783SApple OSS Distributions disable_da_microstackshots(void)
56e7776783SApple OSS Distributions {
57e7776783SApple OSS Distributions 	int64_t zero = 0;
58e7776783SApple OSS Distributions 	CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt64Type, &zero);
59e7776783SApple OSS Distributions 	set_da_microstackshot_period(num);
60e7776783SApple OSS Distributions 	T_LOG("notified da of tasking change, sleeping");
61e7776783SApple OSS Distributions #if TARGET_OS_WATCH
62e7776783SApple OSS Distributions 	sleep(8);
63e7776783SApple OSS Distributions #else /* TARGET_OS_WATCH */
64e7776783SApple OSS Distributions 	sleep(3);
65e7776783SApple OSS Distributions #endif /* !TARGET_OS_WATCH */
66e7776783SApple OSS Distributions }
67e7776783SApple OSS Distributions 
68e7776783SApple OSS Distributions /*
69e7776783SApple OSS Distributions  * Unset the preference to allow da to reset its configuration.
70e7776783SApple OSS Distributions  */
71e7776783SApple OSS Distributions static void
reenable_da_microstackshots(void)72e7776783SApple OSS Distributions reenable_da_microstackshots(void)
73e7776783SApple OSS Distributions {
74e7776783SApple OSS Distributions 	set_da_microstackshot_period(NULL);
75e7776783SApple OSS Distributions }
76e7776783SApple OSS Distributions 
77e7776783SApple OSS Distributions /*
78e7776783SApple OSS Distributions  * Clean up the test's configuration and allow da to activate again.
79e7776783SApple OSS Distributions  */
80e7776783SApple OSS Distributions static void
telemetry_cleanup(void)81e7776783SApple OSS Distributions telemetry_cleanup(void)
82e7776783SApple OSS Distributions {
83e7776783SApple OSS Distributions 	(void)__telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_NONE, 0, 0, 0, 0);
84e7776783SApple OSS Distributions 	reenable_da_microstackshots();
85e7776783SApple OSS Distributions }
86e7776783SApple OSS Distributions 
87e7776783SApple OSS Distributions /*
88e7776783SApple OSS Distributions  * Make sure da hasn't configured the microstackshots -- otherwise the PMI
89e7776783SApple OSS Distributions  * setup command will return EBUSY.
90e7776783SApple OSS Distributions  */
91e7776783SApple OSS Distributions static void
telemetry_init(void)92e7776783SApple OSS Distributions telemetry_init(void)
93e7776783SApple OSS Distributions {
94e7776783SApple OSS Distributions 	disable_da_microstackshots();
95e7776783SApple OSS Distributions 	T_LOG("installing cleanup handler");
96e7776783SApple OSS Distributions 	T_ATEND(telemetry_cleanup);
97e7776783SApple OSS Distributions }
98e7776783SApple OSS Distributions 
99e7776783SApple OSS Distributions volatile static bool spinning = true;
100e7776783SApple OSS Distributions 
101e7776783SApple OSS Distributions static void *
thread_spin(__unused void * arg)102e7776783SApple OSS Distributions thread_spin(__unused void *arg)
103e7776783SApple OSS Distributions {
104e7776783SApple OSS Distributions 	while (spinning) {
105e7776783SApple OSS Distributions 	}
106e7776783SApple OSS Distributions 	return NULL;
107e7776783SApple OSS Distributions }
108e7776783SApple OSS Distributions 
109e7776783SApple OSS Distributions static bool
query_pmi_params(unsigned int * pmi_counter,uint64_t * pmi_period)110e7776783SApple OSS Distributions query_pmi_params(unsigned int *pmi_counter, uint64_t *pmi_period)
111e7776783SApple OSS Distributions {
112e7776783SApple OSS Distributions 	bool pmi_support = true;
113e7776783SApple OSS Distributions 	size_t sysctl_size = sizeof(pmi_counter);
114e7776783SApple OSS Distributions 	int ret = sysctlbyname(
115e7776783SApple OSS Distributions 			"kern.microstackshot.pmi_sample_counter",
116e7776783SApple OSS Distributions 			pmi_counter, &sysctl_size, NULL, 0);
117e7776783SApple OSS Distributions 	if (ret == -1 && errno == ENOENT) {
118e7776783SApple OSS Distributions 		pmi_support = false;
119e7776783SApple OSS Distributions 		T_LOG("no PMI support");
120e7776783SApple OSS Distributions 	} else {
121e7776783SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "query PMI counter");
122e7776783SApple OSS Distributions 	}
123e7776783SApple OSS Distributions 	if (pmi_support) {
124e7776783SApple OSS Distributions 		sysctl_size = sizeof(*pmi_period);
125e7776783SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname(
126e7776783SApple OSS Distributions 				"kern.microstackshot.pmi_sample_period",
127e7776783SApple OSS Distributions 				pmi_period, &sysctl_size, NULL, 0),
128e7776783SApple OSS Distributions 				"query PMI period");
129e7776783SApple OSS Distributions 	}
130e7776783SApple OSS Distributions 	return pmi_support;
131e7776783SApple OSS Distributions }
132e7776783SApple OSS Distributions 
133e7776783SApple OSS Distributions #define MT_MICROSTACKSHOT KDBG_EVENTID(DBG_MONOTONIC, 2, 1)
134e7776783SApple OSS Distributions #define MS_RECORD MACHDBG_CODE(DBG_MACH_STACKSHOT, \
135e7776783SApple OSS Distributions 	        MICROSTACKSHOT_RECORD)
1365c2921b0SApple OSS Distributions #if defined(__arm64__)
137e7776783SApple OSS Distributions #define INSTRS_PERIOD (100ULL * 1000 * 1000)
1385c2921b0SApple OSS Distributions #else /* defined(__arm64__) */
139e7776783SApple OSS Distributions #define INSTRS_PERIOD (1ULL * 1000 * 1000 * 1000)
1405c2921b0SApple OSS Distributions #endif /* defined(__arm64__) */
141e7776783SApple OSS Distributions #define SLEEP_SECS 10
142e7776783SApple OSS Distributions 
143e7776783SApple OSS Distributions T_DECL(pmi_sampling, "attempt to configure microstackshots on PMI",
144*8d741a5dSApple OSS Distributions 		T_META_REQUIRES_SYSCTL_EQ("kern.monotonic.supported", 1), T_META_TAG_VM_NOT_ELIGIBLE)
145e7776783SApple OSS Distributions {
146e7776783SApple OSS Distributions 	start_controlling_ktrace();
147e7776783SApple OSS Distributions 
148e7776783SApple OSS Distributions 	T_SETUPBEGIN;
149e7776783SApple OSS Distributions 	ktrace_session_t s = ktrace_session_create();
150e7776783SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(s, "session create");
151e7776783SApple OSS Distributions 
152e7776783SApple OSS Distributions 	__block int pmi_events = 0;
153e7776783SApple OSS Distributions 	__block int microstackshot_record_events = 0;
154e7776783SApple OSS Distributions 	__block int pmi_records = 0;
155e7776783SApple OSS Distributions 	__block int io_records = 0;
156e7776783SApple OSS Distributions 	__block int interrupt_records = 0;
157e7776783SApple OSS Distributions 	__block int timer_arm_records = 0;
158e7776783SApple OSS Distributions 	__block int unknown_records = 0;
159e7776783SApple OSS Distributions 	__block int empty_records = 0;
160e7776783SApple OSS Distributions 
161e7776783SApple OSS Distributions 	ktrace_events_single(s, MT_MICROSTACKSHOT, ^(__unused struct trace_point *tp) {
162e7776783SApple OSS Distributions 		pmi_events++;
163e7776783SApple OSS Distributions 	});
164e7776783SApple OSS Distributions 	ktrace_events_single_paired(s, MS_RECORD,
165e7776783SApple OSS Distributions 	    ^(struct trace_point *start, __unused struct trace_point *end) {
166e7776783SApple OSS Distributions 		if (start->arg1 & kPMIRecord) {
167e7776783SApple OSS Distributions 		        pmi_records++;
168e7776783SApple OSS Distributions 		}
169e7776783SApple OSS Distributions 		if (start->arg1 & kIORecord) {
170e7776783SApple OSS Distributions 		        io_records++;
171e7776783SApple OSS Distributions 		}
172e7776783SApple OSS Distributions 		if (start->arg1 & kInterruptRecord) {
173e7776783SApple OSS Distributions 		        interrupt_records++;
174e7776783SApple OSS Distributions 		}
175e7776783SApple OSS Distributions 		if (start->arg1 & kTimerArmingRecord) {
176e7776783SApple OSS Distributions 		        timer_arm_records++;
177e7776783SApple OSS Distributions 		}
178e7776783SApple OSS Distributions 
179e7776783SApple OSS Distributions 		if (start->arg2 == end->arg2) {
180e7776783SApple OSS Distributions 			/*
181e7776783SApple OSS Distributions 			 * The buffer didn't grow for this record -- there was
182e7776783SApple OSS Distributions 			 * an error.
183e7776783SApple OSS Distributions 			 */
184e7776783SApple OSS Distributions 			empty_records++;
185e7776783SApple OSS Distributions 		}
186e7776783SApple OSS Distributions 
187e7776783SApple OSS Distributions 		const uint8_t any_record = kPMIRecord | kIORecord | kInterruptRecord |
188e7776783SApple OSS Distributions 		kTimerArmingRecord;
189e7776783SApple OSS Distributions 		if ((start->arg1 & any_record) == 0) {
190e7776783SApple OSS Distributions 		        unknown_records++;
191e7776783SApple OSS Distributions 		}
192e7776783SApple OSS Distributions 
193e7776783SApple OSS Distributions 		microstackshot_record_events++;
194e7776783SApple OSS Distributions 	});
195e7776783SApple OSS Distributions 
196e7776783SApple OSS Distributions 	ktrace_set_completion_handler(s, ^{
197e7776783SApple OSS Distributions 		ktrace_session_destroy(s);
198e7776783SApple OSS Distributions 		T_EXPECT_GT(pmi_events, 0, "saw non-zero PMIs (%g/sec)",
199e7776783SApple OSS Distributions 				pmi_events / (double)SLEEP_SECS);
200e7776783SApple OSS Distributions 		T_EXPECT_GT(pmi_records, 0, "saw non-zero PMI record events (%g/sec)",
201e7776783SApple OSS Distributions 				pmi_records / (double)SLEEP_SECS);
202e7776783SApple OSS Distributions 		T_LOG("saw %d unknown record events", unknown_records);
203e7776783SApple OSS Distributions 		T_EXPECT_GT(microstackshot_record_events, 0,
204e7776783SApple OSS Distributions 				"saw non-zero microstackshot record events (%d -- %g/sec)",
205e7776783SApple OSS Distributions 				microstackshot_record_events,
206e7776783SApple OSS Distributions 				microstackshot_record_events / (double)SLEEP_SECS);
207e7776783SApple OSS Distributions 		T_EXPECT_NE(empty_records, microstackshot_record_events,
208e7776783SApple OSS Distributions 				"saw non-empty records (%d empty)", empty_records);
209e7776783SApple OSS Distributions 
210e7776783SApple OSS Distributions 		if (interrupt_records > 0) {
211e7776783SApple OSS Distributions 			T_LOG("saw %g interrupt records per second",
212e7776783SApple OSS Distributions 					interrupt_records / (double)SLEEP_SECS);
213e7776783SApple OSS Distributions 		} else {
214e7776783SApple OSS Distributions 			T_LOG("saw no interrupt records");
215e7776783SApple OSS Distributions 		}
216e7776783SApple OSS Distributions 		if (io_records > 0) {
217e7776783SApple OSS Distributions 			T_LOG("saw %g I/O records per second",
218e7776783SApple OSS Distributions 					io_records / (double)SLEEP_SECS);
219e7776783SApple OSS Distributions 		} else {
220e7776783SApple OSS Distributions 			T_LOG("saw no I/O records");
221e7776783SApple OSS Distributions 		}
222e7776783SApple OSS Distributions 		if (timer_arm_records > 0) {
223e7776783SApple OSS Distributions 			T_LOG("saw %g timer arming records per second",
224e7776783SApple OSS Distributions 					timer_arm_records / (double)SLEEP_SECS);
225e7776783SApple OSS Distributions 		} else {
226e7776783SApple OSS Distributions 			T_LOG("saw no timer arming records");
227e7776783SApple OSS Distributions 		}
228e7776783SApple OSS Distributions 
229e7776783SApple OSS Distributions 		T_END;
230e7776783SApple OSS Distributions 	});
231e7776783SApple OSS Distributions 
232e7776783SApple OSS Distributions 	T_SETUPEND;
233e7776783SApple OSS Distributions 
234e7776783SApple OSS Distributions 	telemetry_init();
235e7776783SApple OSS Distributions 
236e7776783SApple OSS Distributions 	/*
237e7776783SApple OSS Distributions 	 * Start sampling via telemetry on the instructions PMI.
238e7776783SApple OSS Distributions 	 */
239e7776783SApple OSS Distributions 	int ret = __telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_INSTRS,
240e7776783SApple OSS Distributions 			INSTRS_PERIOD, 0, 0, 0);
241e7776783SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret,
242e7776783SApple OSS Distributions 			"telemetry syscall succeeded, started microstackshots");
243e7776783SApple OSS Distributions 
244e7776783SApple OSS Distributions 	unsigned int pmi_counter = 0;
245e7776783SApple OSS Distributions 	uint64_t pmi_period = 0;
246e7776783SApple OSS Distributions 	bool pmi_support = query_pmi_params(&pmi_counter, &pmi_period);
247e7776783SApple OSS Distributions 	T_QUIET; T_ASSERT_TRUE(pmi_support, "PMI should be supported");
248e7776783SApple OSS Distributions 
249e7776783SApple OSS Distributions 	T_LOG("PMI counter: %u", pmi_counter);
250e7776783SApple OSS Distributions 	T_LOG("PMI period: %llu", pmi_period);
251e7776783SApple OSS Distributions #if defined(__arm64__)
252e7776783SApple OSS Distributions 	const unsigned int instrs_counter = 1;
253e7776783SApple OSS Distributions #else
254e7776783SApple OSS Distributions 	const unsigned int instrs_counter = 0;
255e7776783SApple OSS Distributions #endif // defined(__arm64__)
256e7776783SApple OSS Distributions 	T_QUIET; T_ASSERT_EQ(pmi_counter, instrs_counter,
257e7776783SApple OSS Distributions 			"PMI on instructions retired");
258e7776783SApple OSS Distributions 	T_QUIET; T_ASSERT_EQ(pmi_period, INSTRS_PERIOD, "PMI period is set");
259e7776783SApple OSS Distributions 
260e7776783SApple OSS Distributions 	pthread_t thread;
261e7776783SApple OSS Distributions 	int error = pthread_create(&thread, NULL, thread_spin, NULL);
262e7776783SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(error, "started thread to spin");
263e7776783SApple OSS Distributions 
264e7776783SApple OSS Distributions 	error = ktrace_start(s, dispatch_get_main_queue());
265e7776783SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(error, "started tracing");
266e7776783SApple OSS Distributions 
267e7776783SApple OSS Distributions 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, SLEEP_SECS * NSEC_PER_SEC),
268e7776783SApple OSS Distributions 			dispatch_get_main_queue(), ^{
269e7776783SApple OSS Distributions 		spinning = false;
270e7776783SApple OSS Distributions 		ktrace_end(s, 0);
271e7776783SApple OSS Distributions 		(void)pthread_join(thread, NULL);
272e7776783SApple OSS Distributions 		T_LOG("ending trace session after %d seconds", SLEEP_SECS);
273e7776783SApple OSS Distributions 	});
274e7776783SApple OSS Distributions 
275e7776783SApple OSS Distributions 	dispatch_main();
276e7776783SApple OSS Distributions }
277e7776783SApple OSS Distributions 
278e7776783SApple OSS Distributions T_DECL(error_handling,
279e7776783SApple OSS Distributions 		"ensure that error conditions for the telemetry syscall are observed",
280*8d741a5dSApple OSS Distributions 		T_META_REQUIRES_SYSCTL_EQ("kern.monotonic.supported", 1), T_META_TAG_VM_NOT_ELIGIBLE)
281e7776783SApple OSS Distributions {
282e7776783SApple OSS Distributions 	telemetry_init();
283e7776783SApple OSS Distributions 
284e7776783SApple OSS Distributions 	int ret = __telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_INSTRS,
285e7776783SApple OSS Distributions 	    1, 0, 0, 0);
286e7776783SApple OSS Distributions 	T_EXPECT_EQ(ret, -1, "telemetry shouldn't allow PMI every instruction");
287e7776783SApple OSS Distributions 
288e7776783SApple OSS Distributions 	ret = __telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_INSTRS,
289e7776783SApple OSS Distributions 	    1000 * 1000, 0, 0, 0);
290e7776783SApple OSS Distributions 	T_EXPECT_EQ(ret, -1,
291e7776783SApple OSS Distributions 	    "telemetry shouldn't allow PMI every million instructions");
292e7776783SApple OSS Distributions 
293e7776783SApple OSS Distributions 	ret = __telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_CYCLES,
294e7776783SApple OSS Distributions 	    1, 0, 0, 0);
295e7776783SApple OSS Distributions 	T_EXPECT_EQ(ret, -1, "telemetry shouldn't allow PMI every cycle");
296e7776783SApple OSS Distributions 
297e7776783SApple OSS Distributions 	ret = __telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_CYCLES,
298e7776783SApple OSS Distributions 	    1000 * 1000, 0, 0, 0);
299e7776783SApple OSS Distributions 	T_EXPECT_EQ(ret, -1,
300e7776783SApple OSS Distributions 	    "telemetry shouldn't allow PMI every million cycles");
301e7776783SApple OSS Distributions 
302e7776783SApple OSS Distributions 	ret = __telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_CYCLES,
303e7776783SApple OSS Distributions 	    UINT64_MAX, 0, 0, 0);
304e7776783SApple OSS Distributions 	T_EXPECT_EQ(ret, -1, "telemetry shouldn't allow PMI every UINT64_MAX cycles");
305e7776783SApple OSS Distributions }
306e7776783SApple OSS Distributions 
307e7776783SApple OSS Distributions #define START_EVENT (0xfeedfad0)
308e7776783SApple OSS Distributions #define STOP_EVENT (0xfeedfac0)
309e7776783SApple OSS Distributions 
310e7776783SApple OSS Distributions T_DECL(excessive_sampling,
311e7776783SApple OSS Distributions 		"ensure that microstackshots are not being sampled too frequently",
312*8d741a5dSApple OSS Distributions 		T_META_REQUIRES_SYSCTL_EQ("kern.monotonic.supported", 1), T_META_TAG_VM_NOT_ELIGIBLE)
313e7776783SApple OSS Distributions {
314e7776783SApple OSS Distributions 	unsigned int interrupt_sample_rate = 0;
315e7776783SApple OSS Distributions 	size_t sysctl_size = sizeof(interrupt_sample_rate);
316e7776783SApple OSS Distributions 	T_QUIET;
317e7776783SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(sysctlbyname(
318e7776783SApple OSS Distributions 			"kern.microstackshot.interrupt_sample_rate",
319e7776783SApple OSS Distributions 			&interrupt_sample_rate, &sysctl_size, NULL, 0),
320e7776783SApple OSS Distributions 			"query interrupt sample rate");
321e7776783SApple OSS Distributions 	unsigned int pmi_counter = 0;
322e7776783SApple OSS Distributions 	uint64_t pmi_period = 0;
323e7776783SApple OSS Distributions 	(void)query_pmi_params(&pmi_counter, &pmi_period);
324e7776783SApple OSS Distributions 
325e7776783SApple OSS Distributions 	T_LOG("interrupt sample rate: %uHz", interrupt_sample_rate);
326e7776783SApple OSS Distributions 	T_LOG("PMI counter: %u", pmi_counter);
327e7776783SApple OSS Distributions 	T_LOG("PMI period: %llu", pmi_period);
328e7776783SApple OSS Distributions 
329e7776783SApple OSS Distributions 	start_controlling_ktrace();
330e7776783SApple OSS Distributions 
331e7776783SApple OSS Distributions 	T_SETUPBEGIN;
332e7776783SApple OSS Distributions 	ktrace_session_t s = ktrace_session_create();
333e7776783SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(s, "session create");
334e7776783SApple OSS Distributions 
335e7776783SApple OSS Distributions 	__block int microstackshot_record_events = 0;
336e7776783SApple OSS Distributions 	__block int pmi_records = 0;
337e7776783SApple OSS Distributions 	__block int io_records = 0;
338e7776783SApple OSS Distributions 	__block int interrupt_records = 0;
339e7776783SApple OSS Distributions 	__block int timer_arm_records = 0;
340e7776783SApple OSS Distributions 	__block int unknown_records = 0;
341e7776783SApple OSS Distributions 	__block int empty_records = 0;
342e7776783SApple OSS Distributions 	__block uint64_t first_timestamp_ns = 0;
343e7776783SApple OSS Distributions 	__block uint64_t last_timestamp_ns = 0;
344e7776783SApple OSS Distributions 
345e7776783SApple OSS Distributions 	ktrace_events_single_paired(s, MS_RECORD,
346e7776783SApple OSS Distributions 			^(struct trace_point *start, __unused struct trace_point *end) {
347e7776783SApple OSS Distributions 		if (start->arg1 & kPMIRecord) {
348e7776783SApple OSS Distributions 			pmi_records++;
349e7776783SApple OSS Distributions 		}
350e7776783SApple OSS Distributions 		if (start->arg1 & kIORecord) {
351e7776783SApple OSS Distributions 			io_records++;
352e7776783SApple OSS Distributions 		}
353e7776783SApple OSS Distributions 		if (start->arg1 & kInterruptRecord) {
354e7776783SApple OSS Distributions 			interrupt_records++;
355e7776783SApple OSS Distributions 		}
356e7776783SApple OSS Distributions 		if (start->arg1 & kTimerArmingRecord) {
357e7776783SApple OSS Distributions 			timer_arm_records++;
358e7776783SApple OSS Distributions 		}
359e7776783SApple OSS Distributions 
360e7776783SApple OSS Distributions 		if (start->arg2 == end->arg2) {
361e7776783SApple OSS Distributions 			/*
362e7776783SApple OSS Distributions 			 * The buffer didn't grow for this record -- there was
363e7776783SApple OSS Distributions 			 * an error.
364e7776783SApple OSS Distributions 			 */
365e7776783SApple OSS Distributions 			empty_records++;
366e7776783SApple OSS Distributions 		}
367e7776783SApple OSS Distributions 
368e7776783SApple OSS Distributions 		const uint8_t any_record = kPMIRecord | kIORecord | kInterruptRecord |
369e7776783SApple OSS Distributions 				kTimerArmingRecord;
370e7776783SApple OSS Distributions 		if ((start->arg1 & any_record) == 0) {
371e7776783SApple OSS Distributions 			unknown_records++;
372e7776783SApple OSS Distributions 		}
373e7776783SApple OSS Distributions 
374e7776783SApple OSS Distributions 		microstackshot_record_events++;
375e7776783SApple OSS Distributions 	});
376e7776783SApple OSS Distributions 
377e7776783SApple OSS Distributions 	ktrace_events_single(s, START_EVENT, ^(struct trace_point *tp) {
378e7776783SApple OSS Distributions 		int error = ktrace_convert_timestamp_to_nanoseconds(s,
379e7776783SApple OSS Distributions 				tp->timestamp, &first_timestamp_ns);
380e7776783SApple OSS Distributions 		T_QUIET;
381e7776783SApple OSS Distributions 		T_ASSERT_POSIX_ZERO(error, "converted timestamp to nanoseconds");
382e7776783SApple OSS Distributions 	});
383e7776783SApple OSS Distributions 
384e7776783SApple OSS Distributions 	ktrace_events_single(s, STOP_EVENT, ^(struct trace_point *tp) {
385e7776783SApple OSS Distributions 		int error = ktrace_convert_timestamp_to_nanoseconds(s,
386e7776783SApple OSS Distributions 				tp->timestamp, &last_timestamp_ns);
387e7776783SApple OSS Distributions 		T_QUIET;
388e7776783SApple OSS Distributions 		T_ASSERT_POSIX_ZERO(error, "converted timestamp to nanoseconds");
389e7776783SApple OSS Distributions 		ktrace_end(s, 1);
390e7776783SApple OSS Distributions 	});
391e7776783SApple OSS Distributions 
392e7776783SApple OSS Distributions 	ktrace_set_completion_handler(s, ^{
393e7776783SApple OSS Distributions 		ktrace_session_destroy(s);
394e7776783SApple OSS Distributions 
395e7776783SApple OSS Distributions 		uint64_t duration_ns = last_timestamp_ns - first_timestamp_ns;
396e7776783SApple OSS Distributions 		double duration_secs = (double)duration_ns / 1e9;
397e7776783SApple OSS Distributions 
398e7776783SApple OSS Distributions 		T_LOG("test lasted %g seconds", duration_secs);
399e7776783SApple OSS Distributions 
400e7776783SApple OSS Distributions 		T_MAYFAIL;
401e7776783SApple OSS Distributions 		T_EXPECT_EQ(unknown_records, 0, "saw zero unknown record events");
402e7776783SApple OSS Distributions 		T_MAYFAIL;
403e7776783SApple OSS Distributions 		T_EXPECT_GT(microstackshot_record_events, 0,
404e7776783SApple OSS Distributions 				"saw non-zero microstackshot record events (%d, %gHz)",
405e7776783SApple OSS Distributions 				microstackshot_record_events,
406e7776783SApple OSS Distributions 				microstackshot_record_events / duration_secs);
407e7776783SApple OSS Distributions 		T_EXPECT_NE(empty_records, microstackshot_record_events,
408e7776783SApple OSS Distributions 				"saw non-empty records (%d empty)", empty_records);
409e7776783SApple OSS Distributions 
410e7776783SApple OSS Distributions 		double record_rate_hz = microstackshot_record_events / duration_secs;
411e7776783SApple OSS Distributions 		T_LOG("record rate: %gHz", record_rate_hz);
412e7776783SApple OSS Distributions 		T_LOG("PMI record rate: %gHz", pmi_records / duration_secs);
413e7776783SApple OSS Distributions 		T_LOG("interrupt record rate: %gHz",
414e7776783SApple OSS Distributions 				interrupt_records / duration_secs);
415e7776783SApple OSS Distributions 		T_LOG("I/O record rate: %gHz", io_records / duration_secs);
416e7776783SApple OSS Distributions 		T_LOG("timer arm record rate: %gHz",
417e7776783SApple OSS Distributions 				timer_arm_records / duration_secs);
418e7776783SApple OSS Distributions 
419e7776783SApple OSS Distributions 		T_EXPECT_LE(record_rate_hz, (double)(dt_ncpu() * 50),
420e7776783SApple OSS Distributions 				"found appropriate rate of microstackshots");
421e7776783SApple OSS Distributions 
422e7776783SApple OSS Distributions 		T_END;
423e7776783SApple OSS Distributions 	});
424e7776783SApple OSS Distributions 
425e7776783SApple OSS Distributions 	pthread_t thread;
426e7776783SApple OSS Distributions 	int error = pthread_create(&thread, NULL, thread_spin, NULL);
427e7776783SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(error, "started thread to spin");
428e7776783SApple OSS Distributions 
429e7776783SApple OSS Distributions 	T_SETUPEND;
430e7776783SApple OSS Distributions 
431e7776783SApple OSS Distributions 	error = ktrace_start(s, dispatch_get_main_queue());
432e7776783SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(error, "started tracing");
433e7776783SApple OSS Distributions 	kdebug_trace(START_EVENT, 0, 0, 0, 0);
434e7776783SApple OSS Distributions 
435e7776783SApple OSS Distributions 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, SLEEP_SECS * NSEC_PER_SEC),
436e7776783SApple OSS Distributions 			dispatch_get_main_queue(), ^{
437e7776783SApple OSS Distributions 		spinning = false;
438e7776783SApple OSS Distributions 		kdebug_trace(STOP_EVENT, 0, 0, 0, 0);
439e7776783SApple OSS Distributions 		(void)pthread_join(thread, NULL);
440e7776783SApple OSS Distributions 		T_LOG("ending trace session after %d seconds", SLEEP_SECS);
441e7776783SApple OSS Distributions 	});
442e7776783SApple OSS Distributions 
443e7776783SApple OSS Distributions 	dispatch_main();
444e7776783SApple OSS Distributions }
445