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