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