1 /* Copyright (c) 2018 Apple Inc. All rights reserved. */
3 #include <CoreFoundation/CoreFoundation.h>
4 #include <darwintest.h>
5 #include <dispatch/dispatch.h>
6 #include <ktrace/ktrace.h>
7 #include <kperf/kperf.h>
8 #include <kern/debug.h>
10 #include <sys/kdebug.h>
11 #include <sys/sysctl.h>
12 #include <TargetConditionals.h>
14 #include "ktrace_helpers.h"
21 #define TELEMETRY_CMD_PMI_SETUP 3
23 T_GLOBAL_META(T_META_NAMESPACE("xnu.debugging.telemetry"),
24 T_META_CHECK_LEAKS(false),
27 extern int __telemetry(uint64_t cmd
, uint64_t deadline
, uint64_t interval
,
28 uint64_t leeway
, uint64_t arg4
, uint64_t arg5
);
31 * Microstackshots based on PMI are only supported on devices with monotonic
36 skip_if_pmi_unsupported(void)
39 int ret
= sysctlbyname("kern.monotonic.supported", &supported
,
40 &(size_t){ sizeof(supported
), }, NULL
, 0);
42 T_SKIP("monotonic sysctl generated an error: %d (%s)", errno
,
46 T_SKIP("monotonic must be supported for microstackshots");
51 * Data Analytics (da) also has a microstackshot configuration -- set a PMI
52 * cycle interval of 0 to force it to disable microstackshot on PMI.
56 set_da_microstackshot_period(CFNumberRef num
)
58 CFPreferencesSetValue(CFSTR("microstackshotPMICycleInterval"), num
,
59 CFSTR("com.apple.da"),
62 #else // TARGET_OS_IPHONE
64 #endif // !TARGET_OS_IPHONE
65 kCFPreferencesCurrentHost
);
67 notify_post("com.apple.da.tasking_changed");
71 disable_da_microstackshots(void)
74 CFNumberRef num
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, &zero
);
75 set_da_microstackshot_period(num
);
76 T_LOG("notified da of tasking change, sleeping");
79 #else /* TARGET_OS_WATCH */
81 #endif /* !TARGET_OS_WATCH */
85 * Unset the preference to allow da to reset its configuration.
88 reenable_da_microstackshots(void)
90 set_da_microstackshot_period(NULL
);
94 * Clean up the test's configuration and allow da to activate again.
97 telemetry_cleanup(void)
99 (void)__telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_NONE
, 0, 0, 0, 0);
100 reenable_da_microstackshots();
104 * Make sure da hasn't configured the microstackshots -- otherwise the PMI
105 * setup command will return EBUSY.
110 disable_da_microstackshots();
111 T_LOG("installing cleanup handler");
112 T_ATEND(telemetry_cleanup
);
115 volatile static bool spinning
= true;
118 thread_spin(__unused
void *arg
)
125 #define MT_MICROSTACKSHOT KDBG_EVENTID(DBG_MONOTONIC, 2, 1)
126 #define MS_RECORD MACHDBG_CODE(DBG_MACH_STACKSHOT, \
127 MICROSTACKSHOT_RECORD)
128 #if defined(__arm64__) || defined(__arm__)
129 #define INSTRS_PERIOD (100ULL * 1000 * 1000)
130 #else /* defined(__arm64__) || defined(__arm__) */
131 #define INSTRS_PERIOD (1ULL * 1000 * 1000 * 1000)
132 #endif /* !defined(__arm64__) && !defined(__arm__) */
133 #define SLEEP_SECS 10
135 T_DECL(microstackshot_pmi
, "attempt to configure microstackshots on PMI")
137 skip_if_pmi_unsupported();
138 start_controlling_ktrace();
141 ktrace_session_t s
= ktrace_session_create();
142 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "session create");
144 __block
int pmi_events
= 0;
145 __block
int microstackshot_record_events
= 0;
146 __block
int pmi_records
= 0;
147 __block
int io_records
= 0;
148 __block
int interrupt_records
= 0;
149 __block
int timer_arm_records
= 0;
150 __block
int unknown_records
= 0;
151 __block
int empty_records
= 0;
153 ktrace_events_single(s
, MT_MICROSTACKSHOT
, ^(__unused
struct trace_point
*tp
) {
156 ktrace_events_single_paired(s
, MS_RECORD
,
157 ^(struct trace_point
*start
, __unused
struct trace_point
*end
) {
158 if (start
->arg1
& kPMIRecord
) {
161 if (start
->arg1
& kIORecord
) {
164 if (start
->arg1
& kInterruptRecord
) {
167 if (start
->arg1
& kTimerArmingRecord
) {
171 if (start
->arg2
== end
->arg2
) {
173 * The buffer didn't grow for this record -- there was
179 const uint8_t any_record
= kPMIRecord
| kIORecord
| kInterruptRecord
|
181 if ((start
->arg1
& any_record
) == 0) {
185 microstackshot_record_events
++;
188 ktrace_set_completion_handler(s
, ^{
189 ktrace_session_destroy(s
);
190 T_EXPECT_GT(pmi_events
, 0,
191 "saw non-zero PMIs (%g/sec)", pmi_events
/ (double)SLEEP_SECS
);
192 T_EXPECT_GT(pmi_records
, 0, "saw non-zero PMI record events (%g/sec)",
193 pmi_records
/ (double)SLEEP_SECS
);
194 T_EXPECT_EQ(unknown_records
, 0, "saw zero unknown record events");
195 T_EXPECT_GT(microstackshot_record_events
, 0,
196 "saw non-zero microstackshot record events (%d -- %g/sec)",
197 microstackshot_record_events
,
198 microstackshot_record_events
/ (double)SLEEP_SECS
);
199 T_EXPECT_NE(empty_records
, microstackshot_record_events
,
200 "saw non-empty records (%d empty)", empty_records
);
202 if (interrupt_records
> 0) {
203 T_LOG("saw %g interrupt records per second",
204 interrupt_records
/ (double)SLEEP_SECS
);
206 T_LOG("saw no interrupt records");
208 if (io_records
> 0) {
209 T_LOG("saw %g I/O records per second",
210 io_records
/ (double)SLEEP_SECS
);
212 T_LOG("saw no I/O records");
214 if (timer_arm_records
> 0) {
215 T_LOG("saw %g timer arming records per second",
216 timer_arm_records
/ (double)SLEEP_SECS
);
218 T_LOG("saw no timer arming records");
229 * Start sampling via telemetry on the instructions PMI.
231 int ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_INSTRS
,
232 INSTRS_PERIOD
, 0, 0, 0);
233 T_ASSERT_POSIX_SUCCESS(ret
,
234 "telemetry syscall succeeded, started microstackshots");
237 int error
= pthread_create(&thread
, NULL
, thread_spin
, NULL
);
238 T_ASSERT_POSIX_ZERO(error
, "started thread to spin");
240 error
= ktrace_start(s
, dispatch_get_main_queue());
241 T_ASSERT_POSIX_ZERO(error
, "started tracing");
243 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, SLEEP_SECS
* NSEC_PER_SEC
),
244 dispatch_get_main_queue(), ^{
247 (void)pthread_join(thread
, NULL
);
248 T_LOG("ending trace session after %d seconds", SLEEP_SECS
);
254 T_DECL(error_handling
,
255 "ensure that error conditions for the telemetry syscall are observed")
257 skip_if_pmi_unsupported();
261 int ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_INSTRS
,
263 T_EXPECT_EQ(ret
, -1, "telemetry shouldn't allow PMI every instruction");
265 ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_INSTRS
,
266 1000 * 1000, 0, 0, 0);
268 "telemetry shouldn't allow PMI every million instructions");
270 ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_CYCLES
,
272 T_EXPECT_EQ(ret
, -1, "telemetry shouldn't allow PMI every cycle");
274 ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_CYCLES
,
275 1000 * 1000, 0, 0, 0);
277 "telemetry shouldn't allow PMI every million cycles");
279 ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_CYCLES
,
280 UINT64_MAX
, 0, 0, 0);
281 T_EXPECT_EQ(ret
, -1, "telemetry shouldn't allow PMI every UINT64_MAX cycles");
283 ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_CYCLES
,
284 (1ULL << 55), 0, 0, 0);
285 T_EXPECT_EQ(ret
, -1, "telemetry shouldn't allow PMI with extremely long periods");