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