xref: /xnu-10063.141.1/tests/vm/memorystatus_freeze_test.c (revision d8b80295118ef25ac3a784134bcf95cd8e88109f)
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 
16 #ifdef T_NAMESPACE
17 #undef T_NAMESPACE
18 #endif
19 #include <darwintest.h>
20 #include <darwintest_utils.h>
21 
22 #include "memorystatus_assertion_helpers.h"
23 #include "test_utils.h"
24 
25 T_GLOBAL_META(
26 	T_META_NAMESPACE("xnu.vm"),
27 	T_META_RADAR_COMPONENT_NAME("xnu"),
28 	T_META_RADAR_COMPONENT_VERSION("VM"),
29 	T_META_CHECK_LEAKS(false),
30 	T_META_OWNER("jarrad")
31 	);
32 
33 #define MEM_SIZE_MB                     10
34 #define NUM_ITERATIONS          5
35 #define FREEZE_PAGES_MAX 256
36 
37 #define CREATE_LIST(X) \
38 	X(SUCCESS) \
39 	X(TOO_FEW_ARGUMENTS) \
40 	X(SYSCTL_VM_PAGESIZE_FAILED) \
41 	X(VM_PAGESIZE_IS_ZERO) \
42 	X(DISPATCH_SOURCE_CREATE_FAILED) \
43 	X(INITIAL_SIGNAL_TO_PARENT_FAILED) \
44 	X(SIGNAL_TO_PARENT_FAILED) \
45 	X(MEMORYSTATUS_CONTROL_FAILED) \
46 	X(IS_FREEZABLE_NOT_AS_EXPECTED) \
47 	X(MEMSTAT_PRIORITY_CHANGE_FAILED) \
48 	X(INVALID_ALLOCATE_PAGES_ARGUMENTS) \
49 	X(FROZEN_BIT_SET) \
50 	X(FROZEN_BIT_NOT_SET) \
51 	X(MEMORYSTATUS_CONTROL_ERROR) \
52 	X(UNABLE_TO_ALLOCATE) \
53 	X(EXIT_CODE_MAX) \
54 
55 #define EXIT_CODES_ENUM(VAR) VAR,
56 enum exit_codes_num {
57 	CREATE_LIST(EXIT_CODES_ENUM)
58 };
59 
60 #define EXIT_CODES_STRING(VAR) #VAR,
61 static const char *exit_codes_str[] = {
62 	CREATE_LIST(EXIT_CODES_STRING)
63 };
64 
65 static int
get_vmpage_size(void)66 get_vmpage_size(void)
67 {
68 	int vmpage_size;
69 	size_t size = sizeof(vmpage_size);
70 	int ret = sysctlbyname("vm.pagesize", &vmpage_size, &size, NULL, 0);
71 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to query vm.pagesize");
72 	T_QUIET; T_ASSERT_GT(vmpage_size, 0, "vm.pagesize is not > 0");
73 	return vmpage_size;
74 }
75 
76 static pid_t child_pid = -1;
77 static int freeze_count = 0;
78 
79 void move_to_idle_band(pid_t);
80 void run_freezer_test(int);
81 void freeze_helper_process(void);
82 /* Gets and optionally sets the freeze pages max threshold */
83 int sysctl_freeze_pages_max(int* new_value);
84 
85 /* NB: in_shared_region and get_rprvt are pulled from the memorystatus unit test.
86  * We're moving away from those unit tests, so they're copied here.
87  */
88 
89 /* Cribbed from 'top'... */
90 static int
in_shared_region(mach_vm_address_t addr,cpu_type_t type)91 in_shared_region(mach_vm_address_t addr, cpu_type_t type)
92 {
93 	mach_vm_address_t base = 0, size = 0;
94 
95 	switch (type) {
96 	case CPU_TYPE_ARM:
97 		base = SHARED_REGION_BASE_ARM;
98 		size = SHARED_REGION_SIZE_ARM;
99 		break;
100 
101 	case CPU_TYPE_ARM64:
102 		base = SHARED_REGION_BASE_ARM64;
103 		size = SHARED_REGION_SIZE_ARM64;
104 		break;
105 
106 
107 	case CPU_TYPE_X86_64:
108 		base = SHARED_REGION_BASE_X86_64;
109 		size = SHARED_REGION_SIZE_X86_64;
110 		break;
111 
112 	case CPU_TYPE_I386:
113 		base = SHARED_REGION_BASE_I386;
114 		size = SHARED_REGION_SIZE_I386;
115 		break;
116 
117 	case CPU_TYPE_POWERPC:
118 		base = SHARED_REGION_BASE_PPC;
119 		size = SHARED_REGION_SIZE_PPC;
120 		break;
121 
122 	case CPU_TYPE_POWERPC64:
123 		base = SHARED_REGION_BASE_PPC64;
124 		size = SHARED_REGION_SIZE_PPC64;
125 		break;
126 
127 	default: {
128 		int t = type;
129 
130 		fprintf(stderr, "unknown CPU type: 0x%x\n", t);
131 		abort();
132 	}
133 	}
134 
135 	return addr >= base && addr < (base + size);
136 }
137 
138 /* Get the resident private memory of the given pid */
139 static unsigned long long
get_rprvt(pid_t pid)140 get_rprvt(pid_t pid)
141 {
142 	mach_port_name_t task;
143 	kern_return_t kr;
144 
145 	mach_vm_size_t rprvt = 0;
146 	mach_vm_size_t empty = 0;
147 	mach_vm_size_t fw_private = 0;
148 	mach_vm_size_t pagesize = vm_kernel_page_size;  // The vm_region page info is reported
149 	                                                // in terms of vm_kernel_page_size.
150 	mach_vm_size_t regs = 0;
151 
152 	mach_vm_address_t addr;
153 	mach_vm_size_t size;
154 
155 	int split = 0;
156 
157 	kr = task_for_pid(mach_task_self(), pid, &task);
158 	T_QUIET; T_ASSERT_TRUE(kr == KERN_SUCCESS, "Unable to get task_for_pid of child");
159 
160 	for (addr = 0;; addr += size) {
161 		vm_region_top_info_data_t info;
162 		mach_msg_type_number_t count = VM_REGION_TOP_INFO_COUNT;
163 		mach_port_t object_name;
164 
165 		kr = mach_vm_region(task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info, &count, &object_name);
166 		if (kr != KERN_SUCCESS) {
167 			break;
168 		}
169 
170 #if   defined (__arm64__)
171 		if (in_shared_region(addr, CPU_TYPE_ARM64)) {
172 #else
173 		if (in_shared_region(addr, CPU_TYPE_ARM)) {
174 #endif
175 			// Private Shared
176 			fw_private += info.private_pages_resident * pagesize;
177 
178 			/*
179 			 * Check if this process has the globally shared
180 			 * text and data regions mapped in.  If so, set
181 			 * split to TRUE and avoid checking
182 			 * again.
183 			 */
184 			if (split == FALSE && info.share_mode == SM_EMPTY) {
185 				vm_region_basic_info_data_64_t  b_info;
186 				mach_vm_address_t b_addr = addr;
187 				mach_vm_size_t b_size = size;
188 				count = VM_REGION_BASIC_INFO_COUNT_64;
189 
190 				kr = mach_vm_region(task, &b_addr, &b_size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&b_info, &count, &object_name);
191 				if (kr != KERN_SUCCESS) {
192 					break;
193 				}
194 
195 				if (b_info.reserved) {
196 					split = TRUE;
197 				}
198 			}
199 
200 			/*
201 			 * Short circuit the loop if this isn't a shared
202 			 * private region, since that's the only region
203 			 * type we care about within the current address
204 			 * range.
205 			 */
206 			if (info.share_mode != SM_PRIVATE) {
207 				continue;
208 			}
209 		}
210 
211 		regs++;
212 
213 		/*
214 		 * Update counters according to the region type.
215 		 */
216 
217 		if (info.share_mode == SM_COW && info.ref_count == 1) {
218 			// Treat single reference SM_COW as SM_PRIVATE
219 			info.share_mode = SM_PRIVATE;
220 		}
221 
222 		switch (info.share_mode) {
223 		case SM_LARGE_PAGE:
224 		// Treat SM_LARGE_PAGE the same as SM_PRIVATE
225 		// since they are not shareable and are wired.
226 		case SM_PRIVATE:
227 			rprvt += info.private_pages_resident * pagesize;
228 			rprvt += info.shared_pages_resident * pagesize;
229 			break;
230 
231 		case SM_EMPTY:
232 			empty += size;
233 			break;
234 
235 		case SM_COW:
236 		case SM_SHARED:
237 			if (pid == 0) {
238 				// Treat kernel_task specially
239 				if (info.share_mode == SM_COW) {
240 					rprvt += info.private_pages_resident * pagesize;
241 				}
242 				break;
243 			}
244 
245 			if (info.share_mode == SM_COW) {
246 				rprvt += info.private_pages_resident * pagesize;
247 			}
248 			break;
249 
250 		default:
251 			assert(0);
252 			break;
253 		}
254 	}
255 
256 	return rprvt;
257 }
258 
259 void
260 move_to_idle_band(pid_t pid)
261 {
262 	memorystatus_priority_properties_t props;
263 	/*
264 	 * Freezing a process also moves it to an elevated jetsam band in order to protect it from idle exits.
265 	 * So we move the child process to the idle band to mirror the typical 'idle app being frozen' scenario.
266 	 */
267 	props.priority = JETSAM_PRIORITY_IDLE;
268 	props.user_data = 0;
269 
270 	/*
271 	 * This requires us to run as root (in the absence of entitlement).
272 	 * Hence the T_META_ASROOT(true) in the T_HELPER_DECL.
273 	 */
274 	if (memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES, pid, 0, &props, sizeof(props))) {
275 		exit(MEMSTAT_PRIORITY_CHANGE_FAILED);
276 	}
277 }
278 
279 void
280 freeze_helper_process(void)
281 {
282 	size_t length;
283 	int ret, freeze_enabled, errno_freeze_sysctl;
284 	uint64_t resident_memory_before, resident_memory_after, vmpage_size;
285 	vmpage_size = (uint64_t) get_vmpage_size();
286 	resident_memory_before = get_rprvt(child_pid) / vmpage_size;
287 
288 	T_LOG("Freezing child pid %d", child_pid);
289 	ret = sysctlbyname("kern.memorystatus_freeze", NULL, NULL, &child_pid, sizeof(child_pid));
290 	errno_freeze_sysctl = errno;
291 	sleep(1);
292 
293 	/*
294 	 * The child process toggles its freezable state on each iteration.
295 	 * So a failure for every alternate freeze is expected.
296 	 */
297 	if (freeze_count % 2) {
298 		length = sizeof(freeze_enabled);
299 		T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", &freeze_enabled, &length, NULL, 0),
300 		    "failed to query vm.freeze_enabled");
301 		if (freeze_enabled) {
302 			errno = errno_freeze_sysctl;
303 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_freeze failed");
304 		} else {
305 			/* If freezer is disabled, skip the test. This can happen due to disk space shortage. */
306 			T_LOG("Freeze has been disabled. Terminating early.");
307 			T_END;
308 		}
309 		resident_memory_after = get_rprvt(child_pid) / vmpage_size;
310 		uint64_t freeze_pages_max = (uint64_t) sysctl_freeze_pages_max(NULL);
311 		T_QUIET; T_ASSERT_LT(resident_memory_after, resident_memory_before, "Freeze didn't reduce resident memory set");
312 		if (resident_memory_before > freeze_pages_max) {
313 			T_QUIET; T_ASSERT_LE(resident_memory_before - resident_memory_after, freeze_pages_max, "Freeze pages froze more than the threshold.");
314 		}
315 		ret = sysctlbyname("kern.memorystatus_thaw", NULL, NULL, &child_pid, sizeof(child_pid));
316 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_thaw failed");
317 	} else {
318 		T_QUIET; T_ASSERT_TRUE(ret != KERN_SUCCESS, "Freeze should have failed");
319 		T_LOG("Freeze failed as expected");
320 	}
321 
322 	freeze_count++;
323 
324 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGUSR1), "failed to send SIGUSR1 to child process");
325 }
326 
327 void
328 run_freezer_test(int num_pages)
329 {
330 	int ret;
331 	char sz_str[50];
332 	char **launch_tool_args;
333 	char testpath[PATH_MAX];
334 	uint32_t testpath_buf_size;
335 	dispatch_source_t ds_freeze, ds_proc;
336 
337 	signal(SIGUSR1, SIG_IGN);
338 	ds_freeze = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
339 	T_QUIET; T_ASSERT_NOTNULL(ds_freeze, "dispatch_source_create (ds_freeze)");
340 
341 	dispatch_source_set_event_handler(ds_freeze, ^{
342 		if (freeze_count < NUM_ITERATIONS) {
343 		        freeze_helper_process();
344 		} else {
345 		        kill(child_pid, SIGKILL);
346 		        dispatch_source_cancel(ds_freeze);
347 		}
348 	});
349 	dispatch_activate(ds_freeze);
350 
351 	testpath_buf_size = sizeof(testpath);
352 	ret = _NSGetExecutablePath(testpath, &testpath_buf_size);
353 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "_NSGetExecutablePath");
354 	T_LOG("Executable path: %s", testpath);
355 
356 	sprintf(sz_str, "%d", num_pages);
357 	launch_tool_args = (char *[]){
358 		testpath,
359 		"-n",
360 		"allocate_pages",
361 		"--",
362 		sz_str,
363 		NULL
364 	};
365 
366 	/* Spawn the child process. Suspend after launch until the exit proc handler has been set up. */
367 	ret = dt_launch_tool(&child_pid, launch_tool_args, true, NULL, NULL);
368 	if (ret != 0) {
369 		T_LOG("dt_launch tool returned %d with error code %d", ret, errno);
370 	}
371 	T_QUIET; T_ASSERT_POSIX_SUCCESS(child_pid, "dt_launch_tool");
372 
373 	ds_proc = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)child_pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue());
374 	T_QUIET; T_ASSERT_NOTNULL(ds_proc, "dispatch_source_create (ds_proc)");
375 
376 	dispatch_source_set_event_handler(ds_proc, ^{
377 		int status = 0, code = 0;
378 		pid_t rc = waitpid(child_pid, &status, 0);
379 		T_QUIET; T_ASSERT_EQ(rc, child_pid, "waitpid");
380 		code = WEXITSTATUS(status);
381 
382 		if (code == 0) {
383 		        T_END;
384 		} else if (code > 0 && code < EXIT_CODE_MAX) {
385 		        T_ASSERT_FAIL("Child exited with %s", exit_codes_str[code]);
386 		} else {
387 		        T_ASSERT_FAIL("Child exited with unknown exit code %d", code);
388 		}
389 	});
390 	dispatch_activate(ds_proc);
391 
392 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGCONT), "failed to send SIGCONT to child process");
393 	dispatch_main();
394 }
395 
396 static void
397 allocate_pages(int num_pages)
398 {
399 	int i, j, vmpgsize;
400 	char val;
401 	__block int num_iter = 0;
402 	__block char **buf;
403 	dispatch_source_t ds_signal;
404 	vmpgsize = get_vmpage_size();
405 	if (num_pages < 1) {
406 		printf("Invalid number of pages to allocate: %d\n", num_pages);
407 		exit(INVALID_ALLOCATE_PAGES_ARGUMENTS);
408 	}
409 
410 	buf = (char**)malloc(sizeof(char*) * (size_t)num_pages);
411 
412 	/* Gives us the compression ratio we see in the typical case (~2.7) */
413 	for (j = 0; j < num_pages; j++) {
414 		buf[j] = (char*)malloc((size_t)vmpgsize * sizeof(char));
415 		val = 0;
416 		for (i = 0; i < vmpgsize; i += 16) {
417 			memset(&buf[j][i], val, 16);
418 			if (i < 3400 * (vmpgsize / 4096)) {
419 				val++;
420 			}
421 		}
422 	}
423 
424 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), dispatch_get_main_queue(), ^{
425 		/* Signal to the parent that we're done allocating and it's ok to freeze us */
426 		printf("[%d] Sending initial signal to parent to begin freezing\n", getpid());
427 		if (kill(getppid(), SIGUSR1) != 0) {
428 		        exit(INITIAL_SIGNAL_TO_PARENT_FAILED);
429 		}
430 	});
431 
432 	signal(SIGUSR1, SIG_IGN);
433 	ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
434 	if (ds_signal == NULL) {
435 		exit(DISPATCH_SOURCE_CREATE_FAILED);
436 	}
437 
438 	dispatch_source_set_event_handler(ds_signal, ^{
439 		int current_state, new_state;
440 		volatile int tmp;
441 
442 		/* Make sure all the pages are accessed before trying to freeze again */
443 		for (int x = 0; x < num_pages; x++) {
444 		        tmp = buf[x][0];
445 		}
446 
447 		current_state = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FREEZABLE, getpid(), 0, NULL, 0);
448 		/* Sysprocs start off as unfreezable. Verify that first. */
449 		if (num_iter == 0 && current_state != 0) {
450 		        exit(IS_FREEZABLE_NOT_AS_EXPECTED);
451 		}
452 
453 		/* Toggle freezable state */
454 		new_state = (current_state) ? 0: 1;
455 		printf("[%d] Changing state from %s to %s\n", getpid(),
456 		(current_state) ? "freezable": "unfreezable", (new_state) ? "freezable": "unfreezable");
457 		if (memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE, getpid(), (uint32_t)new_state, NULL, 0) != KERN_SUCCESS) {
458 		        exit(MEMORYSTATUS_CONTROL_FAILED);
459 		}
460 
461 		/* Verify that the state has been set correctly */
462 		current_state = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FREEZABLE, getpid(), 0, NULL, 0);
463 		if (new_state != current_state) {
464 		        exit(IS_FREEZABLE_NOT_AS_EXPECTED);
465 		}
466 		num_iter++;
467 
468 		if (kill(getppid(), SIGUSR1) != 0) {
469 		        exit(SIGNAL_TO_PARENT_FAILED);
470 		}
471 	});
472 	dispatch_activate(ds_signal);
473 	move_to_idle_band(getpid());
474 
475 	dispatch_main();
476 }
477 
478 T_HELPER_DECL(allocate_pages,
479     "allocates pages to freeze",
480     T_META_ASROOT(true)) {
481 	if (argc < 1) {
482 		exit(TOO_FEW_ARGUMENTS);
483 	}
484 
485 	int num_pages = atoi(argv[0]);
486 	allocate_pages(num_pages);
487 }
488 
489 T_DECL(memorystatus_freeze_default_state, "Test that the freezer is enabled or disabled as expected by default.", T_META_ASROOT(true)) {
490 
491 #if TARGET_OS_IOS || TARGET_OS_WATCH
492 	bool expected_freeze_enabled = true;
493 #else
494 	bool expected_freeze_enabled = false;
495 #endif
496 
497 
498 	int freeze_enabled;
499 	size_t length = sizeof(freeze_enabled);
500 	int ret = sysctlbyname("vm.freeze_enabled", &freeze_enabled, &length, NULL, 0);
501 
502 	if (ret != 0) {
503 		if (expected_freeze_enabled) {
504 			T_ASSERT_FAIL("Expected freezer enabled but did not find sysctl vm.freeze_enabled");
505 		} else {
506 			T_PASS("Did not expect freezer to be enabled and did not find sysctl vm.freeze_enabled");
507 		}
508 	} else {
509 		T_ASSERT_EQ(freeze_enabled, expected_freeze_enabled, "Expected freezer sysctl status %d, got %d", expected_freeze_enabled, freeze_enabled);
510 	}
511 }
512 
513 T_DECL(freeze, "VM freezer test",
514 	T_META_BOOTARGS_SET("freeze_enabled=1"),
515 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
516 	T_META_ASROOT(true)) {
517 	run_freezer_test(
518 		(MEM_SIZE_MB << 20) / get_vmpage_size());
519 }
520 
521 static int old_freeze_pages_max = 0;
522 static void
523 reset_freeze_pages_max(void)
524 {
525 	if (old_freeze_pages_max != 0) {
526 		sysctl_freeze_pages_max(&old_freeze_pages_max);
527 	}
528 }
529 
530 int
531 sysctl_freeze_pages_max(int* new_value)
532 {
533 	static int set_end_handler = false;
534 	int freeze_pages_max, ret;
535 	size_t size = sizeof(freeze_pages_max);
536 	ret = sysctlbyname("kern.memorystatus_freeze_pages_max", &freeze_pages_max, &size, new_value, size);
537 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Unable to query kern.memorystatus_freeze_pages_max");
538 	if (!set_end_handler) {
539 		// Save the original value and instruct darwintest to restore it after the test completes
540 		old_freeze_pages_max = freeze_pages_max;
541 		T_ATEND(reset_freeze_pages_max);
542 		set_end_handler = true;
543 	}
544 	return old_freeze_pages_max;
545 }
546 
547 T_DECL(freeze_over_max_threshold, "Max Freeze Threshold is Enforced",
548 	T_META_BOOTARGS_SET("freeze_enabled=1"),
549 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
550 	T_META_ASROOT(true)) {
551 	int freeze_pages_max = FREEZE_PAGES_MAX;
552 	sysctl_freeze_pages_max(&freeze_pages_max);
553 	run_freezer_test(FREEZE_PAGES_MAX * 2);
554 }
555 
556 T_HELPER_DECL(frozen_background, "Frozen background process",
557 	T_META_BOOTARGS_SET("freeze_enabled=1"),
558 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
559 	T_META_ASROOT(true)) {
560 	kern_return_t kern_ret;
561 	/* Set the process to freezable */
562 	kern_ret = memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE, getpid(), 1, NULL, 0);
563 	T_QUIET; T_ASSERT_EQ(kern_ret, KERN_SUCCESS, "set process is freezable");
564 	/* Signal to our parent that we can be frozen */
565 	if (kill(getppid(), SIGUSR1) != 0) {
566 		T_LOG("Unable to signal to parent process!");
567 		exit(1);
568 	}
569 	while (1) {
570 		;
571 	}
572 }
573 
574 static void
575 freeze_process(pid_t pid)
576 {
577 	int ret, freeze_enabled, errno_freeze_sysctl;
578 	size_t length;
579 	T_LOG("Freezing pid %d", pid);
580 
581 	ret = sysctlbyname("kern.memorystatus_freeze", NULL, NULL, &pid, sizeof(pid));
582 	errno_freeze_sysctl = errno;
583 	length = sizeof(freeze_enabled);
584 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", &freeze_enabled, &length, NULL, 0),
585 	    "failed to query vm.freeze_enabled");
586 	if (freeze_enabled) {
587 		errno = errno_freeze_sysctl;
588 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_freeze failed");
589 	} else {
590 		/* If freezer is disabled, skip the test. This can happen due to disk space shortage. */
591 		T_LOG("Freeze has been disabled. Terminating early.");
592 		T_END;
593 	}
594 }
595 
596 static uint32_t max_frozen_demotions_daily_default;
597 
598 static void
599 reset_max_frozen_demotions_daily(void)
600 {
601 	int sysctl_ret = sysctlbyname("kern.memorystatus_max_freeze_demotions_daily", NULL, NULL, &max_frozen_demotions_daily_default, sizeof(max_frozen_demotions_daily_default));
602 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctl_ret, "set kern.memorystatus_max_freeze_demotions_daily to default");
603 }
604 
605 static void
606 allow_unlimited_demotions(void)
607 {
608 	size_t size = sizeof(max_frozen_demotions_daily_default);
609 	uint32_t new_value = UINT32_MAX;
610 	int sysctl_ret = sysctlbyname("kern.memorystatus_max_freeze_demotions_daily", &max_frozen_demotions_daily_default, &size, &new_value, sizeof(new_value));
611 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctl_ret, "kern.memorystatus_max_freeze_demotions_daily = UINT32_MAX");
612 	T_ATEND(reset_max_frozen_demotions_daily);
613 }
614 
615 static void
616 memorystatus_assertion_test_demote_frozen(void)
617 {
618 	/*
619 	 * 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.
620 	 * Then remove thee assertion, and ensure it gets demoted properly.
621 	 */
622 	/* these values will remain fixed during testing */
623 	int             active_limit_mb = 15;   /* arbitrary */
624 	int             inactive_limit_mb = 7;  /* arbitrary */
625 	__block int             demote_value = 1;
626 	/* Launch the child process, and elevate its priority */
627 	int requestedpriority;
628 	dispatch_source_t ds_signal, ds_exit;
629 	requestedpriority = JETSAM_PRIORITY_FREEZER;
630 	allow_unlimited_demotions();
631 
632 	/* Wait for the child process to tell us that it's ready, and then freeze it */
633 	signal(SIGUSR1, SIG_IGN);
634 	ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
635 	T_QUIET; T_ASSERT_NOTNULL(ds_signal, "dispatch_source_create");
636 	dispatch_source_set_event_handler(ds_signal, ^{
637 		int sysctl_ret;
638 		/* Freeze the process, trigger agressive demotion, and check that it hasn't been demoted. */
639 		freeze_process(child_pid);
640 		/* Agressive demotion */
641 		sysctl_ret = sysctlbyname("kern.memorystatus_demote_frozen_processes", NULL, NULL, &demote_value, sizeof(demote_value));
642 		T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctl_ret, "sysctl kern.memorystatus_demote_frozen_processes succeeded");
643 		/* Check */
644 		(void)check_properties(child_pid, requestedpriority, inactive_limit_mb, 0x0, ASSERTION_STATE_IS_SET, "Priority was set");
645 		T_LOG("Relinquishing our assertion.");
646 		/* Relinquish our assertion, and check that it gets demoted. */
647 		relinquish_assertion_priority(child_pid, 0x0);
648 		(void)check_properties(child_pid, JETSAM_PRIORITY_AGING_BAND2, inactive_limit_mb, 0x0, ASSERTION_STATE_IS_RELINQUISHED, "Assertion was reqlinquished.");
649 		/* Kill the child */
650 		T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGKILL), "Killed child process");
651 		T_END;
652 	});
653 
654 	/* Launch the child process and set the initial properties on it. */
655 	child_pid = launch_background_helper("frozen_background", false, true);
656 	set_memlimits(child_pid, active_limit_mb, inactive_limit_mb, false, false);
657 	set_assertion_priority(child_pid, requestedpriority, 0x0);
658 	(void)check_properties(child_pid, requestedpriority, inactive_limit_mb, 0x0, ASSERTION_STATE_IS_SET, "Priority was set");
659 	/* Listen for exit. */
660 	ds_exit = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)child_pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue());
661 	dispatch_source_set_event_handler(ds_exit, ^{
662 		int status = 0, code = 0;
663 		pid_t rc = waitpid(child_pid, &status, 0);
664 		T_QUIET; T_ASSERT_EQ(rc, child_pid, "waitpid");
665 		code = WEXITSTATUS(status);
666 		T_QUIET; T_ASSERT_EQ(code, 0, "Child exited cleanly");
667 		T_END;
668 	});
669 
670 	dispatch_activate(ds_exit);
671 	dispatch_activate(ds_signal);
672 	dispatch_main();
673 }
674 
675 T_DECL(assertion_test_demote_frozen, "demoted frozen process goes to asserted priority.",
676 	T_META_BOOTARGS_SET("freeze_enabled=1"),
677 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
678 	T_META_ASROOT(true)) {
679 	memorystatus_assertion_test_demote_frozen();
680 }
681 
682 static unsigned int
683 get_freeze_daily_pages_max(void)
684 {
685 	unsigned int memorystatus_freeze_daily_mb_max;
686 	size_t length = sizeof(memorystatus_freeze_daily_mb_max);
687 	int ret = sysctlbyname("kern.memorystatus_freeze_daily_mb_max", &memorystatus_freeze_daily_mb_max, &length, NULL, 0);
688 	T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_freeze_daily_mb_max");
689 	return memorystatus_freeze_daily_mb_max * 1024UL * 1024UL / vm_kernel_page_size;
690 }
691 
692 static uint64_t
693 get_budget_multiplier(void)
694 {
695 	uint64_t budget_multiplier = 0;
696 	size_t size = sizeof(budget_multiplier);
697 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_budget_multiplier", &budget_multiplier, &size, NULL, 0),
698 	    "get kern.memorystatus_freeze_budget_multiplier");
699 	return budget_multiplier;
700 }
701 static void
702 set_budget_multiplier(uint64_t multiplier)
703 {
704 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_budget_multiplier", NULL, NULL, &multiplier, sizeof(multiplier)),
705 	    "set kern.memorystatus_freeze_budget_multiplier");
706 }
707 
708 static uint64_t original_budget_multiplier;
709 static void
710 reset_budget_multiplier(void)
711 {
712 	set_budget_multiplier(original_budget_multiplier);
713 }
714 
715 T_DECL(budget_replenishment, "budget replenishes properly",
716 	T_META_BOOTARGS_SET("freeze_enabled=1"),
717 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
718     T_META_REQUIRES_SYSCTL_NE("kern.memorystatus_freeze_daily_mb_max", UINT32_MAX)) {
719 	size_t length;
720 	int ret;
721 	static unsigned int kTestIntervalSecs = 60 * 60 * 32; // 32 Hours
722 	unsigned int memorystatus_freeze_daily_pages_max;
723 	static unsigned int kFixedPointFactor = 100;
724 	static unsigned int kNumSecondsInDay = 60 * 60 * 24;
725 	unsigned int new_budget, expected_new_budget_pages;
726 	size_t new_budget_ln;
727 	vm_size_t page_size = vm_kernel_page_size;
728 
729 	original_budget_multiplier = get_budget_multiplier();
730 	T_ATEND(reset_budget_multiplier);
731 	set_budget_multiplier(100);
732 	/*
733 	 * Calculate a new budget as if the previous interval expired kTestIntervalSecs
734 	 * ago and we used up its entire budget.
735 	 */
736 	length = sizeof(kTestIntervalSecs);
737 	new_budget_ln = sizeof(new_budget);
738 	ret = sysctlbyname("vm.memorystatus_freeze_calculate_new_budget", &new_budget, &new_budget_ln, &kTestIntervalSecs, length);
739 	T_ASSERT_POSIX_SUCCESS(ret, "vm.memorystatus_freeze_calculate_new_budget");
740 
741 	memorystatus_freeze_daily_pages_max = get_freeze_daily_pages_max();
742 	T_LOG("memorystatus_freeze_daily_pages_max %u", memorystatus_freeze_daily_pages_max);
743 	T_LOG("page_size %lu", page_size);
744 
745 	/*
746 	 * We're kTestIntervalSecs past a new interval. Which means we are owed kNumSecondsInDay
747 	 * seconds of budget.
748 	 */
749 	expected_new_budget_pages = memorystatus_freeze_daily_pages_max;
750 	T_LOG("expected_new_budget_pages before %u", expected_new_budget_pages);
751 	T_ASSERT_EQ(kTestIntervalSecs, 60 * 60 * 32, "kTestIntervalSecs did not change");
752 	expected_new_budget_pages += ((kTestIntervalSecs * kFixedPointFactor) / (kNumSecondsInDay)
753 	    * memorystatus_freeze_daily_pages_max) / kFixedPointFactor;
754 	T_LOG("expected_new_budget_pages after %u", expected_new_budget_pages);
755 	T_LOG("memorystatus_freeze_daily_pages_max after %u", memorystatus_freeze_daily_pages_max);
756 
757 	T_QUIET; T_ASSERT_EQ(new_budget, expected_new_budget_pages, "Calculate new budget behaves correctly.");
758 }
759 
760 
761 static bool
762 is_proc_in_frozen_list(pid_t pid, char* name, size_t name_len)
763 {
764 	int bytes_written;
765 	bool found = false;
766 	global_frozen_procs_t *frozen_procs = malloc(sizeof(global_frozen_procs_t));
767 	T_QUIET; T_ASSERT_NOTNULL(frozen_procs, "malloc");
768 
769 	bytes_written = memorystatus_control(MEMORYSTATUS_CMD_FREEZER_CONTROL, 0, FREEZER_CONTROL_GET_PROCS, frozen_procs, sizeof(global_frozen_procs_t));
770 	T_QUIET; T_ASSERT_LE((size_t) bytes_written, sizeof(global_frozen_procs_t), "Didn't overflow buffer");
771 	T_QUIET; T_ASSERT_GT(bytes_written, 0, "Wrote someting");
772 
773 	for (size_t i = 0; i < frozen_procs->gfp_num_frozen; i++) {
774 		if (frozen_procs->gfp_procs[i].fp_pid == pid) {
775 			found = true;
776 			strlcpy(name, frozen_procs->gfp_procs[i].fp_name, name_len);
777 		}
778 	}
779 	return found;
780 }
781 
782 static void
783 unset_testing_pid(void)
784 {
785 	int ret;
786 	ret = memorystatus_control(MEMORYSTATUS_CMD_SET_TESTING_PID, 0, MEMORYSTATUS_FLAGS_UNSET_TESTING_PID, NULL, 0);
787 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, 0, "Drop ownership of jetsam snapshot");
788 }
789 
790 static void
791 set_testing_pid(void)
792 {
793 	int ret;
794 	ret = memorystatus_control(MEMORYSTATUS_CMD_SET_TESTING_PID, 0, MEMORYSTATUS_FLAGS_SET_TESTING_PID, NULL, 0);
795 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "Take ownership of jetsam snapshot");
796 	T_ATEND(unset_testing_pid);
797 }
798 
799 /*
800  * Retrieve a jetsam snapshot.
801  *
802  * return:
803  *      pointer to snapshot.
804  *
805  *	Caller is responsible for freeing snapshot.
806  */
807 static
808 memorystatus_jetsam_snapshot_t *
809 get_jetsam_snapshot(uint32_t flags, bool empty_allowed)
810 {
811 	memorystatus_jetsam_snapshot_t * snapshot = NULL;
812 	int ret;
813 	uint32_t size;
814 
815 	ret = memorystatus_control(MEMORYSTATUS_CMD_GET_JETSAM_SNAPSHOT, 0, flags, NULL, 0);
816 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, 0, "Get jetsam snapshot size");
817 	size = (uint32_t) ret;
818 	if (size == 0 && empty_allowed) {
819 		return snapshot;
820 	}
821 
822 	snapshot = (memorystatus_jetsam_snapshot_t*)malloc(size);
823 	T_QUIET; T_ASSERT_NOTNULL(snapshot, "Allocate snapshot of size %d", size);
824 
825 	ret = memorystatus_control(MEMORYSTATUS_CMD_GET_JETSAM_SNAPSHOT, 0, flags, snapshot, size);
826 	T_QUIET; T_ASSERT_GT(size, 0, "Get jetsam snapshot");
827 
828 	if (((size - sizeof(memorystatus_jetsam_snapshot_t)) / sizeof(memorystatus_jetsam_snapshot_entry_t)) != snapshot->entry_count) {
829 		T_FAIL("Malformed snapshot: %d! Expected %ld + %zd x %ld = %ld\n", size,
830 		    sizeof(memorystatus_jetsam_snapshot_t), snapshot->entry_count, sizeof(memorystatus_jetsam_snapshot_entry_t),
831 		    sizeof(memorystatus_jetsam_snapshot_t) + (snapshot->entry_count * sizeof(memorystatus_jetsam_snapshot_entry_t)));
832 		if (snapshot) {
833 			free(snapshot);
834 		}
835 	}
836 
837 	return snapshot;
838 }
839 
840 /*
841  * Look for the given pid in the snapshot.
842  *
843  * return:
844  *     pointer to pid's entry or NULL if pid is not found.
845  *
846  * Caller has ownership of snapshot before and after call.
847  */
848 static
849 memorystatus_jetsam_snapshot_entry_t *
850 get_jetsam_snapshot_entry(memorystatus_jetsam_snapshot_t *snapshot, pid_t pid)
851 {
852 	T_QUIET; T_ASSERT_NOTNULL(snapshot, "Got snapshot");
853 	for (size_t i = 0; i < snapshot->entry_count; i++) {
854 		memorystatus_jetsam_snapshot_entry_t *curr = &(snapshot->entries[i]);
855 		if (curr->pid == pid) {
856 			return curr;
857 		}
858 	}
859 
860 	return NULL;
861 }
862 
863 static void
864 resume_and_kill_proc(pid_t pid)
865 {
866 	int ret = pid_resume(pid);
867 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "proc resumed after freeze");
868 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(pid, SIGKILL), "Killed process");
869 }
870 
871 static void
872 resume_and_kill_child(void)
873 {
874 	/* Used for test cleanup. proc might not be suspended so pid_resume might fail. */
875 	pid_resume(child_pid);
876 	kill(child_pid, SIGKILL);
877 }
878 
879 static dispatch_source_t
880 run_block_after_signal(int sig, dispatch_block_t block)
881 {
882 	dispatch_source_t ds_signal;
883 	signal(sig, SIG_IGN);
884 	ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) sig, 0, dispatch_get_main_queue());
885 	T_QUIET; T_ASSERT_NOTNULL(ds_signal, "dispatch_source_create");
886 	dispatch_source_set_event_handler(ds_signal, block);
887 	return ds_signal;
888 }
889 
890 /*
891  * Launches the child & runs the given block after the child signals.
892  * If exit_with_child is true, the test will exit when the child exits.
893  */
894 static void
895 test_after_background_helper_launches(bool exit_with_child, const char* variant, dispatch_block_t test_block)
896 {
897 	dispatch_source_t ds_signal, ds_exit;
898 
899 	ds_signal = run_block_after_signal(SIGUSR1, test_block);
900 	/* Launch the child process. */
901 	child_pid = launch_background_helper(variant, false, true);
902 	T_ATEND(resume_and_kill_child);
903 	/* Listen for exit. */
904 	if (exit_with_child) {
905 		ds_exit = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)child_pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue());
906 		dispatch_source_set_event_handler(ds_exit, ^{
907 			int status = 0, code = 0;
908 			pid_t rc = waitpid(child_pid, &status, 0);
909 			T_QUIET; T_ASSERT_EQ(rc, child_pid, "waitpid");
910 			code = WEXITSTATUS(status);
911 			if (code != 0) {
912 			        T_LOG("Child exited with error: %s", exit_codes_str[code]);
913 			}
914 			T_QUIET; T_ASSERT_EQ(code, 0, "Child exited cleanly");
915 			T_END;
916 		});
917 
918 		dispatch_activate(ds_exit);
919 	}
920 	dispatch_activate(ds_signal);
921 }
922 
923 T_DECL(get_frozen_procs, "List processes in the freezer",
924 	T_META_BOOTARGS_SET("freeze_enabled=1"),
925 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1)) {
926 
927 	test_after_background_helper_launches(true, "frozen_background", ^{
928 		proc_name_t name;
929 		/* Place the child in the idle band so that it gets elevated like a typical app. */
930 		move_to_idle_band(child_pid);
931 		/* Freeze the process, and check that it's in the list of frozen processes. */
932 		freeze_process(child_pid);
933 		/* Check */
934 		T_QUIET; T_ASSERT_TRUE(is_proc_in_frozen_list(child_pid, name, sizeof(name)), "Found proc in frozen list");
935 		T_QUIET; T_EXPECT_EQ_STR(name, "memorystatus_freeze_test", "Proc has correct name");
936 		/* Kill the child */
937 		T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGKILL), "Killed child process");
938 		T_END;
939 	});
940 	dispatch_main();
941 }
942 
943 T_DECL(frozen_to_swap_accounting, "jetsam snapshot has frozen_to_swap accounting",
944 	T_META_BOOTARGS_SET("freeze_enabled=1"),
945 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1)) {
946 	static const size_t kSnapshotSleepDelay = 5;
947 	static const size_t kFreezeToDiskMaxDelay = 60;
948 
949 
950 	test_after_background_helper_launches(true, "frozen_background", ^{
951 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
952 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
953 		/* Place the child in the idle band so that it gets elevated like a typical app. */
954 		move_to_idle_band(child_pid);
955 		freeze_process(child_pid);
956 		/*
957 		 * Wait until the child's pages get paged out to disk.
958 		 * If we don't see any pages get sent to disk before kFreezeToDiskMaxDelay seconds,
959 		 * something is either wrong with the compactor or the accounting.
960 		 */
961 		for (size_t i = 0; i < kFreezeToDiskMaxDelay / kSnapshotSleepDelay; i++) {
962 		        snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
963 		        child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
964 		        T_QUIET; T_ASSERT_NOTNULL(child_entry, "Found child in snapshot");
965 		        if (child_entry->jse_frozen_to_swap_pages > 0) {
966 		                break;
967 			}
968 		        free(snapshot);
969 		        sleep(kSnapshotSleepDelay);
970 		}
971 		T_QUIET; T_ASSERT_GT(child_entry->jse_frozen_to_swap_pages, 0ULL, "child has some pages in swap");
972 		free(snapshot);
973 		/* Kill the child */
974 		T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGKILL), "Killed child process");
975 		T_END;
976 	});
977 	dispatch_main();
978 }
979 
980 T_DECL(freezer_snapshot, "App kills are recorded in the freezer snapshot",
981 	T_META_BOOTARGS_SET("freeze_enabled=1"),
982 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1)) {
983 	/* Take ownership of the snapshot to ensure we don't race with another process trying to consume them. */
984 	set_testing_pid();
985 
986 	test_after_background_helper_launches(false, "frozen_background", ^{
987 		int ret;
988 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
989 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
990 
991 		ret = memorystatus_control(MEMORYSTATUS_CMD_TEST_JETSAM, child_pid, 0, 0, 0);
992 		T_ASSERT_POSIX_SUCCESS(ret, "jetsam'd the child");
993 
994 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_FREEZER, false);
995 		T_ASSERT_NOTNULL(snapshot, "Got freezer snapshot");
996 		child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
997 		T_QUIET; T_ASSERT_NOTNULL(child_entry, "Child is in freezer snapshot");
998 		T_QUIET; T_ASSERT_EQ(child_entry->killed, (unsigned long long) JETSAM_REASON_GENERIC, "Child entry was killed");
999 
1000 		free(snapshot);
1001 		T_END;
1002 	});
1003 	dispatch_main();
1004 }
1005 
1006 T_DECL(freezer_snapshot_consume, "Freezer snapshot is consumed on read",
1007 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1008 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1)) {
1009 	/* Take ownership of the snapshot to ensure we don't race with another process trying to consume them. */
1010 	set_testing_pid();
1011 
1012 	test_after_background_helper_launches(false, "frozen_background", ^{
1013 		int ret;
1014 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
1015 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
1016 
1017 		ret = memorystatus_control(MEMORYSTATUS_CMD_TEST_JETSAM, child_pid, 0, 0, 0);
1018 		T_ASSERT_POSIX_SUCCESS(ret, "jetsam'd the child");
1019 
1020 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_FREEZER, false);
1021 		T_ASSERT_NOTNULL(snapshot, "Got first freezer snapshot");
1022 		child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1023 		T_QUIET; T_ASSERT_NOTNULL(child_entry, "Child is in first freezer snapshot");
1024 
1025 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_FREEZER, true);
1026 		if (snapshot != NULL) {
1027 		        child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1028 		        T_QUIET; T_ASSERT_NULL(child_entry, "Child is not in second freezer snapshot");
1029 		}
1030 
1031 		free(snapshot);
1032 		T_END;
1033 	});
1034 	dispatch_main();
1035 }
1036 
1037 T_DECL(freezer_snapshot_frozen_state, "Frozen state is recorded in freezer snapshot",
1038 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1039 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1)) {
1040 	/* Take ownership of the snapshot to ensure we don't race with another process trying to consume them. */
1041 	set_testing_pid();
1042 
1043 	test_after_background_helper_launches(false, "frozen_background", ^{
1044 		int ret;
1045 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
1046 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
1047 
1048 		move_to_idle_band(child_pid);
1049 		freeze_process(child_pid);
1050 
1051 		ret = memorystatus_control(MEMORYSTATUS_CMD_TEST_JETSAM, child_pid, 0, 0, 0);
1052 		T_ASSERT_POSIX_SUCCESS(ret, "jetsam'd the child");
1053 
1054 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_FREEZER, false);
1055 		T_ASSERT_NOTNULL(snapshot, "Got freezer snapshot");
1056 		child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1057 		T_QUIET; T_ASSERT_NOTNULL(child_entry, "Child is in freezer snapshot");
1058 		T_QUIET; T_ASSERT_TRUE(child_entry->state & kMemorystatusFrozen, "Child entry's frozen bit is set");
1059 
1060 		free(snapshot);
1061 		T_END;
1062 	});
1063 	dispatch_main();
1064 }
1065 
1066 T_DECL(freezer_snapshot_thaw_state, "Thaw count is recorded in freezer snapshot",
1067 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1068 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1)) {
1069 	/* Take ownership of the snapshot to ensure we don't race with another process trying to consume them. */
1070 	set_testing_pid();
1071 
1072 	test_after_background_helper_launches(false, "frozen_background", ^{
1073 		int ret;
1074 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
1075 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
1076 
1077 		move_to_idle_band(child_pid);
1078 		ret = pid_suspend(child_pid);
1079 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1080 		freeze_process(child_pid);
1081 		ret = pid_resume(child_pid);
1082 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
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 freezer snapshot");
1089 		child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1090 		T_QUIET; T_ASSERT_NOTNULL(child_entry, "Child is in freezer snapshot");
1091 		T_QUIET; T_ASSERT_TRUE(child_entry->state & kMemorystatusFrozen, "Child entry's frozen bit is still set after thaw");
1092 		T_QUIET; T_ASSERT_TRUE(child_entry->state & kMemorystatusWasThawed, "Child entry was thawed");
1093 		T_QUIET; T_ASSERT_EQ(child_entry->jse_thaw_count, 1ULL, "Child entry's thaw count was incremented");
1094 
1095 		free(snapshot);
1096 		T_END;
1097 	});
1098 	dispatch_main();
1099 }
1100 
1101 T_HELPER_DECL(check_frozen, "Check frozen state", T_META_ASROOT(true)) {
1102 	int kern_ret;
1103 	dispatch_source_t ds_signal;
1104 	__block int is_frozen;
1105 	/* Set the process to freezable */
1106 	kern_ret = memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE, getpid(), 1, NULL, 0);
1107 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kern_ret, "set process is freezable");
1108 
1109 	/* We should not be frozen yet. */
1110 	is_frozen = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FROZEN, getpid(), 0, NULL, 0);
1111 	if (is_frozen == -1) {
1112 		T_LOG("memorystatus_control error: %s", strerror(errno));
1113 		exit(MEMORYSTATUS_CONTROL_ERROR);
1114 	}
1115 	if (is_frozen) {
1116 		exit(FROZEN_BIT_SET);
1117 	}
1118 
1119 	ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
1120 	if (ds_signal == NULL) {
1121 		exit(DISPATCH_SOURCE_CREATE_FAILED);
1122 	}
1123 
1124 	dispatch_source_set_event_handler(ds_signal, ^{
1125 		/* We should now be frozen. */
1126 		is_frozen = memorystatus_control(MEMORYSTATUS_CMD_GET_PROCESS_IS_FROZEN, getpid(), 0, NULL, 0);
1127 		if (is_frozen == -1) {
1128 		        T_LOG("memorystatus_control error: %s", strerror(errno));
1129 		        exit(MEMORYSTATUS_CONTROL_ERROR);
1130 		}
1131 		if (!is_frozen) {
1132 		        exit(FROZEN_BIT_NOT_SET);
1133 		}
1134 		exit(SUCCESS);
1135 	});
1136 	dispatch_activate(ds_signal);
1137 
1138 	sig_t sig_ret = signal(SIGUSR1, SIG_IGN);
1139 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NE(sig_ret, SIG_ERR, "signal(SIGUSR1, SIG_IGN)");
1140 
1141 	/* Signal to our parent that we can be frozen */
1142 	if (kill(getppid(), SIGUSR1) != 0) {
1143 		T_LOG("Unable to signal to parent process!");
1144 		exit(SIGNAL_TO_PARENT_FAILED);
1145 	}
1146 
1147 	dispatch_main();
1148 }
1149 
1150 T_DECL(memorystatus_get_process_is_frozen, "MEMORYSTATUS_CMD_GET_PROCESS_IS_FROZEN returns correct state",
1151 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1152 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1)) {
1153 
1154 	test_after_background_helper_launches(true, "check_frozen", ^{
1155 		int ret;
1156 		/* Freeze the child, resume it, and signal it to check its state */
1157 		move_to_idle_band(child_pid);
1158 		ret = pid_suspend(child_pid);
1159 		T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1160 		freeze_process(child_pid);
1161 		ret = pid_resume(child_pid);
1162 		T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1163 
1164 		kill(child_pid, SIGUSR1);
1165 		/* The child will checks its own frozen state & exit. */
1166 	});
1167 	dispatch_main();
1168 }
1169 
1170 static unsigned int freeze_pages_min_old;
1171 static int throttle_enabled_old;
1172 static void
1173 cleanup_memorystatus_freeze_top_process(void)
1174 {
1175 	sysctlbyname("kern.memorystatus_freeze_pages_min", NULL, NULL, &freeze_pages_min_old, sizeof(freeze_pages_min_old));
1176 	sysctlbyname("kern.memorystatus_freeze_throttle_enabled", NULL, NULL, &throttle_enabled_old, sizeof(throttle_enabled_old));
1177 }
1178 
1179 /*
1180  * Disables heuristics that could prevent us from freezing the child via memorystatus_freeze_top_process.
1181  */
1182 static void
1183 memorystatus_freeze_top_process_setup(void)
1184 {
1185 	size_t freeze_pages_min_size = sizeof(freeze_pages_min_old);
1186 	unsigned int freeze_pages_min_new = 0;
1187 	size_t throttle_enabled_old_size = sizeof(throttle_enabled_old);
1188 	int throttle_enabled_new = 1, ret;
1189 
1190 	ret = sysctlbyname("kern.memorystatus_freeze_pages_min", &freeze_pages_min_old, &freeze_pages_min_size, &freeze_pages_min_new, sizeof(freeze_pages_min_new));
1191 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "set kern.memorystatus_freeze_pages_min");
1192 	ret = sysctlbyname("kern.memorystatus_freeze_throttle_enabled", &throttle_enabled_old, &throttle_enabled_old_size, &throttle_enabled_new, sizeof(throttle_enabled_new));
1193 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "set kern.memorystatus_freeze_throttle_enabled");
1194 	T_ATEND(cleanup_memorystatus_freeze_top_process);
1195 	/* Take ownership of the freezer probabilities for the duration of the test so that we don't race with dasd. */
1196 	set_testing_pid();
1197 }
1198 
1199 /*
1200  * Moves the proc to the idle band and suspends it.
1201  */
1202 static void
1203 prepare_proc_for_freezing(pid_t pid)
1204 {
1205 	move_to_idle_band(pid);
1206 	int ret = pid_suspend(pid);
1207 	T_ASSERT_POSIX_SUCCESS(ret, "proc suspended");
1208 }
1209 
1210 #define P_MEMSTAT_FROZEN 0x00000002
1211 static void
1212 verify_proc_frozen_state(pid_t pid, bool expected)
1213 {
1214 	memorystatus_jetsam_snapshot_t *snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
1215 	memorystatus_jetsam_snapshot_entry_t *entry = get_jetsam_snapshot_entry(snapshot, pid);
1216 	T_ASSERT_NOTNULL(entry, "%d is in snapshot", pid);
1217 	bool is_frozen = (entry->state & P_MEMSTAT_FROZEN) != 0;
1218 	if (is_frozen != expected) {
1219 		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);
1220 	}
1221 	T_ASSERT_EQ(is_frozen, expected, "%s frozen state", entry->name);
1222 	free(snapshot);
1223 }
1224 
1225 static void
1226 verify_proc_is_frozen(pid_t pid)
1227 {
1228 	verify_proc_frozen_state(pid, true);
1229 }
1230 
1231 static void
1232 verify_proc_not_frozen(pid_t pid)
1233 {
1234 	verify_proc_frozen_state(pid, false);
1235 }
1236 
1237 T_DECL(memorystatus_freeze_top_process, "memorystatus_freeze_top_process chooses the correct process",
1238 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1239 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
1240     T_META_ASROOT(true),
1241     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1)) {
1242 	T_SKIP("Skipping flaky test"); // rdar://76986376
1243 	int32_t memorystatus_freeze_band = 0;
1244 	size_t memorystatus_freeze_band_size = sizeof(memorystatus_freeze_band);
1245 	__block errno_t ret;
1246 	__block int maxproc;
1247 	size_t maxproc_size = sizeof(maxproc);
1248 
1249 	ret = sysctlbyname("kern.maxproc", &maxproc, &maxproc_size, NULL, 0);
1250 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.maxproc");
1251 	ret = sysctlbyname("kern.memorystatus_freeze_jetsam_band", &memorystatus_freeze_band, &memorystatus_freeze_band_size, NULL, 0);
1252 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_freeze_jetsam_band");
1253 
1254 	memorystatus_freeze_top_process_setup();
1255 	test_after_background_helper_launches(true, "frozen_background", ^{
1256 		int32_t child_band = JETSAM_PRIORITY_DEFAULT;
1257 		prepare_proc_for_freezing(child_pid);
1258 
1259 		size_t buffer_len = sizeof(memorystatus_properties_entry_v1_t) * (size_t) maxproc;
1260 		memorystatus_properties_entry_v1_t *properties_list = malloc(buffer_len);
1261 		T_QUIET; T_ASSERT_NOTNULL(properties_list, "malloc properties array");
1262 		size_t properties_list_len = 0;
1263 		/* The child needs to age down into the idle band before it's eligible to be frozen. */
1264 		T_LOG("Waiting for child to age into the idle band.");
1265 		while (child_band != JETSAM_PRIORITY_IDLE) {
1266 		        memset(properties_list, 0, buffer_len);
1267 		        properties_list_len = 0;
1268 		        memorystatus_jetsam_snapshot_t *snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
1269 
1270 		        bool found = false;
1271 		        for (size_t i = 0; i < snapshot->entry_count; i++) {
1272 		                memorystatus_jetsam_snapshot_entry_t *snapshot_entry = &snapshot->entries[i];
1273 		                if (snapshot_entry->priority <= memorystatus_freeze_band && !snapshot_entry->killed) {
1274 		                        pid_t pid = snapshot_entry->pid;
1275 		                        memorystatus_properties_entry_v1_t *property_entry = &properties_list[properties_list_len++];
1276 		                        property_entry->version = 1;
1277 		                        property_entry->pid = pid;
1278 		                        if (pid == child_pid) {
1279 		                                found = true;
1280 		                                property_entry->use_probability = 1;
1281 		                                child_band = snapshot_entry->priority;
1282 					} else {
1283 		                                property_entry->use_probability = 0;
1284 					}
1285 		                        strncpy(property_entry->proc_name, snapshot_entry->name, MAXCOMLEN);
1286 		                        property_entry->proc_name[MAXCOMLEN] = '\0';
1287 				}
1288 			}
1289 		        T_QUIET; T_ASSERT_TRUE(found, "Child is in on demand snapshot");
1290 		        free(snapshot);
1291 		}
1292 		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);
1293 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_PROBABILITY");
1294 		free(properties_list);
1295 		int val = 1;
1296 		ret = sysctlbyname("vm.memorystatus_freeze_top_process", NULL, NULL, &val, sizeof(val));
1297 		T_ASSERT_POSIX_SUCCESS(ret, "freeze_top_process");
1298 
1299 		verify_proc_is_frozen(child_pid);
1300 		resume_and_kill_proc(child_pid);
1301 		T_END;
1302 	});
1303 	dispatch_main();
1304 }
1305 static unsigned int use_ordered_list_original;
1306 static unsigned int use_demotion_list_original;
1307 static void
1308 reset_ordered_freeze_mode(void)
1309 {
1310 	sysctlbyname("kern.memorystatus_freezer_use_ordered_list", NULL, NULL, &use_ordered_list_original, sizeof(use_ordered_list_original));
1311 }
1312 
1313 static void
1314 reset_ordered_demote_mode(void)
1315 {
1316 	sysctlbyname("kern.memorystatus_freezer_use_demotion_list", NULL, NULL, &use_demotion_list_original, sizeof(use_demotion_list_original));
1317 }
1318 
1319 static void
1320 enable_ordered_freeze_mode(void)
1321 {
1322 	int ret;
1323 	int val = 1;
1324 	size_t size = sizeof(use_ordered_list_original);
1325 	ret = sysctlbyname("kern.memorystatus_freezer_use_ordered_list", &use_ordered_list_original, &size, &val, sizeof(val));
1326 	T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_freezer_use_ordered_list");
1327 	T_ATEND(reset_ordered_freeze_mode);
1328 }
1329 
1330 static void
1331 enable_ordered_demote_mode(void)
1332 {
1333 	int ret;
1334 	int val = 1;
1335 	size_t size = sizeof(use_demotion_list_original);
1336 	ret = sysctlbyname("kern.memorystatus_freezer_use_demotion_list", &use_demotion_list_original, &size, &val, sizeof(val));
1337 	T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_freezer_use_demotion_list");
1338 	T_ATEND(reset_ordered_demote_mode);
1339 }
1340 
1341 static void
1342 construct_child_freeze_entry(memorystatus_properties_freeze_entry_v1 *entry)
1343 {
1344 	memset(entry, 0, sizeof(memorystatus_properties_freeze_entry_v1));
1345 	entry->version = 1;
1346 	entry->pid = child_pid;
1347 	entry->priority = 1;
1348 
1349 	/* Get the child's name. */
1350 	memorystatus_jetsam_snapshot_t *snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
1351 	memorystatus_jetsam_snapshot_entry_t *snapshot_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1352 	strncpy(entry->proc_name, snapshot_entry->name, sizeof(entry->proc_name));
1353 	free(snapshot);
1354 }
1355 
1356 T_DECL(memorystatus_freeze_top_process_ordered, "memorystatus_freeze_top_process chooses the correct process when using an ordered list",
1357 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1358 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
1359     T_META_ASROOT(true),
1360     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1)) {
1361 	memorystatus_freeze_top_process_setup();
1362 	enable_ordered_freeze_mode();
1363 	test_after_background_helper_launches(true, "frozen_background", ^{
1364 		int ret, val = 1;
1365 		memorystatus_properties_freeze_entry_v1 entries[1];
1366 
1367 		construct_child_freeze_entry(&entries[0]);
1368 		prepare_proc_for_freezing(child_pid);
1369 
1370 		T_LOG("Telling kernel to freeze %s", entries[0].proc_name);
1371 		ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY, entries, sizeof(entries));
1372 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY");
1373 
1374 		ret = sysctlbyname("vm.memorystatus_freeze_top_process", NULL, NULL, &val, sizeof(val));
1375 		T_ASSERT_POSIX_SUCCESS(ret, "freeze_top_process");
1376 
1377 		verify_proc_is_frozen(child_pid);
1378 		resume_and_kill_proc(child_pid);
1379 
1380 		T_END;
1381 	});
1382 	dispatch_main();
1383 }
1384 
1385 static void
1386 memorystatus_freeze_top_process_ordered_wrong_pid(pid_t (^pid_for_entry)(pid_t))
1387 {
1388 	memorystatus_freeze_top_process_setup();
1389 	enable_ordered_freeze_mode();
1390 	test_after_background_helper_launches(true, "frozen_background", ^{
1391 		int ret, val = 1;
1392 		memorystatus_properties_freeze_entry_v1 entries[1];
1393 
1394 		construct_child_freeze_entry(&entries[0]);
1395 		entries[0].pid = pid_for_entry(child_pid);
1396 		prepare_proc_for_freezing(child_pid);
1397 
1398 		T_LOG("Telling kernel to freeze %s", entries[0].proc_name);
1399 		ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY, entries, sizeof(entries));
1400 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY");
1401 
1402 		ret = sysctlbyname("vm.memorystatus_freeze_top_process", NULL, NULL, &val, sizeof(val));
1403 		T_ASSERT_POSIX_SUCCESS(ret, "freeze_top_process");
1404 
1405 		verify_proc_is_frozen(child_pid);
1406 		resume_and_kill_proc(child_pid);
1407 
1408 		T_END;
1409 	});
1410 	dispatch_main();
1411 }
1412 
1413 /*
1414  * Try both with a pid that's used by another process
1415  * and a pid that is likely unused.
1416  * In both cases the child should still get frozen.
1417  */
1418 T_DECL(memorystatus_freeze_top_process_ordered_reused_pid, "memorystatus_freeze_top_process is resilient to pid changes",
1419 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1420 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
1421 	T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1422     T_META_ASROOT(true)) {
1423 	memorystatus_freeze_top_process_ordered_wrong_pid(^(__unused pid_t child) {
1424 		return 1;
1425 	});
1426 }
1427 
1428 T_DECL(memorystatus_freeze_top_process_ordered_wrong_pid, "memorystatus_freeze_top_process is resilient to pid changes",
1429 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1430 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
1431     T_META_ASROOT(true),
1432     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1)) {
1433 	memorystatus_freeze_top_process_ordered_wrong_pid(^(__unused pid_t child) {
1434 		return child + 1000;
1435 	});
1436 }
1437 
1438 T_DECL(memorystatus_freeze_demote_ordered, "memorystatus_demote_frozen_processes_using_demote_list chooses the correct process",
1439 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1440 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
1441     T_META_ASROOT(true),
1442     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1)) {
1443 	memorystatus_freeze_top_process_setup();
1444 	enable_ordered_freeze_mode();
1445 	enable_ordered_demote_mode();
1446 	test_after_background_helper_launches(true, "frozen_background", ^{
1447 		int ret, val = 1;
1448 		int32_t memorystatus_freeze_band = 0;
1449 		size_t memorystatus_freeze_band_size = sizeof(memorystatus_freeze_band);
1450 		memorystatus_jetsam_snapshot_t *snapshot = NULL;
1451 		memorystatus_jetsam_snapshot_entry_t *child_entry = NULL;
1452 		memorystatus_properties_freeze_entry_v1 entries[1];
1453 
1454 		ret = sysctlbyname("kern.memorystatus_freeze_jetsam_band", &memorystatus_freeze_band, &memorystatus_freeze_band_size, NULL, 0);
1455 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.memorystatus_freeze_jetsam_band");
1456 
1457 		construct_child_freeze_entry(&entries[0]);
1458 		prepare_proc_for_freezing(child_pid);
1459 
1460 		T_LOG("Telling kernel to freeze %s", entries[0].proc_name);
1461 		ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY, entries, sizeof(entries));
1462 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY");
1463 
1464 		ret = sysctlbyname("vm.memorystatus_freeze_top_process", NULL, NULL, &val, sizeof(val));
1465 		T_ASSERT_POSIX_SUCCESS(ret, "freeze_top_process");
1466 
1467 		verify_proc_is_frozen(child_pid);
1468 
1469 		/*
1470 		 * Place the child at the head of the demotion list.
1471 		 */
1472 		T_LOG("Telling kernel to demote %s", entries[0].proc_name);
1473 		ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_DEMOTE_PRIORITY, entries, sizeof(entries));
1474 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_DEMOTE_PRIORITY");
1475 
1476 		/* Resume the child */
1477 		ret = pid_resume(child_pid);
1478 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1479 
1480 		/* Trigger a demotion check */
1481 		val = 1;
1482 		ret = sysctlbyname("kern.memorystatus_demote_frozen_processes", NULL, NULL, &val, sizeof(val));
1483 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_demote_frozen_processes succeeded");
1484 
1485 		/* Verify that the child was demoted */
1486 		snapshot = get_jetsam_snapshot(MEMORYSTATUS_FLAGS_SNAPSHOT_ON_DEMAND, false);
1487 		child_entry = get_jetsam_snapshot_entry(snapshot, child_pid);
1488 		T_QUIET; T_ASSERT_NOTNULL(child_entry, "Found child in snapshot");
1489 		T_QUIET; T_ASSERT_LT(child_entry->priority, memorystatus_freeze_band, "child was demoted");
1490 		free(snapshot);
1491 
1492 		T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGKILL), "Killed process");
1493 
1494 		T_END;
1495 	});
1496 	dispatch_main();
1497 }
1498 
1499 static int
1500 memorystatus_freezer_thaw_percentage(void)
1501 {
1502 	int val;
1503 	size_t size = sizeof(val);
1504 	int ret = sysctlbyname("kern.memorystatus_freezer_thaw_percentage", &val, &size, NULL, 0);
1505 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to query kern.memorystatus_freezer_thaw_percentage");
1506 	return val;
1507 }
1508 
1509 static void
1510 reset_interval(void)
1511 {
1512 	uint32_t freeze_daily_budget_mb = 0;
1513 	size_t size = sizeof(freeze_daily_budget_mb);
1514 	int ret;
1515 	uint64_t new_budget;
1516 	ret = sysctlbyname("kern.memorystatus_freeze_daily_mb_max", &freeze_daily_budget_mb, &size, NULL, 0);
1517 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to query kern.memorystatus_freeze_daily_mb_max");
1518 	new_budget = (freeze_daily_budget_mb * (1UL << 20) / vm_page_size);
1519 	ret = sysctlbyname("kern.memorystatus_freeze_budget_pages_remaining", NULL, NULL, &new_budget, sizeof(new_budget));
1520 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to set kern.memorystatus_freeze_budget_pages_remaining");
1521 }
1522 
1523 static pid_t second_child;
1524 static void
1525 cleanup_memorystatus_freezer_thaw_percentage(void)
1526 {
1527 	kill(second_child, SIGKILL);
1528 }
1529 
1530 T_DECL(memorystatus_freezer_thaw_percentage, "memorystatus_freezer_thaw_percentage updates correctly",
1531 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1532 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
1533     T_META_ASROOT(true),
1534     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1)) {
1535 	__block dispatch_source_t first_signal_block;
1536 	/* Take ownership of the freezer probabilities for the duration of the test so that nothing new gets frozen by dasd. */
1537 	set_testing_pid();
1538 	reset_interval();
1539 
1540 	/* Spawn one child that will remain frozen throughout the whole test & another that will be thawed. */
1541 	first_signal_block = run_block_after_signal(SIGUSR1, ^{
1542 		move_to_idle_band(second_child);
1543 		__block int ret = pid_suspend(second_child);
1544 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1545 		freeze_process(second_child);
1546 		T_QUIET; T_ASSERT_EQ(memorystatus_freezer_thaw_percentage(), 0, "thaw percentage is still 0 after freeze");
1547 		dispatch_source_cancel(first_signal_block);
1548 		test_after_background_helper_launches(true, "frozen_background", ^{
1549 			reset_interval();
1550 			T_QUIET; T_ASSERT_EQ(memorystatus_freezer_thaw_percentage(), 0, "new interval starts with a thaw percentage of 0");
1551 			move_to_idle_band(child_pid);
1552 			ret = pid_suspend(child_pid);
1553 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1554 			freeze_process(child_pid);
1555 			ret = pid_resume(child_pid);
1556 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1557 			int percentage_after_thaw = memorystatus_freezer_thaw_percentage();
1558 			T_QUIET; T_ASSERT_GT(percentage_after_thaw, 0, "thaw percentage is higher after thaw");
1559 
1560 			ret = pid_suspend(child_pid);
1561 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1562 			freeze_process(child_pid);
1563 			ret = pid_resume(child_pid);
1564 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1565 			T_QUIET; T_ASSERT_EQ(memorystatus_freezer_thaw_percentage(), percentage_after_thaw, "thaw percentage is unchanged after second thaw");
1566 
1567 			ret = pid_suspend(child_pid);
1568 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1569 			freeze_process(child_pid);
1570 			reset_interval();
1571 			T_QUIET; T_ASSERT_EQ(memorystatus_freezer_thaw_percentage(), 0, "new interval starts with a 0 thaw percentage");
1572 			ret = pid_resume(child_pid);
1573 			T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1574 			T_QUIET; T_ASSERT_GT(memorystatus_freezer_thaw_percentage(), 0, "thaw percentage goes back up in new interval");
1575 
1576 			T_QUIET; T_ASSERT_POSIX_SUCCESS(kill(child_pid, SIGKILL), "failed to kill child");
1577 			T_END;
1578 		});
1579 	});
1580 
1581 	second_child = launch_background_helper("frozen_background", false, true);
1582 	T_ATEND(cleanup_memorystatus_freezer_thaw_percentage);
1583 	dispatch_activate(first_signal_block);
1584 	dispatch_main();
1585 }
1586 
1587 static uint64_t
1588 get_budget_pages_remaining(void)
1589 {
1590 	uint64_t pages_remaining = 0;
1591 	size_t size = sizeof(pages_remaining);
1592 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_budget_pages_remaining", &pages_remaining, &size, NULL, 0),
1593 	    "get kern.memorystatus_freeze_budget_pages_remaining");
1594 	return pages_remaining;
1595 }
1596 
1597 static void
1598 set_budget_pages_remaining(uint64_t pages_remaining)
1599 {
1600 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.memorystatus_freeze_budget_pages_remaining", NULL, NULL, &pages_remaining, sizeof(pages_remaining)),
1601 	    "get kern.memorystatus_freeze_budget_pages_remaining");
1602 }
1603 
1604 static void
1605 enable_freeze(void)
1606 {
1607 	int freeze_enabled = 1;
1608 	size_t length = sizeof(freeze_enabled);
1609 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", NULL, NULL, &freeze_enabled, length),
1610 	    "enable vm.freeze_enabled");
1611 }
1612 
1613 T_DECL(memorystatus_freeze_budget_multiplier, "memorystatus_budget_multiplier multiplies budget",
1614 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1615 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
1616     T_META_ASROOT(true),
1617     T_META_REQUIRES_SYSCTL_NE("kern.memorystatus_freeze_daily_mb_max", UINT32_MAX),
1618     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
1619     T_META_ENABLED(false) /* rdar://87165483 */) {
1620 	/* Disable freezer so that the budget doesn't change out from underneath us. */
1621 	int freeze_enabled = 0;
1622 	size_t length = sizeof(freeze_enabled);
1623 	uint64_t freeze_daily_pages_max;
1624 	T_ATEND(enable_freeze);
1625 	T_QUIET; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.freeze_enabled", NULL, NULL, &freeze_enabled, length),
1626 	    "disable vm.freeze_enabled");
1627 	freeze_daily_pages_max = get_freeze_daily_pages_max();
1628 	original_budget_multiplier = get_budget_multiplier();
1629 	T_ATEND(reset_budget_multiplier);
1630 	set_budget_multiplier(100);
1631 	T_QUIET; T_ASSERT_EQ(get_budget_pages_remaining(), freeze_daily_pages_max, "multiplier=100%%");
1632 	set_budget_multiplier(50);
1633 	T_QUIET; T_ASSERT_EQ(get_budget_pages_remaining(), freeze_daily_pages_max / 2, "multiplier=50%%");
1634 	set_budget_multiplier(200);
1635 	T_QUIET; T_ASSERT_EQ(get_budget_pages_remaining(), freeze_daily_pages_max * 2, "multiplier=200%%");
1636 }
1637 
1638 T_DECL(memorystatus_freeze_set_dasd_trial_identifiers, "set dasd trial identifiers",
1639 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1640 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
1641     T_META_ASROOT(true)) {
1642 #define TEST_STR "freezer-das-trial"
1643 	memorystatus_freezer_trial_identifiers_v1 identifiers = {0};
1644 	identifiers.version = 1;
1645 	strncpy(identifiers.treatment_id, TEST_STR, sizeof(identifiers.treatment_id));
1646 	strncpy(identifiers.experiment_id, TEST_STR, sizeof(identifiers.treatment_id));
1647 	identifiers.deployment_id = 2;
1648 	int ret = memorystatus_control(MEMORYSTATUS_CMD_FREEZER_CONTROL, 0, FREEZER_CONTROL_SET_DASD_TRIAL_IDENTIFIERS, &identifiers, sizeof(identifiers));
1649 	T_WITH_ERRNO; T_ASSERT_EQ(ret, 0, "FREEZER_CONTROL_SET_DASD_TRIAL_IDENTIFIERS");
1650 }
1651 
1652 T_DECL(memorystatus_reset_freezer_state, "FREEZER_CONTROL_RESET_STATE kills frozen proccesses",
1653 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1654 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
1655     T_META_ASROOT(true),
1656     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1)) {
1657 	/* Take ownership of the freezer probabilities for the duration of the test so that nothing new gets frozen by dasd. */
1658 	set_testing_pid();
1659 	reset_interval();
1660 
1661 	test_after_background_helper_launches(false, "frozen_background", ^{
1662 		proc_name_t name;
1663 		int ret;
1664 
1665 		/* Freeze the child and verify they're frozen. */
1666 		move_to_idle_band(child_pid);
1667 		ret = pid_suspend(child_pid);
1668 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1669 		freeze_process(child_pid);
1670 		T_QUIET; T_ASSERT_TRUE(is_proc_in_frozen_list(child_pid, name, sizeof(name)), "Found proc in frozen list");
1671 		T_QUIET; T_EXPECT_EQ_STR(name, "memorystatus_freeze_test", "Proc has correct name");
1672 		/* Set the budget to 0. */
1673 		set_budget_pages_remaining(0);
1674 
1675 		/* FREEZER_CONTROL_RESET_STATE */
1676 		ret = memorystatus_control(MEMORYSTATUS_CMD_FREEZER_CONTROL, 0, FREEZER_CONTROL_RESET_STATE, NULL, 0);
1677 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "FREEZER_CONRTOL_RESET_STATE");
1678 
1679 		/* Verify budget resets to a non-zero value. Some devices may have a configured
1680 		 * budget of 0. Skip this assertion if so. */
1681 		uint64_t budget_after_reset = get_budget_pages_remaining();
1682 		T_QUIET; T_ASSERT_GT(budget_after_reset, 0ULL, "freeze budget after reset > 0");
1683 		/*
1684 		 * Verify child has been killed
1685 		 * Note that the task termination isn't synchronous with the RESET_STATE call so we may
1686 		 * block in waitpid temporarily.
1687 		 */
1688 		int stat;
1689 		while (true) {
1690 		        pid_t wait_p = waitpid(child_pid, &stat, 0);
1691 		        if (wait_p == child_pid) {
1692 		                break;
1693 			}
1694 		}
1695 		T_QUIET; T_ASSERT_TRUE(WIFSIGNALED(stat), "child was signaled");
1696 		T_QUIET; T_ASSERT_EQ(WTERMSIG(stat), SIGKILL, "Child received SIGKILL");
1697 
1698 		T_END;
1699 	});
1700 	dispatch_main();
1701 }
1702 
1703 static void
1704 dock_proc(pid_t pid)
1705 {
1706 	int ret;
1707 	ret = memorystatus_control(MEMORYSTATUS_CMD_ELEVATED_INACTIVEJETSAMPRIORITY_ENABLE, pid, 0, NULL, 0);
1708 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "dock_proc");
1709 }
1710 
1711 T_DECL(memorystatus_freeze_skip_docked, "memorystatus_freeze_top_process does not freeze docked processes",
1712 	T_META_ENABLED(!TARGET_OS_WATCH),
1713 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1714 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
1715     T_META_ASROOT(true),
1716     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1)) {
1717 	memorystatus_freeze_top_process_setup();
1718 	enable_ordered_freeze_mode();
1719 	test_after_background_helper_launches(true, "frozen_background", ^{
1720 		int ret, val = 1;
1721 		memorystatus_properties_freeze_entry_v1 entries[1];
1722 
1723 		dock_proc(child_pid);
1724 		construct_child_freeze_entry(&entries[0]);
1725 		prepare_proc_for_freezing(child_pid);
1726 
1727 		T_LOG("Telling kernel to freeze %s", entries[0].proc_name);
1728 		ret = memorystatus_control(MEMORYSTATUS_CMD_GRP_SET_PROPERTIES, 0, MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY, entries, sizeof(entries));
1729 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY");
1730 
1731 		ret = sysctlbyname("vm.memorystatus_freeze_top_process", NULL, NULL, &val, sizeof(val));
1732 		T_ASSERT_EQ(errno, ESRCH, "freeze_top_process errno");
1733 		T_ASSERT_EQ(ret, -1, "freeze_top_process");
1734 
1735 		verify_proc_not_frozen(child_pid);
1736 		resume_and_kill_proc(child_pid);
1737 
1738 		T_END;
1739 	});
1740 	dispatch_main();
1741 }
1742 
1743 T_HELPER_DECL(corpse_generation, "Generate a large corpse", T_META_ASROOT(false)) {
1744 	/*
1745 	 * Allocate and fault in a bunch of memory so that it takes a while
1746 	 * to generate our corpse.
1747 	 */
1748 	size_t bytes_to_allocate = 304 * (1UL << 20);
1749 	size_t block_size = 8 * (1UL << 20);
1750 	for (size_t i = 0; i < bytes_to_allocate / block_size; i++) {
1751 		unsigned char *ptr = malloc(block_size);
1752 		if (ptr == NULL) {
1753 			T_LOG("Unable to allocate memory in child process!");
1754 			exit(UNABLE_TO_ALLOCATE);
1755 		}
1756 		for (size_t j = 0; j < block_size / vm_page_size; j++) {
1757 			*ptr = (unsigned char) j;
1758 			ptr += vm_page_size;
1759 		}
1760 	}
1761 	dispatch_source_t ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
1762 	if (ds_signal == NULL) {
1763 		T_LOG("Unable to create dispatch source");
1764 		exit(DISPATCH_SOURCE_CREATE_FAILED);
1765 	}
1766 	dispatch_source_set_event_handler(ds_signal, ^{
1767 		uint64_t val = 1;
1768 		/*
1769 		 * We should now be frozen.
1770 		 * Simulate a crash so that our P_MEMSTAT_SKIP bit gets set temporarily.
1771 		 * The parent process will try to kill us due to disk space shortage in parallel.
1772 		 */
1773 		os_fault_with_payload(OS_REASON_LIBSYSTEM, OS_REASON_LIBSYSTEM_CODE_FAULT,
1774 		&val, sizeof(val), "freeze_test", 0);
1775 	});
1776 	dispatch_activate(ds_signal);
1777 
1778 	sig_t sig_ret = signal(SIGUSR1, SIG_IGN);
1779 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NE(sig_ret, SIG_ERR, "signal(SIGUSR1, SIG_IGN)");
1780 
1781 	/* Signal to our parent that we can be frozen */
1782 	if (kill(getppid(), SIGUSR1) != 0) {
1783 		T_LOG("Unable to signal to parent process!");
1784 		exit(SIGNAL_TO_PARENT_FAILED);
1785 	}
1786 	dispatch_main();
1787 }
1788 
1789 T_DECL(memorystatus_disable_freeze_corpse, "memorystatus_disable_freeze with parallel corpse creation",
1790 	T_META_BOOTARGS_SET("freeze_enabled=1"),
1791 	T_META_REQUIRES_SYSCTL_EQ("vm.freeze_enabled", 1),
1792     T_META_ASROOT(true),
1793     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1)) {
1794 	/*
1795 	 * In the past, we've had race conditions w.r.t. killing on disk space shortage
1796 	 * and corpse generation of frozen processes.
1797 	 * This test spwans a frozen helper
1798 	 * which generates a large corpse (which should take in the 10s to 100s of m.s. to complete)
1799 	 * while the test process triggers disk space kills.
1800 	 * We should see that the test process is jetsammed successfully.
1801 	 */
1802 	test_after_background_helper_launches(false, "corpse_generation", ^{
1803 		int ret, val, stat;
1804 		/* Place the child in the idle band so that it gets elevated like a typical app. */
1805 		move_to_idle_band(child_pid);
1806 		ret = pid_suspend(child_pid);
1807 		T_ASSERT_POSIX_SUCCESS(ret, "child suspended");
1808 		freeze_process(child_pid);
1809 
1810 		ret = pid_resume(child_pid);
1811 		T_ASSERT_POSIX_SUCCESS(ret, "child resumed after freeze");
1812 
1813 		kill(child_pid, SIGUSR1);
1814 
1815 		T_ATEND(enable_freeze);
1816 		val = 0;
1817 		ret = sysctlbyname("vm.freeze_enabled", NULL, NULL, &val, sizeof(val));
1818 		T_ASSERT_POSIX_SUCCESS(ret, "freeze disabled");
1819 		/*
1820 		 * Verify child has been killed
1821 		 * Note that the task termination isn't synchronous with the freeze_enabled call so we may
1822 		 * block in waitpid temporarily.
1823 		 */
1824 		while (true) {
1825 		        pid_t wait_p = waitpid(child_pid, &stat, 0);
1826 		        if (wait_p == child_pid) {
1827 		                break;
1828 			}
1829 		}
1830 		T_QUIET; T_ASSERT_TRUE(WIFSIGNALED(stat), "child was signaled");
1831 		T_QUIET; T_ASSERT_EQ(WTERMSIG(stat), SIGKILL, "Child received SIGKILL");
1832 
1833 		T_END;
1834 	});
1835 	dispatch_main();
1836 }
1837