3 #endif /* defined(T_NAMESPACE) */
5 #include <darwintest.h>
6 #include <darwintest_utils.h>
7 #include <dispatch/dispatch.h>
9 #include <ktrace/session.h>
10 #include <ktrace/private.h>
11 #include <System/sys/kdebug.h>
12 #include <kperf/kperf.h>
13 #include <kperfdata/kpdecode.h>
14 #include <os/assumes.h>
16 #include <sys/sysctl.h>
18 #include "kperf_helpers.h"
21 T_META_NAMESPACE("xnu.kperf"),
22 T_META_CHECK_LEAKS(false),
26 #define MAX_THREADS 64
28 volatile static bool running_threads
= true;
31 spinning_thread(void *semp
)
34 T_ASSERT_NOTNULL(semp
, "semaphore passed to thread should not be NULL");
35 dispatch_semaphore_signal(*(dispatch_semaphore_t
*)semp
);
37 while (running_threads
) {
43 #define PERF_STK_KHDR UINT32_C(0x25020014)
44 #define PERF_STK_UHDR UINT32_C(0x25020018)
45 #define PERF_TMR_FIRE KDBG_EVENTID(DBG_PERF, 3, 0)
46 #define PERF_TMR_HNDLR KDBG_EVENTID(DBG_PERF, 3, 2)
47 #define PERF_TMR_PEND KDBG_EVENTID(DBG_PERF, 3, 3)
48 #define PERF_TMR_SKIP KDBG_EVENTID(DBG_PERF, 3, 4)
50 #define SCHED_HANDOFF KDBG_EVENTID(DBG_MACH, DBG_MACH_SCHED, \
52 #define SCHED_SWITCH KDBG_EVENTID(DBG_MACH, DBG_MACH_SCHED, MACH_SCHED)
53 #define SCHED_IDLE KDBG_EVENTID(DBG_MACH, DBG_MACH_SCHED, MACH_IDLE)
55 #define MP_CPUS_CALL UINT32_C(0x1900004)
57 #define DISPATCH_AFTER_EVENT UINT32_C(0xfefffffc)
58 #define TIMEOUT_SECS 10
60 #define TIMER_PERIOD_NS (1 * NSEC_PER_MSEC)
69 * Ensure that kperf is correctly IPIing CPUs that are actively scheduling by
70 * bringing up threads and ensuring that threads on-core are sampled by each
74 T_DECL(ipi_active_cpus
,
75 "make sure that kperf IPIs all active CPUs")
77 int ncpus
= dt_ncpu();
79 T_ASSERT_LT(ncpus
, MAX_CPUS
,
80 "only supports up to %d CPUs", MAX_CPUS
);
81 T_LOG("found %d CPUs", ncpus
);
83 int nthreads
= ncpus
- 1;
85 T_ASSERT_LT(nthreads
, MAX_THREADS
,
86 "only supports up to %d threads", MAX_THREADS
);
88 static pthread_t threads
[MAX_THREADS
];
91 * TODO options to write this to a file and reinterpret a file...
95 * Create threads to bring up all of the CPUs.
98 dispatch_semaphore_t thread_spinning
= dispatch_semaphore_create(0);
100 for (int i
= 0; i
< nthreads
; i
++) {
103 pthread_create(&threads
[i
], NULL
, &spinning_thread
,
104 &thread_spinning
), NULL
);
105 dispatch_semaphore_wait(thread_spinning
, DISPATCH_TIME_FOREVER
);
108 T_LOG("spun up %d thread%s", nthreads
, nthreads
== 1 ? "" : "s");
110 ktrace_session_t s
= ktrace_session_create();
111 T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
113 dispatch_queue_t q
= dispatch_get_global_queue(QOS_CLASS_USER_INITIATED
, 0);
116 * Only set the timeout after we've seen an event that was traced by us.
117 * This helps set a reasonable timeout after we're guaranteed to get a
121 ktrace_events_single(s
, DISPATCH_AFTER_EVENT
,
122 ^(__unused
struct trace_point
*tp
)
124 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
,
125 TIMEOUT_SECS
* NSEC_PER_SEC
), q
, ^{
130 __block
uint64_t nfires
= 0;
131 __block
uint64_t nsamples
= 0;
132 static uint64_t idle_tids
[MAX_CPUS
] = { 0 };
133 __block
int nidles
= 0;
135 ktrace_set_completion_handler(s
, ^{
136 T_LOG("stopping threads");
138 running_threads
= false;
140 for (int i
= 0; i
< nthreads
; i
++) {
142 T_ASSERT_POSIX_ZERO(pthread_join(threads
[i
], NULL
), NULL
);
145 for (int i
= 0; i
< nidles
; i
++) {
146 T_LOG("CPU %d idle thread: %#" PRIx64
, i
, idle_tids
[i
]);
149 T_LOG("saw %" PRIu64
" timer fires, %" PRIu64
" samples, "
150 "%g samples/fire", nfires
, nsamples
,
151 (double)nsamples
/ (double)nfires
);
157 * Track which threads are running on each CPU.
160 static uint64_t tids_on_cpu
[MAX_CPUS
] = { 0 };
162 void (^switch_cb
)(struct trace_point
*) = ^(struct trace_point
*tp
) {
163 uint64_t new_thread
= tp
->arg2
;
164 // uint64_t old_thread = tp->threadid;
166 for (int i
= 0; i
< nidles
; i
++) {
167 if (idle_tids
[i
] == new_thread
) {
172 tids_on_cpu
[tp
->cpuid
] = new_thread
;
175 ktrace_events_single(s
, SCHED_SWITCH
, switch_cb
);
176 ktrace_events_single(s
, SCHED_HANDOFF
, switch_cb
);
179 * Determine the thread IDs of the idle threads on each CPU.
182 ktrace_events_single(s
, SCHED_IDLE
, ^(struct trace_point
*tp
) {
183 uint64_t idle_thread
= tp
->threadid
;
185 tids_on_cpu
[tp
->cpuid
] = 0;
187 for (int i
= 0; i
< nidles
; i
++) {
188 if (idle_tids
[i
] == idle_thread
) {
193 idle_tids
[nidles
++] = idle_thread
;
197 * On each timer fire, go through all the cores and mark any threads
198 * that should be sampled.
201 __block
int last_fire_cpu
= -1;
202 __block
uint64_t sample_missing
= 0;
203 static uint64_t tids_snap
[MAX_CPUS
] = { 0 };
204 __block
int nexpected
= 0;
205 #if defined(__x86_64__)
206 __block
int xcall_from_cpu
= -1;
207 #endif /* defined(__x86_64__) */
208 __block
uint64_t xcall_mask
= 0;
210 ktrace_events_single(s
, PERF_TMR_FIRE
, ^(struct trace_point
*tp
) {
211 int last_expected
= nexpected
;
215 for (int i
= 0; i
< ncpus
; i
++) {
216 uint64_t i_bit
= UINT64_C(1) << i
;
217 if (sample_missing
& i_bit
) {
218 T_LOG("missed sample on CPU %d for thread %#llx from timer on CPU %d (xcall mask = %llx, expected %d samples)",
219 tp
->cpuid
, tids_snap
[i
], last_fire_cpu
,
220 xcall_mask
, last_expected
);
221 sample_missing
&= ~i_bit
;
224 if (tids_on_cpu
[i
] != 0) {
225 tids_snap
[i
] = tids_on_cpu
[i
];
226 sample_missing
|= i_bit
;
232 T_ASSERT_LT((int)tp
->cpuid
, ncpus
, "timer fire should not occur on an IOP");
233 last_fire_cpu
= (int)tp
->cpuid
;
234 #if defined(__x86_64__)
235 xcall_from_cpu
= (int)tp
->cpuid
;
236 #endif /* defined(__x86_64__) */
239 #if defined(__x86_64__)
241 * Watch for the cross-call on Intel, make sure they match what kperf
245 ktrace_events_single(s
, MP_CPUS_CALL
, ^(struct trace_point
*tp
) {
246 if (xcall_from_cpu
!= (int)tp
->cpuid
) {
250 xcall_mask
= tp
->arg1
;
253 #endif /* defined(__x86_64__) */
256 * On the timer handler for each CPU, unset the missing sample bitmap.
259 ktrace_events_single(s
, PERF_TMR_HNDLR
, ^(struct trace_point
*tp
) {
261 if ((int)tp
->cpuid
> ncpus
) {
262 /* skip IOPs; they're not scheduling our threads */
266 sample_missing
&= ~(UINT64_C(1) << tp
->cpuid
);
270 * Configure kperf and ktrace.
273 (void)kperf_action_count_set(1);
275 T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1, KPERF_SAMPLER_KSTACK
),
277 (void)kperf_timer_count_set(1);
279 T_ASSERT_POSIX_SUCCESS(kperf_timer_period_set(0,
280 kperf_ns_to_ticks(TIMER_PERIOD_NS
)), NULL
);
282 T_ASSERT_POSIX_SUCCESS(kperf_timer_action_set(0, 1), NULL
);
284 T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
285 T_ATEND(reset_ktrace
);
287 T_ASSERT_POSIX_ZERO(ktrace_start(s
,
288 dispatch_get_global_queue(QOS_CLASS_USER_INITIATED
, 0)),
291 kdebug_trace(DISPATCH_AFTER_EVENT
, 0, 0, 0, 0);
296 #pragma mark kdebug triggers
298 #define KDEBUG_TRIGGER_TIMEOUT_NS (10 * NSEC_PER_SEC)
300 #define NON_TRIGGER_CLASS UINT8_C(0xfd)
301 #define NON_TRIGGER_SUBCLASS UINT8_C(0xff)
302 #define NON_TRIGGER_CODE UINT8_C(0xff)
304 #define NON_TRIGGER_EVENT \
305 (KDBG_EVENTID(NON_TRIGGER_CLASS, NON_TRIGGER_SUBCLASS, \
309 expect_kdebug_trigger(const char *filter_desc
, const uint32_t *debugids
,
310 unsigned int n_debugids
)
312 __block
int missing_kernel_stacks
= 0;
313 __block
int missing_user_stacks
= 0;
315 kperf_kdebug_filter_t filter
;
317 s
= ktrace_session_create();
318 T_QUIET
; T_ASSERT_NOTNULL(s
, NULL
);
320 ktrace_events_single(s
, PERF_STK_KHDR
, ^(struct trace_point
*tp
) {
321 missing_kernel_stacks
--;
322 T_LOG("saw kernel stack with %lu frames, flags = %#lx", tp
->arg2
,
325 ktrace_events_single(s
, PERF_STK_UHDR
, ^(struct trace_point
*tp
) {
326 missing_user_stacks
--;
327 T_LOG("saw user stack with %lu frames, flags = %#lx", tp
->arg2
,
331 for (unsigned int i
= 0; i
< n_debugids
; i
++) {
332 ktrace_events_single(s
, debugids
[i
], ^(struct trace_point
*tp
) {
333 missing_kernel_stacks
++;
334 missing_user_stacks
++;
335 T_LOG("saw event with debugid 0x%" PRIx32
, tp
->debugid
);
339 ktrace_events_single(s
, NON_TRIGGER_EVENT
,
340 ^(__unused
struct trace_point
*tp
)
345 ktrace_set_completion_handler(s
, ^{
346 T_EXPECT_LE(missing_kernel_stacks
, 0, NULL
);
347 T_EXPECT_LE(missing_user_stacks
, 0, NULL
);
349 ktrace_session_destroy(s
);
353 /* configure kperf */
357 (void)kperf_action_count_set(1);
358 T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1,
359 KPERF_SAMPLER_KSTACK
| KPERF_SAMPLER_USTACK
), NULL
);
361 filter
= kperf_kdebug_filter_create();
362 T_ASSERT_NOTNULL(filter
, NULL
);
364 T_ASSERT_POSIX_SUCCESS(kperf_kdebug_action_set(1), NULL
);
365 T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_add_desc(filter
, filter_desc
),
367 T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_set(filter
), NULL
);
368 kperf_kdebug_filter_destroy(filter
);
370 T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL
);
372 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
374 /* trace the triggering debugids */
376 for (unsigned int i
= 0; i
< n_debugids
; i
++) {
377 T_ASSERT_POSIX_SUCCESS(kdebug_trace(debugids
[i
], 0, 0, 0, 0), NULL
);
380 T_ASSERT_POSIX_SUCCESS(kdebug_trace(NON_TRIGGER_EVENT
, 0, 0, 0, 0), NULL
);
382 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, KDEBUG_TRIGGER_TIMEOUT_NS
),
383 dispatch_get_main_queue(), ^(void)
389 #define TRIGGER_CLASS UINT8_C(0xfe)
390 #define TRIGGER_CLASS_END UINT8_C(0xfd)
391 #define TRIGGER_SUBCLASS UINT8_C(0xff)
392 #define TRIGGER_CODE UINT8_C(0)
393 #define TRIGGER_DEBUGID \
394 (KDBG_EVENTID(TRIGGER_CLASS, TRIGGER_SUBCLASS, TRIGGER_CODE))
396 T_DECL(kdebug_trigger_classes
,
397 "test that kdebug trigger samples on classes")
399 const uint32_t class_debugids
[] = {
400 KDBG_EVENTID(TRIGGER_CLASS
, 1, 1),
401 KDBG_EVENTID(TRIGGER_CLASS
, 2, 1),
402 KDBG_EVENTID(TRIGGER_CLASS_END
, 1, 1) | DBG_FUNC_END
,
403 KDBG_EVENTID(TRIGGER_CLASS_END
, 2, 1) | DBG_FUNC_END
,
406 expect_kdebug_trigger("C0xfe,C0xfdr", class_debugids
,
407 sizeof(class_debugids
) / sizeof(class_debugids
[0]));
411 T_DECL(kdebug_trigger_subclasses
,
412 "test that kdebug trigger samples on subclasses")
414 const uint32_t subclass_debugids
[] = {
415 KDBG_EVENTID(TRIGGER_CLASS
, TRIGGER_SUBCLASS
, 0),
416 KDBG_EVENTID(TRIGGER_CLASS
, TRIGGER_SUBCLASS
, 1),
417 KDBG_EVENTID(TRIGGER_CLASS_END
, TRIGGER_SUBCLASS
, 0) | DBG_FUNC_END
,
418 KDBG_EVENTID(TRIGGER_CLASS_END
, TRIGGER_SUBCLASS
, 1) | DBG_FUNC_END
421 expect_kdebug_trigger("S0xfeff,S0xfdffr", subclass_debugids
,
422 sizeof(subclass_debugids
) / sizeof(subclass_debugids
[0]));
426 T_DECL(kdebug_trigger_debugids
,
427 "test that kdebug trigger samples on debugids")
429 const uint32_t debugids
[] = {
433 expect_kdebug_trigger("D0xfeff0000", debugids
,
434 sizeof(debugids
) / sizeof(debugids
[0]));
439 * TODO Set a single function specifier filter, expect not to trigger of all
440 * events from that class.
443 T_DECL(kdbg_callstacks
,
444 "test that the kdbg_callstacks samples on syscalls")
447 __block
bool saw_user_stack
= false;
449 s
= ktrace_session_create();
450 T_ASSERT_NOTNULL(s
, NULL
);
453 * Make sure BSD events are traced in order to trigger samples on syscalls.
455 ktrace_events_class(s
, DBG_BSD
, ^void (__unused
struct trace_point
*tp
) {});
457 ktrace_events_single(s
, PERF_STK_UHDR
, ^(__unused
struct trace_point
*tp
) {
458 saw_user_stack
= true;
462 ktrace_set_completion_handler(s
, ^{
463 ktrace_session_destroy(s
);
465 T_EXPECT_TRUE(saw_user_stack
,
466 "saw user stack after configuring kdbg_callstacks");
470 #pragma clang diagnostic push
471 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
472 T_ASSERT_POSIX_SUCCESS(kperf_kdbg_callstacks_set(1), NULL
);
473 #pragma clang diagnostic pop
474 T_ATEND(kperf_reset
);
476 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
478 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 10 * NSEC_PER_SEC
),
479 dispatch_get_main_queue(), ^(void) {
488 #define STACKS_WAIT_DURATION_NS (3 * NSEC_PER_SEC)
491 expect_stacks_traced(void (^cb
)(void))
495 s
= ktrace_session_create();
496 T_QUIET
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
498 __block
unsigned int user_stacks
= 0;
499 __block
unsigned int kernel_stacks
= 0;
501 ktrace_events_single(s
, PERF_STK_UHDR
, ^(__unused
struct trace_point
*tp
) {
504 ktrace_events_single(s
, PERF_STK_KHDR
, ^(__unused
struct trace_point
*tp
) {
508 ktrace_set_completion_handler(s
, ^(void) {
509 ktrace_session_destroy(s
);
510 T_EXPECT_GT(user_stacks
, 0U, NULL
);
511 T_EXPECT_GT(kernel_stacks
, 0U, NULL
);
515 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL
);
517 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
519 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, STACKS_WAIT_DURATION_NS
),
520 dispatch_get_main_queue(), ^(void)
527 T_DECL(pet
, "test that PET mode samples kernel and user stacks")
529 configure_kperf_stacks_timer(-1, 10);
530 T_ASSERT_POSIX_SUCCESS(kperf_timer_pet_set(0), NULL
);
532 expect_stacks_traced(^(void) {
539 T_DECL(lightweight_pet
,
540 "test that lightweight PET mode samples kernel and user stacks",
545 configure_kperf_stacks_timer(-1, 10);
546 T_ASSERT_POSIX_SUCCESS(sysctlbyname("kperf.lightweight_pet", NULL
, NULL
,
547 &set
, sizeof(set
)), NULL
);
548 T_ASSERT_POSIX_SUCCESS(kperf_timer_pet_set(0), NULL
);
550 expect_stacks_traced(^(void) {
557 T_DECL(pet_stress
, "repeatedly enable and disable PET mode")
561 configure_kperf_stacks_timer(-1, 10);
562 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_timer_pet_set(0), NULL
);
569 T_DECL(timer_stress
, "repeatedly enable and disable timers")
573 configure_kperf_stacks_timer(-1, 1);