xref: /xnu-11417.121.6/tests/vm/memorystatus_freeze_test.c (revision a1e26a70f38d1d7daa7b49b258e2f8538ad81650)
1 #include <stdio.h>
2 #include <signal.h>
3 #include <sys/proc.h>
4 #include <sys/sysctl.h>
5 #include <sys/kern_memorystatus.h>
6 #include <sys/kern_memorystatus_freeze.h>
7 #include <time.h>
8 #include <mach-o/dyld.h>
9 #include <mach/mach_vm.h>
10 #include <mach/vm_page_size.h>
11 #include <mach/shared_region.h>
12 #include <mach/mach.h>
13 #include <os/reason_private.h>
14 #include <TargetConditionals.h>
15 #include <sys/coalition.h>
16 #include <spawn_private.h>
17 
18 #ifdef T_NAMESPACE
19 #undef T_NAMESPACE
20 #endif
21 #include <darwintest.h>
22 #include <darwintest_utils.h>
23 
24 #include "memorystatus_assertion_helpers.h"
25 #include "test_utils.h"
26 
27 T_GLOBAL_META(
28 	T_META_NAMESPACE("xnu.memorystatus"),
29 	T_META_RADAR_COMPONENT_NAME("xnu"),
30 	T_META_RADAR_COMPONENT_VERSION("VM - memory pressure"),
31 	T_META_CHECK_LEAKS(false),
32 	T_META_OWNER("jarrad"),
33 	T_META_RUN_CONCURRENTLY(false)
34 	);
35 
36 #define MEM_SIZE_MB                     10
37 #define NUM_ITERATIONS          5
38 #define FREEZE_PAGES_MAX 256
39 
40 #define HAS_FREEZER ((TARGET_OS_IOS && !TARGET_OS_XR) || TARGET_OS_WATCH)
41 
42 #define CREATE_LIST(X) \
43 	X(SUCCESS) \
44 	X(TOO_FEW_ARGUMENTS) \
45 	X(SYSCTL_VM_PAGESIZE_FAILED) \
46 	X(VM_PAGESIZE_IS_ZERO) \
47 	X(DISPATCH_SOURCE_CREATE_FAILED) \
48 	X(INITIAL_SIGNAL_TO_PARENT_FAILED) \
49 	X(SIGNAL_TO_PARENT_FAILED) \
50 	X(MEMORYSTATUS_CONTROL_FAILED) \
51 	X(IS_FREEZABLE_NOT_AS_EXPECTED) \
52 	X(MEMSTAT_PRIORITY_CHANGE_FAILED) \
53 	X(INVALID_ALLOCATE_PAGES_ARGUMENTS) \
54 	X(FROZEN_BIT_SET) \
55 	X(FROZEN_BIT_NOT_SET) \
56 	X(MEMORYSTATUS_CONTROL_ERROR) \
57 	X(UNABLE_TO_ALLOCATE) \
58 	X(EXIT_CODE_MAX) \
59 
60 #define EXIT_CODES_ENUM(VAR) VAR,
61 enum exit_codes_num {
62 	CREATE_LIST(EXIT_CODES_ENUM)
63 };
64 
65 #define EXIT_CODES_STRING(VAR) #VAR,
66 static const char *exit_codes_str[] = {
67 	CREATE_LIST(EXIT_CODES_STRING)
68 };
69 
70 static int
get_vmpage_size(void)71 get_vmpage_size(void)
72 {
73 	int vmpage_size;
74 	size_t size = sizeof(vmpage_size);
75 	int ret = sysctlbyname("vm.pagesize", &vmpage_size, &size, NULL, 0);
76 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to query vm.pagesize");
77 	T_QUIET; T_ASSERT_GT(vmpage_size, 0, "vm.pagesize is not > 0");
78 	return vmpage_size;
79 }
80 
81 static pid_t child_pid = -1;
82 static int freeze_count = 0;
83 
84 void move_to_idle_band(pid_t);
85 void run_freezer_test(int);
86 void freeze_helper_process(void);
87 /* Gets and optionally sets the freeze pages max threshold */
88 int sysctl_freeze_pages_max(int* new_value);
89 
90 /* NB: in_shared_region and get_rprvt are pulled from the memorystatus unit test.
91  * We're moving away from those unit tests, so they're copied here.
92  */
93 
94 /* Cribbed from 'top'... */
95 static int
in_shared_region(mach_vm_address_t addr,cpu_type_t type)96 in_shared_region(mach_vm_address_t addr, cpu_type_t type)
97 {
98 	mach_vm_address_t base = 0, size = 0;
99 
100 	switch (type) {
101 	case CPU_TYPE_ARM:
102 		base = SHARED_REGION_BASE_ARM;
103 		size = SHARED_REGION_SIZE_ARM;
104 		break;
105 
106 	case CPU_TYPE_ARM64:
107 		base = SHARED_REGION_BASE_ARM64;
108 		size = SHARED_REGION_SIZE_ARM64;
109 		break;
110 
111 
112 	case CPU_TYPE_X86_64:
113 		base = SHARED_REGION_BASE_X86_64;
114 		size = SHARED_REGION_SIZE_X86_64;
115 		break;
116 
117 	case CPU_TYPE_I386:
118 		base = SHARED_REGION_BASE_I386;
119 		size = SHARED_REGION_SIZE_I386;
120 		break;
121 
122 	case CPU_TYPE_POWERPC:
123 		base = SHARED_REGION_BASE_PPC;
124 		size = SHARED_REGION_SIZE_PPC;
125 		break;
126 
127 	case CPU_TYPE_POWERPC64:
128 		base = SHARED_REGION_BASE_PPC64;
129 		size = SHARED_REGION_SIZE_PPC64;
130 		break;
131 
132 	default: {
133 		int t = type;
134 
135 		fprintf(stderr, "unknown CPU type: 0x%x\n", t);
136 		abort();
137 	}
138 	}
139 
140 	return addr >= base && addr < (base + size);
141 }
142 
143 static int orig_freezer;
144 
145 static void
restore_freezer()146 restore_freezer() {
147 	int ret;
148 	T_LOG("Restoring original vm.freeze_enabled value (%d)...", orig_freezer);
149 	ret = sysctlbyname("vm.freeze_enabled", NULL, NULL, &orig_freezer, sizeof(orig_freezer));
150 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Enabling freezer via sysctl");
151 }
152 
153 static void
check_for_and_enable_freezer()154 check_for_and_enable_freezer() {
155 	int ret, freeze_enabled;
156 	size_t size = sizeof(freeze_enabled);
157 
158 	ret = sysctlbyname("vm.freeze_enabled", &freeze_enabled, &size, NULL, 0);
159 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "Could not find vm.freeze_enabled");
160 
161 	if (!freeze_enabled) {
162 		T_LOG("Freezer not enabled, enabling...");
163 		orig_freezer = freeze_enabled;
164 		freeze_enabled = 1;
165 		ret = sysctlbyname("vm.freeze_enabled", NULL, NULL, &freeze_enabled, sizeof(freeze_enabled));
166 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Enabling freezer via sysctl");
167 		T_ATEND(restore_freezer);
168 	}
169 }
170 
171 /* Get the resident private memory of the given pid */
172 static unsigned long long
get_rprvt(pid_t pid)173 get_rprvt(pid_t pid)
174 {
175 	mach_port_name_t task;
176 	kern_return_t kr;
177 
178 	mach_vm_size_t rprvt = 0;
179 	mach_vm_size_t empty = 0;
180 	mach_vm_size_t fw_private = 0;
181 	mach_vm_size_t pagesize = vm_kernel_page_size;  // The vm_region page info is reported
182 	                                                // in terms of vm_kernel_page_size.
183 	mach_vm_size_t regs = 0;
184 
185 	mach_vm_address_t addr;
186 	mach_vm_size_t size;
187 
188 	int split = 0;
189 
190 	kr = task_for_pid(mach_task_self(), pid, &task);
191 	T_QUIET; T_ASSERT_TRUE(kr == KERN_SUCCESS, "Unable to get task_for_pid of child");
192 
193 	for (addr = 0;; addr += size) {
194 		vm_region_top_info_data_t info;
195 		mach_msg_type_number_t count = VM_REGION_TOP_INFO_COUNT;
196 		mach_port_t object_name;
197 
198 		kr = mach_vm_region(task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info, &count, &object_name);
199 		if (kr != KERN_SUCCESS) {
200 			break;
201 		}
202 
203 #if   defined (__arm64__)
204 		if (in_shared_region(addr, CPU_TYPE_ARM64)) {
205 #else
206 		if (in_shared_region(addr, CPU_TYPE_ARM)) {
207 #endif
208 			// Private Shared
209 			fw_private += info.private_pages_resident * pagesize;
210 
211 			/*
212 			 * Check if this process has the globally shared
213 			 * text and data regions mapped in.  If so, set
214 			 * split to TRUE and avoid checking
215 			 * again.
216 			 */
217 			if (split == FALSE && info.share_mode == SM_EMPTY) {
218 				vm_region_basic_info_data_64_t  b_info;
219 				mach_vm_address_t b_addr = addr;
220 				mach_vm_size_t b_size = size;
221 				count = VM_REGION_BASIC_INFO_COUNT_64;
222 
223 				kr = mach_vm_region(task, &b_addr, &b_size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&b_info, &count, &object_name);
224 				if (kr != KERN_SUCCESS) {
225 					break;
226 				}
227 
228 				if (b_info.reserved) {
229 					split = TRUE;
230 				}
231 			}
232 
233 			/*
234 			 * Short circuit the loop if this isn't a shared
235 			 * private region, since that's the only region
236 			 * type we care about within the current address
237 			 * range.
238 			 */
239 			if (info.share_mode != SM_PRIVATE) {
240 				continue;
241 			}
242 		}
243 
244 		regs++;
245 
246 		/*
247 		 * Update counters according to the region type.
248 		 */
249 
250 		if (info.share_mode == SM_COW && info.ref_count == 1) {
251 			// Treat single reference SM_COW as SM_PRIVATE
252 			info.share_mode = SM_PRIVATE;
253 		}
254 
255 		switch (info.share_mode) {
256 		case SM_LARGE_PAGE:
257 		// Treat SM_LARGE_PAGE the same as SM_PRIVATE
258 		// since they are not shareable and are wired.
259 		case SM_PRIVATE:
260 			rprvt += info.private_pages_resident * pagesize;
261 			rprvt += info.shared_pages_resident * pagesize;
262 			break;
263 
264 		case SM_EMPTY:
265 			empty += size;
266 			break;
267 
268 		case SM_COW:
269 		case SM_SHARED:
270 			if (pid == 0) {
271 				// Treat kernel_task specially
272 				if (info.share_mode == SM_COW) {
273 					rprvt += info.private_pages_resident * pagesize;
274 				}
275 				break;
276 			}
277 
278 			if (info.share_mode == SM_COW) {
279 				rprvt += info.private_pages_resident * pagesize;
280 			}
281 			break;
282 
283 		default:
284 			assert(0);
285 			break;
286 		}
287 	}
288 
289 	return rprvt;
290 }
291 
292 void
293 move_to_idle_band(pid_t pid)
294 {
295 	memorystatus_priority_properties_t props;
296 	/*
297 	 * Freezing a process also moves it to an elevated jetsam band in order to protect it from idle exits.
298 	 * So we move the child process to the idle band to mirror the typical 'idle app being frozen' scenario.
299 	 */
300 	props.priority = JETSAM_PRIORITY_IDLE;
301 	props.user_data = 0;
302 
303 	/*
304 	 * This requires us to run as root (in the absence of entitlement).
305 	 * Hence the T_META_ASROOT(true) in the T_HELPER_DECL.
306 	 */
307 	if (memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES, pid, 0, &props, sizeof(props))) {
308 		exit(MEMSTAT_PRIORITY_CHANGE_FAILED);
309 	}
310 }
311 
312 void
313 freeze_helper_process(void)
314 {
315 	size_t length;
316 	int ret, freeze_enabled, errno_freeze_sysctl;
317 	uint64_t resident_memory_before, resident_memory_after, vmpage_size;
318 	vmpage_size = (uint64_t) get_vmpage_size();
319 	resident_memory_before = get_rprvt(child_pid) / vmpage_size;
320 
321 	T_LOG("Freezing child pid %d", child_pid);
322 	ret = sysctlbyname("kern.memorystatus_freeze", NULL, NULL, &child_pid, sizeof(child_pid));
323 	errno_freeze_sysctl = errno;
324 	sleep(1);
325 
326 	/*
327 	 * The child process toggles its freezable state on each iteration.
328 	 * So a failure for every alternate freeze is expected.
329 	 */
330 	if (freeze_count % 2) {
331 		length = sizeof(freeze_enabled);
332 		T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", &freeze_enabled, &length, NULL, 0),
333 		    "failed to query vm.freeze_enabled");
334 		if (freeze_enabled) {
335 			errno = errno_freeze_sysctl;
336 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_freeze failed");
337 		} else {
338 			/* If freezer is disabled, skip the test. This can happen due to disk space shortage. */
339 			T_LOG("Freeze has been disabled. Terminating early.");
340 			T_END;
341 		}
342 		resident_memory_after = get_rprvt(child_pid) / vmpage_size;
343 		uint64_t freeze_pages_max = (uint64_t) sysctl_freeze_pages_max(NULL);
344 		T_QUIET; T_ASSERT_LT(resident_memory_after, resident_memory_before, "Freeze didn't reduce resident memory set");
345 		if (resident_memory_before > freeze_pages_max) {
346 			T_QUIET; T_ASSERT_LE(resident_memory_before - resident_memory_after, freeze_pages_max, "Freeze pages froze more than the threshold.");
347 		}
348 		ret = sysctlbyname("kern.memorystatus_thaw", NULL, NULL, &child_pid, sizeof(child_pid));
349 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_thaw failed");
350 	} else {
351 		T_QUIET; T_ASSERT_TRUE(ret != KERN_SUCCESS, "Freeze should have failed");
352 		T_LOG("Freeze failed as expected");
353 	}
354 
355 	freeze_count++;
356 
357 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGUSR1), "failed to send SIGUSR1 to child process");
358 }
359 
360 void
361 run_freezer_test(int num_pages)
362 {
363 	int ret;
364 	char sz_str[50];
365 	char **launch_tool_args;
366 	char testpath[PATH_MAX];
367 	uint32_t testpath_buf_size;
368 	dispatch_source_t ds_freeze, ds_proc;
369 
370 	signal(SIGUSR1, SIG_IGN);
371 	ds_freeze = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
372 	T_QUIET; T_ASSERT_NOTNULL(ds_freeze, "dispatch_source_create (ds_freeze)");
373 
374 	dispatch_source_set_event_handler(ds_freeze, ^{
375 		if (freeze_count < NUM_ITERATIONS) {
376 		        freeze_helper_process();
377 		} else {
378 		        kill(child_pid, SIGKILL);
379 		        dispatch_source_cancel(ds_freeze);
380 		}
381 	});
382 	dispatch_activate(ds_freeze);
383 
384 	testpath_buf_size = sizeof(testpath);
385 	ret = _NSGetExecutablePath(testpath, &testpath_buf_size);
386 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "_NSGetExecutablePath");
387 	T_LOG("Executable path: %s", testpath);
388 
389 	sprintf(sz_str, "%d", num_pages);
390 	launch_tool_args = (char *[]){
391 		testpath,
392 		"-n",
393 		"allocate_pages",
394 		"--",
395 		sz_str,
396 		NULL
397 	};
398 
399 	/* Spawn the child process. Suspend after launch until the exit proc handler has been set up. */
400 	ret = dt_launch_tool(&child_pid, launch_tool_args, true, NULL, NULL);
401 	if (ret != 0) {
402 		T_LOG("dt_launch tool returned %d with error code %d", ret, errno);
403 	}
404 	T_QUIET; T_ASSERT_POSIX_SUCCESS(child_pid, "dt_launch_tool");
405 
406 	ds_proc = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)child_pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue());
407 	T_QUIET; T_ASSERT_NOTNULL(ds_proc, "dispatch_source_create (ds_proc)");
408 
409 	dispatch_source_set_event_handler(ds_proc, ^{
410 		int status = 0, code = 0;
411 		pid_t rc = waitpid(child_pid, &status, 0);
412 		T_QUIET; T_ASSERT_EQ(rc, child_pid, "waitpid");
413 		code = WEXITSTATUS(status);
414 
415 		if (code == 0) {
416 		        T_END;
417 		} else if (code > 0 && code < EXIT_CODE_MAX) {
418 		        T_ASSERT_FAIL("Child exited with %s", exit_codes_str[code]);
419 		} else {
420 		        T_ASSERT_FAIL("Child exited with unknown exit code %d", code);
421 		}
422 	});
423 	dispatch_activate(ds_proc);
424 
425 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGCONT), "failed to send SIGCONT to child process");
426 	dispatch_main();
427 }
428 
429 static void
430 allocate_pages(int num_pages)
431 {
432 	int i, j, vmpgsize;
433 	char val;
434 	__block int num_iter = 0;
435 	__block char **buf;
436 	dispatch_source_t ds_signal;
437 	vmpgsize = get_vmpage_size();
438 	if (num_pages < 1) {
439 		printf("Invalid number of pages to allocate: %d\n", num_pages);
440 		exit(INVALID_ALLOCATE_PAGES_ARGUMENTS);
441 	}
442 
443 	buf = (char**)malloc(sizeof(char*) * (size_t)num_pages);
444 
445 	/* Gives us the compression ratio we see in the typical case (~2.7) */
446 	for (j = 0; j < num_pages; j++) {
447 		buf[j] = (char*)malloc((size_t)vmpgsize * sizeof(char));
448 		val = 0;
449 		for (i = 0; i < vmpgsize; i += 16) {
450 			memset(&buf[j][i], val, 16);
451 			if (i < 3400 * (vmpgsize / 4096)) {
452 				val++;
453 			}
454 		}
455 	}
456 
457 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), dispatch_get_main_queue(), ^{
458 		/* Signal to the parent that we're done allocating and it's ok to freeze us */
459 		printf("[%d] Sending initial signal to parent to begin freezing\n", getpid());
460 		if (kill(getppid(), SIGUSR1) != 0) {
461 		        exit(INITIAL_SIGNAL_TO_PARENT_FAILED);
462 		}
463 	});
464 
465 	signal(SIGUSR1, SIG_IGN);
466 	ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
467 	if (ds_signal == NULL) {
468 		exit(DISPATCH_SOURCE_CREATE_FAILED);
469 	}
470 
471 	dispatch_source_set_event_handler(ds_signal, ^{
472 		int current_state, new_state;
473 		volatile int tmp;
474 
475 		/* Make sure all the pages are accessed before trying to freeze again */
476 		for (int x = 0; x < num_pages; x++) {
477 		        tmp = buf[x][0];
478 		}
479 
480 		current_state = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FREEZABLE, getpid(), 0, NULL, 0);
481 		/* Sysprocs start off as unfreezable. Verify that first. */
482 		if (num_iter == 0 && current_state != 0) {
483 		        exit(IS_FREEZABLE_NOT_AS_EXPECTED);
484 		}
485 
486 		/* Toggle freezable state */
487 		new_state = (current_state) ? 0: 1;
488 		printf("[%d] Changing state from %s to %s\n", getpid(),
489 		(current_state) ? "freezable": "unfreezable", (new_state) ? "freezable": "unfreezable");
490 		if (memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE, getpid(), (uint32_t)new_state, NULL, 0) != KERN_SUCCESS) {
491 		        exit(MEMORYSTATUS_CONTROL_FAILED);
492 		}
493 
494 		/* Verify that the state has been set correctly */
495 		current_state = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FREEZABLE, getpid(), 0, NULL, 0);
496 		if (new_state != current_state) {
497 		        exit(IS_FREEZABLE_NOT_AS_EXPECTED);
498 		}
499 		num_iter++;
500 
501 		if (kill(getppid(), SIGUSR1) != 0) {
502 		        exit(SIGNAL_TO_PARENT_FAILED);
503 		}
504 	});
505 	dispatch_activate(ds_signal);
506 	move_to_idle_band(getpid());
507 
508 	dispatch_main();
509 }
510 
511 T_HELPER_DECL(allocate_pages,
512     "allocates pages to freeze",
513     T_META_ASROOT(true)) {
514 	if (argc < 1) {
515 		exit(TOO_FEW_ARGUMENTS);
516 	}
517 
518 	int num_pages = atoi(argv[0]);
519 	allocate_pages(num_pages);
520 }
521 
522 T_DECL(memorystatus_freeze_default_state, "Test that the freezer is enabled or disabled as expected by default.", T_META_ASROOT(true), T_META_TAG_VM_NOT_PREFERRED) {
523 
524 #if TARGET_OS_IOS || TARGET_OS_WATCH
525 	bool expected_freeze_enabled = true;
526 #else
527 	bool expected_freeze_enabled = false;
528 #endif
529 
530 
531 	int freeze_enabled;
532 	size_t length = sizeof(freeze_enabled);
533 	int ret = sysctlbyname("vm.freeze_enabled", &freeze_enabled, &length, NULL, 0);
534 
535 	if (ret != 0) {
536 		if (expected_freeze_enabled) {
537 			T_ASSERT_FAIL("Expected freezer enabled but did not find sysctl vm.freeze_enabled");
538 		} else {
539 			T_PASS("Did not expect freezer to be enabled and did not find sysctl vm.freeze_enabled");
540 		}
541 	} else {
542 		T_ASSERT_EQ(freeze_enabled, expected_freeze_enabled, "Expected freezer sysctl status %d, got %d", expected_freeze_enabled, freeze_enabled);
543 	}
544 }
545 
546 T_DECL(freeze, "VM freezer test",
547 	T_META_ENABLED(HAS_FREEZER),
548 	T_META_ASROOT(true),
549 	T_META_TAG_VM_NOT_PREFERRED) {
550 	check_for_and_enable_freezer();
551 	run_freezer_test(
552 		(MEM_SIZE_MB << 20) / get_vmpage_size());
553 }
554 
555 static int old_freeze_pages_max = 0;
556 static void
557 reset_freeze_pages_max(void)
558 {
559 	if (old_freeze_pages_max != 0) {
560 		sysctl_freeze_pages_max(&old_freeze_pages_max);
561 	}
562 }
563 
564 int
565 sysctl_freeze_pages_max(int* new_value)
566 {
567 	static int set_end_handler = false;
568 	int freeze_pages_max, ret;
569 	size_t size = sizeof(freeze_pages_max);
570 	ret = sysctlbyname("kern.memorystatus_freeze_pages_max", &freeze_pages_max, &size, new_value, size);
571 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Unable to query kern.memorystatus_freeze_pages_max");
572 	if (!set_end_handler) {
573 		// Save the original value and instruct darwintest to restore it after the test completes
574 		old_freeze_pages_max = freeze_pages_max;
575 		T_ATEND(reset_freeze_pages_max);
576 		set_end_handler = true;
577 	}
578 	return old_freeze_pages_max;
579 }
580 
581 T_DECL(freeze_over_max_threshold, "Max Freeze Threshold is Enforced",
582 	T_META_ENABLED(HAS_FREEZER),
583 	T_META_ASROOT(true),
584 	T_META_TAG_VM_NOT_PREFERRED) {
585 	int freeze_pages_max = FREEZE_PAGES_MAX;
586 	check_for_and_enable_freezer();
587 	sysctl_freeze_pages_max(&freeze_pages_max);
588 	run_freezer_test(FREEZE_PAGES_MAX * 2);
589 }
590 
591 T_HELPER_DECL(frozen_background, "Frozen background process",
592 	T_META_ENABLED(HAS_FREEZER),
593 	T_META_ASROOT(true)) {
594 	kern_return_t kern_ret;
595 	check_for_and_enable_freezer();
596 	/* Set the process to freezable */
597 	kern_ret = memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE, getpid(), 1, NULL, 0);
598 	T_QUIET; T_ASSERT_EQ(kern_ret, KERN_SUCCESS, "set process is freezable");
599 	/* Signal to our parent that we can be frozen */
600 	if (kill(getppid(), SIGUSR1) != 0) {
601 		T_LOG("Unable to signal to parent process!");
602 		exit(1);
603 	}
604 	while (1) {
605 		;
606 	}
607 }
608 
609 static void
610 freeze_process(pid_t pid)
611 {
612 	int ret, freeze_enabled, errno_freeze_sysctl;
613 	size_t length;
614 	T_LOG("Freezing pid %d", pid);
615 
616 	ret = sysctlbyname("kern.memorystatus_freeze", NULL, NULL, &pid, sizeof(pid));
617 	errno_freeze_sysctl = errno;
618 	length = sizeof(freeze_enabled);
619 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", &freeze_enabled, &length, NULL, 0),
620 	    "failed to query vm.freeze_enabled");
621 	if (freeze_enabled) {
622 		errno = errno_freeze_sysctl;
623 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_freeze failed");
624 	} else {
625 		/* If freezer is disabled, skip the test. This can happen due to disk space shortage. */
626 		T_LOG("Freeze has been disabled. Terminating early.");
627 		T_END;
628 	}
629 }
630 
631 static uint32_t max_frozen_demotions_daily_default;
632 
633 static void
634 reset_max_frozen_demotions_daily(void)
635 {
636 	int sysctl_ret = sysctlbyname("kern.memorystatus_max_freeze_demotions_daily", NULL, NULL, &max_frozen_demotions_daily_default, sizeof(max_frozen_demotions_daily_default));
637 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctl_ret, "set kern.memorystatus_max_freeze_demotions_daily to default");
638 }
639 
640 static void
641 allow_unlimited_demotions(void)
642 {
643 	size_t size = sizeof(max_frozen_demotions_daily_default);
644 	uint32_t new_value = UINT32_MAX;
645 	int sysctl_ret = sysctlbyname("kern.memorystatus_max_freeze_demotions_daily", &max_frozen_demotions_daily_default, &size, &new_value, sizeof(new_value));
646 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctl_ret, "kern.memorystatus_max_freeze_demotions_daily = UINT32_MAX");
647 	T_ATEND(reset_max_frozen_demotions_daily);
648 }
649 
650 static void
651 memorystatus_assertion_test_demote_frozen(void)
652 {
653 	/*
654 	 * Test that if we assert a priority on a process, freeze it, and then demote all frozen processes, it does not get demoted below the asserted priority.
655 	 * Then remove thee assertion, and ensure it gets demoted properly.
656 	 */
657 	/* these values will remain fixed during testing */
658 	int             active_limit_mb = 15;   /* arbitrary */
659 	int             inactive_limit_mb = 7;  /* arbitrary */
660 	__block int             demote_value = 1;
661 	/* Launch the child process, and elevate its priority */
662 	int requestedpriority;
663 	dispatch_source_t ds_signal, ds_exit;
664 	requestedpriority = JETSAM_PRIORITY_FREEZER;
665 	allow_unlimited_demotions();
666 
667 	/* Wait for the child process to tell us that it's ready, and then freeze it */
668 	signal(SIGUSR1, SIG_IGN);
669 	ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
670 	T_QUIET; T_ASSERT_NOTNULL(ds_signal, "dispatch_source_create");
671 	dispatch_source_set_event_handler(ds_signal, ^{
672 		int sysctl_ret;
673 		/* Freeze the process, trigger agressive demotion, and check that it hasn't been demoted. */
674 		freeze_process(child_pid);
675 		/* Agressive demotion */
676 		sysctl_ret = sysctlbyname("kern.memorystatus_demote_frozen_processes", NULL, NULL, &demote_value, sizeof(demote_value));
677 		T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctl_ret, "sysctl kern.memorystatus_demote_frozen_processes succeeded");
678 		/* Check */
679 		(void)check_properties(child_pid, requestedpriority, inactive_limit_mb, 0x0, ASSERTION_STATE_IS_SET, "Priority was set");
680 		T_LOG("Relinquishing our assertion.");
681 		/* Relinquish our assertion, and check that it gets demoted. */
682 		relinquish_assertion_priority(child_pid, 0x0);
683 		(void)check_properties(child_pid, JETSAM_PRIORITY_AGING_BAND2, inactive_limit_mb, 0x0, ASSERTION_STATE_IS_RELINQUISHED, "Assertion was reqlinquished.");
684 		/* Kill the child */
685 		T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGKILL), "Killed child process");
686 		T_END;
687 	});
688 
689 	/* Launch the child process and set the initial properties on it. */
690 	child_pid = launch_background_helper("frozen_background", false, true);
691 	set_memlimits(child_pid, active_limit_mb, inactive_limit_mb, false, false);
692 	set_assertion_priority(child_pid, requestedpriority, 0x0);
693 	(void)check_properties(child_pid, requestedpriority, active_limit_mb, 0x0, ASSERTION_STATE_IS_SET, "Priority was set");
694 	/* Listen for exit. */
695 	ds_exit = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)child_pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue());
696 	dispatch_source_set_event_handler(ds_exit, ^{
697 		int status = 0, code = 0;
698 		pid_t rc = waitpid(child_pid, &status, 0);
699 		T_QUIET; T_ASSERT_EQ(rc, child_pid, "waitpid");
700 		code = WEXITSTATUS(status);
701 		T_QUIET; T_ASSERT_EQ(code, 0, "Child exited cleanly");
702 		T_END;
703 	});
704 
705 	dispatch_activate(ds_exit);
706 	dispatch_activate(ds_signal);
707 	dispatch_main();
708 }
709 
710 T_DECL(assertion_test_demote_frozen, "demoted frozen process goes to asserted priority.",
711 	/* T_META_ENABLED(HAS_FREEZER), */
712 	T_META_ASROOT(true),
713 	T_META_TAG_VM_NOT_PREFERRED,
714 	T_META_ENABLED(false) /* rdar://133461319 */)
715 {
716 	check_for_and_enable_freezer();
717 	memorystatus_assertion_test_demote_frozen();
718 }
719 
720 static unsigned int
721 get_freeze_daily_pages_max(void)
722 {
723 	unsigned int memorystatus_freeze_daily_mb_max;
724 	size_t length = sizeof(memorystatus_freeze_daily_mb_max);
725 	int ret = sysctlbyname("kern.memorystatus_freeze_daily_mb_max", &memorystatus_freeze_daily_mb_max, &length, NULL, 0);
726 	T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_freeze_daily_mb_max");
727 	return memorystatus_freeze_daily_mb_max * 1024UL * 1024UL / vm_kernel_page_size;
728 }
729 
730 static uint64_t
731 get_budget_multiplier(void)
732 {
733 	uint64_t budget_multiplier = 0;
734 	size_t size = sizeof(budget_multiplier);
735 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_budget_multiplier", &budget_multiplier, &size, NULL, 0),
736 	    "get kern.memorystatus_freeze_budget_multiplier");
737 	return budget_multiplier;
738 }
739 static void
740 set_budget_multiplier(uint64_t multiplier)
741 {
742 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_budget_multiplier", NULL, NULL, &multiplier, sizeof(multiplier)),
743 	    "set kern.memorystatus_freeze_budget_multiplier");
744 }
745 
746 static uint64_t original_budget_multiplier;
747 static void
748 reset_budget_multiplier(void)
749 {
750 	set_budget_multiplier(original_budget_multiplier);
751 }
752 
753 static unsigned int
754 get_memorystatus_swap_all_apps(void)
755 {
756 	unsigned int memorystatus_swap_all_apps;
757 	size_t length = sizeof(memorystatus_swap_all_apps);
758 	int ret = sysctlbyname("kern.memorystatus_swap_all_apps", &memorystatus_swap_all_apps, &length, NULL, 0);
759 	T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_swap_all_apps");
760 	return memorystatus_swap_all_apps;
761 }
762 
763 T_DECL(budget_replenishment, "budget replenishes properly",
764 	T_META_ENABLED(HAS_FREEZER),
765     T_META_REQUIRES_SYSCTL_NE("kern.memorystatus_freeze_daily_mb_max", UINT32_MAX),
766 	T_META_TAG_VM_NOT_PREFERRED) {
767 	size_t length;
768 	int ret;
769 	static unsigned int kTestIntervalSecs = 60 * 60 * 32; // 32 Hours
770 	unsigned int memorystatus_freeze_daily_pages_max;
771 	static unsigned int kFixedPointFactor = 100;
772 	static unsigned int kNumSecondsInDay = 60 * 60 * 24;
773 	unsigned int new_budget, expected_new_budget_pages;
774 	size_t new_budget_ln;
775 	vm_size_t page_size = vm_kernel_page_size;
776 
777 	check_for_and_enable_freezer();
778 
779 	original_budget_multiplier = get_budget_multiplier();
780 	T_ATEND(reset_budget_multiplier);
781 	set_budget_multiplier(100);
782 	/*
783 	 * Calculate a new budget as if the previous interval expired kTestIntervalSecs
784 	 * ago and we used up its entire budget.
785 	 */
786 	length = sizeof(kTestIntervalSecs);
787 	new_budget_ln = sizeof(new_budget);
788 	ret = sysctlbyname("vm.memorystatus_freeze_calculate_new_budget", &new_budget, &new_budget_ln, &kTestIntervalSecs, length);
789 	T_ASSERT_POSIX_SUCCESS(ret, "vm.memorystatus_freeze_calculate_new_budget");
790 
791 	memorystatus_freeze_daily_pages_max = get_freeze_daily_pages_max();
792 	T_LOG("memorystatus_freeze_daily_pages_max %u", memorystatus_freeze_daily_pages_max);
793 	T_LOG("page_size %lu", page_size);
794 
795 	/*
796 	 * We're kTestIntervalSecs past a new interval. Which means we are owed kNumSecondsInDay
797 	 * seconds of budget.
798 	 */
799 	expected_new_budget_pages = memorystatus_freeze_daily_pages_max;
800 	T_LOG("expected_new_budget_pages before %u", expected_new_budget_pages);
801 	T_ASSERT_EQ(kTestIntervalSecs, 60 * 60 * 32, "kTestIntervalSecs did not change");
802 	expected_new_budget_pages += ((kTestIntervalSecs * kFixedPointFactor) / (kNumSecondsInDay)
803 	    * memorystatus_freeze_daily_pages_max) / kFixedPointFactor;
804 	if (get_memorystatus_swap_all_apps()) {
805 		/*
806 		 * memorystatus_swap_all_apps is enabled; the budget is unlimited
807 		 */
808 		expected_new_budget_pages = UINT32_MAX;
809 	}
810 	T_LOG("expected_new_budget_pages after %u", expected_new_budget_pages);
811 	T_LOG("memorystatus_freeze_daily_pages_max after %u", memorystatus_freeze_daily_pages_max);
812 
813 	T_QUIET; T_ASSERT_EQ(new_budget, expected_new_budget_pages, "Calculate new budget behaves correctly.");
814 }
815 
816 static void
817 get_frozen_list(global_frozen_procs_t *frozen_procs)
818 {
819 	int bytes_written;
820 	bytes_written = memorystatus_control(MEMORYSTATUS_CMD_FREEZER_CONTROL, 0, FREEZER_CONTROL_GET_PROCS, frozen_procs, sizeof(global_frozen_procs_t));
821 	T_QUIET; T_ASSERT_LE((size_t) bytes_written, sizeof(global_frozen_procs_t), "Didn't overflow buffer");
822 	T_QUIET; T_ASSERT_GT(bytes_written, 0, "Wrote someting");
823 }
824 
825 static bool
826 is_proc_in_frozen_list(pid_t pid, char* name, size_t name_len)
827 {
828 	bool found = false;
829 	global_frozen_procs_t *frozen_procs = malloc(sizeof(global_frozen_procs_t));
830 	T_QUIET; T_ASSERT_NOTNULL(frozen_procs, "malloc");
831 
832 	get_frozen_list(frozen_procs);
833 
834 	for (size_t i = 0; i < frozen_procs->gfp_num_frozen; i++) {
835 		if (frozen_procs->gfp_procs[i].fp_pid == pid) {
836 			found = true;
837 			strlcpy(name, frozen_procs->gfp_procs[i].fp_name, name_len);
838 		}
839 	}
840 	return found;
841 }
842 
843 static void
844 unset_testing_pid(void)
845 {
846 	int ret;
847 	ret = memorystatus_control(MEMORYSTATUS_CMD_SET_TESTING_PID, 0, MEMORYSTATUS_FLAGS_UNSET_TESTING_PID, NULL, 0);
848 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, 0, "Drop ownership of jetsam snapshot");
849 }
850 
851 static void
852 set_testing_pid(void)
853 {
854 	int ret;
855 	ret = memorystatus_control(MEMORYSTATUS_CMD_SET_TESTING_PID, 0, MEMORYSTATUS_FLAGS_SET_TESTING_PID, NULL, 0);
856 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Take ownership of jetsam snapshot");
857 	T_ATEND(unset_testing_pid);
858 }
859 
860 /*
861  * Retrieve a jetsam snapshot.
862  *
863  * return:
864  *      pointer to snapshot.
865  *
866  *	Caller is responsible for freeing snapshot.
867  */
868 static
869 memorystatus_jetsam_snapshot_t *
870 get_jetsam_snapshot(uint32_t flags, bool empty_allowed)
871 {
872 	memorystatus_jetsam_snapshot_t * snapshot = NULL;
873 	int ret;
874 	uint32_t size;
875 
876 	ret = memorystatus_control(MEMORYSTATUS_CMD_GET_JETSAM_SNAPSHOT, 0, flags, NULL, 0);
877 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, 0, "Get jetsam snapshot size");
878 	size = (uint32_t) ret;
879 	if (size == 0 && empty_allowed) {
880 		return snapshot;
881 	}
882 
883 	snapshot = (memorystatus_jetsam_snapshot_t*)malloc(size);
884 	T_QUIET; T_ASSERT_NOTNULL(snapshot, "Allocate snapshot of size %d", size);
885 
886 	ret = memorystatus_control(MEMORYSTATUS_CMD_GET_JETSAM_SNAPSHOT, 0, flags, snapshot, size);
887 	T_QUIET; T_ASSERT_GT(size, 0, "Get jetsam snapshot");
888 
889 	if (((size - sizeof(memorystatus_jetsam_snapshot_t)) / sizeof(memorystatus_jetsam_snapshot_entry_t)) != snapshot->entry_count) {
890 		T_FAIL("Malformed snapshot: %d! Expected %ld + %zd x %ld = %ld\n", size,
891 		    sizeof(memorystatus_jetsam_snapshot_t), snapshot->entry_count, sizeof(memorystatus_jetsam_snapshot_entry_t),
892 		    sizeof(memorystatus_jetsam_snapshot_t) + (snapshot->entry_count * sizeof(memorystatus_jetsam_snapshot_entry_t)));
893 		if (snapshot) {
894 			free(snapshot);
895 		}
896 	}
897 
898 	return snapshot;
899 }
900 
901 /*
902  * Look for the given pid in the snapshot.
903  *
904  * return:
905  *     pointer to pid's entry or NULL if pid is not found.
906  *
907  * Caller has ownership of snapshot before and after call.
908  */
909 static
910 memorystatus_jetsam_snapshot_entry_t *
911 get_jetsam_snapshot_entry(memorystatus_jetsam_snapshot_t *snapshot, pid_t pid)
912 {
913 	T_QUIET; T_ASSERT_NOTNULL(snapshot, "Got snapshot");
914 	for (size_t i = 0; i < snapshot->entry_count; i++) {
915 		memorystatus_jetsam_snapshot_entry_t *curr = &(snapshot->entries[i]);
916 		if (curr->pid == pid) {
917 			return curr;
918 		}
919 	}
920 
921 	return NULL;
922 }
923 
924 static void
925 resume_and_kill_proc(pid_t pid)
926 {
927 	int ret = pid_resume(pid);
928 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "proc resumed after freeze");
929 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(pid, SIGKILL), "Killed process");
930 }
931 
932 static void
933 resume_and_kill_child(void)
934 {
935 	/* Used for test cleanup. proc might not be suspended so pid_resume might fail. */
936 	pid_resume(child_pid);
937 	kill(child_pid, SIGKILL);
938 }
939 
940 static dispatch_source_t
941 run_block_after_signal(int sig, dispatch_block_t block)
942 {
943 	dispatch_source_t ds_signal;
944 	signal(sig, SIG_IGN);
945 	ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) sig, 0, dispatch_get_main_queue());
946 	T_QUIET; T_ASSERT_NOTNULL(ds_signal, "dispatch_source_create");
947 	dispatch_source_set_event_handler(ds_signal, block);
948 	return ds_signal;
949 }
950 
951 /*
952  * Launches the child & runs the given block after the child signals.
953  * If exit_with_child is true, the test will exit when the child exits.
954  */
955 static void
956 test_after_background_helper_launches(bool exit_with_child, const char* variant, dispatch_block_t test_block)
957 {
958 	dispatch_source_t ds_signal, ds_exit;
959 
960 	ds_signal = run_block_after_signal(SIGUSR1, test_block);
961 	/* Launch the child process. */
962 	child_pid = launch_background_helper(variant, false, true);
963 	T_ATEND(resume_and_kill_child);
964 	/* Listen for exit. */
965 	if (exit_with_child) {
966 		ds_exit = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)child_pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue());
967 		dispatch_source_set_event_handler(ds_exit, ^{
968 			int status = 0, code = 0;
969 			pid_t rc = waitpid(child_pid, &status, 0);
970 			T_QUIET; T_ASSERT_EQ(rc, child_pid, "waitpid");
971 			code = WEXITSTATUS(status);
972 			if (code != 0) {
973 			        T_LOG("Child exited with error: %s", exit_codes_str[code]);
974 			}
975 			T_QUIET; T_ASSERT_EQ(code, 0, "Child exited cleanly");
976 			T_END;
977 		});
978 
979 		dispatch_activate(ds_exit);
980 	}
981 	dispatch_activate(ds_signal);
982 }
983 
984 T_DECL(get_frozen_procs, "List processes in the freezer",
985 	T_META_ENABLED(HAS_FREEZER),
986 	T_META_TAG_VM_NOT_PREFERRED) {
987 
988 	check_for_and_enable_freezer();
989 	test_after_background_helper_launches(true, "frozen_background", ^{
990 		proc_name_t name;
991 		/* Place the child in the idle band so that it gets elevated like a typical app. */
992 		move_to_idle_band(child_pid);
993 		/* Freeze the process, and check that it's in the list of frozen processes. */
994 		freeze_process(child_pid);
995 		/* Check */
996 		T_QUIET; T_ASSERT_TRUE(is_proc_in_frozen_list(child_pid, name, sizeof(name)), "Found proc in frozen list");
997 		T_QUIET; T_EXPECT_EQ_STR(name, "memorystatus_freeze_test", "Proc has correct name");
998 		/* Kill the child */
999 		T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGKILL), "Killed child process");
1000 		T_END;
1001 	});
1002 	dispatch_main();
1003 }
1004 
1005 T_DECL(frozen_to_swap_accounting, "jetsam snapshot has frozen_to_swap accounting",
1006 	T_META_ENABLED(HAS_FREEZER),
1007 	T_META_TAG_VM_NOT_PREFERRED) {
1008 	static const size_t kSnapshotSleepDelay = 5;
1009 	static const size_t kFreezeToDiskMaxDelay = 60;
1010 
1011 
1012 	check_for_and_enable_freezer();
1013 	test_after_background_helper_launches(true, "frozen_background", ^{
1014 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
1015 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
1016 		/* Place the child in the idle band so that it gets elevated like a typical app. */
1017 		move_to_idle_band(child_pid);
1018 		freeze_process(child_pid);
1019 		/*
1020 		 * Wait until the child's pages get paged out to disk.
1021 		 * If we don't see any pages get sent to disk before kFreezeToDiskMaxDelay seconds,
1022 		 * something is either wrong with the compactor or the accounting.
1023 		 */
1024 		for (size_t i = 0; i < kFreezeToDiskMaxDelay / kSnapshotSleepDelay; i++) {
1025 		        snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
1026 		        child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1027 		        T_QUIET; T_ASSERT_NOTNULL(child_entry, "Found child in snapshot");
1028 		        if (child_entry->jse_frozen_to_swap_pages > 0) {
1029 		                break;
1030 			}
1031 		        free(snapshot);
1032 		        sleep(kSnapshotSleepDelay);
1033 		}
1034 		T_QUIET; T_ASSERT_GT(child_entry->jse_frozen_to_swap_pages, 0ULL, "child has some pages in swap");
1035 		free(snapshot);
1036 		/* Kill the child */
1037 		T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGKILL), "Killed child process");
1038 		T_END;
1039 	});
1040 	dispatch_main();
1041 }
1042 
1043 T_DECL(freezer_snapshot, "App kills are recorded in the freezer snapshot",
1044 	T_META_ENABLED(HAS_FREEZER),
1045 	T_META_TAG_VM_NOT_PREFERRED) {
1046 	check_for_and_enable_freezer();
1047 
1048 	/* Take ownership of the snapshot to ensure we don't race with another process trying to consume them. */
1049 	set_testing_pid();
1050 
1051 	test_after_background_helper_launches(false, "frozen_background", ^{
1052 		int ret;
1053 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
1054 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
1055 
1056 		ret = memorystatus_control(MEMORYSTATUS_CMD_TEST_JETSAM, child_pid, 0, 0, 0);
1057 		T_ASSERT_POSIX_SUCCESS(ret, "jetsam'd the child");
1058 
1059 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_FREEZER, false);
1060 		T_ASSERT_NOTNULL(snapshot, "Got freezer snapshot");
1061 		child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1062 		T_QUIET; T_ASSERT_NOTNULL(child_entry, "Child is in freezer snapshot");
1063 		T_QUIET; T_ASSERT_EQ(child_entry->killed, (unsigned long long) JETSAM_REASON_GENERIC, "Child entry was killed");
1064 
1065 		free(snapshot);
1066 		T_END;
1067 	});
1068 	dispatch_main();
1069 }
1070 
1071 T_DECL(freezer_snapshot_consume, "Freezer snapshot is consumed on read",
1072 	T_META_ENABLED(HAS_FREEZER),
1073 	T_META_TAG_VM_NOT_PREFERRED) {
1074 	check_for_and_enable_freezer();
1075 
1076 	/* Take ownership of the snapshot to ensure we don't race with another process trying to consume them. */
1077 	set_testing_pid();
1078 
1079 	test_after_background_helper_launches(false, "frozen_background", ^{
1080 		int ret;
1081 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
1082 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
1083 
1084 		ret = memorystatus_control(MEMORYSTATUS_CMD_TEST_JETSAM, child_pid, 0, 0, 0);
1085 		T_ASSERT_POSIX_SUCCESS(ret, "jetsam'd the child");
1086 
1087 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_FREEZER, false);
1088 		T_ASSERT_NOTNULL(snapshot, "Got first freezer snapshot");
1089 		child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1090 		T_QUIET; T_ASSERT_NOTNULL(child_entry, "Child is in first freezer snapshot");
1091 
1092 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_FREEZER, true);
1093 		if (snapshot != NULL) {
1094 		        child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1095 		        T_QUIET; T_ASSERT_NULL(child_entry, "Child is not in second freezer snapshot");
1096 		}
1097 
1098 		free(snapshot);
1099 		T_END;
1100 	});
1101 	dispatch_main();
1102 }
1103 
1104 T_DECL(freezer_snapshot_frozen_state, "Frozen state is recorded in freezer snapshot",
1105 	T_META_ENABLED(HAS_FREEZER),
1106 	T_META_TAG_VM_NOT_PREFERRED) {
1107 	check_for_and_enable_freezer();
1108 
1109 	/* Take ownership of the snapshot to ensure we don't race with another process trying to consume them. */
1110 	set_testing_pid();
1111 
1112 	test_after_background_helper_launches(false, "frozen_background", ^{
1113 		int ret;
1114 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
1115 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
1116 
1117 		move_to_idle_band(child_pid);
1118 		freeze_process(child_pid);
1119 
1120 		ret = memorystatus_control(MEMORYSTATUS_CMD_TEST_JETSAM, child_pid, 0, 0, 0);
1121 		T_ASSERT_POSIX_SUCCESS(ret, "jetsam'd the child");
1122 
1123 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_FREEZER, false);
1124 		T_ASSERT_NOTNULL(snapshot, "Got freezer snapshot");
1125 		child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1126 		T_QUIET; T_ASSERT_NOTNULL(child_entry, "Child is in freezer snapshot");
1127 		T_QUIET; T_ASSERT_TRUE(child_entry->state & kMemorystatusFrozen, "Child entry's frozen bit is set");
1128 
1129 		free(snapshot);
1130 		T_END;
1131 	});
1132 	dispatch_main();
1133 }
1134 
1135 T_DECL(freezer_snapshot_thaw_state, "Thaw count is recorded in freezer snapshot",
1136 	T_META_ENABLED(HAS_FREEZER),
1137 	T_META_TAG_VM_NOT_PREFERRED) {
1138 	check_for_and_enable_freezer();
1139 
1140 	/* Take ownership of the snapshot to ensure we don't race with another process trying to consume them. */
1141 	set_testing_pid();
1142 
1143 	test_after_background_helper_launches(false, "frozen_background", ^{
1144 		int ret;
1145 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
1146 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
1147 
1148 		move_to_idle_band(child_pid);
1149 		ret = pid_suspend(child_pid);
1150 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1151 		freeze_process(child_pid);
1152 		ret = pid_resume(child_pid);
1153 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1154 
1155 		ret = memorystatus_control(MEMORYSTATUS_CMD_TEST_JETSAM, child_pid, 0, 0, 0);
1156 		T_ASSERT_POSIX_SUCCESS(ret, "jetsam'd the child");
1157 
1158 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_FREEZER, false);
1159 		T_ASSERT_NOTNULL(snapshot, "Got freezer snapshot");
1160 		child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1161 		T_QUIET; T_ASSERT_NOTNULL(child_entry, "Child is in freezer snapshot");
1162 		T_QUIET; T_ASSERT_TRUE(child_entry->state & kMemorystatusFrozen, "Child entry's frozen bit is still set after thaw");
1163 		T_QUIET; T_ASSERT_TRUE(child_entry->state & kMemorystatusWasThawed, "Child entry was thawed");
1164 		T_QUIET; T_ASSERT_EQ(child_entry->jse_thaw_count, 1ULL, "Child entry's thaw count was incremented");
1165 
1166 		free(snapshot);
1167 		T_END;
1168 	});
1169 	dispatch_main();
1170 }
1171 
1172 T_HELPER_DECL(check_frozen, "Check frozen state", T_META_ASROOT(true)) {
1173 	int kern_ret;
1174 	dispatch_source_t ds_signal;
1175 	__block int is_frozen;
1176 	/* Set the process to freezable */
1177 	kern_ret = memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE, getpid(), 1, NULL, 0);
1178 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kern_ret, "set process is freezable");
1179 
1180 	/* We should not be frozen yet. */
1181 	is_frozen = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FROZEN, getpid(), 0, NULL, 0);
1182 	if (is_frozen == -1) {
1183 		T_LOG("memorystatus_control error: %s", strerror(errno));
1184 		exit(MEMORYSTATUS_CONTROL_ERROR);
1185 	}
1186 	if (is_frozen) {
1187 		exit(FROZEN_BIT_SET);
1188 	}
1189 
1190 	ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
1191 	if (ds_signal == NULL) {
1192 		exit(DISPATCH_SOURCE_CREATE_FAILED);
1193 	}
1194 
1195 	dispatch_source_set_event_handler(ds_signal, ^{
1196 		/* We should now be frozen. */
1197 		is_frozen = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FROZEN, getpid(), 0, NULL, 0);
1198 		if (is_frozen == -1) {
1199 		        T_LOG("memorystatus_control error: %s", strerror(errno));
1200 		        exit(MEMORYSTATUS_CONTROL_ERROR);
1201 		}
1202 		if (!is_frozen) {
1203 		        exit(FROZEN_BIT_NOT_SET);
1204 		}
1205 		exit(SUCCESS);
1206 	});
1207 	dispatch_activate(ds_signal);
1208 
1209 	sig_t sig_ret = signal(SIGUSR1, SIG_IGN);
1210 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NE(sig_ret, SIG_ERR, "signal(SIGUSR1, SIG_IGN)");
1211 
1212 	/* Signal to our parent that we can be frozen */
1213 	if (kill(getppid(), SIGUSR1) != 0) {
1214 		T_LOG("Unable to signal to parent process!");
1215 		exit(SIGNAL_TO_PARENT_FAILED);
1216 	}
1217 
1218 	dispatch_main();
1219 }
1220 
1221 T_DECL(memorystatus_get_process_is_frozen, "MEMORYSTATUS_CMD_GET_PROCESS_IS_FROZEN returns correct state",
1222 	T_META_ENABLED(HAS_FREEZER),
1223 	T_META_TAG_VM_NOT_PREFERRED) {
1224 
1225 	check_for_and_enable_freezer();
1226 	test_after_background_helper_launches(true, "check_frozen", ^{
1227 		int ret;
1228 		/* Freeze the child, resume it, and signal it to check its state */
1229 		move_to_idle_band(child_pid);
1230 		ret = pid_suspend(child_pid);
1231 		T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1232 		freeze_process(child_pid);
1233 		ret = pid_resume(child_pid);
1234 		T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1235 
1236 		kill(child_pid, SIGUSR1);
1237 		/* The child will checks its own frozen state & exit. */
1238 	});
1239 	dispatch_main();
1240 }
1241 
1242 static unsigned int freeze_pages_min_old;
1243 static int throttle_enabled_old;
1244 static void
1245 cleanup_memorystatus_freeze_top_process(void)
1246 {
1247 	sysctlbyname("kern.memorystatus_freeze_pages_min", NULL, NULL, &freeze_pages_min_old, sizeof(freeze_pages_min_old));
1248 	sysctlbyname("kern.memorystatus_freeze_throttle_enabled", NULL, NULL, &throttle_enabled_old, sizeof(throttle_enabled_old));
1249 }
1250 
1251 /*
1252  * Disables heuristics that could prevent us from freezing the child via memorystatus_freeze_top_process.
1253  */
1254 static void
1255 memorystatus_freeze_top_process_setup(void)
1256 {
1257 	size_t freeze_pages_min_size = sizeof(freeze_pages_min_old);
1258 	unsigned int freeze_pages_min_new = 0;
1259 	size_t throttle_enabled_old_size = sizeof(throttle_enabled_old);
1260 	int throttle_enabled_new = 1, ret;
1261 
1262 	ret = sysctlbyname("kern.memorystatus_freeze_pages_min", &freeze_pages_min_old, &freeze_pages_min_size, &freeze_pages_min_new, sizeof(freeze_pages_min_new));
1263 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "set kern.memorystatus_freeze_pages_min");
1264 	ret = sysctlbyname("kern.memorystatus_freeze_throttle_enabled", &throttle_enabled_old, &throttle_enabled_old_size, &throttle_enabled_new, sizeof(throttle_enabled_new));
1265 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "set kern.memorystatus_freeze_throttle_enabled");
1266 	T_ATEND(cleanup_memorystatus_freeze_top_process);
1267 	/* Take ownership of the freezer probabilities for the duration of the test so that we don't race with dasd. */
1268 	set_testing_pid();
1269 }
1270 
1271 /*
1272  * Moves the proc to the idle band and suspends it.
1273  */
1274 static void
1275 prepare_proc_for_freezing(pid_t pid)
1276 {
1277 	move_to_idle_band(pid);
1278 	int ret = pid_suspend(pid);
1279 	T_ASSERT_POSIX_SUCCESS(ret, "proc suspended");
1280 }
1281 
1282 #define P_MEMSTAT_FROZEN 0x00000002
1283 static void
1284 verify_proc_frozen_state(pid_t pid, bool expected)
1285 {
1286 	memorystatus_jetsam_snapshot_t *snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
1287 	memorystatus_jetsam_snapshot_entry_t *entry = get_jetsam_snapshot_entry(snapshot, pid);
1288 	T_ASSERT_NOTNULL(entry, "%d is in snapshot", pid);
1289 	bool is_frozen = (entry->state & P_MEMSTAT_FROZEN) != 0;
1290 	if (is_frozen != expected) {
1291 		T_LOG("%s frozen state is wrong. Expected %d, got %d. Skip reason: %d. Jetsam band: %d", entry->name, expected, is_frozen, entry->jse_freeze_skip_reason, entry->priority);
1292 	}
1293 	T_ASSERT_EQ(is_frozen, expected, "%s frozen state", entry->name);
1294 	free(snapshot);
1295 }
1296 
1297 static void
1298 verify_proc_is_frozen(pid_t pid)
1299 {
1300 	verify_proc_frozen_state(pid, true);
1301 }
1302 
1303 static void
1304 verify_proc_not_frozen(pid_t pid)
1305 {
1306 	verify_proc_frozen_state(pid, false);
1307 }
1308 
1309 T_DECL(memorystatus_freeze_top_process, "memorystatus_freeze_top_process chooses the correct process",
1310 	T_META_ENABLED(HAS_FREEZER),
1311     T_META_ASROOT(true),
1312     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1313 	T_META_TAG_VM_NOT_PREFERRED) {
1314 	T_SKIP("Skipping flaky test"); // rdar://76986376
1315 	int32_t memorystatus_freeze_band = 0;
1316 	size_t memorystatus_freeze_band_size = sizeof(memorystatus_freeze_band);
1317 	__block errno_t ret;
1318 	__block int maxproc;
1319 	size_t maxproc_size = sizeof(maxproc);
1320 
1321 	check_for_and_enable_freezer();
1322 	ret = sysctlbyname("kern.maxproc", &maxproc, &maxproc_size, NULL, 0);
1323 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.maxproc");
1324 	ret = sysctlbyname("kern.memorystatus_freeze_jetsam_band", &memorystatus_freeze_band, &memorystatus_freeze_band_size, NULL, 0);
1325 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_freeze_jetsam_band");
1326 
1327 	memorystatus_freeze_top_process_setup();
1328 	test_after_background_helper_launches(true, "frozen_background", ^{
1329 		int32_t child_band = JETSAM_PRIORITY_DEFAULT;
1330 		prepare_proc_for_freezing(child_pid);
1331 
1332 		size_t buffer_len = sizeof(memorystatus_properties_entry_v1_t) * (size_t) maxproc;
1333 		memorystatus_properties_entry_v1_t *properties_list = malloc(buffer_len);
1334 		T_QUIET; T_ASSERT_NOTNULL(properties_list, "malloc properties array");
1335 		size_t properties_list_len = 0;
1336 		/* The child needs to age down into the idle band before it's eligible to be frozen. */
1337 		T_LOG("Waiting for child to age into the idle band.");
1338 		while (child_band != JETSAM_PRIORITY_IDLE) {
1339 		        memset(properties_list, 0, buffer_len);
1340 		        properties_list_len = 0;
1341 		        memorystatus_jetsam_snapshot_t *snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
1342 
1343 		        bool found = false;
1344 		        for (size_t i = 0; i < snapshot->entry_count; i++) {
1345 		                memorystatus_jetsam_snapshot_entry_t *snapshot_entry = &snapshot->entries[i];
1346 		                if (snapshot_entry->priority <= memorystatus_freeze_band && !snapshot_entry->killed) {
1347 		                        pid_t pid = snapshot_entry->pid;
1348 		                        memorystatus_properties_entry_v1_t *property_entry = &properties_list[properties_list_len++];
1349 		                        property_entry->version = 1;
1350 		                        property_entry->pid = pid;
1351 		                        if (pid == child_pid) {
1352 		                                found = true;
1353 		                                property_entry->use_probability = 1;
1354 		                                child_band = snapshot_entry->priority;
1355 					} else {
1356 		                                property_entry->use_probability = 0;
1357 					}
1358 		                        strncpy(property_entry->proc_name, snapshot_entry->name, MAXCOMLEN);
1359 		                        property_entry->proc_name[MAXCOMLEN] = '\0';
1360 				}
1361 			}
1362 		        T_QUIET; T_ASSERT_TRUE(found, "Child is in on demand snapshot");
1363 		        free(snapshot);
1364 		}
1365 		ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_PROBABILITY, properties_list, sizeof(memorystatus_properties_entry_v1_t) * properties_list_len);
1366 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_PROBABILITY");
1367 		free(properties_list);
1368 		int val = 1;
1369 		ret = sysctlbyname("vm.memorystatus_freeze_top_process", NULL, NULL, &val, sizeof(val));
1370 		T_ASSERT_POSIX_SUCCESS(ret, "freeze_top_process");
1371 
1372 		verify_proc_is_frozen(child_pid);
1373 		resume_and_kill_proc(child_pid);
1374 		T_END;
1375 	});
1376 	dispatch_main();
1377 }
1378 static unsigned int use_ordered_list_original;
1379 static unsigned int use_demotion_list_original;
1380 static void
1381 reset_ordered_freeze_mode(void)
1382 {
1383 	sysctlbyname("kern.memorystatus_freezer_use_ordered_list", NULL, NULL, &use_ordered_list_original, sizeof(use_ordered_list_original));
1384 }
1385 
1386 static void
1387 reset_ordered_demote_mode(void)
1388 {
1389 	sysctlbyname("kern.memorystatus_freezer_use_demotion_list", NULL, NULL, &use_demotion_list_original, sizeof(use_demotion_list_original));
1390 }
1391 
1392 static void
1393 enable_ordered_freeze_mode(void)
1394 {
1395 	int ret;
1396 	int val = 1;
1397 	size_t size = sizeof(use_ordered_list_original);
1398 	ret = sysctlbyname("kern.memorystatus_freezer_use_ordered_list", &use_ordered_list_original, &size, &val, sizeof(val));
1399 	T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_freezer_use_ordered_list");
1400 	T_ATEND(reset_ordered_freeze_mode);
1401 }
1402 
1403 static void
1404 enable_ordered_demote_mode(void)
1405 {
1406 	int ret;
1407 	int val = 1;
1408 	size_t size = sizeof(use_demotion_list_original);
1409 	ret = sysctlbyname("kern.memorystatus_freezer_use_demotion_list", &use_demotion_list_original, &size, &val, sizeof(val));
1410 	T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_freezer_use_demotion_list");
1411 	T_ATEND(reset_ordered_demote_mode);
1412 }
1413 
1414 static void
1415 construct_child_freeze_entry(memorystatus_properties_freeze_entry_v1 *entry)
1416 {
1417 	memset(entry, 0, sizeof(memorystatus_properties_freeze_entry_v1));
1418 	entry->version = 1;
1419 	entry->pid = child_pid;
1420 	entry->priority = 1;
1421 
1422 	/* Get the child's name. */
1423 	memorystatus_jetsam_snapshot_t *snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
1424 	memorystatus_jetsam_snapshot_entry_t *snapshot_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1425 	strncpy(entry->proc_name, snapshot_entry->name, sizeof(entry->proc_name));
1426 	free(snapshot);
1427 }
1428 
1429 T_DECL(memorystatus_freeze_top_process_ordered, "memorystatus_freeze_top_process chooses the correct process when using an ordered list",
1430 	T_META_ENABLED(HAS_FREEZER),
1431     T_META_ASROOT(true),
1432     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1433 	T_META_TAG_VM_NOT_PREFERRED) {
1434 	check_for_and_enable_freezer();
1435 	memorystatus_freeze_top_process_setup();
1436 	enable_ordered_freeze_mode();
1437 	test_after_background_helper_launches(true, "frozen_background", ^{
1438 		int ret, val = 1;
1439 		memorystatus_properties_freeze_entry_v1 entries[1];
1440 
1441 		construct_child_freeze_entry(&entries[0]);
1442 		prepare_proc_for_freezing(child_pid);
1443 
1444 		T_LOG("Telling kernel to freeze %s", entries[0].proc_name);
1445 		ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY, entries, sizeof(entries));
1446 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY");
1447 
1448 		ret = sysctlbyname("vm.memorystatus_freeze_top_process", NULL, NULL, &val, sizeof(val));
1449 		T_ASSERT_POSIX_SUCCESS(ret, "freeze_top_process");
1450 
1451 		verify_proc_is_frozen(child_pid);
1452 		resume_and_kill_proc(child_pid);
1453 
1454 		T_END;
1455 	});
1456 	dispatch_main();
1457 }
1458 
1459 static void
1460 memorystatus_freeze_top_process_ordered_wrong_pid(pid_t (^pid_for_entry)(pid_t))
1461 {
1462 	memorystatus_freeze_top_process_setup();
1463 	enable_ordered_freeze_mode();
1464 	test_after_background_helper_launches(true, "frozen_background", ^{
1465 		int ret, val = 1;
1466 		memorystatus_properties_freeze_entry_v1 entries[1];
1467 
1468 		construct_child_freeze_entry(&entries[0]);
1469 		entries[0].pid = pid_for_entry(child_pid);
1470 		prepare_proc_for_freezing(child_pid);
1471 
1472 		T_LOG("Telling kernel to freeze %s", entries[0].proc_name);
1473 		ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY, entries, sizeof(entries));
1474 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY");
1475 
1476 		ret = sysctlbyname("vm.memorystatus_freeze_top_process", NULL, NULL, &val, sizeof(val));
1477 		T_ASSERT_POSIX_SUCCESS(ret, "freeze_top_process");
1478 
1479 		verify_proc_is_frozen(child_pid);
1480 		resume_and_kill_proc(child_pid);
1481 
1482 		T_END;
1483 	});
1484 	dispatch_main();
1485 }
1486 
1487 /*
1488  * Try both with a pid that's used by another process
1489  * and a pid that is likely unused.
1490  * In both cases the child should still get frozen.
1491  */
1492 T_DECL(memorystatus_freeze_top_process_ordered_reused_pid, "memorystatus_freeze_top_process is resilient to pid changes",
1493 	T_META_ENABLED(HAS_FREEZER),
1494 	T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1495     T_META_ASROOT(true),
1496 	T_META_TAG_VM_NOT_PREFERRED) {
1497 	check_for_and_enable_freezer();
1498 	memorystatus_freeze_top_process_ordered_wrong_pid(^(__unused pid_t child) {
1499 		return 1;
1500 	});
1501 }
1502 
1503 T_DECL(memorystatus_freeze_top_process_ordered_wrong_pid, "memorystatus_freeze_top_process is resilient to pid changes",
1504 	T_META_ENABLED(HAS_FREEZER),
1505     T_META_ASROOT(true),
1506     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1507 	T_META_TAG_VM_NOT_PREFERRED) {
1508 	check_for_and_enable_freezer();
1509 	memorystatus_freeze_top_process_ordered_wrong_pid(^(__unused pid_t child) {
1510 		return child + 1000;
1511 	});
1512 }
1513 
1514 T_DECL(memorystatus_freeze_demote_ordered, "memorystatus_demote_frozen_processes_using_demote_list chooses the correct process",
1515 	T_META_ENABLED(HAS_FREEZER),
1516     T_META_ASROOT(true),
1517     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1518 	T_META_TAG_VM_NOT_PREFERRED) {
1519 	check_for_and_enable_freezer();
1520 	memorystatus_freeze_top_process_setup();
1521 	enable_ordered_freeze_mode();
1522 	enable_ordered_demote_mode();
1523 	test_after_background_helper_launches(true, "frozen_background", ^{
1524 		int ret, val = 1;
1525 		int32_t memorystatus_freeze_band = 0;
1526 		size_t memorystatus_freeze_band_size = sizeof(memorystatus_freeze_band);
1527 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
1528 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
1529 		memorystatus_properties_freeze_entry_v1 entries[1];
1530 
1531 		ret = sysctlbyname("kern.memorystatus_freeze_jetsam_band", &memorystatus_freeze_band, &memorystatus_freeze_band_size, NULL, 0);
1532 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_freeze_jetsam_band");
1533 
1534 		construct_child_freeze_entry(&entries[0]);
1535 		prepare_proc_for_freezing(child_pid);
1536 
1537 		T_LOG("Telling kernel to freeze %s", entries[0].proc_name);
1538 		ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY, entries, sizeof(entries));
1539 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY");
1540 
1541 		ret = sysctlbyname("vm.memorystatus_freeze_top_process", NULL, NULL, &val, sizeof(val));
1542 		T_ASSERT_POSIX_SUCCESS(ret, "freeze_top_process");
1543 
1544 		verify_proc_is_frozen(child_pid);
1545 
1546 		/*
1547 		 * Place the child at the head of the demotion list.
1548 		 */
1549 		T_LOG("Telling kernel to demote %s", entries[0].proc_name);
1550 		ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_DEMOTE_PRIORITY, entries, sizeof(entries));
1551 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_DEMOTE_PRIORITY");
1552 
1553 		/* Resume the child */
1554 		ret = pid_resume(child_pid);
1555 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1556 
1557 		/* Trigger a demotion check */
1558 		val = 1;
1559 		ret = sysctlbyname("kern.memorystatus_demote_frozen_processes", NULL, NULL, &val, sizeof(val));
1560 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_demote_frozen_processes succeeded");
1561 
1562 		/* Verify that the child was demoted */
1563 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
1564 		child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1565 		T_QUIET; T_ASSERT_NOTNULL(child_entry, "Found child in snapshot");
1566 		T_QUIET; T_ASSERT_LT(child_entry->priority, memorystatus_freeze_band, "child was demoted");
1567 		free(snapshot);
1568 
1569 		T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGKILL), "Killed process");
1570 
1571 		T_END;
1572 	});
1573 	dispatch_main();
1574 }
1575 
1576 static int
1577 memorystatus_freezer_thaw_percentage(void)
1578 {
1579 	int val;
1580 	size_t size = sizeof(val);
1581 	int ret = sysctlbyname("kern.memorystatus_freezer_thaw_percentage", &val, &size, NULL, 0);
1582 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to query kern.memorystatus_freezer_thaw_percentage");
1583 	return val;
1584 }
1585 
1586 static void
1587 reset_interval(void)
1588 {
1589 	uint32_t freeze_daily_budget_mb = 0;
1590 	size_t size = sizeof(freeze_daily_budget_mb);
1591 	int ret;
1592 	uint64_t new_budget;
1593 	ret = sysctlbyname("kern.memorystatus_freeze_daily_mb_max", &freeze_daily_budget_mb, &size, NULL, 0);
1594 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to query kern.memorystatus_freeze_daily_mb_max");
1595 	new_budget = (freeze_daily_budget_mb * (1UL << 20) / vm_page_size);
1596 	ret = sysctlbyname("kern.memorystatus_freeze_budget_pages_remaining", NULL, NULL, &new_budget, sizeof(new_budget));
1597 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to set kern.memorystatus_freeze_budget_pages_remaining");
1598 }
1599 
1600 static pid_t second_child;
1601 static void
1602 cleanup_memorystatus_freezer_thaw_percentage(void)
1603 {
1604 	kill(second_child, SIGKILL);
1605 }
1606 
1607 T_DECL(memorystatus_freezer_thaw_percentage, "memorystatus_freezer_thaw_percentage updates correctly",
1608 	T_META_ENABLED(HAS_FREEZER),
1609     T_META_ASROOT(true),
1610     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1611 	T_META_TAG_VM_NOT_PREFERRED) {
1612 	__block dispatch_source_t first_signal_block;
1613 	check_for_and_enable_freezer();
1614 	/* Take ownership of the freezer probabilities for the duration of the test so that nothing new gets frozen by dasd. */
1615 	set_testing_pid();
1616 	reset_interval();
1617 
1618 	/* Spawn one child that will remain frozen throughout the whole test & another that will be thawed. */
1619 	first_signal_block = run_block_after_signal(SIGUSR1, ^{
1620 		move_to_idle_band(second_child);
1621 		__block int ret = pid_suspend(second_child);
1622 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1623 		freeze_process(second_child);
1624 		T_QUIET; T_ASSERT_EQ(memorystatus_freezer_thaw_percentage(), 0, "thaw percentage is still 0 after freeze");
1625 		dispatch_source_cancel(first_signal_block);
1626 		test_after_background_helper_launches(true, "frozen_background", ^{
1627 			reset_interval();
1628 			T_QUIET; T_ASSERT_EQ(memorystatus_freezer_thaw_percentage(), 0, "new interval starts with a thaw percentage of 0");
1629 			move_to_idle_band(child_pid);
1630 			ret = pid_suspend(child_pid);
1631 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1632 			freeze_process(child_pid);
1633 			ret = pid_resume(child_pid);
1634 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1635 			int percentage_after_thaw = memorystatus_freezer_thaw_percentage();
1636 			T_QUIET; T_ASSERT_GT(percentage_after_thaw, 0, "thaw percentage is higher after thaw");
1637 
1638 			ret = pid_suspend(child_pid);
1639 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1640 			freeze_process(child_pid);
1641 			ret = pid_resume(child_pid);
1642 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1643 			T_QUIET; T_ASSERT_EQ(memorystatus_freezer_thaw_percentage(), percentage_after_thaw, "thaw percentage is unchanged after second thaw");
1644 
1645 			ret = pid_suspend(child_pid);
1646 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1647 			freeze_process(child_pid);
1648 			reset_interval();
1649 			T_QUIET; T_ASSERT_EQ(memorystatus_freezer_thaw_percentage(), 0, "new interval starts with a 0 thaw percentage");
1650 			ret = pid_resume(child_pid);
1651 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1652 			T_QUIET; T_ASSERT_GT(memorystatus_freezer_thaw_percentage(), 0, "thaw percentage goes back up in new interval");
1653 
1654 			T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGKILL), "failed to kill child");
1655 			T_END;
1656 		});
1657 	});
1658 
1659 	second_child = launch_background_helper("frozen_background", false, true);
1660 	T_ATEND(cleanup_memorystatus_freezer_thaw_percentage);
1661 	dispatch_activate(first_signal_block);
1662 	dispatch_main();
1663 }
1664 
1665 static uint64_t
1666 get_budget_pages_remaining(void)
1667 {
1668 	uint64_t pages_remaining = 0;
1669 	size_t size = sizeof(pages_remaining);
1670 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_budget_pages_remaining", &pages_remaining, &size, NULL, 0),
1671 	    "get kern.memorystatus_freeze_budget_pages_remaining");
1672 	return pages_remaining;
1673 }
1674 
1675 static void
1676 set_budget_pages_remaining(uint64_t pages_remaining)
1677 {
1678 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_budget_pages_remaining", NULL, NULL, &pages_remaining, sizeof(pages_remaining)),
1679 	    "get kern.memorystatus_freeze_budget_pages_remaining");
1680 }
1681 
1682 static void
1683 set_freeze_enabled(int freeze_enabled)
1684 {
1685 	size_t length = sizeof(freeze_enabled);
1686 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", NULL, NULL, &freeze_enabled, length),
1687 	    "enable vm.freeze_enabled");
1688 }
1689 
1690 static void
1691 enable_freeze(void)
1692 {
1693 	set_freeze_enabled(1);
1694 }
1695 
1696 T_DECL(memorystatus_freeze_budget_multiplier, "memorystatus_budget_multiplier multiplies budget",
1697     T_META_ASROOT(true),
1698     T_META_REQUIRES_SYSCTL_NE("kern.memorystatus_freeze_daily_mb_max", UINT32_MAX),
1699     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1700 	T_META_ENABLED(HAS_FREEZER),
1701     T_META_ENABLED(false && HAS_FREEZER) /* rdar://87165483 */,
1702 	T_META_TAG_VM_NOT_PREFERRED) {
1703 	/* Disable freezer so that the budget doesn't change out from underneath us. */
1704 	int freeze_enabled = 0;
1705 	size_t length = sizeof(freeze_enabled);
1706 	uint64_t freeze_daily_pages_max;
1707 	check_for_and_enable_freezer();
1708 	T_ATEND(enable_freeze);
1709 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", NULL, NULL, &freeze_enabled, length),
1710 	    "disable vm.freeze_enabled");
1711 	freeze_daily_pages_max = get_freeze_daily_pages_max();
1712 	original_budget_multiplier = get_budget_multiplier();
1713 	T_ATEND(reset_budget_multiplier);
1714 	set_budget_multiplier(100);
1715 	T_QUIET; T_ASSERT_EQ(get_budget_pages_remaining(), freeze_daily_pages_max, "multiplier=100%%");
1716 	set_budget_multiplier(50);
1717 	T_QUIET; T_ASSERT_EQ(get_budget_pages_remaining(), freeze_daily_pages_max / 2, "multiplier=50%%");
1718 	set_budget_multiplier(200);
1719 	T_QUIET; T_ASSERT_EQ(get_budget_pages_remaining(), freeze_daily_pages_max * 2, "multiplier=200%%");
1720 }
1721 
1722 T_DECL(memorystatus_freeze_set_dasd_trial_identifiers, "set dasd trial identifiers",
1723 	T_META_ENABLED(HAS_FREEZER),
1724     T_META_ASROOT(true),
1725 	T_META_TAG_VM_NOT_PREFERRED) {
1726 #define TEST_STR "freezer-das-trial"
1727 	memorystatus_freezer_trial_identifiers_v1 identifiers = {0};
1728 	identifiers.version = 1;
1729 	check_for_and_enable_freezer();
1730 	strncpy(identifiers.treatment_id, TEST_STR, sizeof(identifiers.treatment_id));
1731 	strncpy(identifiers.experiment_id, TEST_STR, sizeof(identifiers.treatment_id));
1732 	identifiers.deployment_id = 2;
1733 	int ret = memorystatus_control(MEMORYSTATUS_CMD_FREEZER_CONTROL, 0, FREEZER_CONTROL_SET_DASD_TRIAL_IDENTIFIERS, &identifiers, sizeof(identifiers));
1734 	T_WITH_ERRNO; T_ASSERT_EQ(ret, 0, "FREEZER_CONTROL_SET_DASD_TRIAL_IDENTIFIERS");
1735 }
1736 
1737 T_DECL(memorystatus_reset_freezer_state, "FREEZER_CONTROL_RESET_STATE kills frozen proccesses",
1738 	T_META_ENABLED(HAS_FREEZER),
1739     T_META_ASROOT(true),
1740     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1741 	T_META_TAG_VM_NOT_PREFERRED) {
1742 	check_for_and_enable_freezer();
1743 	/* Take ownership of the freezer probabilities for the duration of the test so that nothing new gets frozen by dasd. */
1744 	set_testing_pid();
1745 	reset_interval();
1746 
1747 	test_after_background_helper_launches(false, "frozen_background", ^{
1748 		proc_name_t name;
1749 		int ret;
1750 
1751 		/* Freeze the child and verify they're frozen. */
1752 		move_to_idle_band(child_pid);
1753 		ret = pid_suspend(child_pid);
1754 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1755 		freeze_process(child_pid);
1756 		T_QUIET; T_ASSERT_TRUE(is_proc_in_frozen_list(child_pid, name, sizeof(name)), "Found proc in frozen list");
1757 		T_QUIET; T_EXPECT_EQ_STR(name, "memorystatus_freeze_test", "Proc has correct name");
1758 		/* Set the budget to 0. */
1759 		set_budget_pages_remaining(0);
1760 
1761 		/* FREEZER_CONTROL_RESET_STATE */
1762 		ret = memorystatus_control(MEMORYSTATUS_CMD_FREEZER_CONTROL, 0, FREEZER_CONTROL_RESET_STATE, NULL, 0);
1763 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "FREEZER_CONRTOL_RESET_STATE");
1764 
1765 		/* Verify budget resets to a non-zero value. Some devices may have a configured
1766 		 * budget of 0. Skip this assertion if so. */
1767 		uint64_t budget_after_reset = get_budget_pages_remaining();
1768 #if TARGET_OS_XR
1769 		T_QUIET; T_ASSERT_EQ(budget_after_reset, 0ULL, "freeze budget after reset == 0");
1770 #else
1771 		T_QUIET; T_ASSERT_GT(budget_after_reset, 0ULL, "freeze budget after reset > 0");
1772 #endif
1773 		/*
1774 		 * Verify child has been killed
1775 		 * Note that the task termination isn't synchronous with the RESET_STATE call so we may
1776 		 * block in waitpid temporarily.
1777 		 */
1778 		int stat;
1779 		while (true) {
1780 		        pid_t wait_p = waitpid(child_pid, &stat, 0);
1781 		        if (wait_p == child_pid) {
1782 		                break;
1783 			}
1784 		}
1785 		T_QUIET; T_ASSERT_TRUE(WIFSIGNALED(stat), "child was signaled");
1786 		T_QUIET; T_ASSERT_EQ(WTERMSIG(stat), SIGKILL, "Child received SIGKILL");
1787 
1788 		T_END;
1789 	});
1790 	dispatch_main();
1791 }
1792 
1793 static void
1794 dock_proc(pid_t pid)
1795 {
1796 	int ret;
1797 	ret = memorystatus_control(MEMORYSTATUS_CMD_ELEVATED_INACTIVEJETSAMPRIORITY_ENABLE, pid, 0, NULL, 0);
1798 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "dock_proc");
1799 }
1800 
1801 T_DECL(memorystatus_freeze_skip_docked, "memorystatus_freeze_top_process does not freeze docked processes",
1802 	T_META_ENABLED(HAS_FREEZER && !TARGET_OS_WATCH),
1803     T_META_ASROOT(true),
1804     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1805 	T_META_TAG_VM_NOT_PREFERRED) {
1806 	check_for_and_enable_freezer();
1807 	memorystatus_freeze_top_process_setup();
1808 	enable_ordered_freeze_mode();
1809 	test_after_background_helper_launches(true, "frozen_background", ^{
1810 		int ret, val = 1;
1811 		memorystatus_properties_freeze_entry_v1 entries[1];
1812 
1813 		dock_proc(child_pid);
1814 		construct_child_freeze_entry(&entries[0]);
1815 		prepare_proc_for_freezing(child_pid);
1816 
1817 		T_LOG("Telling kernel to freeze %s", entries[0].proc_name);
1818 		ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY, entries, sizeof(entries));
1819 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY");
1820 
1821 		ret = sysctlbyname("vm.memorystatus_freeze_top_process", NULL, NULL, &val, sizeof(val));
1822 		T_ASSERT_EQ(errno, ESRCH, "freeze_top_process errno");
1823 		T_ASSERT_EQ(ret, -1, "freeze_top_process");
1824 
1825 		verify_proc_not_frozen(child_pid);
1826 		resume_and_kill_proc(child_pid);
1827 
1828 		T_END;
1829 	});
1830 	dispatch_main();
1831 }
1832 
1833 T_HELPER_DECL(corpse_generation, "Generate a large corpse", T_META_ASROOT(false)) {
1834 	/*
1835 	 * Allocate and fault in a bunch of memory so that it takes a while
1836 	 * to generate our corpse.
1837 	 */
1838 	size_t bytes_to_allocate = 304 * (1UL << 20);
1839 	size_t block_size = 8 * (1UL << 20);
1840 	for (size_t i = 0; i < bytes_to_allocate / block_size; i++) {
1841 		unsigned char *ptr = malloc(block_size);
1842 		if (ptr == NULL) {
1843 			T_LOG("Unable to allocate memory in child process!");
1844 			exit(UNABLE_TO_ALLOCATE);
1845 		}
1846 		for (size_t j = 0; j < block_size / vm_page_size; j++) {
1847 			*ptr = (unsigned char) j;
1848 			ptr += vm_page_size;
1849 		}
1850 	}
1851 	dispatch_source_t ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
1852 	if (ds_signal == NULL) {
1853 		T_LOG("Unable to create dispatch source");
1854 		exit(DISPATCH_SOURCE_CREATE_FAILED);
1855 	}
1856 	dispatch_source_set_event_handler(ds_signal, ^{
1857 		uint64_t val = 1;
1858 		/*
1859 		 * We should now be frozen.
1860 		 * Simulate a crash so that our P_MEMSTAT_SKIP bit gets set temporarily.
1861 		 * The parent process will try to kill us due to disk space shortage in parallel.
1862 		 */
1863 		os_fault_with_payload(OS_REASON_LIBSYSTEM, OS_REASON_LIBSYSTEM_CODE_FAULT,
1864 		&val, sizeof(val), "freeze_test", 0);
1865 	});
1866 	dispatch_activate(ds_signal);
1867 
1868 	sig_t sig_ret = signal(SIGUSR1, SIG_IGN);
1869 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NE(sig_ret, SIG_ERR, "signal(SIGUSR1, SIG_IGN)");
1870 
1871 	/* Signal to our parent that we can be frozen */
1872 	if (kill(getppid(), SIGUSR1) != 0) {
1873 		T_LOG("Unable to signal to parent process!");
1874 		exit(SIGNAL_TO_PARENT_FAILED);
1875 	}
1876 	dispatch_main();
1877 }
1878 
1879 T_DECL(memorystatus_disable_freeze_corpse, "memorystatus_disable_freeze with parallel corpse creation",
1880 	T_META_ENABLED(HAS_FREEZER),
1881     T_META_ASROOT(true),
1882     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1883 	T_META_TAG_VM_NOT_PREFERRED) {
1884 	/*
1885 	 * In the past, we've had race conditions w.r.t. killing on disk space shortage
1886 	 * and corpse generation of frozen processes.
1887 	 * This test spwans a frozen helper
1888 	 * which generates a large corpse (which should take in the 10s to 100s of m.s. to complete)
1889 	 * while the test process triggers disk space kills.
1890 	 * We should see that the test process is jetsammed successfully.
1891 	 */
1892 	check_for_and_enable_freezer();
1893 	test_after_background_helper_launches(false, "corpse_generation", ^{
1894 		int ret, val, stat;
1895 		/* Place the child in the idle band so that it gets elevated like a typical app. */
1896 		move_to_idle_band(child_pid);
1897 		ret = pid_suspend(child_pid);
1898 		T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1899 		freeze_process(child_pid);
1900 
1901 		ret = pid_resume(child_pid);
1902 		T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1903 
1904 		kill(child_pid, SIGUSR1);
1905 
1906 		T_ATEND(enable_freeze);
1907 		val = 0;
1908 		ret = sysctlbyname("vm.freeze_enabled", NULL, NULL, &val, sizeof(val));
1909 		T_ASSERT_POSIX_SUCCESS(ret, "freeze disabled");
1910 		/*
1911 		 * Verify child has been killed
1912 		 * Note that the task termination isn't synchronous with the freeze_enabled call so we may
1913 		 * block in waitpid temporarily.
1914 		 */
1915 		while (true) {
1916 		        pid_t wait_p = waitpid(child_pid, &stat, 0);
1917 		        if (wait_p == child_pid) {
1918 		                break;
1919 			}
1920 		}
1921 		T_QUIET; T_ASSERT_TRUE(WIFSIGNALED(stat), "child was signaled");
1922 		T_QUIET; T_ASSERT_EQ(WTERMSIG(stat), SIGKILL, "Child received SIGKILL");
1923 
1924 		T_END;
1925 	});
1926 	dispatch_main();
1927 }
1928 
1929 static int original_unrestrict_coalitions_val;
1930 
1931 static void
1932 unrestrict_coalitions()
1933 {
1934 	int ret, val = 1;
1935 	size_t val_size = sizeof(val);
1936 	size_t original_unrestrict_coalitions_size = sizeof(original_unrestrict_coalitions_val);
1937 	ret = sysctlbyname("kern.unrestrict_coalitions", &original_unrestrict_coalitions_val, &original_unrestrict_coalitions_size, &val, val_size);
1938 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "unrestrict_coalitions");
1939 }
1940 
1941 static void
1942 reset_unrestrict_coalitions()
1943 {
1944 	size_t size = sizeof(original_unrestrict_coalitions_val);
1945 	int ret = sysctlbyname("kern.unrestrict_coalitions", NULL, NULL, &original_unrestrict_coalitions_val, size);
1946 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "unrestrict_coalitions");
1947 }
1948 
1949 static uint64_t
1950 create_coalition(int type)
1951 {
1952 	uint64_t id = 0;
1953 	uint32_t flags = 0;
1954 	uint64_t param[2];
1955 	int ret;
1956 
1957 	COALITION_CREATE_FLAGS_SET_TYPE(flags, type);
1958 	ret = coalition_create(&id, flags);
1959 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "coalition_create");
1960 	T_QUIET; T_ASSERT_GE(id, 0ULL, "coalition_create returned a valid id");
1961 
1962 	/* disable notifications for this coalition so launchd doesn't freak out */
1963 	param[0] = id;
1964 	param[1] = 0;
1965 	ret = sysctlbyname("kern.coalition_notify", NULL, NULL, param, sizeof(param));
1966 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.coalition_notify");
1967 
1968 	return id;
1969 }
1970 
1971 static uint64_t jetsam_coal, resource_coal;
1972 
1973 #define COAL_MAX_MEMBERS 50
1974 #define MAX_XPC_SERVICE_FREEZE 10 /* xnu will only freeze 10 XPC services per coalition. */
1975 int n_coalition_members;
1976 pid_t coalition_members[COAL_MAX_MEMBERS];
1977 
1978 /*
1979  * Spawns the given command as the leader of the given coalitions.
1980  * Process will start in a stopped state (waiting for SIGCONT)
1981  */
1982 static pid_t
1983 spawn_coalition_member(const char *path, char *const *argv, int role, short spawn_flags)
1984 {
1985 	int ret;
1986 	posix_spawnattr_t attr;
1987 	extern char **environ;
1988 	pid_t new_pid = 0;
1989 	kern_return_t kr;
1990 
1991 	ret = posix_spawnattr_init(&attr);
1992 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_init failed with %s", strerror(ret));
1993 
1994 	ret = posix_spawnattr_setcoalition_np(&attr, jetsam_coal,
1995 	    COALITION_TYPE_JETSAM, role);
1996 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_setcoalition_np failed with %s", strerror(ret));
1997 	ret = posix_spawnattr_setcoalition_np(&attr, resource_coal,
1998 	    COALITION_TYPE_RESOURCE, role);
1999 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_setcoalition_np failed with %s", strerror(ret));
2000 
2001 	ret = posix_spawnattr_setflags(&attr, spawn_flags);
2002 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_setflags failed with %s", strerror(ret));
2003 
2004 	ret = posix_spawn(&new_pid, path, NULL, &attr, argv, environ);
2005 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawn failed with %s", strerror(ret));
2006 
2007 	ret = posix_spawnattr_destroy(&attr);
2008 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_destroy failed with %s\n", strerror(ret));
2009 
2010 	kr = memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_MANAGED, new_pid, 1, NULL, 0);
2011 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "memorystatus_control");
2012 
2013 	return new_pid;
2014 }
2015 
2016 static pid_t
2017 get_coalition_leader(pid_t p)
2018 {
2019 	static const size_t kMaxPids = 500;
2020 	int ret;
2021 	int pid_list[kMaxPids];
2022 	size_t pid_list_size = sizeof(pid_list);
2023 
2024 	int iparam[3];
2025 #define p_type  iparam[0]
2026 #define p_order iparam[1]
2027 #define p_pid   iparam[2]
2028 	p_type = COALITION_TYPE_JETSAM;
2029 	p_order = COALITION_SORT_DEFAULT;
2030 	p_pid = p;
2031 
2032 	ret = sysctlbyname("kern.coalition_pid_list", pid_list, &pid_list_size, iparam, sizeof(iparam));
2033 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.coalition_pid_list");
2034 	T_QUIET; T_ASSERT_LE(pid_list_size, kMaxPids * sizeof(int), "coalition is small enough");
2035 
2036 	for (size_t i = 0; i < pid_list_size / sizeof(int); i++) {
2037 		int curr_pid = pid_list[i];
2038 		int roles[COALITION_NUM_TYPES] = {};
2039 		size_t roles_size = sizeof(roles);
2040 
2041 		ret = sysctlbyname("kern.coalition_roles", roles, &roles_size, &curr_pid, sizeof(curr_pid));
2042 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.coalition_roles");
2043 		if (roles[COALITION_TYPE_JETSAM] == COALITION_TASKROLE_LEADER) {
2044 			return curr_pid;
2045 		}
2046 	}
2047 
2048 	T_FAIL("No leader in coalition!");
2049 	return 0;
2050 }
2051 
2052 void
2053 setup_coalitions() {
2054 }
2055 
2056 void
2057 coal_foreach(void (^callback)(pid_t)) {
2058 	for (int i = 0; i < n_coalition_members; i++) {
2059 		callback(coalition_members[i]);
2060 	}
2061 }
2062 
2063 void
2064 kill_all_coalition_members() {
2065 	int __block ret;
2066 	T_LOG("Killing coalition members...");
2067 	coal_foreach(^(pid_t pid){
2068 		ret = kill(pid, SIGKILL);
2069 		T_QUIET; T_EXPECT_POSIX_SUCCESS(ret, "kill");
2070 	});
2071 }
2072 
2073 void
2074 spawn_coalition_and_run(dispatch_block_t after_spawn) {
2075 	int ret, __block i = 0;
2076 	static uint32_t path_size;
2077 	static char pathbuf[PATH_MAX];
2078 	dispatch_source_t sig_source;
2079 	pid_t leader_pid;
2080 
2081 	/* Setup coalitions */
2082 	jetsam_coal = create_coalition(COALITION_TYPE_JETSAM);
2083 	resource_coal = create_coalition(COALITION_TYPE_RESOURCE);
2084 
2085 	path_size = sizeof(pathbuf);
2086 	ret = _NSGetExecutablePath(pathbuf, &path_size);
2087 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "_NSGetExecutablePath");
2088 	static char *const args[] = {
2089 		pathbuf,
2090 		"-n",
2091 		"frozen_background",
2092 		NULL
2093 	};
2094 
2095 	T_ATEND(kill_all_coalition_members);
2096 
2097 	sig_source = run_block_after_signal(SIGUSR1, ^{
2098 		i++;
2099 		if (i < n_coalition_members) {
2100 			/* Spawn next child */
2101 			coalition_members[i] = spawn_coalition_member(pathbuf, args, COALITION_TASKROLE_XPC, 0);
2102 		} else {
2103 			after_spawn();
2104 		}
2105 	});
2106 	dispatch_activate(sig_source);
2107 
2108 	/* Spawn leader */
2109 	child_pid = spawn_coalition_member(pathbuf, args, COALITION_TASKROLE_LEADER, 0);
2110 	coalition_members[0] = child_pid;
2111 
2112 	/* Double-check the coalition was set up correctly */
2113 	leader_pid = get_coalition_leader(child_pid);
2114 	T_ASSERT_EQ(child_pid, leader_pid, "Child is leader of coalition");
2115 }
2116 
2117 static void
2118 kill_all_frozen()
2119 {
2120 	/* Disabling and re-enabling the freezer will evict all frozen processes. */
2121 	set_freeze_enabled(0);
2122 	set_freeze_enabled(1);
2123 }
2124 
2125 static unsigned int
2126 get_used_freezer_slots ()
2127 {
2128 	int ret;
2129 	unsigned int frozen_procs;
2130 	size_t frozen_procs_size = sizeof(frozen_procs);
2131 
2132 	ret = sysctlbyname("kern.memorystatus_freeze_count", &frozen_procs, &frozen_procs_size, NULL, 0);
2133 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "get kern.memorystatus_freeze_count");
2134 
2135 	return frozen_procs;
2136 }
2137 
2138 static void
2139 setup_coalition_freezing()
2140 {
2141 
2142 	/* Setup freezer. Set ordered freeze mode and kill all frozen procs. */
2143 	memorystatus_freeze_top_process_setup();
2144 	enable_ordered_freeze_mode();
2145 }
2146 
2147 static uint32_t
2148 get_max_freezer_slots()
2149 {
2150 	int ret;
2151 	uint32_t max_freeze_processes = 0;
2152 	size_t max_freeze_processes_len = sizeof(max_freeze_processes);
2153 	ret = sysctlbyname("kern.memorystatus_freeze_processes_max", &max_freeze_processes, &max_freeze_processes_len, NULL, 0);
2154 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_freeze_processes_max");
2155 	return max_freeze_processes;
2156 }
2157 
2158 static int orig_freezer_slots = -1;
2159 static void restore_freezer_slots(void);
2160 
2161 static void
2162 set_max_freezer_slots(uint32_t max_freeze_processes)
2163 {
2164 	int ret;
2165 	if (orig_freezer_slots == -1) {
2166 		orig_freezer_slots = get_max_freezer_slots();
2167 		T_ATEND(restore_freezer_slots);
2168 	}
2169 	ret = sysctlbyname("kern.memorystatus_freeze_processes_max", NULL, NULL, &max_freeze_processes, sizeof(max_freeze_processes));
2170 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "set kern.memorystatus_freeze_processes_max");
2171 }
2172 
2173 static void
2174 restore_freezer_slots(void)
2175 {
2176 	set_max_freezer_slots(orig_freezer_slots);
2177 }
2178 
2179 static void
2180 prepare_coalition_for_freezing(void)
2181 {
2182 	coal_foreach(^(pid_t pid){
2183 		prepare_proc_for_freezing(pid);
2184 	});
2185 }
2186 
2187 static int
2188 freeze_coalition_leader(void)
2189 {
2190 	int ret, val = 1;
2191 	memorystatus_properties_freeze_entry_v1 entry;
2192 	construct_child_freeze_entry(&entry);
2193 	ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY, &entry, sizeof(entry));
2194 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY");
2195 	return sysctlbyname("vm.memorystatus_freeze_top_process", NULL, NULL, &val, sizeof(val));
2196 
2197 }
2198 
2199 #define TEST_MAX_FREEZER_SLOTS 8
2200 #define N_UNFROZEN_MEMBERS 3
2201 
2202 T_DECL(memorystatus_coalition_freeze, "Freezing a coalition leader should freeze all of its XPC service members",
2203 	T_META_ENABLED(HAS_FREEZER),
2204 	T_META_ASROOT(true),
2205 	T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
2206 	T_META_TAG_VM_NOT_PREFERRED)
2207 {
2208 	check_for_and_enable_freezer();
2209 	unrestrict_coalitions();
2210 	T_ATEND(reset_unrestrict_coalitions);
2211 	setup_coalition_freezing();
2212 	set_max_freezer_slots(TEST_MAX_FREEZER_SLOTS);
2213 	kill_all_frozen();
2214 	T_QUIET; T_ASSERT_EQ(get_used_freezer_slots(), 0, "No freezer slots used");
2215 
2216 	/* Spawn max freezer slots coalition members */
2217 	n_coalition_members = TEST_MAX_FREEZER_SLOTS;
2218 	static_assert(TEST_MAX_FREEZER_SLOTS <= COAL_MAX_MEMBERS);
2219 	static_assert(TEST_MAX_FREEZER_SLOTS < MAX_XPC_SERVICE_FREEZE);
2220 
2221 	/* Create our coalitions and spawn the leader / "XPC service" members */
2222 	spawn_coalition_and_run(^{
2223 		int i, ret, n_coal_frozen = 0;
2224 		memorystatus_jetsam_snapshot_t *snapshot;
2225 		memorystatus_jetsam_snapshot_entry_t *entry;
2226 
2227 		/* Freeze coalition */
2228 		prepare_coalition_for_freezing();
2229 		ret = freeze_coalition_leader();
2230 		T_EXPECT_POSIX_SUCCESS(ret, "freeze_coalition_leader");
2231 
2232 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
2233 		for (i = 0; i < n_coalition_members; i++) {
2234 			entry = get_jetsam_snapshot_entry(snapshot, coalition_members[i]);
2235 			if (entry->state & P_MEMSTAT_FROZEN) {
2236 				n_coal_frozen++;
2237 			}
2238 		}
2239 		T_ASSERT_EQ(n_coal_frozen, n_coalition_members, "All coalition members frozen");
2240 
2241 		T_END;
2242 	});
2243 	dispatch_main();
2244 }
2245 
2246 T_DECL(memorystatus_coalition_freezer_slot_limit, "Exhausting freezer slots and freezing a large coalition should not exceed slots",
2247 	T_META_ENABLED(HAS_FREEZER),
2248 	T_META_ASROOT(true),
2249 	T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
2250 	T_META_TAG_VM_NOT_PREFERRED)
2251 {
2252 	check_for_and_enable_freezer();
2253 	unrestrict_coalitions();
2254 	T_ATEND(reset_unrestrict_coalitions);
2255 	setup_coalition_freezing();
2256 	set_max_freezer_slots(TEST_MAX_FREEZER_SLOTS);
2257 	kill_all_frozen();
2258 	T_QUIET; T_ASSERT_EQ(get_used_freezer_slots(), 0, "No freezer slots used");
2259 
2260 	/* Spawn max freezer slots + 1 coalition members */
2261 	n_coalition_members = TEST_MAX_FREEZER_SLOTS + N_UNFROZEN_MEMBERS;
2262 	static_assert(TEST_MAX_FREEZER_SLOTS + N_UNFROZEN_MEMBERS <= COAL_MAX_MEMBERS);
2263 	static_assert(TEST_MAX_FREEZER_SLOTS < MAX_XPC_SERVICE_FREEZE);
2264 
2265 	/* Create our coalitions and spawn the leader / "XPC service" members */
2266 	spawn_coalition_and_run(^{
2267 		int i, j, ret, n_coal_frozen = 0;
2268 		memorystatus_jetsam_snapshot_t *snapshot;
2269 		memorystatus_jetsam_snapshot_entry_t *entry;
2270 
2271 		/* Freeze coalition */
2272 		prepare_coalition_for_freezing();
2273 		ret = freeze_coalition_leader();
2274 		T_EXPECT_POSIX_SUCCESS(ret, "freeze_coalition_leader");
2275 
2276 		/* Ensure coalition leader is frozen */
2277 		verify_proc_is_frozen(coalition_members[0]);
2278 
2279 		/* Make sure freezer did not exceed its slots */
2280 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
2281 		for (i = 0; i < n_coalition_members; i++) {
2282 			entry = get_jetsam_snapshot_entry(snapshot, coalition_members[i]);
2283 			if (entry->state & P_MEMSTAT_FROZEN) {
2284 				n_coal_frozen++;
2285 			}
2286 		}
2287 		T_ASSERT_EQ(n_coal_frozen, n_coalition_members - N_UNFROZEN_MEMBERS, "N_UNFROZEN_MEMBERS coalition members remain unfrozen");
2288 
2289 		T_END;
2290 	});
2291 	dispatch_main();
2292 }
2293 
2294 pid_t helper_pid;
2295 static void
2296 kill_helper()
2297 {
2298 	int ret = kill(helper_pid, SIGUSR2);
2299 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kill(helper_pid, SIGUSR2)");
2300 }
2301 
2302 T_DECL(memorystatus_two_coalition_freeze, "Exhausting freezer slots with one coalition and freezing another should fail",
2303 	T_META_ENABLED(HAS_FREEZER),
2304 	T_META_ASROOT(true),
2305 	T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
2306 	T_META_TAG_VM_NOT_PREFERRED)
2307 {
2308 	dispatch_source_t sig_disp, exit_disp;
2309 
2310 	check_for_and_enable_freezer();
2311 	unrestrict_coalitions();
2312 	T_ATEND(reset_unrestrict_coalitions);
2313 	setup_coalition_freezing();
2314 	set_max_freezer_slots(TEST_MAX_FREEZER_SLOTS);
2315 	kill_all_frozen();
2316 	T_QUIET; T_ASSERT_EQ(get_used_freezer_slots(), 0, "No freezer slots used");
2317 
2318 	/* Spawn max freezer slots coalition members */
2319 	n_coalition_members = TEST_MAX_FREEZER_SLOTS;
2320 	static_assert(TEST_MAX_FREEZER_SLOTS <= COAL_MAX_MEMBERS);
2321 	static_assert(TEST_MAX_FREEZER_SLOTS < MAX_XPC_SERVICE_FREEZE);
2322 
2323 	sig_disp = run_block_after_signal(SIGUSR2, ^{
2324 		/* After our child signals, we can try spawning and freezing our coalition */
2325 		spawn_coalition_and_run(^{
2326 			int i, j, ret, n_coal_frozen = 0;
2327 			memorystatus_jetsam_snapshot_t *snapshot;
2328 			memorystatus_jetsam_snapshot_entry_t *entry;
2329 
2330 			/* Freeze coalition */
2331 			prepare_coalition_for_freezing();
2332 			ret = freeze_coalition_leader();
2333 			T_EXPECT_POSIX_FAILURE(ret, ESRCH, "freeze_coalition_leader");
2334 
2335 			/* Make sure freezer did not freeze anyone */
2336 			snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
2337 			for (i = 0; i < n_coalition_members; i++) {
2338 				entry = get_jetsam_snapshot_entry(snapshot, coalition_members[i]);
2339 				if (entry->state & P_MEMSTAT_FROZEN) {
2340 					n_coal_frozen++;
2341 				}
2342 			}
2343 
2344 			T_ASSERT_EQ(n_coal_frozen, 0, "all coalition members remain unfrozen");
2345 
2346 			T_END;
2347 		});
2348 	});
2349 	dispatch_activate(sig_disp);
2350 
2351 
2352 	/* Spawn helper and wait for it to spawn its coalition and freeze it */
2353 	helper_pid = launch_background_helper("coalition_freezer", false, false);
2354 	T_ATEND(kill_helper);
2355 
2356 	exit_disp = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)helper_pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue());
2357 	dispatch_source_set_event_handler(exit_disp, ^{
2358 		int status = 0, code = 0;
2359 		pid_t rc = waitpid(child_pid, &status, 0);
2360 		T_QUIET; T_ASSERT_EQ(rc, child_pid, "waitpid");
2361 		code = WEXITSTATUS(status);
2362 		if (code != 0) {
2363 		        T_LOG("Helper exited with error: %s", exit_codes_str[code]);
2364 		}
2365 		T_QUIET; T_ASSERT_EQ(code, 0, "Helper exited cleanly");
2366 	});
2367 	dispatch_activate(exit_disp);
2368 
2369 	dispatch_main();
2370 }
2371 
2372 T_HELPER_DECL(coalition_freezer, "Spawns a coalition and freezes it",
2373 	T_META_ASROOT(true))
2374 {
2375 	dispatch_source_t sig_disp;
2376 
2377 	/* The parent will have already set up the freezer for us. */
2378 	n_coalition_members = TEST_MAX_FREEZER_SLOTS;
2379 	spawn_coalition_and_run(^{
2380 		prepare_coalition_for_freezing();
2381 		freeze_coalition_leader();
2382 		kill(getppid(), SIGUSR2);
2383 	});
2384 
2385 	sig_disp = run_block_after_signal(SIGUSR2, ^{
2386 		kill_all_coalition_members();
2387 		exit(0);
2388 	});
2389 	dispatch_activate(sig_disp);
2390 
2391 	dispatch_main();
2392 }
2393