]> git.saurik.com Git - apple/xnu.git/blame - tests/telemetry.c
xnu-4903.221.2.tar.gz
[apple/xnu.git] / tests / telemetry.c
CommitLineData
d9a64523
A
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>
7
8enum telemetry_pmi {
9 TELEMETRY_PMI_NONE,
10 TELEMETRY_PMI_INSTRS,
11 TELEMETRY_PMI_CYCLES,
12};
13#define TELEMETRY_CMD_PMI_SETUP 3
14
15T_GLOBAL_META(T_META_NAMESPACE("xnu.debugging.telemetry"),
16 T_META_CHECK_LEAKS(false),
17 T_META_ASROOT(true));
18
19extern int __telemetry(uint64_t cmd, uint64_t deadline, uint64_t interval,
20 uint64_t leeway, uint64_t arg4, uint64_t arg5);
21
22static void
23telemetry_cleanup(void)
24{
25 int ret = __telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_NONE, 0, 0, 0, 0);
26 T_EXPECT_POSIX_SUCCESS(ret, "telemetry(... NONE ...)");
27}
28
29volatile static bool spinning = true;
30static void *
31thread_spin(__unused void *arg)
32{
33 while (spinning) {
34 }
35 return NULL;
36}
37
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__) */
46#define SLEEP_SECS 10
47
48T_DECL(microstackshot_pmi, "attempt to configure microstackshots on PMI")
49{
50#if TARGET_OS_WATCH
51 T_SKIP("unsupported platform");
52#endif /* TARGET_OS_WATCH */
53
54 T_SETUPBEGIN;
55 ktrace_session_t s = ktrace_session_create();
56 T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(s, "session create");
57
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;
66
67 ktrace_events_single(s, MT_MICROSTACKSHOT, ^(__unused struct trace_point *tp) {
68 pmi_events++;
69 });
70 ktrace_events_single_paired(s, MS_RECORD,
71 ^(struct trace_point *start, __unused struct trace_point *end) {
72 if (start->arg1 & kPMIRecord) {
73 pmi_records++;
74 }
75 if (start->arg1 & kIORecord) {
76 io_records++;
77 }
78 if (start->arg1 & kInterruptRecord) {
79 interrupt_records++;
80 }
81 if (start->arg1 & kTimerArmingRecord) {
82 timer_arm_records++;
83 }
84
85 const uint8_t any_record = kPMIRecord | kIORecord | kInterruptRecord |
86 kTimerArmingRecord;
87 if ((start->arg1 & any_record) == 0) {
88 unknown_records++;
89 }
90 if (__builtin_popcount(start->arg1 & any_record) != 1) {
91 multi_records++;
92 }
93
94 microstackshot_record_events++;
95 });
96
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);
108
109 if (interrupt_records > 0) {
110 T_LOG("saw %g interrupt records per second",
111 interrupt_records / (double)SLEEP_SECS);
112 } else {
113 T_LOG("saw no interrupt records");
114 }
115 if (io_records > 0) {
116 T_LOG("saw %g I/O records per second",
117 io_records / (double)SLEEP_SECS);
118 } else {
119 T_LOG("saw no I/O records");
120 }
121 if (timer_arm_records > 0) {
122 T_LOG("saw %g timer arming records per second",
123 timer_arm_records / (double)SLEEP_SECS);
124 } else {
125 T_LOG("saw no timer arming records");
126 }
127
128 T_END;
129 });
130
131 T_SETUPEND;
132
133 /*
134 * Start sampling via telemetry on the instructions PMI.
135 */
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");
140 } else {
141 T_ASSERT_POSIX_SUCCESS(ret,
142 "telemetry syscall succeeded, started microstackshots");
143 T_LOG("installing cleanup handler");
144 T_ATEND(telemetry_cleanup);
145 }
146
147 pthread_t thread;
148 int error = pthread_create(&thread, NULL, thread_spin, NULL);
149 T_ASSERT_POSIX_ZERO(error, "started thread to spin");
150
151 error = ktrace_start(s, dispatch_get_main_queue());
152 T_ASSERT_POSIX_ZERO(error, "started tracing");
153
154 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, SLEEP_SECS * NSEC_PER_SEC),
155 dispatch_get_main_queue(), ^{
156 spinning = false;
157 ktrace_end(s, 0);
158 (void)pthread_join(thread, NULL);
159 T_LOG("ending trace session after %d seconds", SLEEP_SECS);
160 });
161
162 dispatch_main();
163}
164
165T_DECL(error_handling,
166 "ensure that error conditions for the telemetry syscall are observed")
167{
168 int ret = __telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_INSTRS,
169 1, 0, 0, 0);
170 T_EXPECT_EQ(ret, -1, "telemetry shouldn't allow PMI every instruction");
171
172 ret = __telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_INSTRS,
173 1000 * 1000, 0, 0, 0);
174 T_EXPECT_EQ(ret, -1,
175 "telemetry shouldn't allow PMI every million instructions");
176
177 ret = __telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_CYCLES,
178 1, 0, 0, 0);
179 T_EXPECT_EQ(ret, -1, "telemetry shouldn't allow PMI every cycle");
180
181 ret = __telemetry(TELEMETRY_CMD_PMI_SETUP, TELEMETRY_PMI_CYCLES,
182 1000 * 1000, 0, 0, 0);
183 T_EXPECT_EQ(ret, -1,
184 "telemetry shouldn't allow PMI every million cycles");
185}