1 #include <darwintest.h>
2 #include <dispatch/dispatch.h>
3 #include <ktrace/ktrace.h>
4 #include <kern/debug.h>
5 #include <sys/kdebug.h>
6 #include <TargetConditionals.h>
13 #define TELEMETRY_CMD_PMI_SETUP 3
15 T_GLOBAL_META(T_META_NAMESPACE("xnu.debugging.telemetry"),
16 T_META_CHECK_LEAKS(false),
19 extern int __telemetry(uint64_t cmd
, uint64_t deadline
, uint64_t interval
,
20 uint64_t leeway
, uint64_t arg4
, uint64_t arg5
);
23 telemetry_cleanup(void)
25 int ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_NONE
, 0, 0, 0, 0);
26 T_EXPECT_POSIX_SUCCESS(ret
, "telemetry(... NONE ...)");
29 volatile static bool spinning
= true;
31 thread_spin(__unused
void *arg
)
38 #define MT_MICROSTACKSHOT KDBG_EVENTID(DBG_MONOTONIC, 2, 1)
39 #define MS_RECORD MACHDBG_CODE(DBG_MACH_STACKSHOT, \
40 MICROSTACKSHOT_RECORD)
41 #if defined(__arm64__) || defined(__arm__)
42 #define INSTRS_PERIOD (100ULL * 1000 * 1000)
43 #else /* defined(__arm64__) || defined(__arm__) */
44 #define INSTRS_PERIOD (1ULL * 1000 * 1000 * 1000)
45 #endif /* !defined(__arm64__) && !defined(__arm__) */
48 T_DECL(microstackshot_pmi
, "attempt to configure microstackshots on PMI")
51 T_SKIP("unsupported platform");
52 #endif /* TARGET_OS_WATCH */
55 ktrace_session_t s
= ktrace_session_create();
56 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "session create");
58 __block
int pmi_events
= 0;
59 __block
int microstackshot_record_events
= 0;
60 __block
int pmi_records
= 0;
61 __block
int io_records
= 0;
62 __block
int interrupt_records
= 0;
63 __block
int timer_arm_records
= 0;
64 __block
int unknown_records
= 0;
65 __block
int multi_records
= 0;
67 ktrace_events_single(s
, MT_MICROSTACKSHOT
, ^(__unused
struct trace_point
*tp
) {
70 ktrace_events_single_paired(s
, MS_RECORD
,
71 ^(struct trace_point
*start
, __unused
struct trace_point
*end
) {
72 if (start
->arg1
& kPMIRecord
) {
75 if (start
->arg1
& kIORecord
) {
78 if (start
->arg1
& kInterruptRecord
) {
81 if (start
->arg1
& kTimerArmingRecord
) {
85 const uint8_t any_record
= kPMIRecord
| kIORecord
| kInterruptRecord
|
87 if ((start
->arg1
& any_record
) == 0) {
90 if (__builtin_popcount(start
->arg1
& any_record
) != 1) {
94 microstackshot_record_events
++;
97 ktrace_set_completion_handler(s
, ^{
98 ktrace_session_destroy(s
);
99 T_EXPECT_GT(pmi_events
, 0,
100 "saw non-zero PMIs (%g/sec)", pmi_events
/ (double)SLEEP_SECS
);
101 T_EXPECT_GT(pmi_records
, 0, "saw non-zero PMI record events (%g/sec)",
102 pmi_records
/ (double)SLEEP_SECS
);
103 T_EXPECT_EQ(unknown_records
, 0, "saw zero unknown record events");
104 T_EXPECT_EQ(multi_records
, 0, "saw zero multiple record events");
105 T_EXPECT_GT(microstackshot_record_events
, 0,
106 "saw non-zero microstackshot record events (%g/sec)",
107 microstackshot_record_events
/ (double)SLEEP_SECS
);
109 if (interrupt_records
> 0) {
110 T_LOG("saw %g interrupt records per second",
111 interrupt_records
/ (double)SLEEP_SECS
);
113 T_LOG("saw no interrupt records");
115 if (io_records
> 0) {
116 T_LOG("saw %g I/O records per second",
117 io_records
/ (double)SLEEP_SECS
);
119 T_LOG("saw no I/O records");
121 if (timer_arm_records
> 0) {
122 T_LOG("saw %g timer arming records per second",
123 timer_arm_records
/ (double)SLEEP_SECS
);
125 T_LOG("saw no timer arming records");
134 * Start sampling via telemetry on the instructions PMI.
136 int ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_INSTRS
,
137 INSTRS_PERIOD
, 0, 0, 0);
138 if (ret
< 0 && errno
== EBUSY
) {
139 T_PASS("telemetry is busy/active, maybe the events will be seen");
141 T_ASSERT_POSIX_SUCCESS(ret
,
142 "telemetry syscall succeeded, started microstackshots");
143 T_LOG("installing cleanup handler");
144 T_ATEND(telemetry_cleanup
);
148 int error
= pthread_create(&thread
, NULL
, thread_spin
, NULL
);
149 T_ASSERT_POSIX_ZERO(error
, "started thread to spin");
151 error
= ktrace_start(s
, dispatch_get_main_queue());
152 T_ASSERT_POSIX_ZERO(error
, "started tracing");
154 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, SLEEP_SECS
* NSEC_PER_SEC
),
155 dispatch_get_main_queue(), ^{
158 (void)pthread_join(thread
, NULL
);
159 T_LOG("ending trace session after %d seconds", SLEEP_SECS
);
165 T_DECL(error_handling
,
166 "ensure that error conditions for the telemetry syscall are observed")
168 int ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_INSTRS
,
170 T_EXPECT_EQ(ret
, -1, "telemetry shouldn't allow PMI every instruction");
172 ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_INSTRS
,
173 1000 * 1000, 0, 0, 0);
175 "telemetry shouldn't allow PMI every million instructions");
177 ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_CYCLES
,
179 T_EXPECT_EQ(ret
, -1, "telemetry shouldn't allow PMI every cycle");
181 ret
= __telemetry(TELEMETRY_CMD_PMI_SETUP
, TELEMETRY_PMI_CYCLES
,
182 1000 * 1000, 0, 0, 0);
184 "telemetry shouldn't allow PMI every million cycles");