Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | #include <mach-o/dyld.h> #include <spawn.h> #include <spawn_private.h> #include <stdlib.h> #include <sys/spawn_internal.h> #include <sys/sysctl.h> #include <darwintest.h> #include <darwintest_utils.h> 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"); } |