1*e3723e1fSApple OSS Distributions#include <darwintest.h> 2*e3723e1fSApple OSS Distributions#include <darwintest_utils.h> 3*e3723e1fSApple OSS Distributions#include <sys/kern_memorystatus.h> 4*e3723e1fSApple OSS Distributions#include <kern/debug.h> 5*e3723e1fSApple OSS Distributions#include <mach-o/dyld.h> 6*e3723e1fSApple OSS Distributions#include <sys/stackshot.h> 7*e3723e1fSApple OSS Distributions#include <kdd.h> 8*e3723e1fSApple OSS Distributions#include <signal.h> 9*e3723e1fSApple OSS Distributions#include "test_utils.h" 10*e3723e1fSApple OSS Distributions 11*e3723e1fSApple OSS Distributions#define RECURSIONS 25 12*e3723e1fSApple OSS Distributions#define FIRST_RECURSIVE_FRAME 3 13*e3723e1fSApple OSS Distributions 14*e3723e1fSApple OSS DistributionsT_GLOBAL_META( 15*e3723e1fSApple OSS Distributions T_META_NAMESPACE("xnu.stackshot.accuracy"), 16*e3723e1fSApple OSS Distributions T_META_RADAR_COMPONENT_NAME("xnu"), 17*e3723e1fSApple OSS Distributions T_META_RADAR_COMPONENT_VERSION("stackshot"), 18*e3723e1fSApple OSS Distributions T_META_OWNER("jonathan_w_adams"), 19*e3723e1fSApple OSS Distributions T_META_CHECK_LEAKS(false), 20*e3723e1fSApple OSS Distributions T_META_ASROOT(true), 21*e3723e1fSApple OSS Distributions XNU_T_META_SOC_SPECIFIC 22*e3723e1fSApple OSS Distributions ); 23*e3723e1fSApple OSS Distributions 24*e3723e1fSApple OSS Distributions 25*e3723e1fSApple OSS Distributionsvoid child_init(void); 26*e3723e1fSApple OSS Distributionsvoid parent_helper_singleproc(int); 27*e3723e1fSApple OSS Distributions 28*e3723e1fSApple OSS Distributions#define CHECK_FOR_FAULT_STATS (1 << 0) 29*e3723e1fSApple OSS Distributions#define WRITE_STACKSHOT_BUFFER_TO_TMP (1 << 1) 30*e3723e1fSApple OSS Distributions#define CHECK_FOR_KERNEL_THREADS (1 << 2) 31*e3723e1fSApple OSS Distributionsint check_stackshot(void *, int); 32*e3723e1fSApple OSS Distributions 33*e3723e1fSApple OSS Distributions/* used for WRITE_STACKSHOT_BUFFER_TO_TMP */ 34*e3723e1fSApple OSS Distributionsstatic char const *current_scenario_name; 35*e3723e1fSApple OSS Distributionsstatic pid_t child_pid; 36*e3723e1fSApple OSS Distributions 37*e3723e1fSApple OSS Distributions/* helpers */ 38*e3723e1fSApple OSS Distributions 39*e3723e1fSApple OSS Distributionsstatic void __attribute__((noinline)) 40*e3723e1fSApple OSS Distributionschild_recurse(int r, int spin, void (^cb)(void)) 41*e3723e1fSApple OSS Distributions{ 42*e3723e1fSApple OSS Distributions if (r > 0) { 43*e3723e1fSApple OSS Distributions child_recurse(r - 1, spin, cb); 44*e3723e1fSApple OSS Distributions } 45*e3723e1fSApple OSS Distributions 46*e3723e1fSApple OSS Distributions cb(); 47*e3723e1fSApple OSS Distributions 48*e3723e1fSApple OSS Distributions /* wait forever */ 49*e3723e1fSApple OSS Distributions if (spin == 0) { 50*e3723e1fSApple OSS Distributions sleep(100000); 51*e3723e1fSApple OSS Distributions } else if (spin == 2) { 52*e3723e1fSApple OSS Distributions int v = 1; 53*e3723e1fSApple OSS Distributions /* ssh won't let the session die if we still have file handles open to its output. */ 54*e3723e1fSApple OSS Distributions close(STDERR_FILENO); 55*e3723e1fSApple OSS Distributions close(STDOUT_FILENO); 56*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.wedge_thread", NULL, NULL, &v, sizeof(v)), 57*e3723e1fSApple OSS Distributions "wedged thread in the kernel"); 58*e3723e1fSApple OSS Distributions } else { 59*e3723e1fSApple OSS Distributions while (1) { 60*e3723e1fSApple OSS Distributions __asm__ volatile("" : : : "memory"); 61*e3723e1fSApple OSS Distributions } 62*e3723e1fSApple OSS Distributions } 63*e3723e1fSApple OSS Distributions} 64*e3723e1fSApple OSS Distributions 65*e3723e1fSApple OSS DistributionsT_HELPER_DECL(simple_child_process, "child process that will be frozen and others") 66*e3723e1fSApple OSS Distributions{ 67*e3723e1fSApple OSS Distributions child_init(); 68*e3723e1fSApple OSS Distributions} 69*e3723e1fSApple OSS Distributions 70*e3723e1fSApple OSS DistributionsT_HELPER_DECL(sid_child_process, "child process that setsid()s") 71*e3723e1fSApple OSS Distributions{ 72*e3723e1fSApple OSS Distributions pid_t ppid = getppid(); 73*e3723e1fSApple OSS Distributions 74*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(setsid(), "session id set"); 75*e3723e1fSApple OSS Distributions 76*e3723e1fSApple OSS Distributions child_recurse(RECURSIONS, 2, ^{ 77*e3723e1fSApple OSS Distributions kill(ppid, SIGUSR1); 78*e3723e1fSApple OSS Distributions }); 79*e3723e1fSApple OSS Distributions 80*e3723e1fSApple OSS Distributions T_ASSERT_FAIL("child_init returned!"); 81*e3723e1fSApple OSS Distributions} 82*e3723e1fSApple OSS Distributions 83*e3723e1fSApple OSS Distributionsstatic void 84*e3723e1fSApple OSS Distributionskill_children(void) 85*e3723e1fSApple OSS Distributions{ 86*e3723e1fSApple OSS Distributions kill(child_pid, SIGKILL); 87*e3723e1fSApple OSS Distributions} 88*e3723e1fSApple OSS Distributions 89*e3723e1fSApple OSS Distributionsstatic void * 90*e3723e1fSApple OSS Distributionstake_stackshot(pid_t target_pid, uint64_t extra_flags, uint64_t since_timestamp) 91*e3723e1fSApple OSS Distributions{ 92*e3723e1fSApple OSS Distributions void *stackshot_config; 93*e3723e1fSApple OSS Distributions int err, retries = 5; 94*e3723e1fSApple OSS Distributions uint64_t stackshot_flags = STACKSHOT_KCDATA_FORMAT | 95*e3723e1fSApple OSS Distributions STACKSHOT_THREAD_WAITINFO | 96*e3723e1fSApple OSS Distributions STACKSHOT_GET_DQ; 97*e3723e1fSApple OSS Distributions 98*e3723e1fSApple OSS Distributions /* we should be able to verify delta stackshots */ 99*e3723e1fSApple OSS Distributions if (since_timestamp != 0) { 100*e3723e1fSApple OSS Distributions stackshot_flags |= STACKSHOT_COLLECT_DELTA_SNAPSHOT; 101*e3723e1fSApple OSS Distributions } 102*e3723e1fSApple OSS Distributions 103*e3723e1fSApple OSS Distributions stackshot_flags |= extra_flags; 104*e3723e1fSApple OSS Distributions 105*e3723e1fSApple OSS Distributions stackshot_config = stackshot_config_create(); 106*e3723e1fSApple OSS Distributions T_ASSERT_NOTNULL(stackshot_config, "allocate stackshot config"); 107*e3723e1fSApple OSS Distributions 108*e3723e1fSApple OSS Distributions err = stackshot_config_set_flags(stackshot_config, stackshot_flags); 109*e3723e1fSApple OSS Distributions T_ASSERT_EQ(err, 0, "set flags on stackshot config"); 110*e3723e1fSApple OSS Distributions 111*e3723e1fSApple OSS Distributions err = stackshot_config_set_pid(stackshot_config, target_pid); 112*e3723e1fSApple OSS Distributions T_ASSERT_EQ(err, 0, "set target pid on stackshot config"); 113*e3723e1fSApple OSS Distributions 114*e3723e1fSApple OSS Distributions if (since_timestamp != 0) { 115*e3723e1fSApple OSS Distributions err = stackshot_config_set_delta_timestamp(stackshot_config, since_timestamp); 116*e3723e1fSApple OSS Distributions T_ASSERT_EQ(err, 0, "set prev snapshot time on stackshot config"); 117*e3723e1fSApple OSS Distributions } 118*e3723e1fSApple OSS Distributions 119*e3723e1fSApple OSS Distributions while (retries > 0) { 120*e3723e1fSApple OSS Distributions err = stackshot_capture_with_config(stackshot_config); 121*e3723e1fSApple OSS Distributions if (err == 0) { 122*e3723e1fSApple OSS Distributions break; 123*e3723e1fSApple OSS Distributions } else if (err == EBUSY || err == ETIMEDOUT) { 124*e3723e1fSApple OSS Distributions T_LOG("stackshot capture returned %d (%s)\n", err, strerror(err)); 125*e3723e1fSApple OSS Distributions if (retries == 0) { 126*e3723e1fSApple OSS Distributions T_ASSERT_FAIL("failed to take stackshot with error after retries: %d: %s\n", err, strerror(err)); 127*e3723e1fSApple OSS Distributions } 128*e3723e1fSApple OSS Distributions 129*e3723e1fSApple OSS Distributions retries--; 130*e3723e1fSApple OSS Distributions continue; 131*e3723e1fSApple OSS Distributions } else { 132*e3723e1fSApple OSS Distributions T_ASSERT_FAIL("failed to take stackshot with error: %d: %s\n", err, strerror(err)); 133*e3723e1fSApple OSS Distributions } 134*e3723e1fSApple OSS Distributions } 135*e3723e1fSApple OSS Distributions 136*e3723e1fSApple OSS Distributions return stackshot_config; 137*e3723e1fSApple OSS Distributions} 138*e3723e1fSApple OSS Distributions 139*e3723e1fSApple OSS Distributionsint 140*e3723e1fSApple OSS Distributionscheck_stackshot(void *stackshot_config, int flags) 141*e3723e1fSApple OSS Distributions{ 142*e3723e1fSApple OSS Distributions void *buf; 143*e3723e1fSApple OSS Distributions uint32_t buflen, kcdata_type; 144*e3723e1fSApple OSS Distributions kcdata_iter_t iter; 145*e3723e1fSApple OSS Distributions NSError *nserror = nil; 146*e3723e1fSApple OSS Distributions pid_t target_pid; 147*e3723e1fSApple OSS Distributions int ret = 0; 148*e3723e1fSApple OSS Distributions uint64_t expected_return_addr = 0; 149*e3723e1fSApple OSS Distributions bool found_fault_stats = false; 150*e3723e1fSApple OSS Distributions struct stackshot_fault_stats fault_stats = {0}; 151*e3723e1fSApple OSS Distributions 152*e3723e1fSApple OSS Distributions buf = stackshot_config_get_stackshot_buffer(stackshot_config); 153*e3723e1fSApple OSS Distributions T_ASSERT_NOTNULL(buf, "stackshot buffer is not null"); 154*e3723e1fSApple OSS Distributions buflen = stackshot_config_get_stackshot_size(stackshot_config); 155*e3723e1fSApple OSS Distributions T_ASSERT_GT(buflen, 0, "valid stackshot buffer length"); 156*e3723e1fSApple OSS Distributions target_pid = ((struct stackshot_config*)stackshot_config)->sc_pid; 157*e3723e1fSApple OSS Distributions T_ASSERT_GT(target_pid, 0, "valid target_pid"); 158*e3723e1fSApple OSS Distributions 159*e3723e1fSApple OSS Distributions /* if need to write it to fs, do it now */ 160*e3723e1fSApple OSS Distributions if (flags & WRITE_STACKSHOT_BUFFER_TO_TMP) { 161*e3723e1fSApple OSS Distributions char sspath[MAXPATHLEN]; 162*e3723e1fSApple OSS Distributions strlcpy(sspath, current_scenario_name, sizeof(sspath)); 163*e3723e1fSApple OSS Distributions strlcat(sspath, ".kcdata", sizeof(sspath)); 164*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(dt_resultfile(sspath, sizeof(sspath)), 165*e3723e1fSApple OSS Distributions "create result file path"); 166*e3723e1fSApple OSS Distributions 167*e3723e1fSApple OSS Distributions FILE *f = fopen(sspath, "w"); 168*e3723e1fSApple OSS Distributions T_WITH_ERRNO; T_QUIET; T_ASSERT_NOTNULL(f, 169*e3723e1fSApple OSS Distributions "open stackshot output file"); 170*e3723e1fSApple OSS Distributions 171*e3723e1fSApple OSS Distributions size_t written = fwrite(buf, buflen, 1, f); 172*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(written, "wrote stackshot to file"); 173*e3723e1fSApple OSS Distributions 174*e3723e1fSApple OSS Distributions fclose(f); 175*e3723e1fSApple OSS Distributions } 176*e3723e1fSApple OSS Distributions 177*e3723e1fSApple OSS Distributions /* begin iterating */ 178*e3723e1fSApple OSS Distributions iter = kcdata_iter(buf, buflen); 179*e3723e1fSApple OSS Distributions T_ASSERT_EQ(kcdata_iter_type(iter), KCDATA_BUFFER_BEGIN_STACKSHOT, "buffer is a stackshot"); 180*e3723e1fSApple OSS Distributions 181*e3723e1fSApple OSS Distributions /* time to iterate */ 182*e3723e1fSApple OSS Distributions iter = kcdata_iter_next(iter); 183*e3723e1fSApple OSS Distributions KCDATA_ITER_FOREACH(iter) { 184*e3723e1fSApple OSS Distributions kcdata_type = kcdata_iter_type(iter); 185*e3723e1fSApple OSS Distributions NSNumber *parsedPid; 186*e3723e1fSApple OSS Distributions NSMutableDictionary *parsedContainer, *parsedThreads; 187*e3723e1fSApple OSS Distributions 188*e3723e1fSApple OSS Distributions if ((flags & CHECK_FOR_FAULT_STATS) != 0 && 189*e3723e1fSApple OSS Distributions kcdata_type == STACKSHOT_KCTYPE_STACKSHOT_FAULT_STATS) { 190*e3723e1fSApple OSS Distributions memcpy(&fault_stats, kcdata_iter_payload(iter), sizeof(fault_stats)); 191*e3723e1fSApple OSS Distributions found_fault_stats = true; 192*e3723e1fSApple OSS Distributions } 193*e3723e1fSApple OSS Distributions 194*e3723e1fSApple OSS Distributions if (kcdata_type != KCDATA_TYPE_CONTAINER_BEGIN) { 195*e3723e1fSApple OSS Distributions continue; 196*e3723e1fSApple OSS Distributions } 197*e3723e1fSApple OSS Distributions 198*e3723e1fSApple OSS Distributions if (kcdata_iter_container_type(iter) != STACKSHOT_KCCONTAINER_TASK) { 199*e3723e1fSApple OSS Distributions continue; 200*e3723e1fSApple OSS Distributions } 201*e3723e1fSApple OSS Distributions 202*e3723e1fSApple OSS Distributions parsedContainer = parseKCDataContainer(&iter, &nserror); 203*e3723e1fSApple OSS Distributions T_ASSERT_NOTNULL(parsedContainer, "parsedContainer is not null"); 204*e3723e1fSApple OSS Distributions T_ASSERT_NULL(nserror, "no NSError occured while parsing the kcdata container"); 205*e3723e1fSApple OSS Distributions 206*e3723e1fSApple OSS Distributions /* 207*e3723e1fSApple OSS Distributions * given that we've targetted the pid, we can be sure that this 208*e3723e1fSApple OSS Distributions * ts_pid will be the pid we expect 209*e3723e1fSApple OSS Distributions */ 210*e3723e1fSApple OSS Distributions parsedPid = parsedContainer[@"task_snapshots"][@"task_snapshot"][@"ts_pid"]; 211*e3723e1fSApple OSS Distributions T_ASSERT_EQ([parsedPid intValue], target_pid, "found correct pid"); 212*e3723e1fSApple OSS Distributions 213*e3723e1fSApple OSS Distributions /* start parsing the threads */ 214*e3723e1fSApple OSS Distributions parsedThreads = parsedContainer[@"task_snapshots"][@"thread_snapshots"]; 215*e3723e1fSApple OSS Distributions for (id th_key in parsedThreads) { 216*e3723e1fSApple OSS Distributions uint32_t frame_index = 0; 217*e3723e1fSApple OSS Distributions 218*e3723e1fSApple OSS Distributions if ((flags & CHECK_FOR_KERNEL_THREADS) == 0) { 219*e3723e1fSApple OSS Distributions /* skip threads that don't have enough frames */ 220*e3723e1fSApple OSS Distributions if ([parsedThreads[th_key][@"user_stack_frames"] count] < RECURSIONS) { 221*e3723e1fSApple OSS Distributions continue; 222*e3723e1fSApple OSS Distributions } 223*e3723e1fSApple OSS Distributions 224*e3723e1fSApple OSS Distributions for (id frame in parsedThreads[th_key][@"user_stack_frames"]) { 225*e3723e1fSApple OSS Distributions if ((frame_index >= FIRST_RECURSIVE_FRAME) && (frame_index < (RECURSIONS - FIRST_RECURSIVE_FRAME))) { 226*e3723e1fSApple OSS Distributions if (expected_return_addr == 0ull) { 227*e3723e1fSApple OSS Distributions expected_return_addr = [frame[@"lr"] unsignedLongLongValue]; 228*e3723e1fSApple OSS Distributions } else { 229*e3723e1fSApple OSS Distributions T_QUIET; 230*e3723e1fSApple OSS Distributions T_ASSERT_EQ(expected_return_addr, [frame[@"lr"] unsignedLongLongValue], "expected return address found"); 231*e3723e1fSApple OSS Distributions } 232*e3723e1fSApple OSS Distributions } 233*e3723e1fSApple OSS Distributions frame_index ++; 234*e3723e1fSApple OSS Distributions } 235*e3723e1fSApple OSS Distributions } else { 236*e3723e1fSApple OSS Distributions T_ASSERT_NOTNULL(parsedThreads[th_key][@"kernel_stack_frames"], 237*e3723e1fSApple OSS Distributions "found kernel stack frames"); 238*e3723e1fSApple OSS Distributions } 239*e3723e1fSApple OSS Distributions 240*e3723e1fSApple OSS Distributions } 241*e3723e1fSApple OSS Distributions } 242*e3723e1fSApple OSS Distributions 243*e3723e1fSApple OSS Distributions if (found_fault_stats) { 244*e3723e1fSApple OSS Distributions T_LOG("number of pages faulted in: %d", fault_stats.sfs_pages_faulted_in); 245*e3723e1fSApple OSS Distributions T_LOG("MATUs spent faulting: %lld", fault_stats.sfs_time_spent_faulting); 246*e3723e1fSApple OSS Distributions T_LOG("MATUS fault time limit: %lld", fault_stats.sfs_system_max_fault_time); 247*e3723e1fSApple OSS Distributions T_LOG("did we stop because of the limit?: %s", fault_stats.sfs_stopped_faulting ? "yes" : "no"); 248*e3723e1fSApple OSS Distributions if (expected_return_addr != 0ull) { 249*e3723e1fSApple OSS Distributions T_ASSERT_GT(fault_stats.sfs_pages_faulted_in, 0, "faulted at least one page in"); 250*e3723e1fSApple OSS Distributions T_LOG("NOTE: successfully faulted in the pages"); 251*e3723e1fSApple OSS Distributions } else { 252*e3723e1fSApple OSS Distributions T_LOG("NOTE: We were not able to fault the stack's pages back in"); 253*e3723e1fSApple OSS Distributions 254*e3723e1fSApple OSS Distributions /* if we couldn't fault the pages back in, then at least verify that we tried */ 255*e3723e1fSApple OSS Distributions T_ASSERT_GT(fault_stats.sfs_time_spent_faulting, 0ull, "spent time trying to fault"); 256*e3723e1fSApple OSS Distributions } 257*e3723e1fSApple OSS Distributions } else if ((flags & CHECK_FOR_KERNEL_THREADS) == 0) { 258*e3723e1fSApple OSS Distributions T_ASSERT_NE(expected_return_addr, 0ull, "found child thread with recursions"); 259*e3723e1fSApple OSS Distributions } 260*e3723e1fSApple OSS Distributions 261*e3723e1fSApple OSS Distributions if (flags & CHECK_FOR_FAULT_STATS) { 262*e3723e1fSApple OSS Distributions T_ASSERT_EQ(found_fault_stats, true, "found fault stats"); 263*e3723e1fSApple OSS Distributions } 264*e3723e1fSApple OSS Distributions 265*e3723e1fSApple OSS Distributions return ret; 266*e3723e1fSApple OSS Distributions} 267*e3723e1fSApple OSS Distributions 268*e3723e1fSApple OSS Distributionsvoid 269*e3723e1fSApple OSS Distributionschild_init(void) 270*e3723e1fSApple OSS Distributions{ 271*e3723e1fSApple OSS Distributions#if !TARGET_OS_OSX 272*e3723e1fSApple OSS Distributions int freeze_state; 273*e3723e1fSApple OSS Distributions#endif /* !TARGET_OS_OSX */ 274*e3723e1fSApple OSS Distributions pid_t pid = getpid(); 275*e3723e1fSApple OSS Distributions char padding[16 * 1024]; 276*e3723e1fSApple OSS Distributions __asm__ volatile(""::"r"(padding)); 277*e3723e1fSApple OSS Distributions 278*e3723e1fSApple OSS Distributions T_LOG("child pid: %d\n", pid); 279*e3723e1fSApple OSS Distributions 280*e3723e1fSApple OSS Distributions#if !TARGET_OS_OSX 281*e3723e1fSApple OSS Distributions /* allow us to be frozen */ 282*e3723e1fSApple OSS Distributions freeze_state = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FREEZABLE, pid, 0, NULL, 0); 283*e3723e1fSApple OSS Distributions if (freeze_state == 0) { 284*e3723e1fSApple OSS Distributions T_LOG("CHILD was found to be UNFREEZABLE, enabling freezing."); 285*e3723e1fSApple OSS Distributions memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE, pid, 1, NULL, 0); 286*e3723e1fSApple OSS Distributions freeze_state = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FREEZABLE, pid, 0, NULL, 0); 287*e3723e1fSApple OSS Distributions T_ASSERT_EQ(freeze_state, 1, "successfully set freezeability"); 288*e3723e1fSApple OSS Distributions } 289*e3723e1fSApple OSS Distributions#else 290*e3723e1fSApple OSS Distributions T_LOG("Cannot change freezeability as freezing is only available on embedded devices"); 291*e3723e1fSApple OSS Distributions#endif /* !TARGET_OS_OSX */ 292*e3723e1fSApple OSS Distributions 293*e3723e1fSApple OSS Distributions /* 294*e3723e1fSApple OSS Distributions * recurse a bunch of times to generate predictable data in the stackshot, 295*e3723e1fSApple OSS Distributions * then send SIGUSR1 to the parent to let it know that we are done. 296*e3723e1fSApple OSS Distributions */ 297*e3723e1fSApple OSS Distributions child_recurse(RECURSIONS, 0, ^{ 298*e3723e1fSApple OSS Distributions kill(getppid(), SIGUSR1); 299*e3723e1fSApple OSS Distributions }); 300*e3723e1fSApple OSS Distributions 301*e3723e1fSApple OSS Distributions T_ASSERT_FAIL("child_recurse returned, but it must not?"); 302*e3723e1fSApple OSS Distributions} 303*e3723e1fSApple OSS Distributions 304*e3723e1fSApple OSS Distributionsvoid 305*e3723e1fSApple OSS Distributionsparent_helper_singleproc(int spin) 306*e3723e1fSApple OSS Distributions{ 307*e3723e1fSApple OSS Distributions dispatch_semaphore_t child_done_sema = dispatch_semaphore_create(0); 308*e3723e1fSApple OSS Distributions dispatch_queue_t dq = dispatch_queue_create("com.apple.stackshot_accuracy.basic_sp", NULL); 309*e3723e1fSApple OSS Distributions void *stackshot_config; 310*e3723e1fSApple OSS Distributions 311*e3723e1fSApple OSS Distributions dispatch_async(dq, ^{ 312*e3723e1fSApple OSS Distributions char padding[16 * 1024]; 313*e3723e1fSApple OSS Distributions __asm__ volatile(""::"r"(padding)); 314*e3723e1fSApple OSS Distributions 315*e3723e1fSApple OSS Distributions child_recurse(RECURSIONS, spin, ^{ 316*e3723e1fSApple OSS Distributions dispatch_semaphore_signal(child_done_sema); 317*e3723e1fSApple OSS Distributions }); 318*e3723e1fSApple OSS Distributions }); 319*e3723e1fSApple OSS Distributions 320*e3723e1fSApple OSS Distributions dispatch_semaphore_wait(child_done_sema, DISPATCH_TIME_FOREVER); 321*e3723e1fSApple OSS Distributions T_LOG("done waiting for child"); 322*e3723e1fSApple OSS Distributions 323*e3723e1fSApple OSS Distributions /* take the stackshot and parse it */ 324*e3723e1fSApple OSS Distributions stackshot_config = take_stackshot(getpid(), 0, 0); 325*e3723e1fSApple OSS Distributions 326*e3723e1fSApple OSS Distributions /* check that the stackshot has the stack frames */ 327*e3723e1fSApple OSS Distributions check_stackshot(stackshot_config, WRITE_STACKSHOT_BUFFER_TO_TMP); 328*e3723e1fSApple OSS Distributions 329*e3723e1fSApple OSS Distributions T_LOG("done!"); 330*e3723e1fSApple OSS Distributions} 331*e3723e1fSApple OSS Distributions 332*e3723e1fSApple OSS DistributionsT_DECL(basic, "test that no-fault stackshot works correctly", T_META_TAG_VM_PREFERRED) 333*e3723e1fSApple OSS Distributions{ 334*e3723e1fSApple OSS Distributions char path[PATH_MAX]; 335*e3723e1fSApple OSS Distributions uint32_t path_size = sizeof(path); 336*e3723e1fSApple OSS Distributions char *args[] = { path, "-n", "simple_child_process", NULL }; 337*e3723e1fSApple OSS Distributions dispatch_queue_t dq = dispatch_queue_create("com.apple.stackshot_accuracy.basic", NULL); 338*e3723e1fSApple OSS Distributions dispatch_semaphore_t child_done_sema = dispatch_semaphore_create(0); 339*e3723e1fSApple OSS Distributions dispatch_source_t child_sig_src; 340*e3723e1fSApple OSS Distributions void *stackshot_config; 341*e3723e1fSApple OSS Distributions 342*e3723e1fSApple OSS Distributions current_scenario_name = __func__; 343*e3723e1fSApple OSS Distributions 344*e3723e1fSApple OSS Distributions T_LOG("parent pid: %d\n", getpid()); 345*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(_NSGetExecutablePath(path, &path_size), "_NSGetExecutablePath"); 346*e3723e1fSApple OSS Distributions 347*e3723e1fSApple OSS Distributions /* check if we can run the child successfully */ 348*e3723e1fSApple OSS Distributions#if !TARGET_OS_OSX 349*e3723e1fSApple OSS Distributions int freeze_state = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FREEZABLE, getpid(), 0, NULL, 0); 350*e3723e1fSApple OSS Distributions if (freeze_state == -1) { 351*e3723e1fSApple OSS Distributions T_SKIP("This device doesn't have CONFIG_FREEZE enabled."); 352*e3723e1fSApple OSS Distributions } 353*e3723e1fSApple OSS Distributions#endif 354*e3723e1fSApple OSS Distributions 355*e3723e1fSApple OSS Distributions /* setup signal handling */ 356*e3723e1fSApple OSS Distributions signal(SIGUSR1, SIG_IGN); 357*e3723e1fSApple OSS Distributions child_sig_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dq); 358*e3723e1fSApple OSS Distributions dispatch_source_set_event_handler(child_sig_src, ^{ 359*e3723e1fSApple OSS Distributions dispatch_semaphore_signal(child_done_sema); 360*e3723e1fSApple OSS Distributions }); 361*e3723e1fSApple OSS Distributions dispatch_activate(child_sig_src); 362*e3723e1fSApple OSS Distributions 363*e3723e1fSApple OSS Distributions /* create the child process */ 364*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(dt_launch_tool(&child_pid, args, false, NULL, NULL), "child launched"); 365*e3723e1fSApple OSS Distributions T_ATEND(kill_children); 366*e3723e1fSApple OSS Distributions 367*e3723e1fSApple OSS Distributions /* wait until the child has recursed enough */ 368*e3723e1fSApple OSS Distributions dispatch_semaphore_wait(child_done_sema, dispatch_time(DISPATCH_TIME_NOW, 10 /*seconds*/ * 1000000000ULL)); 369*e3723e1fSApple OSS Distributions 370*e3723e1fSApple OSS Distributions T_LOG("child finished, parent executing"); 371*e3723e1fSApple OSS Distributions 372*e3723e1fSApple OSS Distributions /* take the stackshot and parse it */ 373*e3723e1fSApple OSS Distributions stackshot_config = take_stackshot(child_pid, 0, 0); 374*e3723e1fSApple OSS Distributions 375*e3723e1fSApple OSS Distributions /* check that the stackshot has the stack frames */ 376*e3723e1fSApple OSS Distributions check_stackshot(stackshot_config, WRITE_STACKSHOT_BUFFER_TO_TMP); 377*e3723e1fSApple OSS Distributions 378*e3723e1fSApple OSS Distributions T_LOG("all done, killing child"); 379*e3723e1fSApple OSS Distributions 380*e3723e1fSApple OSS Distributions /* tell the child to quit */ 381*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGTERM), "killed child"); 382*e3723e1fSApple OSS Distributions} 383*e3723e1fSApple OSS Distributions 384*e3723e1fSApple OSS DistributionsT_DECL(basic_singleproc, "test that no-fault stackshot works correctly in single process setting", T_META_TAG_VM_PREFERRED) 385*e3723e1fSApple OSS Distributions{ 386*e3723e1fSApple OSS Distributions current_scenario_name = __func__; 387*e3723e1fSApple OSS Distributions parent_helper_singleproc(0); 388*e3723e1fSApple OSS Distributions} 389*e3723e1fSApple OSS Distributions 390*e3723e1fSApple OSS DistributionsT_DECL(basic_singleproc_spin, "test that no-fault stackshot works correctly in single process setting with spinning", T_META_TAG_VM_PREFERRED) 391*e3723e1fSApple OSS Distributions{ 392*e3723e1fSApple OSS Distributions current_scenario_name = __func__; 393*e3723e1fSApple OSS Distributions parent_helper_singleproc(1); 394*e3723e1fSApple OSS Distributions} 395*e3723e1fSApple OSS Distributions 396*e3723e1fSApple OSS DistributionsT_DECL(fault, "test that faulting stackshots work correctly", T_META_TAG_VM_PREFERRED) 397*e3723e1fSApple OSS Distributions{ 398*e3723e1fSApple OSS Distributions dispatch_queue_t dq = dispatch_queue_create("com.apple.stackshot_fault_accuracy", NULL); 399*e3723e1fSApple OSS Distributions dispatch_source_t child_sig_src; 400*e3723e1fSApple OSS Distributions dispatch_semaphore_t child_done_sema = dispatch_semaphore_create(0); 401*e3723e1fSApple OSS Distributions void *stackshot_config; 402*e3723e1fSApple OSS Distributions int oldftm, newval = 1, freeze_enabled, oldratio, newratio = 0; 403*e3723e1fSApple OSS Distributions size_t oldlen = sizeof(oldftm), fe_len = sizeof(freeze_enabled), ratiolen = sizeof(oldratio); 404*e3723e1fSApple OSS Distributions char path[PATH_MAX]; 405*e3723e1fSApple OSS Distributions uint32_t path_size = sizeof(path); 406*e3723e1fSApple OSS Distributions char *args[] = { path, "-n", "simple_child_process", NULL }; 407*e3723e1fSApple OSS Distributions 408*e3723e1fSApple OSS Distributions current_scenario_name = __func__; 409*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(_NSGetExecutablePath(path, &path_size), "_NSGetExecutablePath"); 410*e3723e1fSApple OSS Distributions 411*e3723e1fSApple OSS Distributions#if TARGET_OS_OSX 412*e3723e1fSApple OSS Distributions T_SKIP("freezing is not available on macOS"); 413*e3723e1fSApple OSS Distributions#endif /* TARGET_OS_OSX */ 414*e3723e1fSApple OSS Distributions 415*e3723e1fSApple OSS Distributions /* Try checking if freezing is enabled at all */ 416*e3723e1fSApple OSS Distributions if (sysctlbyname("vm.freeze_enabled", &freeze_enabled, &fe_len, NULL, 0) == -1) { 417*e3723e1fSApple OSS Distributions if (errno == ENOENT) { 418*e3723e1fSApple OSS Distributions T_SKIP("This device doesn't have CONFIG_FREEZE enabled."); 419*e3723e1fSApple OSS Distributions } else { 420*e3723e1fSApple OSS Distributions T_FAIL("failed to query vm.freeze_enabled, errno: %d", errno); 421*e3723e1fSApple OSS Distributions } 422*e3723e1fSApple OSS Distributions } 423*e3723e1fSApple OSS Distributions 424*e3723e1fSApple OSS Distributions if (!freeze_enabled) { 425*e3723e1fSApple OSS Distributions T_SKIP("Freeze is not enabled, skipping test."); 426*e3723e1fSApple OSS Distributions } 427*e3723e1fSApple OSS Distributions 428*e3723e1fSApple OSS Distributions /* signal handling */ 429*e3723e1fSApple OSS Distributions signal(SIGUSR1, SIG_IGN); 430*e3723e1fSApple OSS Distributions child_sig_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dq); 431*e3723e1fSApple OSS Distributions dispatch_source_set_event_handler(child_sig_src, ^{ 432*e3723e1fSApple OSS Distributions dispatch_semaphore_signal(child_done_sema); 433*e3723e1fSApple OSS Distributions }); 434*e3723e1fSApple OSS Distributions dispatch_activate(child_sig_src); 435*e3723e1fSApple OSS Distributions 436*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(dt_launch_tool(&child_pid, args, false, NULL, NULL), "child launched"); 437*e3723e1fSApple OSS Distributions T_ATEND(kill_children); 438*e3723e1fSApple OSS Distributions 439*e3723e1fSApple OSS Distributions dispatch_semaphore_wait(child_done_sema, DISPATCH_TIME_FOREVER); 440*e3723e1fSApple OSS Distributions 441*e3723e1fSApple OSS Distributions /* keep processes in memory */ 442*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_to_memory", &oldftm, &oldlen, &newval, sizeof(newval)), 443*e3723e1fSApple OSS Distributions "disabled freezing to disk"); 444*e3723e1fSApple OSS Distributions 445*e3723e1fSApple OSS Distributions /* set the ratio to zero */ 446*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_private_shared_pages_ratio", &oldratio, &ratiolen, &newratio, sizeof(newratio)), "disabled private:shared ratio checking"); 447*e3723e1fSApple OSS Distributions 448*e3723e1fSApple OSS Distributions /* freeze the child */ 449*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze", NULL, 0, &child_pid, sizeof(child_pid)), 450*e3723e1fSApple OSS Distributions "froze child"); 451*e3723e1fSApple OSS Distributions 452*e3723e1fSApple OSS Distributions /* Sleep to allow the compressor to finish compressing the child */ 453*e3723e1fSApple OSS Distributions sleep(5); 454*e3723e1fSApple OSS Distributions 455*e3723e1fSApple OSS Distributions /* take the stackshot and parse it */ 456*e3723e1fSApple OSS Distributions stackshot_config = take_stackshot(child_pid, STACKSHOT_ENABLE_BT_FAULTING | STACKSHOT_ENABLE_UUID_FAULTING, 0); 457*e3723e1fSApple OSS Distributions 458*e3723e1fSApple OSS Distributions /* check that the stackshot has the stack frames */ 459*e3723e1fSApple OSS Distributions check_stackshot(stackshot_config, CHECK_FOR_FAULT_STATS | WRITE_STACKSHOT_BUFFER_TO_TMP); 460*e3723e1fSApple OSS Distributions 461*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_to_memory", NULL, 0, &oldftm, sizeof(oldftm)), 462*e3723e1fSApple OSS Distributions "reset freezing to disk"); 463*e3723e1fSApple OSS Distributions 464*e3723e1fSApple OSS Distributions /* reset the private:shared ratio */ 465*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_private_shared_pages_ratio", NULL, 0, &oldratio, sizeof(oldratio)), "reset private:shared ratio"); 466*e3723e1fSApple OSS Distributions 467*e3723e1fSApple OSS Distributions T_LOG("all done, killing child"); 468*e3723e1fSApple OSS Distributions 469*e3723e1fSApple OSS Distributions /* tell the child to quit */ 470*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGTERM), "killed child"); 471*e3723e1fSApple OSS Distributions} 472*e3723e1fSApple OSS Distributions 473*e3723e1fSApple OSS DistributionsT_DECL(fault_singleproc, "test that faulting stackshots work correctly in a single process setting", T_META_TAG_VM_PREFERRED) 474*e3723e1fSApple OSS Distributions{ 475*e3723e1fSApple OSS Distributions dispatch_semaphore_t child_done_sema = dispatch_semaphore_create(0); 476*e3723e1fSApple OSS Distributions dispatch_queue_t dq = dispatch_queue_create("com.apple.stackshot_accuracy.fault_sp", NULL); 477*e3723e1fSApple OSS Distributions void *stackshot_config; 478*e3723e1fSApple OSS Distributions __block pthread_t child_thread; 479*e3723e1fSApple OSS Distributions char *child_stack; 480*e3723e1fSApple OSS Distributions size_t child_stacklen; 481*e3723e1fSApple OSS Distributions 482*e3723e1fSApple OSS Distributions#if !TARGET_OS_OSX 483*e3723e1fSApple OSS Distributions T_SKIP("madvise(..., ..., MADV_PAGEOUT) is not available on embedded platforms"); 484*e3723e1fSApple OSS Distributions#endif /* !TARGET_OS_OSX */ 485*e3723e1fSApple OSS Distributions 486*e3723e1fSApple OSS Distributions current_scenario_name = __func__; 487*e3723e1fSApple OSS Distributions 488*e3723e1fSApple OSS Distributions dispatch_async(dq, ^{ 489*e3723e1fSApple OSS Distributions char padding[16 * 1024]; 490*e3723e1fSApple OSS Distributions __asm__ volatile(""::"r"(padding)); 491*e3723e1fSApple OSS Distributions 492*e3723e1fSApple OSS Distributions child_recurse(RECURSIONS, 0, ^{ 493*e3723e1fSApple OSS Distributions child_thread = pthread_self(); 494*e3723e1fSApple OSS Distributions dispatch_semaphore_signal(child_done_sema); 495*e3723e1fSApple OSS Distributions }); 496*e3723e1fSApple OSS Distributions }); 497*e3723e1fSApple OSS Distributions 498*e3723e1fSApple OSS Distributions dispatch_semaphore_wait(child_done_sema, DISPATCH_TIME_FOREVER); 499*e3723e1fSApple OSS Distributions T_LOG("done waiting for child"); 500*e3723e1fSApple OSS Distributions 501*e3723e1fSApple OSS Distributions child_stack = pthread_get_stackaddr_np(child_thread); 502*e3723e1fSApple OSS Distributions child_stacklen = pthread_get_stacksize_np(child_thread); 503*e3723e1fSApple OSS Distributions child_stack -= child_stacklen; 504*e3723e1fSApple OSS Distributions T_LOG("child stack: [0x%p - 0x%p]: 0x%zu bytes", (void *)child_stack, 505*e3723e1fSApple OSS Distributions (void *)(child_stack + child_stacklen), child_stacklen); 506*e3723e1fSApple OSS Distributions 507*e3723e1fSApple OSS Distributions /* paging out the child */ 508*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(madvise(child_stack, child_stacklen, MADV_PAGEOUT), "paged out via madvise(2) the child stack"); 509*e3723e1fSApple OSS Distributions 510*e3723e1fSApple OSS Distributions /* take the stackshot and parse it */ 511*e3723e1fSApple OSS Distributions stackshot_config = take_stackshot(getpid(), STACKSHOT_ENABLE_BT_FAULTING | STACKSHOT_ENABLE_UUID_FAULTING, 0); 512*e3723e1fSApple OSS Distributions 513*e3723e1fSApple OSS Distributions /* check that the stackshot has the stack frames */ 514*e3723e1fSApple OSS Distributions check_stackshot(stackshot_config, CHECK_FOR_FAULT_STATS | WRITE_STACKSHOT_BUFFER_TO_TMP); 515*e3723e1fSApple OSS Distributions 516*e3723e1fSApple OSS Distributions T_LOG("done!"); 517*e3723e1fSApple OSS Distributions} 518*e3723e1fSApple OSS Distributions 519*e3723e1fSApple OSS DistributionsT_DECL(zombie, "test that threads wedged in the kernel can be stackshot'd", T_META_TAG_VM_PREFERRED) 520*e3723e1fSApple OSS Distributions{ 521*e3723e1fSApple OSS Distributions dispatch_queue_t dq = dispatch_queue_create("com.apple.stackshot_accuracy.zombie", NULL); 522*e3723e1fSApple OSS Distributions dispatch_semaphore_t child_done_sema = dispatch_semaphore_create(0); 523*e3723e1fSApple OSS Distributions dispatch_source_t child_sig_src; 524*e3723e1fSApple OSS Distributions void *stackshot_config; 525*e3723e1fSApple OSS Distributions char path[PATH_MAX]; 526*e3723e1fSApple OSS Distributions uint32_t path_size = sizeof(path); 527*e3723e1fSApple OSS Distributions char *args[] = { path, "-n", "sid_child_process", NULL }; 528*e3723e1fSApple OSS Distributions 529*e3723e1fSApple OSS Distributions current_scenario_name = __func__; 530*e3723e1fSApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(_NSGetExecutablePath(path, &path_size), "_NSGetExecutablePath"); 531*e3723e1fSApple OSS Distributions 532*e3723e1fSApple OSS Distributions T_LOG("parent pid: %d\n", getpid()); 533*e3723e1fSApple OSS Distributions 534*e3723e1fSApple OSS Distributions /* setup signal handling */ 535*e3723e1fSApple OSS Distributions signal(SIGUSR1, SIG_IGN); 536*e3723e1fSApple OSS Distributions child_sig_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dq); 537*e3723e1fSApple OSS Distributions dispatch_source_set_event_handler(child_sig_src, ^{ 538*e3723e1fSApple OSS Distributions dispatch_semaphore_signal(child_done_sema); 539*e3723e1fSApple OSS Distributions }); 540*e3723e1fSApple OSS Distributions dispatch_activate(child_sig_src); 541*e3723e1fSApple OSS Distributions 542*e3723e1fSApple OSS Distributions /* create the child process */ 543*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(dt_launch_tool(&child_pid, args, false, NULL, NULL), "child launched"); 544*e3723e1fSApple OSS Distributions T_ATEND(kill_children); 545*e3723e1fSApple OSS Distributions 546*e3723e1fSApple OSS Distributions /* wait until the child has recursed enough */ 547*e3723e1fSApple OSS Distributions dispatch_semaphore_wait(child_done_sema, DISPATCH_TIME_FOREVER); 548*e3723e1fSApple OSS Distributions 549*e3723e1fSApple OSS Distributions T_LOG("child finished, parent executing. invoking jetsam"); 550*e3723e1fSApple OSS Distributions 551*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(memorystatus_control(MEMORYSTATUS_CMD_TEST_JETSAM, child_pid, 0, 0, 0), 552*e3723e1fSApple OSS Distributions "jetsam'd the child"); 553*e3723e1fSApple OSS Distributions 554*e3723e1fSApple OSS Distributions /* Sleep to allow the target process to become zombified */ 555*e3723e1fSApple OSS Distributions sleep(1); 556*e3723e1fSApple OSS Distributions 557*e3723e1fSApple OSS Distributions /* take the stackshot and parse it */ 558*e3723e1fSApple OSS Distributions stackshot_config = take_stackshot(child_pid, 0, 0); 559*e3723e1fSApple OSS Distributions 560*e3723e1fSApple OSS Distributions /* check that the stackshot has the stack frames */ 561*e3723e1fSApple OSS Distributions check_stackshot(stackshot_config, CHECK_FOR_KERNEL_THREADS | WRITE_STACKSHOT_BUFFER_TO_TMP); 562*e3723e1fSApple OSS Distributions 563*e3723e1fSApple OSS Distributions T_LOG("all done, unwedging and killing child"); 564*e3723e1fSApple OSS Distributions 565*e3723e1fSApple OSS Distributions int v = 1; 566*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.unwedge_thread", NULL, NULL, &v, sizeof(v)), 567*e3723e1fSApple OSS Distributions "unwedged child"); 568*e3723e1fSApple OSS Distributions 569*e3723e1fSApple OSS Distributions /* tell the child to quit */ 570*e3723e1fSApple OSS Distributions T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGTERM), "killed child"); 571*e3723e1fSApple OSS Distributions} 572