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