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