#include #include #include #include #include #include #include #include T_GLOBAL_META( T_META_NAMESPACE("xnu.vm"), T_META_RADAR_COMPONENT_NAME("xnu"), T_META_RADAR_COMPONENT_VERSION("VM")); static void set_small_relaunch_values(posix_spawnattr_t *attrs) { static const uint32_t kNumRelaunchValues = 16; static uint32_t relaunch_values[kNumRelaunchValues] = {0}; int ret; /* * Set the relaunch times to very small values (in m.s.). * Everything under 5 seconds is expected to fall in the high relaunch behavior bucket. */ for (uint32_t i = 0; i < kNumRelaunchValues; i++) { relaunch_values[i] = i; } ret = posix_spawnattr_set_jetsam_ttr_np(attrs, kNumRelaunchValues, relaunch_values); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_set_jetsam_ttr_np"); } T_DECL(set_high_relaunch_behavior, "supply very small time to relaunch values", T_META_TAG_VM_PREFERRED) { posix_spawnattr_t attrs; _posix_spawnattr_t psattr; uint32_t relaunch_flags = 0; posix_spawnattr_init(&attrs); set_small_relaunch_values(&attrs); psattr = *(_posix_spawnattr_t *)&attrs; relaunch_flags = psattr->psa_jetsam_flags & POSIX_SPAWN_JETSAM_RELAUNCH_BEHAVIOR_MASK; T_QUIET; T_ASSERT_EQ(relaunch_flags, POSIX_SPAWN_JETSAM_RELAUNCH_BEHAVIOR_HIGH, "relaunch behavior is high"); posix_spawnattr_destroy(&attrs); } T_DECL(set_medium_relaunch_behavior, "supply very large time to relaunch values", T_META_TAG_VM_PREFERRED) { posix_spawnattr_t attrs; _posix_spawnattr_t psattr; int ret; static const uint32_t kNumRelaunchValues = 16; static uint32_t relaunch_values[kNumRelaunchValues] = {0}; uint32_t relaunch_flags = 0; posix_spawnattr_init(&attrs); /* * Set the relaunch times to medium large values (in m.s.). * Everything over between 5 and 10 seconds is expected to fall in the medium relaunch behavior bucket. */ for (uint32_t i = 0; i < kNumRelaunchValues; i++) { relaunch_values[i] = 5000 + i; } ret = posix_spawnattr_set_jetsam_ttr_np(&attrs, kNumRelaunchValues, relaunch_values); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_set_jetsam_ttr_np"); psattr = *(_posix_spawnattr_t *)&attrs; relaunch_flags = psattr->psa_jetsam_flags & POSIX_SPAWN_JETSAM_RELAUNCH_BEHAVIOR_MASK; T_QUIET; T_ASSERT_EQ(relaunch_flags, POSIX_SPAWN_JETSAM_RELAUNCH_BEHAVIOR_MED, "relaunch behavior is medium"); posix_spawnattr_destroy(&attrs); } T_DECL(set_low_relaunch_behavior, "supply very large time to relaunch values", T_META_TAG_VM_PREFERRED) { posix_spawnattr_t attrs; _posix_spawnattr_t psattr; int ret; static const uint32_t kNumRelaunchValues = 16; static uint32_t relaunch_values[kNumRelaunchValues] = {0}; uint32_t relaunch_flags = 0; posix_spawnattr_init(&attrs); /* * Set the relaunch times to very large values (in m.s.). * Everything over 10 seconds is expected to fall in the low relaunch behavior bucket. */ for (uint32_t i = 0; i < kNumRelaunchValues; i++) { relaunch_values[i] = 10000 + i; } ret = posix_spawnattr_set_jetsam_ttr_np(&attrs, kNumRelaunchValues, relaunch_values); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_set_jetsam_ttr_np"); psattr = *(_posix_spawnattr_t *)&attrs; relaunch_flags = psattr->psa_jetsam_flags & POSIX_SPAWN_JETSAM_RELAUNCH_BEHAVIOR_MASK; T_QUIET; T_ASSERT_EQ(relaunch_flags, POSIX_SPAWN_JETSAM_RELAUNCH_BEHAVIOR_LOW, "relaunch behavior is low"); posix_spawnattr_destroy(&attrs); } T_DECL(set_high_relaunch_with_mixed_histogram, "supply slightly more small values than large values", T_META_TAG_VM_PREFERRED) { posix_spawnattr_t attrs; _posix_spawnattr_t psattr; int ret; static const uint32_t kNumRelaunchValues = 16; static uint32_t relaunch_values[kNumRelaunchValues] = {0}; uint32_t relaunch_flags = 0; posix_spawnattr_init(&attrs); /* * Make sure the high likelihood bucket (<5 seconds) is a bit larger than the others */ for (uint32_t i = 0; i < kNumRelaunchValues; i++) { if (i % 2 == 0) { relaunch_values[i] = i; } else { if (i % 3 == 0) { relaunch_values[i] = 10000 + i; } else { relaunch_values[i] = 5000 + i; } } } ret = posix_spawnattr_set_jetsam_ttr_np(&attrs, kNumRelaunchValues, relaunch_values); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_set_jetsam_ttr_np"); psattr = *(_posix_spawnattr_t *)&attrs; relaunch_flags = psattr->psa_jetsam_flags & POSIX_SPAWN_JETSAM_RELAUNCH_BEHAVIOR_MASK; T_QUIET; T_ASSERT_EQ(relaunch_flags, POSIX_SPAWN_JETSAM_RELAUNCH_BEHAVIOR_HIGH, "relaunch behavior is high"); posix_spawnattr_destroy(&attrs); } extern char **environ; T_HELPER_DECL(check_relaunch_flags, "Check that we have the high relaunch likelihood flag set") { int relaunch_flags; size_t size = sizeof(relaunch_flags); int ret = sysctlbyname("kern.memorystatus_relaunch_flags", &relaunch_flags, &size, NULL, 0); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to query vm.pagesize"); T_QUIET; T_ASSERT_EQ(relaunch_flags, POSIX_SPAWN_JETSAM_RELAUNCH_BEHAVIOR_HIGH, "relaunch behavior is high"); } T_HELPER_DECL(exec_into_check_relaunch_flags, "Do an exec into check_relaunch_flags") { posix_spawnattr_t attrs; int ret; char testpath[PATH_MAX]; uint32_t testpath_buf_size; char **arguments; testpath_buf_size = sizeof(testpath); ret = _NSGetExecutablePath(testpath, &testpath_buf_size); T_QUIET; T_ASSERT_POSIX_ZERO(ret, "_NSGetExecutablePath"); arguments = (char *[]) { testpath, "-n", "check_relaunch_flags", NULL }; posix_spawnattr_init(&attrs); ret = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETEXEC); T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix spawn set exec flag"); ret = posix_spawn(NULL, testpath, NULL, &attrs, arguments, environ); T_FAIL("posix_spawn failed with %s\n", strerror(ret)); } static void posix_spawn_helper_and_wait_for_exit(char *name) { posix_spawnattr_t attrs; int ret; pid_t child_pid; char testpath[PATH_MAX]; uint32_t testpath_buf_size; char **arguments; testpath_buf_size = sizeof(testpath); ret = _NSGetExecutablePath(testpath, &testpath_buf_size); T_QUIET; T_ASSERT_POSIX_ZERO(ret, "_NSGetExecutablePath"); posix_spawnattr_init(&attrs); set_small_relaunch_values(&attrs); arguments = (char *[]) { testpath, "-n", name, NULL }; ret = posix_spawn(&child_pid, testpath, NULL, &attrs, arguments, environ); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawn"); while (true) { int status; pid_t rc = waitpid(child_pid, &status, 0); if (rc == -1 && errno == EINTR) { continue; } T_QUIET; T_ASSERT_EQ(rc, child_pid, "waitpid"); T_QUIET; T_ASSERT_TRUE(WIFEXITED(status), "Exited cleanly"); T_QUIET; T_ASSERT_EQ(WEXITSTATUS(status), 0, "return code was 0"); break; } posix_spawnattr_destroy(&attrs); } T_DECL(posix_spawn_sets_relaunch_flags, "Check that posix_spawn sets the relaunch flags on the new proc", T_META_TAG_VM_PREFERRED) { posix_spawn_helper_and_wait_for_exit("check_relaunch_flags"); } T_DECL(relaunch_flags_persist_across_exec, "Check that the relaunch flags persist across exec", T_META_TAG_VM_PREFERRED) { posix_spawn_helper_and_wait_for_exit("exec_into_check_relaunch_flags"); }