1#ifdef T_NAMESPACE 2#undef T_NAMESPACE 3#endif 4#include <darwintest.h> 5#include <darwintest_utils.h> 6 7#include <kdd.h> 8#include <kern/kcdata.h> 9#include <kern/debug.h> 10#include <kern/block_hint.h> 11#include <mach/mach.h> 12#include <mach/mach_init.h> 13#include <mach/mach_traps.h> 14#include <mach/message.h> 15#include <mach/port.h> 16#include <mach/semaphore.h> 17#include <mach/task.h> 18#include <os/lock.h> 19#include <pthread.h> 20#include <signal.h> 21#include <sys/sysctl.h> 22#include <sys/stackshot.h> 23#include <sys/types.h> 24#include <stdlib.h> 25#include <unistd.h> 26#include <TargetConditionals.h> 27 28#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) 29#include <pcre.h> 30#endif 31 32 33T_GLOBAL_META( 34 T_META_NAMESPACE("xnu.scheduler"), 35 T_META_RADAR_COMPONENT_NAME("xnu"), 36 T_META_RADAR_COMPONENT_VERSION("stackshot"), 37 T_META_OWNER("jonathan_w_adams"), 38 T_META_ASROOT(true) 39); 40 41#include <Foundation/Foundation.h> 42 43#define SENDS_TO_BLOCK 6 44#define NUMRETRIES 5 45#define KRWLCK_STORES_EXCL_OWNER 0 46 47#define KMUTEX_SYSCTL_CHECK_EXISTS 0 48#define KMUTEX_SYSCTL_ACQUIRE_WAIT 1 49#define KMUTEX_SYSCTL_ACQUIRE_NOWAIT 2 50#define KMUTEX_SYSCTL_SIGNAL 3 51#define KMUTEX_SYSCTL_TEARDOWN 4 52 53#define KRWLCK_SYSCTL_CHECK_EXISTS 0 54#define KRWLCK_SYSCTL_RACQUIRE_NOWAIT 1 55#define KRWLCK_SYSCTL_RACQUIRE_WAIT 2 56#define KRWLCK_SYSCTL_WACQUIRE_NOWAIT 3 57#define KRWLCK_SYSCTL_WACQUIRE_WAIT 4 58#define KRWLCK_SYSCTL_SIGNAL 5 59#define KRWLCK_SYSCTL_TEARDOWN 6 60 61static const char kmutex_ctl[] = "debug.test_MutexOwnerCtl"; 62static const char krwlck_ctl[] = "debug.test_RWLockOwnerCtl"; 63 64static mach_port_t test_send_port = MACH_PORT_NULL; 65static mach_port_t test_recv_port = MACH_PORT_NULL; 66 67static void * 68take_stackshot(uint32_t extra_flags, uint64_t since_timestamp) 69{ 70 void * stackshot = NULL; 71 int ret = 0; 72 uint32_t stackshot_flags = STACKSHOT_SAVE_LOADINFO | 73 STACKSHOT_GET_GLOBAL_MEM_STATS | 74 STACKSHOT_SAVE_IMP_DONATION_PIDS | 75 STACKSHOT_KCDATA_FORMAT; 76 77 if (since_timestamp != 0) 78 stackshot_flags |= STACKSHOT_COLLECT_DELTA_SNAPSHOT; 79 80 stackshot_flags |= extra_flags; 81 82 stackshot = stackshot_config_create(); 83 T_QUIET; T_ASSERT_NOTNULL(stackshot, "Allocating stackshot config"); 84 85 ret = stackshot_config_set_flags(stackshot, stackshot_flags); 86 T_ASSERT_POSIX_ZERO(ret, "Setting flags on stackshot config"); 87 88 ret = stackshot_config_set_pid(stackshot, getpid()); 89 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Setting target pid on stackshot config"); 90 91 if (since_timestamp != 0) { 92 ret = stackshot_config_set_delta_timestamp(stackshot, since_timestamp); 93 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Setting prev snapshot time on stackshot config"); 94 } 95 96 for (int retries = NUMRETRIES; retries > 0; retries--) { 97 ret = stackshot_capture_with_config(stackshot); 98 T_QUIET; T_ASSERT_TRUE(ret == 0 || ret == EBUSY || ret == ETIMEDOUT, 99 "Attempting to take stackshot (error %d)...", ret); 100 if (retries == 0 && (ret == EBUSY || ret == ETIMEDOUT)) 101 T_ASSERT_FAIL("Failed to take stackshot after %d retries: got %d (%s)", NUMRETRIES, ret, strerror(ret)); 102 if (ret == 0) 103 break; 104 } 105 return stackshot; 106} 107 108static void 109save_stackshot(void *stackshot, const char *filename) 110{ 111 void *buf = stackshot_config_get_stackshot_buffer(stackshot); 112 T_QUIET; T_ASSERT_NOTNULL(buf, "buf"); 113 size_t size = stackshot_config_get_stackshot_size(stackshot); 114 FILE *f = fopen(filename, "w"); 115 T_QUIET; T_ASSERT_NOTNULL(f, "f"); 116 fwrite(buf, size, 1, f); 117 fclose(f); 118} 119 120static 121void check_python(void *stackshot, const char *func, const char *fmt, ...) 122{ 123 char sspath[MAXPATHLEN]; 124 strlcpy(sspath, func, sizeof(sspath)); 125 strlcat(sspath, ".kcdata", sizeof(sspath)); 126 T_QUIET; T_ASSERT_POSIX_ZERO(dt_resultfile(sspath, sizeof(sspath)), 127 "create result file path"); 128 129 save_stackshot(stackshot, sspath); 130 131#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) 132 va_list args; 133 va_start(args, fmt); 134 char *re_string = NULL; 135 vasprintf(&re_string, fmt, args); 136 va_end(args); 137 T_QUIET; T_ASSERT_NOTNULL(re_string, "vasprintf"); 138 139 const char *pcreErrorStr; 140 int pcreErrorOffset; 141 pcre *re = pcre_compile(re_string, 0, &pcreErrorStr, &pcreErrorOffset, NULL); 142 T_QUIET; T_ASSERT_NOTNULL(re, "pcre_compile"); 143 144 char *kcdata_invoke; 145 asprintf(&kcdata_invoke, "/usr/local/bin/kcdata --pretty %s", sspath); 146 T_QUIET; T_ASSERT_NOTNULL(kcdata_invoke, "asprintf"); 147 148 bool found = false; 149 FILE *p = popen(kcdata_invoke, "r"); 150 T_QUIET; T_ASSERT_NOTNULL(p, "popen"); 151 while (1) { 152 char *line = NULL; 153 size_t linecap = 0; 154 ssize_t linesize = getline(&line, &linecap, p); 155 if (linesize < 0) { 156 if (line) 157 free(line); 158 break; 159 } 160 int pcre_ret = pcre_exec(re, NULL, line, strlen(line), 0, 0, NULL, 0); 161 if (pcre_ret == 0){ 162 T_LOG("line: %s", line); 163 found = true; 164 } 165 free(line); 166 } 167 T_EXPECT_TRUE(found, "found a match to \"%s\" in output of \"%s\"", re_string, kcdata_invoke); 168 pclose(p); 169 pcre_free(re); 170 free(re_string); 171 free(kcdata_invoke); 172#endif 173} 174 175 176// waitinfo can be NULL, but len must be non-null and point to the length of the waitinfo array. 177// when the function returns, len will be set to the number of waitinfo structs found in the stackshot. 178static void 179find_blocking_info(void * stackshot, struct stackshot_thread_waitinfo *waitinfo, int *len) 180{ 181 void *buf = NULL; 182 uint32_t t = 0; 183 uint32_t buflen = 0; 184 NSError *error = nil; 185 NSMutableDictionary *parsed_container = nil; 186 NSArray *parsed_waitinfo = nil; 187 188 T_QUIET; T_ASSERT_NOTNULL(len, "Length pointer shouldn't be NULL"); 189 int oldlen = *len; 190 *len = 0; 191 192 buf = stackshot_config_get_stackshot_buffer(stackshot); 193 T_QUIET; T_ASSERT_NOTNULL(buf, "Getting stackshot buffer"); 194 buflen = stackshot_config_get_stackshot_size(stackshot); 195 196 kcdata_iter_t iter = kcdata_iter(buf, buflen); 197 198 T_QUIET; T_ASSERT_TRUE(kcdata_iter_type(iter) == KCDATA_BUFFER_BEGIN_STACKSHOT || 199 kcdata_iter_type(iter) == KCDATA_BUFFER_BEGIN_DELTA_STACKSHOT, 200 "Checking start of stackshot buffer"); 201 202 iter = kcdata_iter_next(iter); 203 KCDATA_ITER_FOREACH(iter) 204 { 205 t = kcdata_iter_type(iter); 206 207 if (t != KCDATA_TYPE_CONTAINER_BEGIN) { 208 continue; 209 } 210 211 if (kcdata_iter_container_type(iter) != STACKSHOT_KCCONTAINER_TASK) { 212 continue; 213 } 214 215 parsed_container = parseKCDataContainer(&iter, &error); 216 T_QUIET; T_ASSERT_TRUE(!error, "Error while parsing container: %d (%s)", 217 (int)error.code, [error.domain UTF8String]); 218 T_QUIET; T_ASSERT_TRUE(parsed_container && !error, "Parsing container"); 219 220 parsed_waitinfo = parsed_container[@"task_snapshots"][@"thread_waitinfo"]; 221 for (id elem in parsed_waitinfo) { 222 /* check to see that tid matches expected idle status */ 223 uint8_t type = [elem[@"wait_type"] unsignedCharValue]; 224 if (type != kThreadWaitNone) { 225 if (waitinfo && *len < oldlen) { 226 struct stackshot_thread_waitinfo *curr = &waitinfo[*len]; 227 curr->wait_type = type; 228 curr->owner = [elem[@"owner"] unsignedLongLongValue]; 229 curr->waiter = [elem[@"waiter"] unsignedLongLongValue]; 230 curr->context = [elem[@"context"] unsignedLongLongValue]; 231 } 232 (*len)++; 233 } 234 } 235 [parsed_container release]; 236 } 237} 238 239/* perform various actions with a mutex in kernel memory. note that, since we aren't allowed 240 * to go to user space while still holding a mutex, the lock-acquiring actions in this kernel 241 * sysctl will either lock and immediately release the lock, or lock and wait until a semaphore 242 * is signalled, then unlock. if called with CHECK_EXISTS, returns whether or not the sysctl 243 * exist in the kernel (to determine if we're running with CONFIG_XNUPOST defined). Else, 244 * returns 1. */ 245static int kmutex_action(int action) 246{ 247 int ret = 0; 248 if (action == KMUTEX_SYSCTL_CHECK_EXISTS) { 249 ret = sysctlbyname(krwlck_ctl, NULL, NULL, NULL, 0); 250 return !(ret == -1); 251 } 252 253 char * action_name = ""; 254 switch(action) { 255 case KMUTEX_SYSCTL_ACQUIRE_WAIT: 256 action_name = "lock (and wait)"; 257 break; 258 case KMUTEX_SYSCTL_ACQUIRE_NOWAIT: 259 action_name = "lock"; 260 break; 261 case KMUTEX_SYSCTL_SIGNAL: 262 action_name = "signal to holder of"; 263 break; 264 case KMUTEX_SYSCTL_TEARDOWN: 265 action_name = "tear down"; 266 break; 267 default: 268 T_ASSERT_FAIL("Somebody passed the wrong argument to kmutex_action: %d", action); 269 break; 270 } 271 272 ret = sysctlbyname(kmutex_ctl, NULL, NULL, &action, sizeof(int)); 273 T_ASSERT_POSIX_SUCCESS(ret, "sysctl: %s kernel mutex", action_name); 274 return 1; 275} 276 277static void 278sysctl_kmutex_test_match(uint64_t context) 279{ 280 int ret = 0; 281 unsigned long long unslid_kmutex_address = 0; 282 size_t addrsize = sizeof(unslid_kmutex_address); 283 284 ret = sysctlbyname(kmutex_ctl, &unslid_kmutex_address, &addrsize, NULL, 0); 285 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Getting unslid location of kernel mutex. Size is %llu", 286 (unsigned long long)addrsize); 287 T_EXPECT_EQ(context, unslid_kmutex_address, 288 "Context should match unslid location of mutex in kernel memory"); 289} 290 291/* We don't really care what goes into these messages, we're just sending something to a port. */ 292static void 293msg_send_helper(mach_port_t remote_port) 294{ 295 int ret; 296 mach_msg_header_t * msg = NULL; 297 298 ret = vm_allocate(mach_task_self(), 299 (vm_address_t *)&msg, 300 PAGE_SIZE, 301 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE); 302 303 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Allocating vm page %p", (void*)msg); 304 msg->msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0); 305 msg->msgh_size = PAGE_SIZE; 306 msg->msgh_remote_port = remote_port; 307 msg->msgh_local_port = MACH_PORT_NULL; 308 msg->msgh_voucher_port = MACH_PORT_NULL; 309 ret = mach_msg(msg, 310 MACH_SEND_MSG | MACH_MSG_OPTION_NONE, 311 PAGE_SIZE, 312 0, 313 MACH_PORT_NULL, 314 MACH_MSG_TIMEOUT_NONE, 315 MACH_PORT_NULL); 316 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Sending message to port %d", remote_port); 317 318 vm_deallocate(mach_task_self(), (vm_address_t)msg, PAGE_SIZE); 319 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Deallocating vm page %p", (void*)msg); 320} 321 322static void 323msg_recv_helper(mach_port_t local_port) 324{ 325 int ret = 0; 326 mach_msg_size_t size = 2*PAGE_SIZE; 327 mach_msg_header_t * msg = NULL; 328 ret = vm_allocate(mach_task_self(), 329 (vm_address_t *)&msg, 330 size, 331 VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE ); 332 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Allocating page %p for message", (void*)msg); 333 334 ret = mach_msg(msg, 335 MACH_RCV_MSG, 336 0, 337 size, 338 local_port, 339 MACH_MSG_TIMEOUT_NONE, 340 MACH_PORT_NULL); 341 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Received message on port %d", local_port); 342 ret = vm_deallocate(mach_task_self(), (vm_address_t)msg, PAGE_SIZE); 343 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Deallocating page %p", (void*)msg); 344} 345 346/* perform various actions with a rwlock in kernel memory. note that, since we aren't allowed 347 * to go to user space while still holding a rwlock, the lock-acquiring actions in this kernel 348 * sysctl will either lock and immediately release the lock, or lock and wait until a semaphore 349 * is signalled, then unlock. if called with CHECK_EXISTS, returns whether or not the sysctl 350 * exist in the kernel (to determine if we're running with CONFIG_XNUPOST defined). Else, 351 * returns 1. */ 352static int 353krwlck_action(int action) 354{ 355 int ret = 0; 356 if (action == KRWLCK_SYSCTL_CHECK_EXISTS) { 357 ret = sysctlbyname(krwlck_ctl, NULL, NULL, NULL, 0); 358 return !(ret == -1); 359 } 360 361 char * action_name = ""; 362 switch(action) { 363 case KRWLCK_SYSCTL_RACQUIRE_NOWAIT: 364 action_name = "shared lock"; 365 break; 366 case KRWLCK_SYSCTL_RACQUIRE_WAIT: 367 action_name = "shared lock (and wait)"; 368 break; 369 case KRWLCK_SYSCTL_WACQUIRE_NOWAIT: 370 action_name = "exclusive lock"; 371 break; 372 case KRWLCK_SYSCTL_WACQUIRE_WAIT: 373 action_name = "exclusive lock (and wait)"; 374 break; 375 case KRWLCK_SYSCTL_SIGNAL: 376 action_name = "signal to holder of"; 377 break; 378 case KRWLCK_SYSCTL_TEARDOWN: 379 action_name = "tear down"; 380 break; 381 default: 382 T_ASSERT_FAIL("Somebody passed the wrong argument to krwlck_action: %d", action); 383 break; 384 } 385 386 ret = sysctlbyname(krwlck_ctl, NULL, NULL, &action, sizeof(int)); 387 T_ASSERT_POSIX_SUCCESS(ret, "sysctl: %s kernel rwlock", action_name); 388 return 1; 389} 390 391static void 392sysctl_krwlck_test_match(uint64_t context) 393{ 394 int ret = 0; 395 unsigned long long unslid_krwlck_address = 0; 396 size_t addrsize = sizeof(unslid_krwlck_address); 397 398 ret = sysctlbyname(krwlck_ctl, &unslid_krwlck_address, &addrsize, NULL, 0); 399 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Getting unslid location of kernel rwlock"); 400 T_EXPECT_EQ(context, unslid_krwlck_address, "Context should match unslid location of rwlock in kernel memory"); 401} 402 403/* "Grabbing" threads: only purpose is to grab a sync primitive and hang. */ 404 405static void * 406kmutex_grabbing_thread(void * arg) 407{ 408 (void)arg; 409 kmutex_action(KMUTEX_SYSCTL_ACQUIRE_NOWAIT); 410 return NULL; 411} 412 413static void * 414kmutex_grab_and_wait_thread(void * arg) 415{ 416 (void)arg; 417 kmutex_action(KMUTEX_SYSCTL_ACQUIRE_WAIT); 418 return NULL; 419} 420 421static void * 422sem_grabbing_thread(void * arg) 423{ 424 semaphore_t *sem = (semaphore_t *)arg; 425 semaphore_wait(*sem); 426 return NULL; 427} 428 429static void * 430msg_blocking_thread(void * arg) 431{ 432 (void)arg; 433 msg_recv_helper(test_send_port); 434 435 for (int i = 0; i < SENDS_TO_BLOCK; i++) 436 msg_send_helper(test_recv_port); // will block on test_send_port until message is received 437 return NULL; 438} 439 440static void * 441ulock_blocking_thread(void * arg) 442{ 443 os_unfair_lock_t oul = (os_unfair_lock_t)arg; 444 os_unfair_lock_lock(oul); 445 os_unfair_lock_unlock(oul); 446 return NULL; 447} 448 449// acquires a kernel rwlock for writing, and then waits on a kernel semaphore. 450static void * 451krwlck_write_waiting_thread(void * arg) 452{ 453 (void)arg; 454 krwlck_action(KRWLCK_SYSCTL_WACQUIRE_WAIT); 455 return NULL; 456} 457 458// attempts to acquire a kernel rwlock for reading, and doesn't wait on a semaphore afterwards. 459static void * 460krwlck_read_grabbing_thread(void * arg) 461{ 462 (void)arg; 463 krwlck_action(KRWLCK_SYSCTL_RACQUIRE_NOWAIT); 464 return NULL; 465} 466 467static void * 468pthread_mutex_blocking_thread(void * arg) 469{ 470 pthread_mutex_t *mtx = (pthread_mutex_t *)arg; 471 pthread_mutex_lock(mtx); 472 pthread_mutex_unlock(mtx); 473 return NULL; 474} 475 476static void * 477pthread_rwlck_blocking_thread(void * arg) 478{ 479 pthread_rwlock_t *rwlck = (pthread_rwlock_t *)arg; 480 pthread_rwlock_rdlock(rwlck); 481 pthread_rwlock_unlock(rwlck); 482 return NULL; 483} 484 485static void * 486pthread_cond_blocking_thread(void * arg) 487{ 488 pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; 489 pthread_cond_t *cond = (pthread_cond_t *)arg; 490 pthread_cond_wait(cond, &mtx); 491 pthread_mutex_unlock(&mtx); 492 return NULL; 493} 494 495static void * 496waitpid_blocking_thread(void * arg) 497{ 498 pid_t pid = (pid_t)arg; 499 500 int ret = waitpid(pid, NULL, 0); 501 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Reaping child."); 502 return NULL; 503} 504 505/* 506 * Uses a debug sysctl to initialize a kernel mutex. 507 * 508 * The 'waiting' thread grabs this kernel mutex, and immediately waits on a kernel semaphore. 509 * The 'grabbing' thread just attempts to lock the kernel mutex. 510 * When the semaphore is signalled, the 'waiting' thread will unlock the kernel mutex, 511 * giving the opportunity for the 'grabbing' thread to lock it and then immediately unlock it. 512 * This allows us to create a situation in the kernel where we know a thread to be blocked 513 * on a kernel mutex. 514 */ 515static void 516test_kmutex_blocking(void) 517{ 518 int ret = 0; 519 int len = 2; 520 struct stackshot_thread_waitinfo waitinfo[2] = { { 0 }, { 0 } }; 521 uint64_t thread_id = 0; 522 pthread_t grabbing, waiting; 523 524 T_LOG("Starting %s", __FUNCTION__); 525 ret = pthread_create(&waiting, NULL, kmutex_grab_and_wait_thread, NULL); // thread will block until we signal it 526 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Spawning grab and wait thread"); 527 sleep(1); // give time for thread to block 528 ret = pthread_create(&grabbing, NULL, kmutex_grabbing_thread, NULL); // thread should immediately block 529 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Spawning waiting thread"); 530 sleep(3); // give (lots of) time for thread to give up spinning on lock 531 532 void * stackshot = take_stackshot(STACKSHOT_THREAD_WAITINFO, 0); 533 534 ret = pthread_threadid_np(waiting, &thread_id); // this is the thread that currently holds the kernel mutex 535 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Getting integer value of thread id"); 536 537 check_python(stackshot, __func__, "thread \\d+: semaphore port \\w+ with unknown owner"); 538 539 find_blocking_info(stackshot, (struct stackshot_thread_waitinfo *)&waitinfo, &len); 540 541 T_EXPECT_EQ(len, 2, "There should only be two blocking threads"); 542 for (int i = 0; i < len; i++) { 543 struct stackshot_thread_waitinfo *curr = &waitinfo[i]; 544 if (curr->wait_type == kThreadWaitSemaphore) 545 continue; 546 T_EXPECT_EQ(curr->wait_type, kThreadWaitKernelMutex, "Wait type should match expected KernelMutex value"); 547 T_EXPECT_EQ(curr->owner, thread_id, "Thread ID of blocking thread should match 'owner' field in stackshot"); 548 sysctl_kmutex_test_match(curr->context); 549 550 check_python(stackshot, __func__, "thread \\d+: kernel mutex %llx owned by thread %lld", curr->context, thread_id); 551 } 552 553 kmutex_action(KMUTEX_SYSCTL_SIGNAL); // waiting thread should now unblock. 554 ret = pthread_join(waiting, NULL); 555 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Joining on waiting thread"); 556 ret = pthread_join(grabbing, NULL); 557 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Joining on grabber thread"); 558 kmutex_action(KMUTEX_SYSCTL_TEARDOWN); 559 stackshot_config_dealloc(stackshot); 560} 561 562/* Initialize a userspace semaphore, and spawn a thread to block on it. */ 563static void 564test_semaphore_blocking(void) 565{ 566 int ret = 0; 567 semaphore_t sem; 568 struct stackshot_thread_waitinfo waitinfo = { 0 }; 569 int len = 1; 570 uint64_t pid = 0; 571 572 T_LOG("Starting %s", __FUNCTION__); 573 ret = semaphore_create(mach_task_self(), &sem, SYNC_POLICY_FIFO, 0); 574 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Creating semaphore"); 575 pthread_t tid; 576 ret = pthread_create(&tid, NULL, sem_grabbing_thread, (void*)&sem); // thread should immediately block 577 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Creating semaphore grabbing thread"); 578 579 sleep(1); // give time for thread to block 580 581 void * stackshot = take_stackshot(STACKSHOT_THREAD_WAITINFO, 0); 582 find_blocking_info(stackshot, (struct stackshot_thread_waitinfo *)&waitinfo, &len); 583 T_EXPECT_EQ(len, 1, "Only one blocking thread should exist"); 584 T_EXPECT_EQ(waitinfo.wait_type, kThreadWaitSemaphore, "Wait type should match expected Semaphore value"); 585 586 pid = (uint64_t)getpid(); 587 T_EXPECT_EQ(waitinfo.owner, pid, "Owner value should match process ID"); 588 589 check_python(stackshot, __func__, "thread \\d+: semaphore port \\w+ owned by pid %d", (int)pid); 590 591 ret = semaphore_signal(sem); 592 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Signalling semaphore"); 593 ret = pthread_join(tid, NULL); 594 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Joining on grabber thread"); 595 ret = semaphore_destroy(mach_task_self(), sem); 596 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Destroying semaphore"); 597 stackshot_config_dealloc(stackshot); 598} 599 600/* Spawn a process to send a message to, and block while both sending and receiving in different contexts. */ 601static void 602test_mach_msg_blocking(void) 603{ 604 int ret = 0; 605 pthread_t tid; 606 void *stackshot = NULL; 607 struct stackshot_thread_waitinfo waitinfo = { 0 }; 608 int len = 1; 609 610 T_LOG("Starting %s", __FUNCTION__); 611 ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &test_send_port); 612 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Allocating send port"); 613 ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &test_recv_port); 614 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Allocating recv port"); 615 ret = mach_port_insert_right(mach_task_self(), test_send_port, test_send_port, MACH_MSG_TYPE_MAKE_SEND); 616 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Getting send right to send port"); 617 ret = mach_port_insert_right(mach_task_self(), test_recv_port, test_recv_port, MACH_MSG_TYPE_MAKE_SEND); 618 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "Getting send right to recv port"); 619 620 ret = pthread_create(&tid, NULL, msg_blocking_thread, (void*)&test_send_port); // thread should block on test_recv_port soon 621 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Creating message blocking thread"); 622 623 sleep(1); // give time for thread to block 624 stackshot = take_stackshot(STACKSHOT_THREAD_WAITINFO, 0); 625 find_blocking_info(stackshot, (struct stackshot_thread_waitinfo *)&waitinfo, &len); 626 627 T_EXPECT_EQ(len, 1, "Only one blocking thread should exist"); 628 T_EXPECT_EQ(waitinfo.wait_type, kThreadWaitPortReceive, "Wait type should match expected PortReceive value"); 629 630 check_python(stackshot, __func__, "thread \\d+: mach_msg receive on port \\w+ name %llx", (long long)test_send_port); 631 632 stackshot_config_dealloc(stackshot); 633 634 msg_send_helper(test_send_port); // ping! msg_blocking_thread will now try to test_send_port us stuff, and block until we receive. 635 636 sleep(1); // give time for thread to block 637 stackshot = take_stackshot(STACKSHOT_THREAD_WAITINFO, 0); 638 find_blocking_info(stackshot, (struct stackshot_thread_waitinfo *)&waitinfo, &len); 639 T_EXPECT_EQ(len, 1, "Only one blocking thread should exist"); 640 T_EXPECT_EQ(waitinfo.wait_type, kThreadWaitPortSend, "Wait type should match expected PortSend value"); 641 642 check_python(stackshot, __func__, "thread \\d+: mach_msg send on port \\w+ owned by pid %d", (int)getpid()); 643 644 stackshot_config_dealloc(stackshot); 645 646 msg_recv_helper(test_recv_port); // thread should block until we receive one of its messages 647 ret = pthread_join(tid, NULL); 648 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Joining on blocking thread"); 649} 650 651static void 652test_ulock_blocking(void) 653{ 654 int ret = 0; 655 void *stackshot = NULL; 656 uint64_t thread_id = 0; 657 pthread_t tid; 658 struct os_unfair_lock_s ouls = OS_UNFAIR_LOCK_INIT; 659 os_unfair_lock_t oul = &ouls; 660 struct stackshot_thread_waitinfo waitinfo = { 0 }; 661 int len = 1; 662 663 T_LOG("Starting %s", __FUNCTION__); 664 os_unfair_lock_lock(oul); 665 ret = pthread_create(&tid, NULL, ulock_blocking_thread, (void*)oul); 666 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Creating ulock blocking thread"); 667 sleep(3); // give time for thread to spawn, fall back to kernel for contention, and block 668 669 stackshot = take_stackshot(STACKSHOT_THREAD_WAITINFO, 0); 670 671 find_blocking_info(stackshot, (struct stackshot_thread_waitinfo *)&waitinfo, &len); 672 T_EXPECT_EQ(len, 1, "Only one blocking thread should exist"); 673 T_EXPECT_EQ(waitinfo.wait_type, kThreadWaitUserLock, "Wait type should match expected UserLock value"); 674 675 os_unfair_lock_unlock(oul); 676 ret = pthread_join(tid, NULL); // wait for thread to unblock and exit 677 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Joining on blocking thread"); 678 679 ret = pthread_threadid_np(NULL, &thread_id); // this thread is the "owner" of the ulock 680 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Getting integer value of thread id"); 681 T_EXPECT_EQ(waitinfo.owner, thread_id, "Thread ID of blocking thread should match 'owner' field in stackshot"); 682 683 check_python(stackshot, __func__, "thread \\d+: unfair lock \\w+ owned by thread %lld", thread_id); 684 stackshot_config_dealloc(stackshot); 685 return; 686} 687 688static void 689test_krwlock_blocking(void) 690{ 691 int ret = 0; 692 void *stackshot = NULL; 693 uint64_t thread_id = 0; 694 pthread_t waiting, grabbing; 695 int len = 2; 696 struct stackshot_thread_waitinfo waitinfo[2] = { { 0 }, { 0 } }; 697 698 T_LOG("Starting %s", __FUNCTION__); 699 // this thread should spawn, acquire a kernel rwlock for write, and then wait on a semaphore 700 ret = pthread_create(&waiting, NULL, krwlck_write_waiting_thread, NULL); 701 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Creating krwlck write waiting thread"); 702 sleep(1); // give time for thread to block 703 // this thread should spawn and try to acquire the same kernel rwlock for read, but block 704 ret = pthread_create(&grabbing, NULL, krwlck_read_grabbing_thread, NULL); 705 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Creating krwlck read grabbing thread"); 706 sleep(1); // give time for thread to block 707 708 stackshot = take_stackshot(STACKSHOT_THREAD_WAITINFO, 0); 709 710 check_python(stackshot, __func__, "thread \\d+: semaphore port \\w+ with unknown owner"); 711 712 find_blocking_info(stackshot, (struct stackshot_thread_waitinfo *)&waitinfo, &len); 713 714 T_EXPECT_EQ(len, 2, "There should only be two blocking threads"); 715 for (int i = 0; i < len; i++) { 716 struct stackshot_thread_waitinfo *curr = &waitinfo[i]; 717 if (curr->wait_type == kThreadWaitSemaphore) 718 continue; 719 T_EXPECT_EQ(curr->wait_type, kThreadWaitKernelRWLockRead, "Wait type should match expected KRWLockRead value"); 720 sysctl_krwlck_test_match(curr->context); 721 722 check_python(stackshot, __func__, "thread \\d+: krwlock %llx for reading", curr->context); 723 724#if KRWLCK_STORES_EXCL_OWNER /* A future planned enhancement */ 725 ret = pthread_threadid_np(waiting, &thread_id); // this is the thread that currently holds the kernel mutex 726 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Getting integer value of thread id"); 727 T_EXPECT_EQ(curr->owner, thread_id, "Thread ID of blocking thread should match 'owner' field in stackshot"); 728#else 729 (void)thread_id; // suppress compiler warning about unused variable 730#endif /* RWLCK_STORES_EXCL_OWNER */ 731 } 732 733 krwlck_action(KRWLCK_SYSCTL_SIGNAL); // pthread should now unblock & finish 734 ret = pthread_join(waiting, NULL); 735 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Joining on waiting thread"); 736 ret = pthread_join(grabbing, NULL); 737 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Joining on grabbing thread"); 738 krwlck_action(KRWLCK_SYSCTL_TEARDOWN); 739 stackshot_config_dealloc(stackshot); 740} 741 742 743static void 744test_pthread_mutex_blocking(void) 745{ 746 int ret = 0; 747 void *stackshot = NULL; 748 uint64_t thread_id = 0; 749 pthread_t tid; 750 struct stackshot_thread_waitinfo waitinfo = { 0 }; 751 pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; 752 int len = 1; 753 754 T_LOG("Starting %s", __FUNCTION__); 755 756 ret = pthread_threadid_np(NULL, &thread_id); // this thread is the "owner" of the mutex 757 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Getting integer value of thread id"); 758 759 pthread_mutex_lock(&mtx); 760 ret = pthread_create(&tid, NULL, pthread_mutex_blocking_thread, (void*)&mtx); 761 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Creating pthread mutex blocking thread"); 762 sleep(2); // give time for thread to block 763 764 stackshot = take_stackshot(STACKSHOT_THREAD_WAITINFO, 0); 765 766 check_python(stackshot, __func__, "thread \\d+: pthread mutex %llx owned by thread %lld", &mtx, thread_id); 767 768 find_blocking_info(stackshot, (struct stackshot_thread_waitinfo *)&waitinfo, &len); 769 T_EXPECT_EQ(len, 1, "Only one blocking thread should exist"); 770 T_EXPECT_EQ(waitinfo.wait_type, kThreadWaitPThreadMutex, 771 "Wait type should match expected PThreadMutex value"); 772 stackshot_config_dealloc(stackshot); 773 774 pthread_mutex_unlock(&mtx); 775 ret = pthread_join(tid, NULL); // wait for thread to unblock and exit 776 777 778 T_EXPECT_EQ(waitinfo.owner, thread_id, 779 "Thread ID of blocking thread should match 'owner' field in stackshot"); 780 T_EXPECT_EQ(waitinfo.context, (uint64_t)&mtx, 781 "Userspace address of mutex should match 'context' field in stackshot"); 782} 783 784static void 785test_pthread_rwlck_blocking(void) 786{ 787 int ret = 0; 788 void *stackshot = NULL; 789 pthread_t tid; 790 struct stackshot_thread_waitinfo waitinfo = { 0 }; 791 pthread_rwlock_t rwlck = PTHREAD_RWLOCK_INITIALIZER; 792 int len = 1; 793 794 T_LOG("Starting %s", __FUNCTION__); 795 pthread_rwlock_wrlock(&rwlck); 796 ret = pthread_create(&tid, NULL, pthread_rwlck_blocking_thread, (void*)&rwlck); 797 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Creating pthread rwlck blocking thread"); 798 sleep(2); 799 800 stackshot = take_stackshot(STACKSHOT_THREAD_WAITINFO, 0); 801 802 check_python(stackshot, __func__, "thread \\d+: pthread rwlock %llx for reading", (long long)&rwlck); 803 804 find_blocking_info(stackshot, (struct stackshot_thread_waitinfo *)&waitinfo, &len); 805 T_EXPECT_EQ(len, 1, "Only one blocking thread should exist"); 806 T_EXPECT_EQ(waitinfo.wait_type, kThreadWaitPThreadRWLockRead, 807 "Wait type should match expected PThreadRWLockRead value"); 808 stackshot_config_dealloc(stackshot); 809 810 pthread_rwlock_unlock(&rwlck); 811 ret = pthread_join(tid, NULL); // wait for thread to unblock and exit 812 T_EXPECT_EQ(waitinfo.context, (uint64_t)&rwlck, 813 "Userspace address of rwlck should match 'context' field in stackshot"); 814} 815 816 817 818static void 819test_pthread_cond_blocking(void) 820{ 821 int ret = 0; 822 void *stackshot = NULL; 823 pthread_t tid; 824 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 825 struct stackshot_thread_waitinfo waitinfo = { 0 }; 826 int len = 1; 827 828 T_LOG("Starting %s", __FUNCTION__); 829 ret = pthread_create(&tid, NULL, pthread_cond_blocking_thread, (void*)&cond); 830 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Creating pthread condvar blocking thread"); 831 sleep(2); 832 833 stackshot = take_stackshot(STACKSHOT_THREAD_WAITINFO, 0); 834 835 check_python(stackshot, __func__, "thread \\d+: pthread condvar %llx", (long long)&cond); 836 837 find_blocking_info(stackshot, (struct stackshot_thread_waitinfo *)&waitinfo, &len); 838 T_EXPECT_EQ(len, 1, "Only one blocking thread should exist"); 839 T_EXPECT_EQ(waitinfo.wait_type, kThreadWaitPThreadCondVar, 840 "Wait type should match expected PThreadCondVar value"); 841 stackshot_config_dealloc(stackshot); 842 843 pthread_cond_signal(&cond); 844 ret = pthread_join(tid, NULL); // wait for thread to unblock and exit 845 T_EXPECT_EQ(waitinfo.context, (uint64_t)&cond, 846 "Userspace address of condvar should match 'context' field in stackshot"); 847 pthread_cond_destroy(&cond); 848} 849 850static void 851test_waitpid_blocking(void) 852{ 853 int ret = 0; 854 pid_t pid = 0; 855 void *stackshot = NULL; 856 struct stackshot_thread_waitinfo waitinfo = { 0 }; 857 int len = 1; 858 pthread_t tid; 859 860 T_LOG("Starting %s", __FUNCTION__); 861 if ((pid = fork()) == 0) { 862 pause(); 863 } else { 864 T_ASSERT_POSIX_SUCCESS(ret, "Running in parent. Child pid is %d", pid); 865 866 sleep(1); // allow enough time for child to run & sleep 867 ret = pthread_create(&tid, NULL, waitpid_blocking_thread, (void*)pid); 868 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Creating waitpid blocking thread"); 869 870 sleep(1); // allow enough time for reaping thread to waitpid & block 871 stackshot = take_stackshot(STACKSHOT_THREAD_WAITINFO, 0); 872 find_blocking_info(stackshot, (struct stackshot_thread_waitinfo *)&waitinfo, &len); 873 T_EXPECT_EQ(len, 1, "Only one blocking thread should exist"); 874 T_EXPECT_EQ(waitinfo.wait_type, kThreadWaitOnProcess, 875 "Wait type should match expected WaitOnProcess value"); 876 877 check_python(stackshot, __func__, "thread \\d+: waitpid, for pid %d", (int)pid); 878 879 stackshot_config_dealloc(stackshot); 880 T_EXPECT_EQ(waitinfo.owner, pid, 881 "Process ID of blocking process should match 'owner' field in stackshot"); 882 883 ret = kill(pid, SIGUSR1); // wake up child so waitpid thread can reap it & exit 884 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Send SIGUSR1 to child process"); 885 ret = pthread_join(tid, NULL); 886 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Join on waitpid thread"); 887 } 888} 889 890/* 891 * 892 * Test declarations 893 * 894 */ 895 896T_DECL(stackshot_block_owner_klocks, "tests stackshot block owner for kernel locks") { 897 /* check to see if kmutex sysctl exists before running kmutex test */ 898 if (kmutex_action(KMUTEX_SYSCTL_CHECK_EXISTS)) 899 test_kmutex_blocking(); 900 /* check to see if krwlck sysctl exists before running krwlck test */ 901 if (krwlck_action(KRWLCK_SYSCTL_CHECK_EXISTS)) 902 test_krwlock_blocking(); 903 test_ulock_blocking(); 904} 905 906T_DECL(stackshot_block_owner_pthread_mutex, "tests stackshot block owner: pthread mutex") { 907 test_pthread_mutex_blocking(); 908} 909 910T_DECL(stackshot_block_owner_pthread_rwlck, "tests stackshot block owner: pthread rw locks") { 911 test_pthread_rwlck_blocking(); 912} 913 914T_DECL(stackshot_block_owner_pthread_condvar, "tests stackshot block owner: pthread condvar") { 915 test_pthread_cond_blocking(); 916} 917 918T_DECL(stackshot_block_owner_semaphore, "tests stackshot block owner: semaphore") { 919 test_semaphore_blocking(); 920} 921 922T_DECL(stackshot_block_owner_mach_msg, "tests stackshot block owner: mach messaging") { 923 test_mach_msg_blocking(); 924} 925 926T_DECL(stackshot_block_owner_waitpid, "tests stackshot block owner: waitpid") { 927 test_waitpid_blocking(); 928} 929