1 // Copyright (c) 2017-2020 Apple Computer, Inc. All rights reserved.
3 #include <darwintest.h>
4 #include <darwintest_utils.h>
5 #include <dispatch/dispatch.h>
7 #include <ktrace/session.h>
8 #include <ktrace/private.h>
9 #include <sys/kdebug.h>
10 #include <sys/syscall.h>
11 #include <kperf/kpc.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"
19 #include "ktrace_helpers.h"
22 T_META_NAMESPACE("xnu.ktrace"),
23 T_META_CHECK_LEAKS(false),
27 #define MAX_THREADS 64
29 volatile static bool running_threads
= true;
32 spinning_thread(void *semp
)
35 T_ASSERT_NOTNULL(semp
, "semaphore passed to thread should not be NULL");
36 dispatch_semaphore_signal(*(dispatch_semaphore_t
*)semp
);
38 while (running_threads
) {
44 #define PERF_STK_KHDR UINT32_C(0x25020014)
45 #define PERF_STK_UHDR UINT32_C(0x25020018)
46 #define PERF_TMR_FIRE KDBG_EVENTID(DBG_PERF, 3, 0)
47 #define PERF_TMR_HNDLR KDBG_EVENTID(DBG_PERF, 3, 2)
48 #define PERF_TMR_PEND KDBG_EVENTID(DBG_PERF, 3, 3)
49 #define PERF_TMR_SKIP KDBG_EVENTID(DBG_PERF, 3, 4)
50 #define PERF_KPC_CONFIG KDBG_EVENTID(DBG_PERF, 6, 4)
51 #define PERF_KPC_REG KDBG_EVENTID(DBG_PERF, 6, 5)
52 #define PERF_KPC_REG32 KDBG_EVENTID(DBG_PERF, 6, 7)
53 #define PERF_INSTR_DATA KDBG_EVENTID(DBG_PERF, 1, 17)
54 #define PERF_EVENT KDBG_EVENTID(DBG_PERF, 0, 0)
56 #define SCHED_DISPATCH KDBG_EVENTID(DBG_MACH, DBG_MACH_SCHED, MACH_DISPATCH)
57 #define SCHED_SWITCH KDBG_EVENTID(DBG_MACH, DBG_MACH_SCHED, MACH_SCHED)
58 #define SCHED_HANDOFF KDBG_EVENTID(DBG_MACH, DBG_MACH_SCHED, MACH_STACK_HANDOFF)
59 #define SCHED_IDLE KDBG_EVENTID(DBG_MACH, DBG_MACH_SCHED, MACH_IDLE)
61 #define MP_CPUS_CALL UINT32_C(0x1900004)
63 #define DISPATCH_AFTER_EVENT UINT32_C(0xfefffffc)
64 #define TIMEOUT_SECS 10
66 #define TIMER_PERIOD_NS (1 * NSEC_PER_MSEC)
69 start_tracing_with_timeout(ktrace_session_t s
, unsigned int timeout_secs
)
71 // Only set the timeout after we've seen an event that was traced by us.
72 // This helps set a reasonable timeout after we're guaranteed to get a
74 dispatch_queue_t q
= dispatch_get_global_queue(QOS_CLASS_USER_INITIATED
, 0);
76 ktrace_events_single(s
, DISPATCH_AFTER_EVENT
,
77 ^(__unused
struct trace_point
*tp
)
79 T_LOG("arming timer to stop tracing after %d seconds", timeout_secs
);
80 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
,
81 timeout_secs
* NSEC_PER_SEC
), q
, ^{
82 T_LOG("ending tracing due to timeout");
86 ktrace_set_collection_interval(s
, 100);
88 T_ASSERT_POSIX_ZERO(ktrace_start(s
, q
), "start ktrace");
90 kdebug_trace(DISPATCH_AFTER_EVENT
, 0, 0, 0, 0);
91 T_LOG("trace point emitted");
95 configure_kperf_timer_samplers(uint64_t period_ns
, uint32_t samplers
)
99 (void)kperf_action_count_set(1);
101 T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1, samplers
),
103 (void)kperf_timer_count_set(1);
105 T_ASSERT_POSIX_SUCCESS(kperf_timer_period_set(0,
106 kperf_ns_to_ticks(period_ns
)), NULL
);
108 T_ASSERT_POSIX_SUCCESS(kperf_timer_action_set(0, 1), NULL
);
110 T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
116 timestamp_secs(ktrace_session_t s
, uint64_t timestamp
)
120 T_ASSERT_POSIX_ZERO(ktrace_convert_timestamp_to_nanoseconds(s
, timestamp
,
122 return (double)ns
/ NSEC_PER_SEC
;
125 #pragma mark - timers
127 // Ensure that kperf is correctly sampling CPUs that are actively scheduling by
128 // bringing up threads and ensuring that threads on-core are sampled by each
131 T_DECL(kperf_sample_active_cpus
,
132 "make sure that kperf samples all active CPUs")
134 start_controlling_ktrace();
138 int ncpus
= dt_ncpu();
140 T_ASSERT_LT(ncpus
, MAX_CPUS
,
141 "only supports up to %d CPUs", MAX_CPUS
);
142 T_LOG("found %d CPUs", ncpus
);
144 int nthreads
= ncpus
- 1;
146 T_ASSERT_LT(nthreads
, MAX_THREADS
,
147 "only supports up to %d threads", MAX_THREADS
);
149 static pthread_t threads
[MAX_THREADS
];
151 ktrace_session_t s
= ktrace_session_create();
152 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
153 ktrace_set_collection_interval(s
, 100);
155 __block
uint64_t nfires
= 0;
156 __block
uint64_t nsamples
= 0;
157 static uint64_t idle_tids
[MAX_CPUS
] = { 0 };
158 __block
double sum_saturation
= 0;
159 __block
uint64_t last_nsamples
= 0;
161 // As a test debugging aid, take an additonal argument that specifies the
162 // number of fires to stop tracing after. This also turns on additional
163 // logging of scheduler trace events.
166 stopafter
= atoi(argv
[0]);
168 T_ASSERT_FAIL("argument must be positive");
172 static uint64_t first_timestamp
= 0;
173 static uint64_t last_timestamp
= 0;
174 ktrace_events_any(s
, ^(struct trace_point
*tp
) {
175 if (first_timestamp
== 0) {
176 first_timestamp
= tp
->timestamp
;
178 last_timestamp
= tp
->timestamp
;
181 ktrace_set_completion_handler(s
, ^{
182 T_LOG("stopping threads");
184 running_threads
= false;
186 for (int i
= 0; i
< nthreads
; i
++) {
188 T_ASSERT_POSIX_ZERO(pthread_join(threads
[i
], NULL
), NULL
);
191 double saturation
= sum_saturation
/ nfires
* 100;
193 T_LOG("over %.1f seconds, saw %" PRIu64
" timer fires, %" PRIu64
194 " samples, %g samples/fire, %.2f%% saturation",
195 timestamp_secs(s
, last_timestamp
- first_timestamp
), nfires
,
196 nsamples
, (double)nsamples
/ (double)nfires
, saturation
);
197 T_ASSERT_GT(saturation
, 95.0,
198 "saw reasonable percentage of full samples");
203 // Track which threads are running on each CPU.
204 static uint64_t tids_on_cpu
[MAX_CPUS
] = { 0 };
205 void (^switch_cb
)(struct trace_point
*, const char *name
) =
206 ^(struct trace_point
*tp
, const char *name
) {
207 uint64_t new_thread
= tp
->arg2
;
209 if (idle_tids
[tp
->cpuid
] != new_thread
) {
210 tids_on_cpu
[tp
->cpuid
] = new_thread
;
214 T_LOG("%.7g: %s on %d: %llx", timestamp_secs(s
, tp
->timestamp
),
215 name
, tp
->cpuid
, tp
->arg2
);
219 ktrace_events_single(s
, SCHED_SWITCH
, ^(struct trace_point
*tp
) {
220 switch_cb(tp
, "switch");
222 ktrace_events_single(s
, SCHED_HANDOFF
, ^(struct trace_point
*tp
) {
223 switch_cb(tp
, "hndoff");
226 // Determine the thread IDs of the idle threads on each CPU.
227 ktrace_events_single(s
, SCHED_IDLE
, ^(struct trace_point
*tp
) {
228 if (tp
->debugid
& DBG_FUNC_END
) {
231 tids_on_cpu
[tp
->cpuid
] = 0;
232 idle_tids
[tp
->cpuid
] = tp
->threadid
;
234 T_LOG("%.7g: idle on %d: %llx", timestamp_secs(s
, tp
->timestamp
),
235 tp
->cpuid
, tp
->threadid
);
239 // On each timer fire, go through all the cores and mark any threads
240 // that should be sampled.
242 __block
int last_fire_cpu
= -1;
243 static bool sample_missing
[MAX_CPUS
] = { false };
244 static uint64_t tids_snap
[MAX_CPUS
] = { 0 };
245 __block
int nexpected
= 0;
246 __block
int nextra
= 0;
247 __block
int nidles
= 0;
249 ktrace_events_single(s
, PERF_TMR_FIRE
, ^(struct trace_point
*tp
) {
250 T_QUIET
; T_ASSERT_EQ((tp
->debugid
& DBG_FUNC_START
), 0,
251 "no timer fire start events are allowed");
252 int last_expected
= nexpected
;
256 for (int i
= 0; i
< ncpus
; i
++) {
257 if (sample_missing
[i
]) {
258 T_LOG("missed sample on CPU %d for thread %#llx from "
259 "timer on CPU %d (expected %d samples)",
260 tp
->cpuid
, tids_snap
[i
], last_fire_cpu
, last_expected
);
261 sample_missing
[i
] = false;
264 if (tids_on_cpu
[i
] != 0) {
265 tids_snap
[i
] = tids_on_cpu
[i
];
266 sample_missing
[i
] = true;
271 T_LOG("%.7g: FIRE on %d: %d extra, %d idles",
272 timestamp_secs(s
, tp
->timestamp
), tp
->cpuid
, nextra
, nidles
);
279 if (last_expected
== 0) {
282 sum_saturation
+= (double)(nsamples
- last_nsamples
) /
285 last_nsamples
= nsamples
;
290 T_ASSERT_LT((int)tp
->cpuid
, ncpus
, "timer fire should not occur on an IOP");
291 last_fire_cpu
= (int)tp
->cpuid
;
293 if (stopafter
&& (uint64_t)stopafter
== nfires
) {
298 // On the timer handler for each CPU, unset the missing sample bitmap.
300 ktrace_events_single(s
, PERF_TMR_HNDLR
, ^(struct trace_point
*tp
) {
302 if ((int)tp
->cpuid
> ncpus
) {
303 // Skip IOPs; they're not scheduling any relevant threads.
307 if (!sample_missing
[tp
->cpuid
] && idle_tids
[tp
->cpuid
] != 0) {
308 T_LOG("sampled additional thread %llx on CPU %d", tp
->threadid
,
312 if (tp
->threadid
== idle_tids
[tp
->cpuid
]) {
313 T_LOG("sampled idle thread on CPU %d", tp
->cpuid
);
316 sample_missing
[tp
->cpuid
] = false;
319 configure_kperf_timer_samplers(TIMER_PERIOD_NS
, KPERF_SAMPLER_KSTACK
);
323 start_tracing_with_timeout(s
, TIMEOUT_SECS
);
325 // Create threads to bring up all of the CPUs.
327 dispatch_semaphore_t thread_spinning
= dispatch_semaphore_create(0);
329 for (int i
= 0; i
< nthreads
; i
++) {
332 pthread_create(&threads
[i
], NULL
, &spinning_thread
,
333 &thread_spinning
), NULL
);
334 dispatch_semaphore_wait(thread_spinning
, DISPATCH_TIME_FOREVER
);
337 T_LOG("spun up %d thread%s", nthreads
, nthreads
== 1 ? "" : "s");
342 #define FIRES_THRESHOLD (5000)
344 T_DECL(kperf_timer_fires_enough_times
,
345 "ensure the correct number of timers fire in a period of time")
347 start_controlling_ktrace();
349 dispatch_semaphore_t thread_spinning
= dispatch_semaphore_create(0);
351 ktrace_session_t s
= ktrace_session_create();
352 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
353 ktrace_set_collection_interval(s
, 100);
355 __block
uint64_t nfires
= 0;
356 __block
uint64_t first_fire_ns
= 0;
357 __block
uint64_t last_fire_ns
= 0;
359 int ncpus
= dt_ncpu();
361 ktrace_events_single(s
, PERF_TMR_FIRE
, ^(struct trace_point
*tp
) {
363 if (first_fire_ns
== 0) {
364 ktrace_convert_timestamp_to_nanoseconds(s
, tp
->timestamp
,
367 ktrace_convert_timestamp_to_nanoseconds(s
, tp
->timestamp
,
370 T_QUIET
; T_ASSERT_LT((int)tp
->cpuid
, ncpus
,
371 "timer fire should not occur on an IOP");
372 if (nfires
>= FIRES_THRESHOLD
) {
377 configure_kperf_timer_samplers(TIMER_PERIOD_NS
, KPERF_SAMPLER_KSTACK
);
381 T_ASSERT_POSIX_ZERO(pthread_create(&thread
, NULL
, &spinning_thread
,
382 &thread_spinning
), NULL
);
383 dispatch_semaphore_wait(thread_spinning
, DISPATCH_TIME_FOREVER
);
385 ktrace_set_completion_handler(s
, ^{
386 running_threads
= false;
388 double duration_secs
= (double)(last_fire_ns
- first_fire_ns
) /
390 T_LOG("stopping thread after %.2f seconds", duration_secs
);
392 T_QUIET
; T_ASSERT_POSIX_ZERO(pthread_join(thread
, NULL
), NULL
);
394 T_LOG("saw %" PRIu64
" timer fires (%g fires/second)", nfires
,
395 (double)nfires
/ (double)duration_secs
);
396 double expected_nfires
= duration_secs
* NSEC_PER_SEC
/ TIMER_PERIOD_NS
;
397 T_LOG("expecting %g timer fires", expected_nfires
);
398 double nfires_seen_pct
= expected_nfires
/ nfires
* 100;
399 T_ASSERT_GT(nfires_seen_pct
, 95.0,
400 "saw reasonable number of missed timer fires");
401 T_ASSERT_LT(nfires_seen_pct
, 105.0,
402 "saw reasonable number of extra timer fires");
407 start_tracing_with_timeout(s
, TIMEOUT_SECS
);
412 // kperf_timer_not_oversampling ensures that the profiling timer fires are
413 // spaced apart by the programmed timer period. Otherwise, tools that rely on
414 // sample count as a proxy for CPU usage will over-estimate.
416 #define FIRE_PERIOD_THRESHOLD_NS \
417 (TIMER_PERIOD_NS - (uint64_t)(TIMER_PERIOD_NS * 0.05))
421 unsigned int tail_slot
;
422 unsigned int slot_size
;
425 #define CIRQ_INIT(TYPE, NSLOTS) \
427 .nslots = NSLOTS, .tail_slot = 0, .slot_size = sizeof(TYPE), \
431 cirq_get(struct cirq
*cq
, unsigned int i
)
433 return (char *)cq
+ sizeof(*cq
) + (cq
->slot_size
* i
);
439 struct cirq
*cq
= vcq
;
440 unsigned int tail_slot
= cq
->tail_slot
;
441 unsigned int top_slot
= (tail_slot
> 0 ? tail_slot
: cq
->nslots
) - 1;
442 return cirq_get(cq
, top_slot
);
448 struct cirq
*cq
= vcq
;
449 unsigned int tail_slot
= cq
->tail_slot
;
450 unsigned int next_slot
= tail_slot
== cq
->nslots
- 1 ? 0 : tail_slot
+ 1;
451 cq
->tail_slot
= next_slot
;
452 return cirq_get(cq
, tail_slot
);
456 cirq_for(void *vcq
, void (^iter
)(void *elt
))
458 struct cirq
*cq
= vcq
;
459 for (unsigned int i
= cq
->tail_slot
; i
< cq
->nslots
; i
++) {
460 iter(cirq_get(cq
, i
));
462 for (unsigned int i
= 0; i
< cq
->tail_slot
; i
++) {
463 iter(cirq_get(cq
, i
));
467 #define HISTORY_LEN 5
470 uint64_t iv_instant_ns
;
474 struct cirq_instval
{
476 struct instval elts
[HISTORY_LEN
];
481 uint64_t elts
[HISTORY_LEN
];
484 struct cpu_oversample
{
485 struct cirq_instval timer_latencies
;
486 struct cirq_instval fire_latencies
;
490 cpu_oversample_log(struct cpu_oversample
*cpu
, unsigned int cpuid
)
492 T_LOG("CPU %d timer latencies:", cpuid
);
493 __block
int i
= -HISTORY_LEN
;
494 cirq_for(&cpu
->timer_latencies
, ^(void *viv
) {
495 struct instval
*iv
= viv
;
496 T_LOG("\t%llu timer latency %d: %llu", iv
->iv_instant_ns
, i
,
501 T_LOG("CPU %d fire latencies:", cpuid
);
503 cirq_for(&cpu
->fire_latencies
, ^(void *viv
) {
504 struct instval
*iv
= viv
;
505 T_LOG("\t%llu fire latency %d: %llu", iv
->iv_instant_ns
, i
, iv
->iv_val
);
510 T_DECL(kperf_timer_not_oversampling
,
511 "ensure that time between fires is long enough")
513 start_controlling_ktrace();
515 ktrace_session_t s
= ktrace_session_create();
516 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
517 // Try not to perturb the system with more work.
518 ktrace_set_collection_interval(s
, 1000);
519 __block
uint64_t nfires
= 0;
520 __block
uint64_t first_fire_ns
= 0;
521 __block
uint64_t last_fire_ns
= 0;
522 __block
unsigned int last_fire_cpuid
= 0;
524 int ncpus
= dt_ncpu();
525 T_QUIET
; T_ASSERT_GT(ncpus
, 0, "should see positive number of CPUs");
527 struct cpu_oversample
*per_cpu
= calloc((unsigned int)ncpus
,
529 T_QUIET
; T_WITH_ERRNO
;
530 T_ASSERT_NOTNULL(per_cpu
, "allocated timer latency tracking");
531 for (int i
= 0; i
< ncpus
; i
++) {
532 per_cpu
[i
].timer_latencies
.cq
= CIRQ_INIT(struct instval
, HISTORY_LEN
);
533 per_cpu
[i
].fire_latencies
.cq
= CIRQ_INIT(struct instval
, HISTORY_LEN
);
536 __block
bool in_stackshot
= false;
537 __block
uint64_t last_stackshot_ns
= 0;
539 // Stackshots are the primary source of interrupt latency on the system.
540 ktrace_events_single(s
, KDBG_EVENTID(DBG_BSD
, DBG_BSD_EXCP_SC
,
541 SYS_stack_snapshot_with_config
), ^(struct trace_point
*tp
) {
542 bool start
= tp
->debugid
& DBG_FUNC_START
;
543 uint64_t cur_ns
= relns_from_abs(s
, tp
->timestamp
);
544 T_LOG("%llu: %s stackshot syscall from process %s",
545 cur_ns
, start
? "start" : "finish", tp
->command
);
546 in_stackshot
= start
;
548 last_stackshot_ns
= cur_ns
;
552 struct cirq_u64
*fire_times
= calloc(1, sizeof(*fire_times
));
553 T_ASSERT_NOTNULL(fire_times
, "allocated fire time tracking");
554 fire_times
->cq
= CIRQ_INIT(uint64_t, HISTORY_LEN
);
556 // Track the decrementer's latency values to find any unexpectedly long
557 // interrupt latencies that could affect the firing cadence.
558 ktrace_events_single(s
, MACHDBG_CODE(DBG_MACH_EXCP_DECI
, 0),
559 ^(struct trace_point
*tp
) {
560 uint64_t cur_ns
= relns_from_abs(s
, tp
->timestamp
);
561 uint64_t latency_ns
= ns_from_abs(s
, 0 - tp
->arg1
);
562 struct instval
*latency
= cirq_push(&per_cpu
[tp
->cpuid
].timer_latencies
);
563 latency
->iv_instant_ns
= cur_ns
;
564 latency
->iv_val
= latency_ns
;
567 ktrace_events_single(s
, PERF_TMR_FIRE
, ^(struct trace_point
*tp
) {
568 T_QUIET
; T_ASSERT_LT((int)tp
->cpuid
, ncpus
,
569 "timer fire should not occur on an IOP");
572 uint64_t cur_ns
= relns_from_abs(s
, tp
->timestamp
);
573 uint64_t *fire_ns
= cirq_push(fire_times
);
576 struct cpu_oversample
*cur_cpu
= &per_cpu
[tp
->cpuid
];
577 struct instval
*last_timer_latency
= cirq_top(
578 &cur_cpu
->timer_latencies
);
579 uint64_t timer_latency_ns
= last_timer_latency
->iv_val
;
581 if (first_fire_ns
== 0) {
582 first_fire_ns
= cur_ns
;
584 struct cpu_oversample
*last_cpu
= &per_cpu
[last_fire_cpuid
];
585 struct instval
*last_latency
= cirq_top(&last_cpu
->fire_latencies
);
586 uint64_t last_fire_latency_ns
= last_latency
->iv_val
;
588 if (timer_latency_ns
> TIMER_PERIOD_NS
/ 4) {
589 T_LOG("%llu: long timer latency at fire: %llu", cur_ns
,
593 // Long interrupt latencies will cause the timer to miss its fire
594 // time and report a fire past when it should have, making the next
595 // period too short. Keep track of the latency as a leeway
596 // adjustment for the subsequent fire.
597 uint64_t fire_period_ns
= cur_ns
- last_fire_ns
;
598 uint64_t fire_period_adj_ns
= fire_period_ns
+
599 last_fire_latency_ns
+ timer_latency_ns
;
600 bool too_short
= fire_period_adj_ns
< FIRE_PERIOD_THRESHOLD_NS
;
602 T_LOG("%llu: period of timer fire %llu is %llu + %llu + %llu = "
604 cur_ns
, nfires
, fire_period_ns
, last_fire_latency_ns
,
605 timer_latency_ns
, fire_period_adj_ns
,
606 FIRE_PERIOD_THRESHOLD_NS
);
608 T_LOG("short profile timer fired on CPU %d", tp
->cpuid
);
609 cpu_oversample_log(cur_cpu
, tp
->cpuid
);
611 if (cur_cpu
== last_cpu
) {
612 T_LOG("fired back-to-back on CPU %d", tp
->cpuid
);
614 T_LOG("previous profile timer fired on CPU %d",
616 cpu_oversample_log(last_cpu
, last_fire_cpuid
);
619 T_LOG("profile timer fires:");
620 cirq_for(fire_times
, ^(void *vu64
) {
621 T_LOG("\tfire: %llu", *(uint64_t *)vu64
);
623 if (nfires
< (unsigned int)ncpus
) {
624 T_LOG("ignoring timer fire %llu as context may be missing",
628 T_LOG("skipping assertion because stackshot is "
630 } else if (last_stackshot_ns
!= 0 &&
631 cur_ns
> last_stackshot_ns
&&
632 cur_ns
- last_stackshot_ns
< TIMER_PERIOD_NS
) {
633 T_LOG("skipping assertion because stackshot happened "
635 cur_ns
- last_stackshot_ns
);
637 T_ASSERT_FAIL("profiling period is shorter than "
638 "expected with no stackshot interference");
643 struct instval
*latency
= cirq_push(&cur_cpu
->fire_latencies
);
644 latency
->iv_instant_ns
= cur_ns
;
645 latency
->iv_val
= timer_latency_ns
;
647 // Snapshot this timer fire's interrupt latency, so the next one
648 // can make an adjustment to the period.
649 last_fire_latency_ns
= timer_latency_ns
;
651 last_fire_ns
= cur_ns
;
652 last_fire_cpuid
= tp
->cpuid
;
654 if (nfires
>= FIRES_THRESHOLD
) {
659 configure_kperf_timer_samplers(TIMER_PERIOD_NS
, KPERF_SAMPLER_TINFO
);
661 ktrace_set_completion_handler(s
, ^{
662 double duration_secs
= (double)(last_fire_ns
- first_fire_ns
) /
664 T_LOG("stopping trace after %.2f seconds", duration_secs
);
666 T_PASS("saw %" PRIu64
" timer fires (%g fires/second) without "
667 "oversampling", nfires
, (double)nfires
/ (double)duration_secs
);
672 start_tracing_with_timeout(s
, 5);
674 // Get all CPUs out of idle.
675 uint64_t *counts
= kpc_counterbuf_alloc();
676 (void)kpc_get_cpu_counters(true,KPC_CLASS_CONFIGURABLE_MASK
, NULL
, counts
);
682 T_DECL(kperf_timer_stress
, "repeatedly enable and disable timers")
684 start_controlling_ktrace();
686 const int niters
= 500;
687 for (int i
= 0; i
< niters
; i
++) {
688 configure_kperf_stacks_timer(-1, 1, true);
690 T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
694 T_LOG("configured kperf with a timer %d times", niters
);
697 #pragma mark - kdebug triggers
699 #define KDEBUG_TRIGGER_TIMEOUT_NS (10 * NSEC_PER_SEC)
701 #define NON_TRIGGER_CLASS UINT32_C(0xfd)
702 #define NON_TRIGGER_SUBCLASS UINT32_C(0xff)
703 #define NON_TRIGGER_CODE UINT32_C(0xff)
705 #define NON_TRIGGER_EVENT \
706 (KDBG_EVENTID(NON_TRIGGER_CLASS, NON_TRIGGER_SUBCLASS, \
710 expect_kdebug_trigger(const char *filter_desc
, const uint32_t *debugids
,
711 unsigned int n_debugids
)
713 __block
int missing_kernel_stacks
= 0;
714 __block
int missing_user_stacks
= 0;
716 kperf_kdebug_filter_t filter
;
718 s
= ktrace_session_create();
719 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
720 ktrace_set_collection_interval(s
, 100);
722 ktrace_events_single(s
, PERF_STK_KHDR
, ^(struct trace_point
*tp
) {
723 missing_kernel_stacks
--;
724 T_LOG("saw kernel stack with %" PRIu64
" frames, flags = %#"
725 PRIx64
, tp
->arg2
, tp
->arg1
);
727 ktrace_events_single(s
, PERF_STK_UHDR
, ^(struct trace_point
*tp
) {
728 missing_user_stacks
--;
729 T_LOG("saw user stack with %" PRIu64
" frames, flags = %#"
730 PRIx64
, tp
->arg2
, tp
->arg1
);
733 for (unsigned int i
= 0; i
< n_debugids
; i
++) {
734 ktrace_events_single(s
, debugids
[i
], ^(struct trace_point
*tp
) {
735 missing_kernel_stacks
++;
736 missing_user_stacks
++;
737 T_LOG("saw event with debugid 0x%" PRIx32
, tp
->debugid
);
741 ktrace_events_single(s
, NON_TRIGGER_EVENT
,
742 ^(__unused
struct trace_point
*tp
)
747 ktrace_set_completion_handler(s
, ^{
748 T_EXPECT_LE(missing_kernel_stacks
, 0, NULL
);
749 T_EXPECT_LE(missing_user_stacks
, 0, NULL
);
751 ktrace_session_destroy(s
);
757 (void)kperf_action_count_set(1);
758 T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1,
759 KPERF_SAMPLER_KSTACK
| KPERF_SAMPLER_USTACK
), NULL
);
761 filter
= kperf_kdebug_filter_create();
762 T_ASSERT_NOTNULL(filter
, NULL
);
764 T_ASSERT_POSIX_SUCCESS(kperf_kdebug_action_set(1), NULL
);
765 T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_add_desc(filter
, filter_desc
),
767 T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_set(filter
), NULL
);
768 kperf_kdebug_filter_destroy(filter
);
770 T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL
);
772 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
774 // Trace the triggering events.
776 for (unsigned int i
= 0; i
< n_debugids
; i
++) {
777 T_ASSERT_POSIX_SUCCESS(kdebug_trace(debugids
[i
], 0, 0, 0, 0), NULL
);
780 T_ASSERT_POSIX_SUCCESS(kdebug_trace(NON_TRIGGER_EVENT
, 0, 0, 0, 0), NULL
);
782 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, KDEBUG_TRIGGER_TIMEOUT_NS
),
783 dispatch_get_main_queue(), ^(void)
789 #define TRIGGER_CLASS UINT32_C(0xfe)
790 #define TRIGGER_CLASS_END UINT32_C(0xfd)
791 #define TRIGGER_SUBCLASS UINT32_C(0xff)
792 #define TRIGGER_CODE UINT32_C(0)
793 #define TRIGGER_DEBUGID \
794 (KDBG_EVENTID(TRIGGER_CLASS, TRIGGER_SUBCLASS, TRIGGER_CODE))
796 T_DECL(kperf_kdebug_trigger_classes
,
797 "test that kdebug trigger samples on classes")
799 start_controlling_ktrace();
801 const uint32_t class_debugids
[] = {
802 KDBG_EVENTID(TRIGGER_CLASS
, 1, 1),
803 KDBG_EVENTID(TRIGGER_CLASS
, 2, 1),
804 KDBG_EVENTID(TRIGGER_CLASS_END
, 1, 1) | DBG_FUNC_END
,
805 KDBG_EVENTID(TRIGGER_CLASS_END
, 2, 1) | DBG_FUNC_END
,
808 expect_kdebug_trigger("C0xfe,C0xfdr", class_debugids
,
809 sizeof(class_debugids
) / sizeof(class_debugids
[0]));
813 T_DECL(kperf_kdebug_trigger_subclasses
,
814 "test that kdebug trigger samples on subclasses")
816 start_controlling_ktrace();
818 const uint32_t subclass_debugids
[] = {
819 KDBG_EVENTID(TRIGGER_CLASS
, TRIGGER_SUBCLASS
, 0),
820 KDBG_EVENTID(TRIGGER_CLASS
, TRIGGER_SUBCLASS
, 1),
821 KDBG_EVENTID(TRIGGER_CLASS_END
, TRIGGER_SUBCLASS
, 0) | DBG_FUNC_END
,
822 KDBG_EVENTID(TRIGGER_CLASS_END
, TRIGGER_SUBCLASS
, 1) | DBG_FUNC_END
825 expect_kdebug_trigger("S0xfeff,S0xfdffr", subclass_debugids
,
826 sizeof(subclass_debugids
) / sizeof(subclass_debugids
[0]));
830 T_DECL(kperf_kdebug_trigger_debugids
,
831 "test that kdebug trigger samples on debugids")
833 start_controlling_ktrace();
835 const uint32_t debugids
[] = {
839 expect_kdebug_trigger("D0xfeff0000", debugids
,
840 sizeof(debugids
) / sizeof(debugids
[0]));
844 // TODO Set a single function specifier filter, expect not to trigger of all
845 // events from that class.
853 T_DECL(kperf_kdbg_callstacks
,
854 "test that the kdbg_callstacks samples on syscalls")
856 start_controlling_ktrace();
859 __block
bool saw_user_stack
= false;
861 s
= ktrace_session_create();
862 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
863 ktrace_set_collection_interval(s
, 100);
865 // Make sure BSD events are traced in order to trigger samples on syscalls.
866 ktrace_events_class(s
, DBG_BSD
, ^void (__unused
struct trace_point
*tp
) {});
868 ktrace_events_single(s
, PERF_STK_UHDR
, ^(__unused
struct trace_point
*tp
) {
869 saw_user_stack
= true;
873 ktrace_set_completion_handler(s
, ^{
874 ktrace_session_destroy(s
);
876 T_EXPECT_TRUE(saw_user_stack
,
877 "saw user stack after configuring kdbg_callstacks");
881 #pragma clang diagnostic push
882 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
883 T_ASSERT_POSIX_SUCCESS(kperf_kdbg_callstacks_set(1), NULL
);
884 #pragma clang diagnostic pop
885 T_ATEND(reset_kperf
);
887 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
889 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 10 * NSEC_PER_SEC
),
890 dispatch_get_main_queue(), ^(void) {
899 #define STACKS_WAIT_DURATION_NS (3 * NSEC_PER_SEC)
902 expect_stacks_traced(void (^setup
)(ktrace_session_t s
), void (^complete
)(void))
906 s
= ktrace_session_create();
907 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
908 ktrace_set_collection_interval(s
, 100);
913 __block
unsigned int user_stacks
= 0;
914 __block
unsigned int kernel_stacks
= 0;
916 ktrace_events_single(s
, PERF_STK_UHDR
, ^(__unused
struct trace_point
*tp
) {
919 ktrace_events_single(s
, PERF_STK_KHDR
, ^(__unused
struct trace_point
*tp
) {
923 ktrace_set_completion_handler(s
, ^(void) {
924 ktrace_session_destroy(s
);
925 T_EXPECT_GT(user_stacks
, 0U, NULL
);
926 T_EXPECT_GT(kernel_stacks
, 0U, NULL
);
930 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL
);
932 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
934 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, STACKS_WAIT_DURATION_NS
),
935 dispatch_get_main_queue(), ^(void)
942 T_DECL(kperf_pet
, "test that PET mode samples kernel and user stacks")
944 start_controlling_ktrace();
946 configure_kperf_stacks_timer(-1, 10, false);
947 T_ASSERT_POSIX_SUCCESS(kperf_timer_pet_set(0), NULL
);
949 expect_stacks_traced(NULL
, ^(void) {
956 T_DECL(kperf_lightweight_pet
,
957 "test that lightweight PET mode samples kernel and user stacks",
960 start_controlling_ktrace();
964 configure_kperf_stacks_timer(-1, 10, false);
965 T_ASSERT_POSIX_SUCCESS(sysctlbyname("kperf.lightweight_pet", NULL
, NULL
,
966 &set
, sizeof(set
)), NULL
);
967 T_ASSERT_POSIX_SUCCESS(kperf_timer_pet_set(0), NULL
);
969 __block
uint64_t nfires
= 0;
971 expect_stacks_traced(^(ktrace_session_t s
) {
972 ktrace_events_single(s
, PERF_TMR_FIRE
, ^(struct trace_point
*tp
) {
975 T_ASSERT_EQ(tp
->arg1
, (uint64_t)0,
976 "timer fire should have timer ID of 0");
978 T_ASSERT_EQ(tp
->arg2
, (uint64_t)1,
979 "timer fire should have PET bit set");
982 T_ASSERT_GT(nfires
, (uint64_t)0, "timer fired at least once");
989 T_DECL(kperf_pet_stress
, "repeatedly enable and disable PET mode")
991 start_controlling_ktrace();
993 const int niters
= 500;
994 for (int i
= 0; i
< niters
; i
++) {
995 configure_kperf_stacks_timer(-1, 1, true);
996 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_timer_pet_set(0), NULL
);
998 T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
1003 T_PASS("configured kperf PET %d times", niters
);
1008 T_DECL(kperf_pmc_config_only
,
1009 "shouldn't show PMC config events unless requested")
1011 start_controlling_ktrace();
1013 __block
bool saw_kpc_config
= false;
1014 __block
bool saw_kpc_reg
= false;
1016 ktrace_session_t s
= ktrace_session_create();
1017 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
1018 ktrace_set_collection_interval(s
, 100);
1020 ktrace_events_single(s
, PERF_KPC_CONFIG
,
1021 ^(__unused
struct trace_point
*tp
) {
1022 saw_kpc_config
= true;
1024 ktrace_events_single(s
, PERF_KPC_REG
,
1025 ^(__unused
struct trace_point
*tp
) {
1028 ktrace_events_single(s
, PERF_KPC_REG32
,
1029 ^(__unused
struct trace_point
*tp
) {
1033 ktrace_set_completion_handler(s
, ^{
1034 ktrace_session_destroy(s
);
1035 T_EXPECT_FALSE(saw_kpc_config
,
1036 "should see no KPC configs without sampler enabled");
1037 T_EXPECT_FALSE(saw_kpc_reg
,
1038 "should see no KPC registers without sampler enabled");
1042 uint32_t nconfigs
= kpc_get_config_count(KPC_CLASS_CONFIGURABLE_MASK
);
1043 uint64_t *config
= calloc(nconfigs
, sizeof(*config
));
1045 int ret
= kpc_set_config(KPC_CLASS_CONFIGURABLE_MASK
, config
);
1046 T_ASSERT_POSIX_SUCCESS(ret
, "configured kpc");
1048 T_ASSERT_POSIX_SUCCESS(kpc_set_counting(KPC_CLASS_CONFIGURABLE_MASK
),
1049 "kpc_set_counting");
1051 (void)kperf_action_count_set(1);
1052 T_ATEND(reset_kperf
);
1054 T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1, KPERF_SAMPLER_PMC_CPU
),
1057 (void)kperf_timer_count_set(1);
1059 T_ASSERT_POSIX_SUCCESS(kperf_timer_period_set(0,
1060 kperf_ns_to_ticks(TIMER_PERIOD_NS
)), NULL
);
1062 T_ASSERT_POSIX_SUCCESS(kperf_timer_action_set(0, 1), NULL
);
1064 T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
1066 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
1068 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 10 * NSEC_PER_SEC
),
1069 dispatch_get_main_queue(), ^(void) {
1077 skip_if_monotonic_unsupported(void)
1081 size_t supported_size
= sizeof(supported
);
1083 r
= sysctlbyname("kern.monotonic.supported", &supported
, &supported_size
,
1087 T_SKIP("could not find \"kern.monotonic.supported\" sysctl");
1091 T_SKIP("monotonic is not supported on this platform");
1095 #define INSTRS_CYCLES_UPPER 500
1096 #define INSTRS_CYCLES_LOWER 50
1098 T_DECL(kperf_sample_instrs_cycles
,
1099 "ensure instructions and cycles are sampled")
1101 skip_if_monotonic_unsupported();
1103 start_controlling_ktrace();
1105 ktrace_session_t sess
= ktrace_session_create();
1106 T_QUIET
; T_WITH_ERRNO
; T_ASSERT_NOTNULL(sess
, "ktrace_session_create");
1107 ktrace_set_collection_interval(sess
, 100);
1109 __block
uint64_t ninstrs_cycles
= 0;
1110 __block
uint64_t nzeroes
= 0;
1111 ktrace_events_single(sess
, PERF_INSTR_DATA
,
1112 ^(__unused
struct trace_point
*tp
) {
1114 if (tp
->arg1
== 0) {
1115 T_LOG("%llx (%s)\n", tp
->threadid
, tp
->command
);
1118 if (ninstrs_cycles
>= INSTRS_CYCLES_UPPER
) {
1119 ktrace_end(sess
, 1);
1123 ktrace_set_collection_interval(sess
, 200);
1125 ktrace_set_completion_handler(sess
, ^{
1126 T_EXPECT_GE(ninstrs_cycles
, (uint64_t)INSTRS_CYCLES_LOWER
,
1127 "saw enough instructions and cycles events");
1128 T_EXPECT_EQ(nzeroes
, UINT64_C(0),
1129 "saw no events with 0 instructions");
1133 (void)kperf_action_count_set(1);
1134 T_ATEND(reset_kperf
);
1136 T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1,
1137 KPERF_SAMPLER_TH_INSTRS_CYCLES
), NULL
);
1139 (void)kperf_timer_count_set(1);
1141 T_ASSERT_POSIX_SUCCESS(kperf_timer_period_set(0,
1142 kperf_ns_to_ticks(TIMER_PERIOD_NS
)), NULL
);
1144 T_ASSERT_POSIX_SUCCESS(kperf_timer_action_set(0, 1), NULL
);
1146 T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), "start kperf sampling");
1148 T_ASSERT_POSIX_ZERO(ktrace_start(sess
, dispatch_get_main_queue()),
1151 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 10 * NSEC_PER_SEC
),
1152 dispatch_get_main_queue(), ^(void) {
1153 ktrace_end(sess
, 1);