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