X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/eee3565979933af707c711411001ba11fe406a3c..813fb2f63a553c957e917ede5f119b021d6ce391:/tools/tests/darwintests/kperf_backtracing.c diff --git a/tools/tests/darwintests/kperf_backtracing.c b/tools/tests/darwintests/kperf_backtracing.c index 37773a57f..f48931af2 100644 --- a/tools/tests/darwintests/kperf_backtracing.c +++ b/tools/tests/darwintests/kperf_backtracing.c @@ -43,11 +43,18 @@ expect_frame(const char **bt, unsigned int bt_len, CSSymbolRef symbol, } /* - * Expect to see user and kernel stacks with a known signature. + * Expect to see either user or kernel stacks on thread with ID `tid` with a + * signature of `bt` of length `bt_len`. Updates `stacks_seen` when stack + * is found. + * + * Can also allow stacks to be larger than the signature -- additional frames + * near the current PC will be ignored. This allows stacks to potentially be + * in the middle of a signalling system call (which signals that it is safe to + * start sampling). */ static void expect_backtrace(ktrace_session_t s, uint64_t tid, unsigned int *stacks_seen, - bool kern, const char **bt, unsigned int bt_len) + bool kern, const char **bt, unsigned int bt_len, unsigned int allow_larger_by) { CSSymbolicatorRef symb; uint32_t hdr_debugid; @@ -55,6 +62,7 @@ expect_backtrace(ktrace_session_t s, uint64_t tid, unsigned int *stacks_seen, __block unsigned int stacks = 0; __block unsigned int frames = 0; __block unsigned int hdr_frames = 0; + __block unsigned int allow_larger = allow_larger_by; if (kern) { static CSSymbolicatorRef kern_symb; @@ -88,6 +96,7 @@ expect_backtrace(ktrace_session_t s, uint64_t tid, unsigned int *stacks_seen, return; } + T_LOG("found stack from thread %#lx", tp->threadid); stacks++; if (!(tp->arg1 & 1)) { T_FAIL("invalid %s stack on thread %#lx", kern ? "kernel" : "user", @@ -99,8 +108,14 @@ expect_backtrace(ktrace_session_t s, uint64_t tid, unsigned int *stacks_seen, /* ignore extra link register or value pointed to by stack pointer */ hdr_frames -= 1; - T_QUIET; T_EXPECT_EQ(hdr_frames, bt_len, + T_QUIET; T_EXPECT_GE(hdr_frames, bt_len, "number of frames in header"); + T_QUIET; T_EXPECT_LE(hdr_frames, bt_len + allow_larger, + "number of frames in header"); + if (hdr_frames > bt_len && allow_larger > 0) { + allow_larger = hdr_frames - bt_len; + hdr_frames = bt_len; + } T_LOG("%s stack seen", kern ? "kernel" : "user"); frames = 0; @@ -111,7 +126,15 @@ expect_backtrace(ktrace_session_t s, uint64_t tid, unsigned int *stacks_seen, return; } - for (int i = 0; i < 4 && frames < hdr_frames; i++, frames++) { + int i = 0; + + if (frames == 0 && hdr_frames > bt_len) { + /* skip frames near the PC */ + i = (int)allow_larger; + allow_larger -= 4; + } + + for (; i < 4 && frames < hdr_frames; i++, frames++) { unsigned long addr = (&tp->arg1)[i]; CSSymbolRef symbol = CSSymbolicatorGetSymbolWithAddressAtTime( symb, addr, kCSNow); @@ -137,15 +160,16 @@ expect_backtrace(ktrace_session_t s, uint64_t tid, unsigned int *stacks_seen, * backtrace). */ static int __attribute__((noinline,not_tail_called)) -recurse_a(bool spin, unsigned int frames); +recurse_a(dispatch_semaphore_t spinning, unsigned int frames); static int __attribute__((noinline,not_tail_called)) -recurse_b(bool spin, unsigned int frames); +recurse_b(dispatch_semaphore_t spinning, unsigned int frames); static int __attribute__((noinline,not_tail_called)) -recurse_a(bool spin, unsigned int frames) +recurse_a(dispatch_semaphore_t spinning, unsigned int frames) { if (frames == 0) { - if (spin) { + if (spinning) { + dispatch_semaphore_signal(spinning); for (;;); } else { kdebug_trace(TRIGGERING_DEBUGID, 0, 0, 0, 0); @@ -153,14 +177,15 @@ recurse_a(bool spin, unsigned int frames) } } - return recurse_b(spin, frames - 1) + 1; + return recurse_b(spinning, frames - 1) + 1; } static int __attribute__((noinline,not_tail_called)) -recurse_b(bool spin, unsigned int frames) +recurse_b(dispatch_semaphore_t spinning, unsigned int frames) { if (frames == 0) { - if (spin) { + if (spinning) { + dispatch_semaphore_signal(spinning); for (;;); } else { kdebug_trace(TRIGGERING_DEBUGID, 0, 0, 0, 0); @@ -168,7 +193,7 @@ recurse_b(bool spin, unsigned int frames) } } - return recurse_a(spin, frames - 1) + 1; + return recurse_a(spinning, frames - 1) + 1; } #define USER_FRAMES (12) @@ -204,17 +229,29 @@ static const char *kernel_bt[KERNEL_FRAMES] = { #error "architecture unsupported" #endif /* defined(__arm__) */ -static dispatch_once_t backtrace_start_once; -static dispatch_semaphore_t backtrace_start; +static dispatch_once_t backtrace_once; +static dispatch_semaphore_t backtrace_started; +static dispatch_semaphore_t backtrace_go; +/* + * Another thread to run with a known backtrace. + * + * Take a semaphore that will be signalled when the thread is spinning at the + * correct frame. If the semaphore is NULL, don't spin and instead make a + * kdebug_trace system call, which can trigger a deterministic backtrace itself. + */ static void * backtrace_thread(void *arg) { - bool spin; + dispatch_semaphore_t notify_spinning; unsigned int calls; - spin = (bool)arg; - dispatch_semaphore_wait(backtrace_start, DISPATCH_TIME_FOREVER); + notify_spinning = (dispatch_semaphore_t)arg; + + dispatch_semaphore_signal(backtrace_started); + if (!notify_spinning) { + dispatch_semaphore_wait(backtrace_go, DISPATCH_TIME_FOREVER); + } /* * backtrace_thread, recurse_a, recurse_b, ...[, __kdebug_trace64] @@ -222,7 +259,7 @@ backtrace_thread(void *arg) * Always make one less call for this frame (backtrace_thread). */ calls = USER_FRAMES - RECURSE_START_OFFSET - 1 /* backtrace_thread */; - if (spin) { + if (notify_spinning) { /* * Spinning doesn't end up calling __kdebug_trace64. */ @@ -231,23 +268,36 @@ backtrace_thread(void *arg) T_LOG("backtrace thread calling into %d frames (already at %d frames)", calls, RECURSE_START_OFFSET); - (void)recurse_a(spin, calls); + (void)recurse_a(notify_spinning, calls); return NULL; } static uint64_t -create_backtrace_thread(bool spin) +create_backtrace_thread(dispatch_semaphore_t notify_spinning) { - pthread_t thread; + pthread_t thread = NULL; uint64_t tid; - dispatch_once(&backtrace_start_once, ^(void) { - backtrace_start = dispatch_semaphore_create(0); + dispatch_once(&backtrace_once, ^{ + backtrace_started = dispatch_semaphore_create(0); + T_QUIET; T_ASSERT_NOTNULL(backtrace_started, NULL); + + if (!notify_spinning) { + backtrace_go = dispatch_semaphore_create(0); + T_QUIET; T_ASSERT_NOTNULL(backtrace_go, NULL); + } }); T_QUIET; T_ASSERT_POSIX_ZERO(pthread_create(&thread, NULL, backtrace_thread, - (void *)spin), NULL); + (void *)notify_spinning), NULL); + T_QUIET; T_ASSERT_NOTNULL(thread, "backtrace thread created"); + dispatch_semaphore_wait(backtrace_started, DISPATCH_TIME_FOREVER); + T_QUIET; T_ASSERT_POSIX_ZERO(pthread_threadid_np(thread, &tid), NULL); + T_QUIET; T_ASSERT_NE(tid, UINT64_C(0), + "backtrace thread created does not have ID 0"); + + T_LOG("starting thread with ID 0x%" PRIx64, tid); return tid; } @@ -255,16 +305,16 @@ create_backtrace_thread(bool spin) static void start_backtrace_thread(void) { - T_QUIET; T_ASSERT_NOTNULL(backtrace_start, + T_QUIET; T_ASSERT_NOTNULL(backtrace_go, "thread to backtrace created before starting it"); - dispatch_semaphore_signal(backtrace_start); + dispatch_semaphore_signal(backtrace_go); } #define TEST_TIMEOUT_NS (5 * NSEC_PER_SEC) T_DECL(kdebug_trigger_backtraces, "test that backtraces from kdebug trigger are correct", - T_META_ASROOT(YES)) + T_META_ASROOT(true)) { static unsigned int stacks_seen = 0; ktrace_session_t s; @@ -276,9 +326,9 @@ T_DECL(kdebug_trigger_backtraces, T_ASSERT_POSIX_ZERO(ktrace_filter_pid(s, getpid()), NULL); - tid = create_backtrace_thread(false); - expect_backtrace(s, tid, &stacks_seen, false, user_bt, USER_FRAMES); - expect_backtrace(s, tid, &stacks_seen, true, kernel_bt, KERNEL_FRAMES); + tid = create_backtrace_thread(NULL); + expect_backtrace(s, tid, &stacks_seen, false, user_bt, USER_FRAMES, 0); + expect_backtrace(s, tid, &stacks_seen, true, kernel_bt, KERNEL_FRAMES, 0); /* * The triggering event must be traced (and thus registered with libktrace) @@ -315,6 +365,7 @@ T_DECL(kdebug_trigger_backtraces, dispatch_after(dispatch_time(DISPATCH_TIME_NOW, TEST_TIMEOUT_NS), dispatch_get_main_queue(), ^(void) { + T_LOG("ending test after timeout"); ktrace_end(s, 0); }); @@ -323,11 +374,12 @@ T_DECL(kdebug_trigger_backtraces, T_DECL(user_backtraces_timer, "test that user backtraces on a timer are correct", - T_META_ASROOT(YES)) + T_META_ASROOT(true)) { static unsigned int stacks_seen = 0; ktrace_session_t s; uint64_t tid; + dispatch_semaphore_t wait_for_spinning = dispatch_semaphore_create(0); s = ktrace_session_create(); T_QUIET; T_ASSERT_NOTNULL(s, "ktrace_session_create"); @@ -336,9 +388,9 @@ T_DECL(user_backtraces_timer, configure_kperf_stacks_timer(getpid(), 10); - tid = create_backtrace_thread(true); - /* not calling kdebug_trace(2) on the last frame */ - expect_backtrace(s, tid, &stacks_seen, false, user_bt, USER_FRAMES - 1); + tid = create_backtrace_thread(wait_for_spinning); + /* potentially calling dispatch function and system call */ + expect_backtrace(s, tid, &stacks_seen, false, user_bt, USER_FRAMES - 1, 2); ktrace_set_completion_handler(s, ^(void) { T_EXPECT_GE(stacks_seen, 1U, "saw at least one stack"); @@ -349,13 +401,15 @@ T_DECL(user_backtraces_timer, T_QUIET; T_ASSERT_POSIX_SUCCESS(kperf_sample_set(1), NULL); - T_ASSERT_POSIX_ZERO(ktrace_start(s, dispatch_get_main_queue()), NULL); + /* wait until the thread that will be backtraced is spinning */ + dispatch_semaphore_wait(wait_for_spinning, DISPATCH_TIME_FOREVER); - start_backtrace_thread(); + T_ASSERT_POSIX_ZERO(ktrace_start(s, dispatch_get_main_queue()), NULL); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, TEST_TIMEOUT_NS), dispatch_get_main_queue(), ^(void) { + T_LOG("ending test after timeout"); ktrace_end(s, 0); }); @@ -363,6 +417,6 @@ T_DECL(user_backtraces_timer, } /* TODO test kernel stacks in all modes */ -/* TODO PET mode backtracing */ +/* TODO legacy PET mode backtracing */ /* TODO test deep stacks, further than 128 frames, make sure they are truncated */ /* TODO test constrained stacks */