1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <assert.h>
7 #include <signal.h>
8 #include <spawn.h>
9 #include <spawn_private.h>
10 #include <stdint.h>
11 #include <sys/sysctl.h>
12 #include <sys/spawn_internal.h>
13 #include <sys/kern_memorystatus.h>
14 #include <mach-o/dyld.h>
15 #include <os/voucher_private.h>
16
17 #include <darwintest.h>
18 #include <darwintest_multiprocess.h>
19 #include <darwintest_utils.h>
20
21 #include "memorystatus_assertion_helpers.h"
22
23 T_GLOBAL_META(
24 T_META_NAMESPACE("xnu.memorystatus"),
25 T_META_RADAR_COMPONENT_NAME("xnu"),
26 T_META_RADAR_COMPONENT_VERSION("VM"),
27 T_META_CHECK_LEAKS(false),
28 T_META_RUN_CONCURRENTLY(true),
29 T_META_TAG_VM_PREFERRED
30 );
31
32 #define IDLE_AGEOUT_S 30
33 #define AGEOUT_STUCK_S 35
34
35 /*
36 * This test has multiple sub-tests that set and then verify jetsam priority transitions
37 * as though they were driven by assertions. It uses the MEMORYSTATUS_CMD_SET_MEMLIMIT_PROPERTIES
38 * version of the memorystatus_control() system call and specifically tests the use of the
39 * MEMORYSTATUS_SET_PRIORITY_ASSERTION flag.
40 *
41 * The kernel will apply policy that chooses a maximum jetsam priority, resolving conflicts
42 * between an assertion driven priority and clean/dirty transition policy.
43 *
44 * Processes that do not opt into dirty-tracking should behave as they always have.
45 * This is the typical App transition behavior.
46 *
47 * Processes that do opt into dirty-tracking have more complex policy:
48 * For example:
49 * A MAX assertion priority will prevent a dirty process from transitioning to a clean
50 * state if the process opts into idle-exit.
51 * See: memorystatus_schedule_idle_demotion_locked() where we note that
52 * the process isn't going to be making the trip to the lower bands.
53 *
54 * But a MAX assertion evaluation will not prevent a clean process from transition to dirty.
55 * Assertion driven priorities should not change memory limits, they are expected to
56 * just change a process's position in the jetsam priority bands.
57 *
58 * MEMORYSTATUS_CMD_xxx requires root (in the absence of entitlement).
59 * Use T_META_ASROOT(true) to accomplish this.
60 *
61 * A note on test strategy. It is not necessary to spawn a child to test these
62 * assertion calls. The test can act on itself, that is, it can make calls to
63 * set and relinquish assertion state just like it can make calls to do dirty/clean
64 * transitions. Of course, in reality, we expect only runningboardd to manipulate
65 * assertion based priorities.
66 */
67
68 static void
proc_will_set_clean(pid_t pid)69 proc_will_set_clean(pid_t pid)
70 {
71 proc_set_dirty(pid, false);
72 T_LOG("pid[%d] --> now clean", pid);
73 return;
74 }
75
76 static void
proc_will_set_dirty(pid_t pid)77 proc_will_set_dirty(pid_t pid)
78 {
79 proc_set_dirty(pid, true);
80 T_LOG("pid[%d] --> now dirty", pid);
81 return;
82 }
83
84 static void
proc_set_managed(pid_t pid,bool managed)85 proc_set_managed(pid_t pid, bool managed)
86 {
87 int err;
88 err = memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_MANAGED, pid,
89 managed, NULL, 0);
90 T_QUIET; T_ASSERT_POSIX_SUCCESS(err,
91 "memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_MANAGED)");
92 }
93
94 /*
95 * Make repetitive (eg: back-to-back) calls using MEMORYSTATUS_SET_PRIORITY_ASSERTION.
96 * We know that runningboardd may try to relinquish its hold on an assertion priority
97 * when it hasn't first set the assertion priority. The kernel must survive this
98 * pattern even though it might be considered poor behavior on runningboardd's part.
99 * When dirty tracking processes are involved, we are exercising the kernel's
100 * idle-deferred paths. Only assertion state (whether or not assertion state is
101 * set or relinquished) is verified in this round of tests.
102 * Test is invoked three times:
103 * Scenario 1) as a non-dirty-tracking process (like a typical app)
104 * relinquish assertion priority multiple times
105 * set same assertion priority multiple times.
106 * Scenario 2) setup a dirty-tracking process that is clean (like a typical extension)
107 * relinquish assertion priority multiple times
108 * set same assertion priority multiple times.
109 * Scenario 3) setup dirty-tracking process that is dirty (like a typical extension)
110 * relinquish assertion priority multiple times
111 * set same assertion priority multiple times.
112 */
113
114 static void
memorystatus_assertion_test_repetitive(char * test,boolean_t turn_on_dirty_tracking,boolean_t start_clean)115 memorystatus_assertion_test_repetitive(char *test, boolean_t turn_on_dirty_tracking, boolean_t start_clean)
116 {
117 int count;
118 int maxcount = 3;
119 boolean_t verbose;
120 uint32_t state;
121 uint64_t user_data = 0;
122 pid_t mypid = getpid();
123
124 /* these values will remain fixed during testing */
125 int active_limit_mb = 35; /* arbitrary */
126 int inactive_limit_mb = 25; /* arbitrary */
127
128 /* these values may vary during test */
129 int requestedpriority = 0;
130 int assertionpriority = 0;
131
132 T_SETUPBEGIN;
133
134 requestedpriority = JETSAM_PRIORITY_UI_SUPPORT;
135 assertionpriority = JETSAM_PRIORITY_FOREGROUND;
136 set_memlimits(mypid, active_limit_mb, inactive_limit_mb, true, true);
137 set_priority(mypid, requestedpriority, 0, false);
138
139 if (turn_on_dirty_tracking) {
140 proc_track_dirty(mypid, (PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT | PROC_DIRTY_DEFER));
141
142 if (start_clean) {
143 proc_will_set_clean(mypid);
144 } else {
145 proc_will_set_dirty(mypid);
146 }
147 } else {
148 /*
149 * Do nothing.
150 * Acts like an app with no dirty tracking
151 * By default launches in the requested priority and is
152 * considered idle because it's below FG band.
153 */
154 }
155
156 proc_set_managed(mypid, true);
157
158 verbose = false;
159 (void)get_priority_props(mypid, verbose, NULL, NULL, NULL, NULL);
160
161 /* log current setup state */
162 T_LOG("SETUP STATE COMPLETE: Test %s", test);
163
164 T_SETUPEND;
165
166 int i;
167 boolean_t ret;
168 for (i = 0; i < 2; i++) {
169 if (i == 1 && turn_on_dirty_tracking) {
170 T_LOG("Avoid idle-deferred - sleeping for %d s", IDLE_AGEOUT_S);
171 sleep(IDLE_AGEOUT_S);
172
173 if (start_clean) {
174 proc_will_set_dirty(mypid);
175 } else {
176 proc_will_set_clean(mypid);
177 }
178
179 (void)get_priority_props(mypid, verbose, NULL, NULL, NULL, &state);
180 }
181
182 /*
183 * Relinquish assertion priority even though we don't
184 * currently hold an assertion priority.
185 */
186 for (count = 0; count < maxcount; count++) {
187 if (relinquish_assertion_priority(mypid, user_data)) {
188 T_ASSERT_FAIL("relinquish_assertion_priority failed");
189 }
190 }
191
192 /* Verify assertion state is relinquished */
193 (void)get_priority_props(mypid, verbose, NULL, NULL, NULL, &state);
194
195 ret = verify_assertion_state(state, ASSERTION_STATE_IS_RELINQUISHED);
196 T_QUIET;
197 T_ASSERT_TRUE(ret, "verify_assertion_state failed");
198
199
200
201 /*
202 * Set an assertion priority multiple times in a row.
203 */
204 for (count = 0; count < maxcount; count++) {
205 if (set_assertion_priority(mypid, assertionpriority, user_data) != 0) {
206 T_ASSERT_FAIL("set_assertion_priority failed");
207 }
208 }
209
210 /* Verify state holds an assertion priority */
211 (void)get_priority_props(mypid, verbose, NULL, NULL, NULL, &state);
212
213 ret = verify_assertion_state(state, ASSERTION_STATE_IS_SET);
214 T_QUIET;
215 T_ASSERT_TRUE(ret, "verify_assertion_state failed");
216 }
217 }
218
219 /*
220 * Process is dirty tracking and opts into pressured exit.
221 */
222 static void
memorystatus_assertion_test_allow_idle_exit(void)223 memorystatus_assertion_test_allow_idle_exit(void)
224 {
225 pid_t mypid = getpid();
226
227 /* these values will remain fixed during testing */
228 int active_limit_mb = 35; /* arbitrary */
229 int inactive_limit_mb = 25; /* arbitrary */
230
231 /* these values may vary during test */
232 int requestedpriority = JETSAM_PRIORITY_UI_SUPPORT;
233
234 T_SETUPBEGIN;
235
236 set_memlimits(mypid, active_limit_mb, inactive_limit_mb, true, true);
237 set_priority(mypid, requestedpriority, 0, false);
238
239 proc_track_dirty(mypid, (PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT | PROC_DIRTY_DEFER));
240 proc_set_managed(mypid, true);
241
242 proc_will_set_clean(mypid);
243
244 (void)check_properties(mypid, JETSAM_PRIORITY_IDLE_DEFERRED, active_limit_mb, 0x0, ASSERTION_STATE_IS_RELINQUISHED, "Clean start");
245
246 T_LOG("SETUP STATE COMPLETE");
247
248 int g_jetsam_aging_policy = 0;
249 /*
250 * Jetsam aging policy
251 * Failure to retrieve is not fatal.
252 */
253 size_t size = sizeof(g_jetsam_aging_policy);
254 if (sysctlbyname("kern.jetsam_aging_policy", &g_jetsam_aging_policy, &size, NULL, 0) != 0) {
255 T_LOG(__func__, true, "Unable to retrieve jetsam aging policy (not fatal)");
256 }
257
258 T_SETUPEND;
259
260 /*
261 * Relinquish assertion priority even though we don't hold it. No change in state expected.
262 */
263 T_LOG("********Test0 clean: no state change on relinquish");
264 relinquish_assertion_priority(mypid, 0xF00D);
265 (void)check_properties(mypid, JETSAM_PRIORITY_IDLE_DEFERRED, active_limit_mb, 0xF00D, ASSERTION_STATE_IS_RELINQUISHED, "Test0");
266
267 T_LOG("********Test1 clean: deferred now assertion[10]");
268 set_assertion_priority(mypid, JETSAM_PRIORITY_FOREGROUND, 0xFEED);
269 (void)check_properties(mypid, JETSAM_PRIORITY_FOREGROUND, active_limit_mb, 0xFEED, ASSERTION_STATE_IS_SET, "Test1");
270
271 /* Test2 */
272 T_LOG("********Test2 clean: assertion[10 -> 3]");
273 set_assertion_priority(mypid, JETSAM_PRIORITY_BACKGROUND, 0xFACE);
274 (void)check_properties(mypid, JETSAM_PRIORITY_BACKGROUND, active_limit_mb, 0xFACE, ASSERTION_STATE_IS_SET, "Test2");
275
276 /* Test3 */
277 T_LOG("********Test3 clean: assertion[3 -> 0], but now deferred");
278 relinquish_assertion_priority(mypid, 0xBEEF);
279 (void)check_properties(mypid, JETSAM_PRIORITY_IDLE_DEFERRED, active_limit_mb, 0xBEEF, ASSERTION_STATE_IS_RELINQUISHED, "Test3");
280
281 T_LOG("Avoid idle-deferred moving forward. Sleeping for %d s", IDLE_AGEOUT_S);
282 sleep(IDLE_AGEOUT_S);
283
284 (void)check_properties(mypid, JETSAM_PRIORITY_IDLE, inactive_limit_mb, 0xBEEF, ASSERTION_STATE_IS_RELINQUISHED, "Test3");
285
286 /* Test4 */
287 T_LOG("********Test4 clean: deferred now assertion[10]");
288 set_assertion_priority(mypid, JETSAM_PRIORITY_FOREGROUND, 0xFEED);
289 (void)check_properties(mypid, JETSAM_PRIORITY_FOREGROUND, active_limit_mb, 0xFEED, ASSERTION_STATE_IS_SET, "Test4");
290
291
292 /* Test5 */
293 T_LOG("********Test5 dirty: set dirty priority but assertion[10] prevails");
294 proc_will_set_dirty(mypid); /* active priority is less than FG*/
295 (void)check_properties(mypid, JETSAM_PRIORITY_FOREGROUND, active_limit_mb, 0xFEED, ASSERTION_STATE_IS_SET, "Test5");
296
297 /* Test6 */
298 T_LOG("********Test6 dirty: assertion[10 -> 3] but dirty priority prevails");
299 set_assertion_priority(mypid, JETSAM_PRIORITY_BACKGROUND, 0xFEEB); /* active priority is > BG */
300 (void)check_properties(mypid, JETSAM_PRIORITY_UI_SUPPORT, active_limit_mb, 0xFEEB, ASSERTION_STATE_IS_SET, "Test6");
301
302 /* Test7 */
303 T_LOG("********Test7 dirty: assertion[3 -> 0] but dirty prevails");
304 relinquish_assertion_priority(mypid, 0xBEEF);
305 (void)check_properties(mypid, JETSAM_PRIORITY_UI_SUPPORT, active_limit_mb, 0xBEEF, ASSERTION_STATE_IS_RELINQUISHED, "Test7");
306
307
308 /* Test8 */
309 T_LOG("********Test8 dirty: assertion[0 -> 10] overrides dirty");
310 set_assertion_priority(mypid, JETSAM_PRIORITY_FOREGROUND, 0xFEED);
311 (void)check_properties(mypid, JETSAM_PRIORITY_FOREGROUND, active_limit_mb, 0xFEED, ASSERTION_STATE_IS_SET, "Test8");
312
313 /* Test9 */
314 T_LOG("********Test9 dirty wants to go clean, but clean state is prevented as assertion[10] prevails");
315 proc_will_set_clean(mypid);
316 (void)check_properties(mypid, JETSAM_PRIORITY_FOREGROUND, active_limit_mb, 0xFEED, ASSERTION_STATE_IS_SET, "Test9");
317
318 /* Test10 */
319 T_LOG("********Test10 dirty goes dirty and stays dirty, and assertion[10] prevails again");
320 proc_will_set_dirty(mypid);
321 (void)check_properties(mypid, JETSAM_PRIORITY_FOREGROUND, active_limit_mb, 0xFEED, ASSERTION_STATE_IS_SET, "Test10");
322
323 /* Test11 */
324 T_LOG("********Test11 dirty: assertion[10 -> 3] but dirty prevails");
325 set_assertion_priority(mypid, JETSAM_PRIORITY_BACKGROUND, 0xFACE);
326 (void)check_properties(mypid, JETSAM_PRIORITY_UI_SUPPORT, active_limit_mb, 0xFACE, ASSERTION_STATE_IS_SET, "Test11");
327
328 /* Test12 */
329 T_LOG("********Test12 dirty: assertion[3 -> 0] but dirty prevails");
330 relinquish_assertion_priority(mypid, 0xBEEF);
331 (void)check_properties(mypid, JETSAM_PRIORITY_UI_SUPPORT, active_limit_mb, 0xBEEF, ASSERTION_STATE_IS_RELINQUISHED, "Test12");
332
333
334 /* Test13 */
335 T_LOG("********Test13 dirty goes clean: both assertion[0] and clean");
336 proc_will_set_clean(mypid);
337 /* For sysproc aging policy the daemon should be at idle deferred and with an active memory limit */
338 (void)check_properties(mypid, JETSAM_PRIORITY_IDLE_DEFERRED, active_limit_mb, 0xBEEF, ASSERTION_STATE_IS_RELINQUISHED, "Test13");
339 }
340
341 /*
342 * Process is dirty tracking and does not opt into pressured exit.
343 * This test lives above Foreground. Assertions will have no affect
344 * except where the assertion priority bumps it above the requested priority.
345 */
346 static void
memorystatus_assertion_test_do_not_allow_idle_exit(void)347 memorystatus_assertion_test_do_not_allow_idle_exit(void)
348 {
349 pid_t mypid = getpid();
350
351 /* these values will remain fixed during testing */
352 int active_limit_mb = 35; /* arbitrary */
353 int inactive_limit_mb = 25; /* arbitrary */
354 int requestedpriority = JETSAM_PRIORITY_AUDIO_AND_ACCESSORY;
355
356 T_SETUPBEGIN;
357
358 set_memlimits(mypid, active_limit_mb, inactive_limit_mb, true, true);
359 set_priority(mypid, requestedpriority, 0, false);
360 proc_track_dirty(mypid, (PROC_DIRTY_TRACK));
361 proc_set_managed(mypid, true);
362
363 proc_will_set_dirty(mypid);
364
365 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, active_limit_mb, 0x0, ASSERTION_STATE_IS_RELINQUISHED, "Dirty start");
366
367 proc_will_set_clean(mypid);
368
369 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, inactive_limit_mb, 0x0, ASSERTION_STATE_IS_RELINQUISHED, "Clean transition");
370
371 T_LOG("SETUP STATE COMPLETE");
372
373 T_SETUPEND;
374
375 /*
376 * Relinquish assertion priority even though we don't hold it. No change in state expected.
377 */
378
379
380 /* Test0 */
381 T_LOG("********Test0 clean: no state change on relinquish");
382 relinquish_assertion_priority(mypid, 0xF00D);
383 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, inactive_limit_mb, 0xF00D, ASSERTION_STATE_IS_RELINQUISHED, "Test0");
384
385 /* Test1 */
386 T_LOG("********Test1 clean: assertion[0 -> 10] but inactive priority prevails");
387 set_assertion_priority(mypid, JETSAM_PRIORITY_FOREGROUND, 0xFEED);
388 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, inactive_limit_mb, 0xFEED, ASSERTION_STATE_IS_SET, "Test1");
389
390 /* Test2 */
391 T_LOG("********Test2 clean: assertion[10 -> 3] but inactive priority prevails");
392 set_assertion_priority(mypid, JETSAM_PRIORITY_BACKGROUND, 0xFACE);
393 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, inactive_limit_mb, 0xFACE, ASSERTION_STATE_IS_SET, "Test2");
394
395 /* Test3 */
396 T_LOG("********Test3 clean: assertion[3 -> 0], but inactive priority prevails");
397 relinquish_assertion_priority(mypid, 0xBEEF);
398 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, inactive_limit_mb, 0xBEEF, ASSERTION_STATE_IS_RELINQUISHED, "Test3");
399
400 /* Test4 */
401 T_LOG("********Test4 go dirty: assertion[0] has no affect, active priority prevails");
402 proc_will_set_dirty(mypid);
403 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, active_limit_mb, 0xBEEF, ASSERTION_STATE_IS_RELINQUISHED, "Test4");
404
405 /* Test5 */
406 T_LOG("********Test5 dirty: assertion[0 -> 10] active priority prevails");
407 set_assertion_priority(mypid, JETSAM_PRIORITY_FOREGROUND, 0xFEED);
408 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, active_limit_mb, 0xFEED, ASSERTION_STATE_IS_SET, "Test5");
409
410 /* Test6 */
411 T_LOG("********Test6 dirty: assertion[10 -> 3] active priority prevails");
412 set_assertion_priority(mypid, JETSAM_PRIORITY_BACKGROUND, 0xFACE);
413 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, active_limit_mb, 0xFACE, ASSERTION_STATE_IS_SET, "Test6");
414
415 /* Test 7 */
416 T_LOG("********Test7 dirty: assertion[3 -> 0], active priority prevails");
417 relinquish_assertion_priority(mypid, 0xBEEF);
418 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, active_limit_mb, 0xBEEF, ASSERTION_STATE_IS_RELINQUISHED, "Test7");
419
420 /* Test8 */
421 T_LOG("********Test8 dirty: assertion[0 -> 19], dirty but now assertion[19] prevails");
422 set_assertion_priority(mypid, JETSAM_PRIORITY_CRITICAL, 0xFEED);
423 (void)check_properties(mypid, JETSAM_PRIORITY_CRITICAL, active_limit_mb, 0xFEED, ASSERTION_STATE_IS_SET, "Test8");
424
425
426 /* Test9 */
427 T_LOG("********Test9 go clean: inactive priority but assertion[19] prevails");
428 proc_will_set_clean(mypid);
429 (void)check_properties(mypid, JETSAM_PRIORITY_CRITICAL, inactive_limit_mb, 0xFEED, ASSERTION_STATE_IS_SET, "Test9");
430
431 /* Test10 */
432 T_LOG("********Test10 clean: assertion[19 -> 3] inactive limit prevails");
433 set_assertion_priority(mypid, JETSAM_PRIORITY_BACKGROUND, 0xFACE);
434 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, inactive_limit_mb, 0xFACE, ASSERTION_STATE_IS_SET, "Test10");
435
436
437 /* Test11 */
438 T_LOG("********Test11 clean: assertion[3 -> 0] inactive priority still prevails");
439 relinquish_assertion_priority(mypid, 0xBEEF);
440 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, inactive_limit_mb, 0xBEEF, ASSERTION_STATE_IS_RELINQUISHED, "Test11");
441
442 /* Test12 */
443 T_LOG("********Test12 dirty goes clean: both assertion[0] and clean");
444 proc_will_set_clean(mypid);
445 (void)check_properties(mypid, JETSAM_PRIORITY_AUDIO_AND_ACCESSORY, inactive_limit_mb, 0xBEEF, ASSERTION_STATE_IS_RELINQUISHED, "Test12");
446 }
447
448 T_DECL(assertion_test_bad_flags, "verify bad flag returns an error", T_META_TIMEOUT(30), T_META_ASROOT(true)) {
449 int err;
450 uint32_t flag = 0;
451
452 memorystatus_priority_properties_t mjp = { 0 };
453
454 mjp.priority = JETSAM_PRIORITY_FOREGROUND;
455 mjp.user_data = 0;
456
457 /*
458 * init a bad flag
459 */
460
461 flag = 0xf;
462
463 err = memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES, getpid(), flag, &mjp, sizeof(mjp));
464
465 T_QUIET;
466 T_ASSERT_POSIX_FAILURE(err, EINVAL, "MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES should fail with bad flags (err=%d)", err);
467 }
468
469 T_DECL(set_assertion_pri_unmanaged,
470 "verify that an unmanaged process cannot have assertion-driven priority set")
471 {
472 int err;
473 memorystatus_priority_properties_t mjp = {
474 .priority = JETSAM_PRIORITY_FOREGROUND,
475 .user_data = 0x0
476 };
477 err = memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES,
478 getpid(), MEMORYSTATUS_SET_PRIORITY_ASSERTION, &mjp, sizeof(mjp));
479
480 T_EXPECT_POSIX_FAILURE(err, EPERM,
481 "Should not be able to set assertion-driven priority of unmanaged process.");
482 }
483
484
485 #if TARGET_OS_OSX
486 /*
487 * Idle band deferral, aka aging band/demotion, has been disabled on macOS till
488 * we do the daemon hygiene work on macOS to make sure that processes don't change
489 * their role after spawn e.g. apps opting into dirty-tracking/idle-exit.
490 * The following set of tests rely on PROC_DIRTY_DEFER, aka aging bands, for the tests.
491 */
492 #else /* TARGET_OS_OSX */
493
494 T_HELPER_DECL(idle_age_as_app_then_sysproc,
495 "Launch as managed, begin idle aging, then enroll in ActivityTracking")
496 {
497 pid_t my_pid = getpid();
498 int32_t priority;
499
500 // Set self as managed and begin idle aging
501 proc_set_managed(my_pid, true);
502 set_priority(my_pid, JETSAM_PRIORITY_IDLE, 0, false);
503 // process must have a fatal memlimit to enroll in dirtytracking while
504 // managed
505 set_memlimits(my_pid, 100, 100, true, true);
506
507 get_priority_props(my_pid, FALSE, &priority, NULL, NULL, NULL);
508 T_EXPECT_EQ(priority, JETSAM_PRIORITY_AGING_BAND2, "Process is placed in App aging band");
509
510 // Enroll in dirty tracking
511 proc_track_dirty(my_pid,
512 (PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT | PROC_DIRTY_DEFER));
513
514 get_priority_props(my_pid, FALSE, &priority, NULL, NULL, NULL);
515 T_EXPECT_EQ(priority, JETSAM_PRIORITY_AGING_BAND1, "Process is placed in SysProc aging band");
516
517 T_LOG("Sleeping for %d sec...", IDLE_AGEOUT_S);
518 sleep(IDLE_AGEOUT_S);
519
520 get_priority_props(my_pid, FALSE, &priority, NULL, NULL, NULL);
521 T_EXPECT_EQ(priority, JETSAM_PRIORITY_IDLE, "Process ages to IDLE");
522 }
523
524 T_DECL(idle_aging_app_to_sysproc,
525 "Processes that transition from App -> SysProc while aging are deferred properly")
526 {
527 dt_helper_t helper = dt_child_helper("idle_age_as_app_then_sysproc");
528
529 dt_run_helpers(&helper, 1, 90);
530 }
531
532 T_HELPER_DECL(idle_age_timeout_importance_boost,
533 "Enroll in ActivityTracking, idle age with held assertion")
534 {
535 pid_t my_pid = getpid();
536 int32_t priority, ret;
537
538 // Set managed, enroll in dirty tracking, and pretend we have an importance assertion voucher
539 proc_set_managed(my_pid, true);
540 set_memlimits(my_pid, 100, 100, true, true);
541 proc_track_dirty(my_pid,
542 (PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT | PROC_DIRTY_DEFER));
543 ret = memorystatus_control(MEMORYSTATUS_CMD_SET_TESTING_PID, getpid(), MEMORYSTATUS_FLAGS_SET_IMP_TESTING_PID, NULL, 0);
544 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_CMD_SET_TESTING_PID");
545
546 get_priority_props(my_pid, FALSE, &priority, NULL, NULL, NULL);
547 T_EXPECT_EQ(priority, JETSAM_PRIORITY_AGING_BAND1, "Process is placed in SysProc aging band");
548
549 T_LOG("Sleeping for %d sec...", IDLE_AGEOUT_S);
550 sleep(IDLE_AGEOUT_S);
551
552 get_priority_props(my_pid, FALSE, &priority, NULL, NULL, NULL);
553 T_EXPECT_EQ(priority, JETSAM_PRIORITY_AGING_BAND1_STUCK, "Process ages to JETSAM_PRIORITY_AGING_BAND1_STUCK");
554
555 T_LOG("Sleeping for %d sec...", AGEOUT_STUCK_S);
556 sleep(AGEOUT_STUCK_S);
557
558 get_priority_props(my_pid, FALSE, &priority, NULL, NULL, NULL);
559 T_EXPECT_EQ(priority, JETSAM_PRIORITY_IDLE, "Process ages to JETSAM_PRIORITY_IDLE");
560 }
561
562 T_DECL(idle_aging_leaked_importance_boost,
563 "Idle aging with leaked importance boost")
564 {
565 dt_helper_t helper = dt_child_helper("idle_age_timeout_importance_boost");
566
567 dt_run_helpers(&helper, 1, 80);
568 }
569 T_DECL(assertion_test_repetitive_non_dirty_tracking, "Scenario #1 - repetitive assertion priority on non-dirty-tracking process", T_META_TIMEOUT(60), T_META_ASROOT(true)) {
570 /*
571 * Verify back-to-back assertion calls set assertion state as expected.
572 * false --> non-dirty-tracking process (like a typical app)
573 * false --> clean/dirty does not apply here
574 */
575
576 memorystatus_assertion_test_repetitive("Scenario #1", false, false);
577 }
578
579 T_DECL(assertion_test_repetitive_dirty_tracking_clean, "Scenario #2 - repetitive assertion priority on clean dirty-tracking process", T_META_TIMEOUT(60), T_META_ASROOT(true)) {
580 /*
581 * Verify back-to-back assertion calls set assertion state as expected.
582 * true --> dirty-tracking process (like a typical extension/widget)
583 * true --> start clean / inactive
584 * This will exercise idle-deferred paths.
585 */
586 memorystatus_assertion_test_repetitive("Scenario #2", true, true);
587 }
588
589 T_DECL(assertion_test_repetitive_dirty_tracking_dirty, "Scenario #3 - repetitive assertion priority on dirty dirty-tracking processes", T_META_TIMEOUT(60), T_META_ASROOT(true)) {
590 /*
591 * Verify back-to-back assertion calls set assertion state as expected.
592 * true --> dirty-tracking process (like a typical extension/widget)
593 * false --> start dirty / active state
594 * This will exercise idle-deferred paths.
595 */
596 memorystatus_assertion_test_repetitive("Scenario #3", true, false);
597 }
598
599
600 T_DECL(assertion_test_allow_idle_exit, "set assertion priorities on process supporting idle exit", T_META_TIMEOUT(360), T_META_ASROOT(true)) {
601 memorystatus_assertion_test_allow_idle_exit();
602 }
603
604 T_DECL(assertion_test_do_not_allow_idle_exit, "set assertion priorities on process no idle exit allowed", T_META_TIMEOUT(360), T_META_ASROOT(true)) {
605 memorystatus_assertion_test_do_not_allow_idle_exit();
606 }
607 #endif /* TARGET_OS_OSX */
608
609 T_DECL(daemon_memlimits,
610 "test that daemons have their memlimits set correctly according to dirtiness",
611 T_META_ENABLED(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR))
612 {
613 int ret;
614 const uint32_t memlimit_active = 6; /* 6 MB */
615 const uint32_t memlimit_inactive = 4; /* 4 MB */
616 pid_t pid = getpid();
617
618 set_priority(pid, JETSAM_PRIORITY_UI_SUPPORT, 0, false);
619 set_memlimits(pid, memlimit_active, memlimit_inactive, true, true);
620
621 ret = proc_track_dirty(pid, PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT | PROC_DIRTY_DEFER);
622 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "proc_track_dirty()");
623
624 check_properties(pid, JETSAM_PRIORITY_IDLE_DEFERRED, memlimit_active, 0x0,
625 false, "jetsam_test_daemon_memlimit - #1 post-track-dirty");
626
627 T_LOG("Sleeping for %d to allow idle-ageout.", IDLE_AGEOUT_S);
628 sleep(IDLE_AGEOUT_S);
629
630 check_properties(pid, JETSAM_PRIORITY_IDLE, memlimit_inactive, 0x0, false,
631 "jetsam_test_daemon_memlimit - #4 post-sleep");
632
633 proc_set_dirty(pid, true);
634
635 check_properties(pid, JETSAM_PRIORITY_UI_SUPPORT, memlimit_active, 0x0,
636 false, "jetsam_test_daemon_memlimit - #2 post-set-dirty");
637
638 proc_set_dirty(pid, false);
639
640 check_properties(pid, JETSAM_PRIORITY_IDLE_DEFERRED, memlimit_active, 0x0,
641 false, "jetsam_test_daemon_memlimit - #3 post-clear-dirty");
642
643 T_LOG("Sleeping for %d s to allow idle-ageout.", IDLE_AGEOUT_S);
644 sleep(IDLE_AGEOUT_S);
645
646 check_properties(pid, JETSAM_PRIORITY_IDLE, memlimit_inactive, 0x0, false,
647 "jetsam_test_daemon_memlimit - #4 post-sleep");
648
649 proc_set_dirty(pid, true);
650
651 check_properties(pid, JETSAM_PRIORITY_UI_SUPPORT, memlimit_active, 0x0,
652 false, "jetsam_test_daemon_memlimit - #5 post-set-dirty-2");
653
654 proc_set_dirty(pid, false);
655
656 check_properties(pid, JETSAM_PRIORITY_IDLE_DEFERRED, memlimit_active, 0x0, false,
657 "jetsam_test_daemon_memlimit - #6 post-clear-dirty-2");
658 }
659