1 /* Copyright (c) 2018-2019 Apple Inc. All rights reserved. */
3 #include <CoreSymbolication/CoreSymbolication.h>
4 #include <darwintest.h>
5 #include <dispatch/dispatch.h>
6 #include <kperf/kperf.h>
7 #include <ktrace/session.h>
8 #include <ktrace/private.h>
9 #include <System/sys/kdebug.h>
12 #include "kperf_helpers.h"
13 #include "ktrace_helpers.h"
15 #define CALLSTACK_VALID 0x1
16 #define CALLSTACK_TRUNCATED 0x10
19 T_META_NAMESPACE("xnu.ktrace"),
20 T_META_CHECK_LEAKS(false));
23 expect_frame(const char **bt
, unsigned int bt_len
, CSSymbolRef symbol
,
24 uint64_t addr
, unsigned int bt_idx
, unsigned int max_frames
)
27 unsigned int frame_idx
= max_frames
- bt_idx
- 1;
30 T_LOG("frame %2u: skipping system frame '%s'", frame_idx
,
31 CSSymbolGetName(symbol
));
35 T_LOG("checking frame %d: %llx", bt_idx
, addr
);
36 T_ASSERT_FALSE(CSIsNull(symbol
), "invalid symbol for return address");
38 if (frame_idx
>= bt_len
) {
39 T_FAIL("unexpected frame '%s' (%#" PRIx64
") at index %u",
40 CSSymbolGetName(symbol
), addr
, frame_idx
);
44 name
= CSSymbolGetName(symbol
);
45 T_QUIET
; T_ASSERT_NOTNULL(name
, NULL
);
46 T_EXPECT_EQ_STR(name
, bt
[frame_idx
],
47 "frame %2u: saw '%s', expected '%s'",
48 frame_idx
, name
, bt
[frame_idx
]);
52 * Expect to see either user or kernel stacks on thread with ID `tid` with a
53 * signature of `bt` of length `bt_len`. Updates `stacks_seen` when stack
56 * Can also allow stacks to be larger than the signature -- additional frames
57 * near the current PC will be ignored. This allows stacks to potentially be
58 * in the middle of a signalling system call (which signals that it is safe to
62 expect_backtrace(ktrace_session_t s
, uint64_t tid
, unsigned int *stacks_seen
,
63 bool kern
, const char **bt
, unsigned int bt_len
, unsigned int allow_larger_by
)
65 CSSymbolicatorRef symb
;
67 uint32_t data_debugid
;
68 __block
unsigned int stacks
= 0;
69 __block
unsigned int frames
= 0;
70 __block
unsigned int hdr_frames
= 0;
71 __block
unsigned int allow_larger
= allow_larger_by
;
76 static CSSymbolicatorRef kern_symb
;
77 static dispatch_once_t kern_symb_once
;
79 hdr_debugid
= PERF_STK_KHDR
;
80 data_debugid
= PERF_STK_KDATA
;
82 dispatch_once(&kern_symb_once
, ^(void) {
83 kern_symb
= CSSymbolicatorCreateWithMachKernel();
84 T_QUIET
; T_ASSERT_FALSE(CSIsNull(kern_symb
), NULL
);
88 static CSSymbolicatorRef user_symb
;
89 static dispatch_once_t user_symb_once
;
91 hdr_debugid
= PERF_STK_UHDR
;
92 data_debugid
= PERF_STK_UDATA
;
94 dispatch_once(&user_symb_once
, ^(void) {
95 user_symb
= CSSymbolicatorCreateWithTask(mach_task_self());
96 T_QUIET
; T_ASSERT_FALSE(CSIsNull(user_symb
), NULL
);
97 T_QUIET
; T_ASSERT_TRUE(CSSymbolicatorIsTaskValid(user_symb
), NULL
);
102 ktrace_events_single(s
, hdr_debugid
, ^(struct trace_point
*tp
) {
103 if (tid
!= 0 && tid
!= tp
->threadid
) {
107 T_LOG("found %s stack from thread %#" PRIx64
, kern
? "kernel" : "user",
110 if (!(tp
->arg1
& 1)) {
111 T_FAIL("invalid %s stack on thread %#" PRIx64
,
112 kern
? "kernel" : "user", tp
->threadid
);
116 hdr_frames
= (unsigned int)tp
->arg2
;
117 /* ignore extra link register or value pointed to by stack pointer */
120 T_QUIET
; T_EXPECT_GE(hdr_frames
, bt_len
,
121 "number of frames in header");
122 T_QUIET
; T_EXPECT_LE(hdr_frames
, bt_len
+ allow_larger
,
123 "number of frames in header");
124 if (hdr_frames
> bt_len
&& allow_larger
> 0) {
125 allow_larger
= hdr_frames
- bt_len
;
129 T_LOG("%s stack seen", kern
? "kernel" : "user");
133 ktrace_events_single(s
, data_debugid
, ^(struct trace_point
*tp
) {
134 if (tid
!= 0 && tid
!= tp
->threadid
) {
140 if (frames
== 0 && hdr_frames
> bt_len
) {
141 /* skip frames near the PC */
142 i
= (int)allow_larger
;
146 for (; i
< 4 && frames
< hdr_frames
; i
++, frames
++) {
147 uint64_t addr
= (&tp
->arg1
)[i
];
148 CSSymbolRef symbol
= CSSymbolicatorGetSymbolWithAddressAtTime(
151 expect_frame(bt
, bt_len
, symbol
, addr
, frames
, hdr_frames
);
154 /* saw the end of the user stack */
155 if (hdr_frames
== frames
) {
166 #define TRIGGERING_DEBUGID (0xfeff0f00)
169 * These functions must return an int to avoid the function prologue being
170 * hoisted out of the path to the spin (breaking being able to get a good
173 static int __attribute__((noinline
, not_tail_called
))
174 recurse_a(dispatch_semaphore_t spinning
, unsigned int frames
);
175 static int __attribute__((noinline
, not_tail_called
))
176 recurse_b(dispatch_semaphore_t spinning
, unsigned int frames
);
178 static int __attribute__((noinline
, not_tail_called
))
179 recurse_a(dispatch_semaphore_t spinning
, unsigned int frames
)
183 dispatch_semaphore_signal(spinning
);
188 kdebug_trace(TRIGGERING_DEBUGID
, 0, 0, 0, 0);
193 return recurse_b(spinning
, frames
- 1) + 1;
196 static int __attribute__((noinline
, not_tail_called
))
197 recurse_b(dispatch_semaphore_t spinning
, unsigned int frames
)
201 dispatch_semaphore_signal(spinning
);
206 kdebug_trace(TRIGGERING_DEBUGID
, 0, 0, 0, 0);
211 return recurse_a(spinning
, frames
- 1) + 1;
214 #define USER_FRAMES (12)
216 #if defined(__x86_64__)
218 #define RECURSE_START_OFFSET (3)
220 #else /* defined(__x86_64__) */
222 #define RECURSE_START_OFFSET (2)
224 #endif /* !defined(__x86_64__) */
226 static const char *user_bt
[USER_FRAMES
] = {
227 #if defined(__x86_64__)
229 * x86_64 has an extra "thread_start" frame here.
232 #endif /* defined(__x86_64__) */
235 "recurse_a", "recurse_b", "recurse_a", "recurse_b",
236 "recurse_a", "recurse_b", "recurse_a", "recurse_b",
237 #if !defined(__x86_64__)
239 * Pick up the slack to make the number of frames constant.
242 #endif /* !defined(__x86_64__) */
248 #define KERNEL_FRAMES (2)
249 static const char *kernel_bt
[KERNEL_FRAMES
] = {
250 "unix_syscall", "kdebug_trace64"
253 #elif defined(__arm64__)
255 #define KERNEL_FRAMES (4)
256 static const char *kernel_bt
[KERNEL_FRAMES
] = {
257 "fleh_synchronous", "sleh_synchronous", "unix_syscall", "kdebug_trace64"
260 #elif defined(__x86_64__)
262 #define KERNEL_FRAMES (2)
263 static const char *kernel_bt
[KERNEL_FRAMES
] = {
264 "unix_syscall64", "kdebug_trace64"
268 #error "architecture unsupported"
269 #endif /* defined(__arm__) */
271 static dispatch_once_t backtrace_once
;
272 static dispatch_semaphore_t backtrace_started
;
273 static dispatch_semaphore_t backtrace_go
;
276 * Another thread to run with a known backtrace.
278 * Take a semaphore that will be signalled when the thread is spinning at the
279 * correct frame. If the semaphore is NULL, don't spin and instead make a
280 * kdebug_trace system call, which can trigger a deterministic backtrace itself.
283 backtrace_thread(void *arg
)
285 dispatch_semaphore_t notify_spinning
;
288 notify_spinning
= (dispatch_semaphore_t
)arg
;
290 dispatch_semaphore_signal(backtrace_started
);
291 if (!notify_spinning
) {
292 dispatch_semaphore_wait(backtrace_go
, DISPATCH_TIME_FOREVER
);
296 * backtrace_thread, recurse_a, recurse_b, ...[, __kdebug_trace64]
298 * Always make one less call for this frame (backtrace_thread).
300 calls
= USER_FRAMES
- RECURSE_START_OFFSET
- 1 /* backtrace_thread */;
301 if (notify_spinning
) {
303 * Spinning doesn't end up calling __kdebug_trace64.
308 T_LOG("backtrace thread calling into %d frames (already at %d frames)",
309 calls
, RECURSE_START_OFFSET
);
310 (void)recurse_a(notify_spinning
, calls
);
315 create_backtrace_thread(void *(*thread_fn
)(void *),
316 dispatch_semaphore_t notify_spinning
)
318 pthread_t thread
= NULL
;
321 dispatch_once(&backtrace_once
, ^{
322 backtrace_started
= dispatch_semaphore_create(0);
323 T_QUIET
; T_ASSERT_NOTNULL(backtrace_started
, NULL
);
325 if (!notify_spinning
) {
326 backtrace_go
= dispatch_semaphore_create(0);
327 T_QUIET
; T_ASSERT_NOTNULL(backtrace_go
, NULL
);
331 T_QUIET
; T_ASSERT_POSIX_ZERO(pthread_create(&thread
, NULL
, thread_fn
,
332 (void *)notify_spinning
), NULL
);
333 T_QUIET
; T_ASSERT_NOTNULL(thread
, "backtrace thread created");
334 dispatch_semaphore_wait(backtrace_started
, DISPATCH_TIME_FOREVER
);
336 T_QUIET
; T_ASSERT_POSIX_ZERO(pthread_threadid_np(thread
, &tid
), NULL
);
337 T_QUIET
; T_ASSERT_NE(tid
, UINT64_C(0),
338 "backtrace thread created does not have ID 0");
340 T_LOG("starting thread with ID 0x%" PRIx64
, tid
);
346 start_backtrace_thread(void)
348 T_QUIET
; T_ASSERT_NOTNULL(backtrace_go
,
349 "thread to backtrace created before starting it");
350 dispatch_semaphore_signal(backtrace_go
);
354 #define TEST_TIMEOUT_NS (30 * NSEC_PER_SEC)
355 #else /* TARGET_OS_WATCH */
356 #define TEST_TIMEOUT_NS (5 * NSEC_PER_SEC)
357 #endif /* !TARGET_OS_WATCH */
359 T_DECL(kperf_stacks_kdebug_trig
,
360 "test that backtraces from kdebug trigger are correct",
363 static unsigned int stacks_seen
= 0;
365 kperf_kdebug_filter_t filter
;
368 start_controlling_ktrace();
370 s
= ktrace_session_create();
371 T_ASSERT_NOTNULL(s
, "ktrace session was created");
373 ktrace_set_collection_interval(s
, 100);
375 T_ASSERT_POSIX_ZERO(ktrace_filter_pid(s
, getpid()), NULL
);
377 tid
= create_backtrace_thread(backtrace_thread
, NULL
);
378 expect_backtrace(s
, tid
, &stacks_seen
, false, user_bt
, USER_FRAMES
, 0);
379 expect_backtrace(s
, tid
, &stacks_seen
, true, kernel_bt
, KERNEL_FRAMES
, 0);
382 * The triggering event must be traced (and thus registered with libktrace)
385 ktrace_events_single(s
, TRIGGERING_DEBUGID
,
386 ^(__unused
struct trace_point
*tp
){ });
388 ktrace_set_completion_handler(s
, ^(void) {
389 T_EXPECT_GE(stacks_seen
, 2U, "saw both kernel and user stacks");
390 ktrace_session_destroy(s
);
395 filter
= kperf_kdebug_filter_create();
396 T_ASSERT_NOTNULL(filter
, "kperf kdebug filter was created");
398 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_add_debugid(filter
,
399 TRIGGERING_DEBUGID
), NULL
);
400 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_set(filter
), NULL
);
401 (void)kperf_action_count_set(1);
402 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1,
403 KPERF_SAMPLER_USTACK
| KPERF_SAMPLER_KSTACK
), NULL
);
404 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_kdebug_action_set(1), NULL
);
405 kperf_kdebug_filter_destroy(filter
);
407 T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL
);
409 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
411 start_backtrace_thread();
413 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, TEST_TIMEOUT_NS
),
414 dispatch_get_main_queue(), ^(void)
416 T_LOG("ending test after timeout");
423 T_DECL(kperf_ustack_timer
,
424 "test that user backtraces on a timer are correct",
427 static unsigned int stacks_seen
= 0;
430 dispatch_semaphore_t wait_for_spinning
= dispatch_semaphore_create(0);
432 start_controlling_ktrace();
434 s
= ktrace_session_create();
435 T_QUIET
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
437 ktrace_set_collection_interval(s
, 100);
439 ktrace_filter_pid(s
, getpid());
441 configure_kperf_stacks_timer(getpid(), 10, false);
443 tid
= create_backtrace_thread(backtrace_thread
, wait_for_spinning
);
444 /* potentially calling dispatch function and system call */
445 expect_backtrace(s
, tid
, &stacks_seen
, false, user_bt
, USER_FRAMES
- 1, 2);
447 ktrace_set_completion_handler(s
, ^(void) {
448 T_EXPECT_GE(stacks_seen
, 1U, "saw at least one stack");
449 ktrace_session_destroy(s
);
454 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL
);
456 /* wait until the thread that will be backtraced is spinning */
457 dispatch_semaphore_wait(wait_for_spinning
, DISPATCH_TIME_FOREVER
);
459 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
461 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, TEST_TIMEOUT_NS
),
462 dispatch_get_main_queue(), ^(void)
464 T_LOG("ending test after timeout");
471 static volatile bool spin
= true;
473 __attribute__((noinline
, not_tail_called
))
475 recurse_spin(dispatch_semaphore_t notify_sema
, int depth
)
478 recurse_spin(notify_sema
, depth
- 1);
480 dispatch_semaphore_signal(notify_sema
);
486 spin_thread(void *arg
)
488 dispatch_semaphore_t notify_sema
= arg
;
489 dispatch_semaphore_signal(backtrace_started
);
490 recurse_spin(notify_sema
, 257);
494 T_DECL(kperf_ustack_trunc
, "ensure stacks are marked as truncated")
496 start_controlling_ktrace();
498 ktrace_session_t s
= ktrace_session_create();
499 T_ASSERT_NOTNULL(s
, "ktrace session was created");
501 ktrace_set_collection_interval(s
, 100);
504 T_ASSERT_POSIX_ZERO(ktrace_filter_pid(s
, getpid()), NULL
);
506 configure_kperf_stacks_timer(getpid(), 10, false);
508 __block
bool saw_stack
= false;
509 ktrace_set_completion_handler(s
, ^{
510 T_EXPECT_TRUE(saw_stack
, "saw the user stack");
514 dispatch_semaphore_t notify_sema
= dispatch_semaphore_create(0);
515 uint64_t tid
= create_backtrace_thread(spin_thread
, notify_sema
);
517 ktrace_events_single(s
, PERF_STK_UHDR
, ^(struct trace_point
*tp
) {
518 if (tp
->threadid
!= tid
) {
521 T_LOG("found %llu frame stack", tp
->arg2
);
522 T_EXPECT_BITS_SET(tp
->arg1
, CALLSTACK_VALID
,
523 "found valid callstack");
524 T_EXPECT_BITS_SET(tp
->arg1
, CALLSTACK_TRUNCATED
,
525 "found truncated callstack");
530 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL
);
532 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()),
535 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, TEST_TIMEOUT_NS
),
536 dispatch_get_main_queue(), ^(void)
538 T_LOG("ending test after timeout");
545 T_DECL(kperf_ustack_maxlen
, "ensure stacks up to 256 frames can be captured")
547 start_controlling_ktrace();
549 ktrace_session_t s
= ktrace_session_create();
550 T_ASSERT_NOTNULL(s
, "ktrace session was created");
552 ktrace_set_collection_interval(s
, 100);
555 T_ASSERT_POSIX_ZERO(ktrace_filter_pid(s
, getpid()), NULL
);
557 configure_kperf_stacks_timer(getpid(), 10, false);
559 __block
bool saw_stack
= false;
560 __block
bool saw_stack_data
= false;
561 __block
uint64_t nevents
= 0;
562 ktrace_set_completion_handler(s
, ^{
563 T_EXPECT_TRUE(saw_stack
, "saw the user stack");
564 T_LOG("saw %" PRIu64
" stack data events", nevents
);
565 T_EXPECT_TRUE(saw_stack_data
, "saw all frames of the user stack");
569 dispatch_semaphore_t notify_sema
= dispatch_semaphore_create(0);
570 uint64_t tid
= create_backtrace_thread(spin_thread
, notify_sema
);
572 ktrace_events_single(s
, PERF_STK_UHDR
, ^(struct trace_point
*tp
) {
573 if (tp
->threadid
!= tid
) {
576 T_LOG("found %llu frame stack", tp
->arg2
);
577 T_EXPECT_BITS_SET(tp
->arg1
, CALLSTACK_VALID
,
578 "found valid callstack");
579 T_EXPECT_EQ(tp
->arg2
, UINT64_C(256),
580 "found the correct number of frames");
584 ktrace_events_single(s
, PERF_STK_UDATA
, ^(struct trace_point
*tp
) {
585 if (tp
->threadid
!= tid
&& !saw_stack
) {
589 if (nevents
== 256 / 4) {
592 saw_stack_data
= true;
595 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL
);
597 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()),
600 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, TEST_TIMEOUT_NS
),
601 dispatch_get_main_queue(), ^(void)
603 T_LOG("ending test after timeout");
610 /* TODO test kernel stacks in all modes */
611 /* TODO legacy PET mode backtracing */