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