1 #include <CoreSymbolication/CoreSymbolication.h>
2 #include <darwintest.h>
3 #include <dispatch/dispatch.h>
4 #include <kperf/kperf.h>
8 #include "kperf_helpers.h"
10 #define PERF_STK_KHDR UINT32_C(0x25020014)
11 #define PERF_STK_UHDR UINT32_C(0x25020018)
12 #define PERF_STK_KDATA UINT32_C(0x2502000c)
13 #define PERF_STK_UDATA UINT32_C(0x25020010)
16 expect_frame(const char **bt
, unsigned int bt_len
, CSSymbolRef symbol
,
17 unsigned long addr
, unsigned int bt_idx
, unsigned int max_frames
)
20 unsigned int frame_idx
= max_frames
- bt_idx
- 1;
23 T_LOG("frame %2u: skipping system frame", frame_idx
);
27 if (CSIsNull(symbol
)) {
28 T_FAIL("invalid symbol for address %#lx at frame %d", addr
, frame_idx
);
32 if (frame_idx
>= bt_len
) {
33 T_FAIL("unexpected frame '%s' (%#lx) at index %u",
34 CSSymbolGetName(symbol
), addr
, frame_idx
);
38 name
= CSSymbolGetName(symbol
);
39 T_QUIET
; T_ASSERT_NOTNULL(name
, NULL
);
40 T_EXPECT_EQ_STR(name
, bt
[frame_idx
],
41 "frame %2u: saw '%s', expected '%s'",
42 frame_idx
, name
, bt
[frame_idx
]);
46 * Expect to see either user or kernel stacks on thread with ID `tid` with a
47 * signature of `bt` of length `bt_len`. Updates `stacks_seen` when stack
50 * Can also allow stacks to be larger than the signature -- additional frames
51 * near the current PC will be ignored. This allows stacks to potentially be
52 * in the middle of a signalling system call (which signals that it is safe to
56 expect_backtrace(ktrace_session_t s
, uint64_t tid
, unsigned int *stacks_seen
,
57 bool kern
, const char **bt
, unsigned int bt_len
, unsigned int allow_larger_by
)
59 CSSymbolicatorRef symb
;
61 uint32_t data_debugid
;
62 __block
unsigned int stacks
= 0;
63 __block
unsigned int frames
= 0;
64 __block
unsigned int hdr_frames
= 0;
65 __block
unsigned int allow_larger
= allow_larger_by
;
68 static CSSymbolicatorRef kern_symb
;
69 static dispatch_once_t kern_symb_once
;
71 hdr_debugid
= PERF_STK_KHDR
;
72 data_debugid
= PERF_STK_KDATA
;
74 dispatch_once(&kern_symb_once
, ^(void) {
75 kern_symb
= CSSymbolicatorCreateWithMachKernel();
76 T_QUIET
; T_ASSERT_FALSE(CSIsNull(kern_symb
), NULL
);
80 static CSSymbolicatorRef user_symb
;
81 static dispatch_once_t user_symb_once
;
83 hdr_debugid
= PERF_STK_UHDR
;
84 data_debugid
= PERF_STK_UDATA
;
86 dispatch_once(&user_symb_once
, ^(void) {
87 user_symb
= CSSymbolicatorCreateWithTask(mach_task_self());
88 T_QUIET
; T_ASSERT_FALSE(CSIsNull(user_symb
), NULL
);
89 T_QUIET
; T_ASSERT_TRUE(CSSymbolicatorIsTaskValid(user_symb
), NULL
);
94 ktrace_events_single(s
, hdr_debugid
, ^(struct trace_point
*tp
) {
95 if (tid
!= 0 && tid
!= tp
->threadid
) {
99 T_LOG("found stack from thread %#lx", tp
->threadid
);
101 if (!(tp
->arg1
& 1)) {
102 T_FAIL("invalid %s stack on thread %#lx", kern
? "kernel" : "user",
107 hdr_frames
= (unsigned int)tp
->arg2
;
108 /* ignore extra link register or value pointed to by stack pointer */
111 T_QUIET
; T_EXPECT_GE(hdr_frames
, bt_len
,
112 "number of frames in header");
113 T_QUIET
; T_EXPECT_LE(hdr_frames
, bt_len
+ allow_larger
,
114 "number of frames in header");
115 if (hdr_frames
> bt_len
&& allow_larger
> 0) {
116 allow_larger
= hdr_frames
- bt_len
;
120 T_LOG("%s stack seen", kern
? "kernel" : "user");
124 ktrace_events_single(s
, data_debugid
, ^(struct trace_point
*tp
) {
125 if (tid
!= 0 && tid
!= tp
->threadid
) {
131 if (frames
== 0 && hdr_frames
> bt_len
) {
132 /* skip frames near the PC */
133 i
= (int)allow_larger
;
137 for (; i
< 4 && frames
< hdr_frames
; i
++, frames
++) {
138 unsigned long addr
= (&tp
->arg1
)[i
];
139 CSSymbolRef symbol
= CSSymbolicatorGetSymbolWithAddressAtTime(
142 expect_frame(bt
, bt_len
, symbol
, addr
, frames
, hdr_frames
);
145 /* saw the end of the user stack */
146 if (hdr_frames
== frames
) {
155 #define TRIGGERING_DEBUGID (0xfeff0f00)
158 * These functions must return an int to avoid the function prologue being
159 * hoisted out of the path to the spin (breaking being able to get a good
162 static int __attribute__((noinline
,not_tail_called
))
163 recurse_a(dispatch_semaphore_t spinning
, unsigned int frames
);
164 static int __attribute__((noinline
,not_tail_called
))
165 recurse_b(dispatch_semaphore_t spinning
, unsigned int frames
);
167 static int __attribute__((noinline
,not_tail_called
))
168 recurse_a(dispatch_semaphore_t spinning
, unsigned int frames
)
172 dispatch_semaphore_signal(spinning
);
175 kdebug_trace(TRIGGERING_DEBUGID
, 0, 0, 0, 0);
180 return recurse_b(spinning
, frames
- 1) + 1;
183 static int __attribute__((noinline
,not_tail_called
))
184 recurse_b(dispatch_semaphore_t spinning
, unsigned int frames
)
188 dispatch_semaphore_signal(spinning
);
191 kdebug_trace(TRIGGERING_DEBUGID
, 0, 0, 0, 0);
196 return recurse_a(spinning
, frames
- 1) + 1;
199 #define USER_FRAMES (12)
201 #if defined(__x86_64__)
202 #define RECURSE_START_OFFSET (4)
203 #else /* defined(__x86_64__) */
204 #define RECURSE_START_OFFSET (3)
205 #endif /* defined(__x86_64__) */
207 static const char *user_bt
[USER_FRAMES
] = {
208 #if defined(__x86_64__)
210 #endif /* defined(__x86_64__) */
213 "recurse_a", "recurse_b", "recurse_a", "recurse_b",
214 "recurse_a", "recurse_b", "recurse_a",
215 #if !defined(__x86_64__)
217 #endif /* !defined(__x86_64__) */
221 #if defined(__x86_64__)
223 #define KERNEL_FRAMES (2)
224 static const char *kernel_bt
[KERNEL_FRAMES
] = {
225 "unix_syscall64", "kdebug_trace64"
229 #error "architecture unsupported"
230 #endif /* defined(__arm__) */
232 static dispatch_once_t backtrace_once
;
233 static dispatch_semaphore_t backtrace_started
;
234 static dispatch_semaphore_t backtrace_go
;
237 * Another thread to run with a known backtrace.
239 * Take a semaphore that will be signalled when the thread is spinning at the
240 * correct frame. If the semaphore is NULL, don't spin and instead make a
241 * kdebug_trace system call, which can trigger a deterministic backtrace itself.
244 backtrace_thread(void *arg
)
246 dispatch_semaphore_t notify_spinning
;
249 notify_spinning
= (dispatch_semaphore_t
)arg
;
251 dispatch_semaphore_signal(backtrace_started
);
252 if (!notify_spinning
) {
253 dispatch_semaphore_wait(backtrace_go
, DISPATCH_TIME_FOREVER
);
257 * backtrace_thread, recurse_a, recurse_b, ...[, __kdebug_trace64]
259 * Always make one less call for this frame (backtrace_thread).
261 calls
= USER_FRAMES
- RECURSE_START_OFFSET
- 1 /* backtrace_thread */;
262 if (notify_spinning
) {
264 * Spinning doesn't end up calling __kdebug_trace64.
269 T_LOG("backtrace thread calling into %d frames (already at %d frames)",
270 calls
, RECURSE_START_OFFSET
);
271 (void)recurse_a(notify_spinning
, calls
);
276 create_backtrace_thread(dispatch_semaphore_t notify_spinning
)
278 pthread_t thread
= NULL
;
281 dispatch_once(&backtrace_once
, ^{
282 backtrace_started
= dispatch_semaphore_create(0);
283 T_QUIET
; T_ASSERT_NOTNULL(backtrace_started
, NULL
);
285 if (!notify_spinning
) {
286 backtrace_go
= dispatch_semaphore_create(0);
287 T_QUIET
; T_ASSERT_NOTNULL(backtrace_go
, NULL
);
291 T_QUIET
; T_ASSERT_POSIX_ZERO(pthread_create(&thread
, NULL
, backtrace_thread
,
292 (void *)notify_spinning
), NULL
);
293 T_QUIET
; T_ASSERT_NOTNULL(thread
, "backtrace thread created");
294 dispatch_semaphore_wait(backtrace_started
, DISPATCH_TIME_FOREVER
);
296 T_QUIET
; T_ASSERT_POSIX_ZERO(pthread_threadid_np(thread
, &tid
), NULL
);
297 T_QUIET
; T_ASSERT_NE(tid
, UINT64_C(0),
298 "backtrace thread created does not have ID 0");
300 T_LOG("starting thread with ID 0x%" PRIx64
, tid
);
306 start_backtrace_thread(void)
308 T_QUIET
; T_ASSERT_NOTNULL(backtrace_go
,
309 "thread to backtrace created before starting it");
310 dispatch_semaphore_signal(backtrace_go
);
313 #define TEST_TIMEOUT_NS (5 * NSEC_PER_SEC)
315 T_DECL(kdebug_trigger_backtraces
,
316 "test that backtraces from kdebug trigger are correct",
319 static unsigned int stacks_seen
= 0;
321 kperf_kdebug_filter_t filter
;
324 s
= ktrace_session_create();
325 T_ASSERT_NOTNULL(s
, "ktrace session was created");
327 T_ASSERT_POSIX_ZERO(ktrace_filter_pid(s
, getpid()), NULL
);
329 tid
= create_backtrace_thread(NULL
);
330 expect_backtrace(s
, tid
, &stacks_seen
, false, user_bt
, USER_FRAMES
, 0);
331 expect_backtrace(s
, tid
, &stacks_seen
, true, kernel_bt
, KERNEL_FRAMES
, 0);
334 * The triggering event must be traced (and thus registered with libktrace)
337 ktrace_events_single(s
, TRIGGERING_DEBUGID
,
338 ^(__unused
struct trace_point
*tp
){ });
340 ktrace_set_completion_handler(s
, ^(void) {
341 T_EXPECT_GE(stacks_seen
, 2U, "saw both kernel and user stacks");
342 ktrace_session_destroy(s
);
347 filter
= kperf_kdebug_filter_create();
348 T_ASSERT_NOTNULL(filter
, "kperf kdebug filter was created");
350 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_add_debugid(filter
,
351 TRIGGERING_DEBUGID
), NULL
);
352 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_kdebug_filter_set(filter
), NULL
);
353 (void)kperf_action_count_set(1);
354 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_action_samplers_set(1,
355 KPERF_SAMPLER_USTACK
| KPERF_SAMPLER_KSTACK
), NULL
);
356 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_kdebug_action_set(1), NULL
);
357 kperf_kdebug_filter_destroy(filter
);
359 T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL
);
361 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
363 start_backtrace_thread();
365 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, TEST_TIMEOUT_NS
),
366 dispatch_get_main_queue(), ^(void)
368 T_LOG("ending test after timeout");
375 T_DECL(user_backtraces_timer
,
376 "test that user backtraces on a timer are correct",
379 static unsigned int stacks_seen
= 0;
382 dispatch_semaphore_t wait_for_spinning
= dispatch_semaphore_create(0);
384 s
= ktrace_session_create();
385 T_QUIET
; T_ASSERT_NOTNULL(s
, "ktrace_session_create");
387 ktrace_filter_pid(s
, getpid());
389 configure_kperf_stacks_timer(getpid(), 10);
391 tid
= create_backtrace_thread(wait_for_spinning
);
392 /* potentially calling dispatch function and system call */
393 expect_backtrace(s
, tid
, &stacks_seen
, false, user_bt
, USER_FRAMES
- 1, 2);
395 ktrace_set_completion_handler(s
, ^(void) {
396 T_EXPECT_GE(stacks_seen
, 1U, "saw at least one stack");
397 ktrace_session_destroy(s
);
402 T_QUIET
; T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL
);
404 /* wait until the thread that will be backtraced is spinning */
405 dispatch_semaphore_wait(wait_for_spinning
, DISPATCH_TIME_FOREVER
);
407 T_ASSERT_POSIX_ZERO(ktrace_start(s
, dispatch_get_main_queue()), NULL
);
409 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, TEST_TIMEOUT_NS
),
410 dispatch_get_main_queue(), ^(void)
412 T_LOG("ending test after timeout");
419 /* TODO test kernel stacks in all modes */
420 /* TODO legacy PET mode backtracing */
421 /* TODO test deep stacks, further than 128 frames, make sure they are truncated */
422 /* TODO test constrained stacks */