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