]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/darwintests/kperf.c
a37ed39e2db100520cee2b32dfda32336e0e8225
[apple/xnu.git] / tools / tests / darwintests / kperf.c
1 #include <darwintest.h>
2 #include <dispatch/dispatch.h>
3 #include <inttypes.h>
4 #include <ktrace.h>
5 #include <ktrace_private.h>
6 #include <kperf/kperf.h>
7 #include <kperfdata/kpdecode.h>
8 #include <os/assumes.h>
9 #include <stdint.h>
10 #include <sys/sysctl.h>
11
12 #include "kperf_helpers.h"
13
14 #define PERF_STK_KHDR UINT32_C(0x25020014)
15 #define PERF_STK_UHDR UINT32_C(0x25020018)
16
17 /* KDEBUG TRIGGER */
18
19 #define KDEBUG_TRIGGER_TIMEOUT_NS (10 * NSEC_PER_SEC)
20
21 #define NON_TRIGGER_CLASS UINT8_C(0xfd)
22 #define NON_TRIGGER_SUBCLASS UINT8_C(0xff)
23 #define NON_TRIGGER_CODE UINT8_C(0xff)
24
25 #define NON_TRIGGER_EVENT \
26 (KDBG_EVENTID(NON_TRIGGER_CLASS, NON_TRIGGER_SUBCLASS, NON_TRIGGER_CODE))
27
28 static void
29 expect_kdebug_trigger(const char *filter_desc, const uint32_t *debugids,
30 unsigned int n_debugids)
31 {
32 __block int missing_kernel_stacks = 0;
33 __block int missing_user_stacks = 0;
34 ktrace_session_t s;
35 kperf_kdebug_filter_t filter;
36
37 s = ktrace_session_create();
38 T_QUIET; T_ASSERT_NOTNULL(s, NULL);
39
40 ktrace_events_single(s, PERF_STK_KHDR, ^(struct trace_point *tp) {
41 missing_kernel_stacks--;
42 T_LOG("saw kernel stack with %lu frames, flags = %#lx", tp->arg2,
43 tp->arg1);
44 });
45 ktrace_events_single(s, PERF_STK_UHDR, ^(struct trace_point *tp) {
46 missing_user_stacks--;
47 T_LOG("saw user stack with %lu frames, flags = %#lx", tp->arg2,
48 tp->arg1);
49 });
50
51 for (unsigned int i = 0; i < n_debugids; i++) {
52 ktrace_events_single(s, debugids[i], ^(struct trace_point *tp) {
53 missing_kernel_stacks++;
54 missing_user_stacks++;
55 T_LOG("saw event with debugid 0x%" PRIx32, tp->debugid);
56 });
57 }
58
59 ktrace_events_single(s, NON_TRIGGER_EVENT,
60 ^(__unused struct trace_point *tp)
61 {
62 ktrace_end(s, 0);
63 });
64
65 ktrace_set_completion_handler(s, ^{
66 T_EXPECT_LE(missing_kernel_stacks, 0, NULL);
67 T_EXPECT_LE(missing_user_stacks, 0, NULL);
68
69 ktrace_session_destroy(s);
70 T_END;
71 });
72
73 /* configure kperf */
74
75 kperf_reset();
76
77 (void)kperf_action_count_set(1);
78 T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1,
79 KPERF_SAMPLER_KSTACK | KPERF_SAMPLER_USTACK), NULL);
80
81 filter = kperf_kdebug_filter_create();
82 T_ASSERT_NOTNULL(filter, NULL);
83
84 T_ASSERT_POSIX_SUCCESS(kperf_kdebug_action_set(1), NULL);
85 T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_add_desc(filter, filter_desc),
86 NULL);
87 T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_set(filter), NULL);
88 kperf_kdebug_filter_destroy(filter);
89
90 T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL);
91
92 T_ASSERT_POSIX_ZERO(ktrace_start(s, dispatch_get_main_queue()), NULL);
93
94 /* trace the triggering debugids */
95
96 for (unsigned int i = 0; i < n_debugids; i++) {
97 T_ASSERT_POSIX_SUCCESS(kdebug_trace(debugids[i], 0, 0, 0, 0), NULL);
98 }
99
100 T_ASSERT_POSIX_SUCCESS(kdebug_trace(NON_TRIGGER_EVENT, 0, 0, 0, 0), NULL);
101
102 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, KDEBUG_TRIGGER_TIMEOUT_NS),
103 dispatch_get_main_queue(), ^(void)
104 {
105 ktrace_end(s, 1);
106 });
107 }
108
109 #define TRIGGER_CLASS UINT8_C(0xfe)
110 #define TRIGGER_CLASS_END UINT8_C(0xfd)
111 #define TRIGGER_SUBCLASS UINT8_C(0xff)
112 #define TRIGGER_CODE UINT8_C(0)
113 #define TRIGGER_DEBUGID \
114 (KDBG_EVENTID(TRIGGER_CLASS, TRIGGER_SUBCLASS, TRIGGER_CODE))
115
116 T_DECL(kdebug_trigger_classes, "test that kdebug trigger samples on classes",
117 T_META_ASROOT(true))
118 {
119 const uint32_t class_debugids[] = {
120 KDBG_EVENTID(TRIGGER_CLASS, 1, 1),
121 KDBG_EVENTID(TRIGGER_CLASS, 2, 1),
122 KDBG_EVENTID(TRIGGER_CLASS_END, 1, 1) | DBG_FUNC_END,
123 KDBG_EVENTID(TRIGGER_CLASS_END, 2, 1) | DBG_FUNC_END,
124 };
125
126 expect_kdebug_trigger("C0xfe,C0xfdr", class_debugids,
127 sizeof(class_debugids) / sizeof(class_debugids[0]));
128 dispatch_main();
129 }
130
131 T_DECL(kdebug_trigger_subclasses,
132 "test that kdebug trigger samples on subclasses",
133 T_META_ASROOT(true))
134 {
135 const uint32_t subclass_debugids[] = {
136 KDBG_EVENTID(TRIGGER_CLASS, TRIGGER_SUBCLASS, 0),
137 KDBG_EVENTID(TRIGGER_CLASS, TRIGGER_SUBCLASS, 1),
138 KDBG_EVENTID(TRIGGER_CLASS_END, TRIGGER_SUBCLASS, 0) | DBG_FUNC_END,
139 KDBG_EVENTID(TRIGGER_CLASS_END, TRIGGER_SUBCLASS, 1) | DBG_FUNC_END
140 };
141
142 expect_kdebug_trigger("S0xfeff,S0xfdffr", subclass_debugids,
143 sizeof(subclass_debugids) / sizeof(subclass_debugids[0]));
144 dispatch_main();
145 }
146
147 T_DECL(kdebug_trigger_debugids, "test that kdebug trigger samples on debugids",
148 T_META_ASROOT(true))
149 {
150 const uint32_t debugids[] = {
151 TRIGGER_DEBUGID
152 };
153
154 expect_kdebug_trigger("D0xfeff0000", debugids,
155 sizeof(debugids) / sizeof(debugids[0]));
156 dispatch_main();
157 }
158
159 /*
160 * TODO Set a single function specifier filter, expect not to trigger of all
161 * events from that class.
162 */
163
164 T_DECL(kdbg_callstacks, "test that the kdbg_callstacks samples on syscalls",
165 T_META_ASROOT(true))
166 {
167 ktrace_session_t s;
168 __block bool saw_user_stack = false;
169
170 s = ktrace_session_create();
171 T_ASSERT_NOTNULL(s, NULL);
172
173 /*
174 * Make sure BSD events are traced in order to trigger samples on syscalls.
175 */
176 ktrace_events_class(s, DBG_BSD,
177 ^void(__unused struct trace_point *tp) {});
178
179 ktrace_events_single(s, PERF_STK_UHDR, ^(__unused struct trace_point *tp) {
180 saw_user_stack = true;
181 ktrace_end(s, 1);
182 });
183
184 ktrace_set_completion_handler(s, ^{
185 ktrace_session_destroy(s);
186
187 T_EXPECT_TRUE(saw_user_stack,
188 "saw user stack after configuring kdbg_callstacks");
189
190 /*
191 * Ensure user stacks are not sampled after resetting kdbg_callstacks.
192 */
193 ktrace_session_t s_after = ktrace_session_create();
194 T_ASSERT_NOTNULL(s_after, NULL);
195
196 #pragma clang diagnostic push
197 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
198 T_ASSERT_POSIX_SUCCESS(kperf_kdbg_callstacks_set(0), NULL);
199 #pragma clang diagnostic pop
200
201 ktrace_events_class(s_after, DBG_BSD,
202 ^void(__unused struct trace_point *tp) {});
203
204 __block bool saw_extra_stack = false;
205
206 ktrace_events_single(s_after, PERF_STK_UHDR,
207 ^(__unused struct trace_point *tp)
208 {
209 saw_extra_stack = true;
210 ktrace_end(s_after, 1);
211 });
212
213 ktrace_set_completion_handler(s_after, ^(void) {
214 ktrace_session_destroy(s_after);
215 T_EXPECT_FALSE(saw_extra_stack,
216 "saw user stack after disabling kdbg_callstacks)");
217 kperf_reset();
218 T_END;
219 });
220
221 T_ASSERT_POSIX_ZERO(ktrace_start(s_after, dispatch_get_main_queue()),
222 NULL);
223
224 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC),
225 dispatch_get_main_queue(), ^(void)
226 {
227 ktrace_end(s_after, 1);
228 });
229 });
230
231 #pragma clang diagnostic push
232 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
233 T_ASSERT_POSIX_SUCCESS(kperf_kdbg_callstacks_set(1), NULL);
234 #pragma clang diagnostic pop
235
236 T_ASSERT_POSIX_ZERO(ktrace_start(s, dispatch_get_main_queue()), NULL);
237
238 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC),
239 dispatch_get_main_queue(), ^(void)
240 {
241 ktrace_end(s, 1);
242 });
243
244 dispatch_main();
245 }
246
247 /*
248 * PET mode
249 */
250
251 #define STACKS_WAIT_DURATION_NS (3 * NSEC_PER_SEC)
252
253 static void
254 expect_stacks_traced(void (^cb)(void))
255 {
256 ktrace_session_t s;
257
258 s = ktrace_session_create();
259 T_QUIET; T_ASSERT_NOTNULL(s, "ktrace_session_create");
260
261 __block unsigned int user_stacks = 0;
262 __block unsigned int kernel_stacks = 0;
263
264 ktrace_events_single(s, PERF_STK_UHDR, ^(__unused struct trace_point *tp) {
265 user_stacks++;
266 });
267 ktrace_events_single(s, PERF_STK_KHDR, ^(__unused struct trace_point *tp) {
268 kernel_stacks++;
269 });
270
271 ktrace_set_completion_handler(s, ^(void) {
272 ktrace_session_destroy(s);
273 T_EXPECT_GT(user_stacks, 0U, NULL);
274 T_EXPECT_GT(kernel_stacks, 0U, NULL);
275 cb();
276 });
277
278 T_QUIET; T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL);
279
280 T_ASSERT_POSIX_ZERO(ktrace_start(s, dispatch_get_main_queue()), NULL);
281
282 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, STACKS_WAIT_DURATION_NS),
283 dispatch_get_main_queue(), ^(void)
284 {
285 kperf_reset();
286 ktrace_end(s, 0);
287 });
288 }
289
290 T_DECL(pet, "test that PET mode samples kernel and user stacks",
291 T_META_ASROOT(true))
292 {
293 configure_kperf_stacks_timer(-1, 10);
294 T_ASSERT_POSIX_SUCCESS(kperf_timer_pet_set(0), NULL);
295
296 expect_stacks_traced(^(void) {
297 T_END;
298 });
299
300 dispatch_main();
301 }
302
303 T_DECL(lightweight_pet,
304 "test that lightweight PET mode samples kernel and user stacks",
305 T_META_ASROOT(true))
306 {
307 int set = 1;
308
309 configure_kperf_stacks_timer(-1, 10);
310 T_ASSERT_POSIX_SUCCESS(sysctlbyname("kperf.lightweight_pet", NULL, NULL,
311 &set, sizeof(set)), NULL);
312 T_ASSERT_POSIX_SUCCESS(kperf_timer_pet_set(0), NULL);
313
314 expect_stacks_traced(^(void) {
315 T_END;
316 });
317
318 dispatch_main();
319 }