xref: /xnu-12377.81.4/tests/vm/memorystatus_sort_test.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1*043036a2SApple OSS Distributions #include <signal.h>
2*043036a2SApple OSS Distributions #include <spawn.h>
3*043036a2SApple OSS Distributions #include <stdlib.h>
4*043036a2SApple OSS Distributions #include <sys/sysctl.h>
5*043036a2SApple OSS Distributions 
6*043036a2SApple OSS Distributions #include <darwintest.h>
7*043036a2SApple OSS Distributions #include <dispatch/dispatch.h>
8*043036a2SApple OSS Distributions #include <mach-o/dyld.h>
9*043036a2SApple OSS Distributions 
10*043036a2SApple OSS Distributions /* internal */
11*043036a2SApple OSS Distributions #include <spawn_private.h>
12*043036a2SApple OSS Distributions #include <sys/coalition.h>
13*043036a2SApple OSS Distributions #include <sys/kern_memorystatus.h>
14*043036a2SApple OSS Distributions 
15*043036a2SApple OSS Distributions T_GLOBAL_META(
16*043036a2SApple OSS Distributions 	T_META_NAMESPACE("xnu.vm"),
17*043036a2SApple OSS Distributions 	T_META_RADAR_COMPONENT_NAME("xnu"),
18*043036a2SApple OSS Distributions 	T_META_RADAR_COMPONENT_VERSION("VM"));
19*043036a2SApple OSS Distributions 
20*043036a2SApple OSS Distributions #define NUM_PER_ROLE 3 /* Number of procs per role in coalition (besides leader) */
21*043036a2SApple OSS Distributions #define NUM_PROCS_IN_COALITION (NUM_PER_ROLE * (COALITION_NUM_TASKROLES - 1) + 1)
22*043036a2SApple OSS Distributions #define NUM_COALITIONS 3
23*043036a2SApple OSS Distributions 
24*043036a2SApple OSS Distributions #define COAL_ORDER_NUM_PIDS (NUM_PROCS_IN_COALITION + COALITION_NUM_TASKROLES - 1)
25*043036a2SApple OSS Distributions typedef struct {
26*043036a2SApple OSS Distributions 	pid_t pids[NUM_PROCS_IN_COALITION]; // An array of pids in this coalition. Owned by this struct.
27*043036a2SApple OSS Distributions 	pid_t expected_order[COAL_ORDER_NUM_PIDS]; // An array of pids in this coalition in proper sorted order.
28*043036a2SApple OSS Distributions 	uint64_t ids[COALITION_NUM_TYPES];
29*043036a2SApple OSS Distributions 	size_t leader_footprint;
30*043036a2SApple OSS Distributions } coalition_info_t;
31*043036a2SApple OSS Distributions 
32*043036a2SApple OSS Distributions /*
33*043036a2SApple OSS Distributions  * Children pids spawned by this test that need to be cleaned up.
34*043036a2SApple OSS Distributions  * Has to be a global because the T_ATEND API doesn't take any arguments.
35*043036a2SApple OSS Distributions  */
36*043036a2SApple OSS Distributions #define kMaxChildrenProcs NUM_PROCS_IN_COALITION * NUM_COALITIONS + 1
37*043036a2SApple OSS Distributions static pid_t children_pids[kMaxChildrenProcs];
38*043036a2SApple OSS Distributions static size_t num_children = 0;
39*043036a2SApple OSS Distributions 
40*043036a2SApple OSS Distributions /*
41*043036a2SApple OSS Distributions  * Sets up a new coalition.
42*043036a2SApple OSS Distributions  */
43*043036a2SApple OSS Distributions static void init_coalition(coalition_info_t*, size_t leader_fp);
44*043036a2SApple OSS Distributions 
45*043036a2SApple OSS Distributions /*
46*043036a2SApple OSS Distributions  * Places all procs in the coalition in the given band.
47*043036a2SApple OSS Distributions  */
48*043036a2SApple OSS Distributions static void place_coalition_in_band(const coalition_info_t *, int band);
49*043036a2SApple OSS Distributions 
50*043036a2SApple OSS Distributions /*
51*043036a2SApple OSS Distributions  * Place the given proc in the given band.
52*043036a2SApple OSS Distributions  */
53*043036a2SApple OSS Distributions static void place_proc_in_band(pid_t pid, int band);
54*043036a2SApple OSS Distributions 
55*043036a2SApple OSS Distributions /*
56*043036a2SApple OSS Distributions  * Cleans up any children processes.
57*043036a2SApple OSS Distributions  */
58*043036a2SApple OSS Distributions static void cleanup_children(void);
59*043036a2SApple OSS Distributions 
60*043036a2SApple OSS Distributions /*
61*043036a2SApple OSS Distributions  * Check if we're on a kernel where we can test coalitions.
62*043036a2SApple OSS Distributions  */
63*043036a2SApple OSS Distributions static bool has_unrestrict_coalitions(void);
64*043036a2SApple OSS Distributions 
65*043036a2SApple OSS Distributions /*
66*043036a2SApple OSS Distributions  * Unrestrict coalition syscalls.
67*043036a2SApple OSS Distributions  */
68*043036a2SApple OSS Distributions static void unrestrict_coalitions(void);
69*043036a2SApple OSS Distributions 
70*043036a2SApple OSS Distributions /*
71*043036a2SApple OSS Distributions  * Restrict coalition syscalls
72*043036a2SApple OSS Distributions  */
73*043036a2SApple OSS Distributions static void restrict_coalitions(void);
74*043036a2SApple OSS Distributions 
75*043036a2SApple OSS Distributions /*
76*043036a2SApple OSS Distributions  * Allocate the requested number of pages and fault them in.
77*043036a2SApple OSS Distributions  * Used to achieve a desired footprint.
78*043036a2SApple OSS Distributions  */
79*043036a2SApple OSS Distributions static void *allocate_pages(int);
80*043036a2SApple OSS Distributions 
81*043036a2SApple OSS Distributions /*
82*043036a2SApple OSS Distributions  * Get the vm page size.
83*043036a2SApple OSS Distributions  */
84*043036a2SApple OSS Distributions static int get_vmpage_size(void);
85*043036a2SApple OSS Distributions 
86*043036a2SApple OSS Distributions /*
87*043036a2SApple OSS Distributions  * Launch a proc with a role in a coalition.
88*043036a2SApple OSS Distributions  * If coalition_ids is NULL, skip adding the proc to the coalition.
89*043036a2SApple OSS Distributions  */
90*043036a2SApple OSS Distributions static pid_t
91*043036a2SApple OSS Distributions launch_proc_in_coalition(uint64_t *coalition_ids, int role, int num_pages);
92*043036a2SApple OSS Distributions 
93*043036a2SApple OSS Distributions static void
bufprint(char ** buf,size_t * size,const char * fmt,...)94*043036a2SApple OSS Distributions bufprint(char **buf, size_t *size, const char *fmt, ...)
95*043036a2SApple OSS Distributions {
96*043036a2SApple OSS Distributions 	va_list list;
97*043036a2SApple OSS Distributions 	int n_written;
98*043036a2SApple OSS Distributions 
99*043036a2SApple OSS Distributions 	va_start(list, fmt);
100*043036a2SApple OSS Distributions 	n_written = vsnprintf(*buf, *size, fmt, list);
101*043036a2SApple OSS Distributions 	va_end(list);
102*043036a2SApple OSS Distributions 
103*043036a2SApple OSS Distributions 	if (n_written > 0) {
104*043036a2SApple OSS Distributions 		*buf += n_written;
105*043036a2SApple OSS Distributions 		*size -= n_written;
106*043036a2SApple OSS Distributions 	}
107*043036a2SApple OSS Distributions }
108*043036a2SApple OSS Distributions 
109*043036a2SApple OSS Distributions static char *
pids_str(pid_t * pids,int n_pids)110*043036a2SApple OSS Distributions pids_str(pid_t *pids, int n_pids)
111*043036a2SApple OSS Distributions {
112*043036a2SApple OSS Distributions 	int i;
113*043036a2SApple OSS Distributions 	size_t buf_len = n_pids * 8 + 2; /* For good measure */
114*043036a2SApple OSS Distributions 	char *buf = malloc(buf_len);
115*043036a2SApple OSS Distributions 	char *obuf = buf;
116*043036a2SApple OSS Distributions 
117*043036a2SApple OSS Distributions 	bufprint(&buf, &buf_len, "(");
118*043036a2SApple OSS Distributions 
119*043036a2SApple OSS Distributions 	for (i = 0; (i < n_pids) && (buf_len > 0); i++) {
120*043036a2SApple OSS Distributions 		if (pids[i] == -1) {
121*043036a2SApple OSS Distributions 			bufprint(&buf, &buf_len, "), (");
122*043036a2SApple OSS Distributions 		} else {
123*043036a2SApple OSS Distributions 			bool is_last = (i == (n_pids - 1)) || (pids[i + 1] == -1);
124*043036a2SApple OSS Distributions 			bufprint(&buf, &buf_len, "%d%s", pids[i], is_last ? "" : ", ");
125*043036a2SApple OSS Distributions 		}
126*043036a2SApple OSS Distributions 	}
127*043036a2SApple OSS Distributions 
128*043036a2SApple OSS Distributions 	bufprint(&buf, &buf_len, ")");
129*043036a2SApple OSS Distributions 
130*043036a2SApple OSS Distributions 	return obuf;
131*043036a2SApple OSS Distributions }
132*043036a2SApple OSS Distributions 
133*043036a2SApple OSS Distributions /*
134*043036a2SApple OSS Distributions  * Sorts the given jetsam band with the desired order and verifies that the
135*043036a2SApple OSS Distributions  * sort was done correctly.
136*043036a2SApple OSS Distributions  * `expected_order` is an array of groups of PIDs separated by `-1`, where PIDs
137*043036a2SApple OSS Distributions  * in each group are re-orderable. For instance, for the expected order:
138*043036a2SApple OSS Distributions  * [1, 2, -1, 3, -1, 4]
139*043036a2SApple OSS Distributions  * the orderings of
140*043036a2SApple OSS Distributions  * 1, 2, 3, 4 and 2, 1, 3, 4 are both valid since 1 and 2 are in the same group.
141*043036a2SApple OSS Distributions  */
142*043036a2SApple OSS Distributions static void
sort_and_verify(unsigned int prio,memorystatus_jetsam_sort_order_t order,pid_t * expected_order,size_t expected_order_len)143*043036a2SApple OSS Distributions sort_and_verify(
144*043036a2SApple OSS Distributions 	unsigned int prio,
145*043036a2SApple OSS Distributions 	memorystatus_jetsam_sort_order_t order,
146*043036a2SApple OSS Distributions 	pid_t *expected_order,
147*043036a2SApple OSS Distributions 	size_t expected_order_len)
148*043036a2SApple OSS Distributions {
149*043036a2SApple OSS Distributions 	size_t i, j, n_pids, group_idx;
150*043036a2SApple OSS Distributions 	bool in_order;
151*043036a2SApple OSS Distributions 	pid_t *actual_order;
152*043036a2SApple OSS Distributions 	pid_t *original_expected_order;
153*043036a2SApple OSS Distributions 
154*043036a2SApple OSS Distributions 	/* Bigger than we need it, but that's fine */
155*043036a2SApple OSS Distributions 	actual_order = malloc(sizeof(pid_t) * expected_order_len);
156*043036a2SApple OSS Distributions 
157*043036a2SApple OSS Distributions 	/* Make a copy of expected_order since we'll be overwriting it */
158*043036a2SApple OSS Distributions 	original_expected_order = malloc(sizeof(pid_t) * expected_order_len);
159*043036a2SApple OSS Distributions 	memcpy(original_expected_order, expected_order, sizeof(pid_t) * expected_order_len);
160*043036a2SApple OSS Distributions 
161*043036a2SApple OSS Distributions 	/*
162*043036a2SApple OSS Distributions 	 * Add only the actual pids from expected_order in to tell memorystatus which
163*043036a2SApple OSS Distributions 	 * PIDs we care about
164*043036a2SApple OSS Distributions 	 */
165*043036a2SApple OSS Distributions 	n_pids = 0;
166*043036a2SApple OSS Distributions 	for (i = 0; i < expected_order_len; i++) {
167*043036a2SApple OSS Distributions 		if (expected_order[i] != -1) {
168*043036a2SApple OSS Distributions 			actual_order[n_pids] = expected_order[i];
169*043036a2SApple OSS Distributions 			n_pids++;
170*043036a2SApple OSS Distributions 		}
171*043036a2SApple OSS Distributions 	}
172*043036a2SApple OSS Distributions 
173*043036a2SApple OSS Distributions 	int ret = memorystatus_control(MEMORYSTATUS_CMD_TEST_JETSAM_SORT, prio, order,
174*043036a2SApple OSS Distributions 	    actual_order, n_pids * sizeof(pid_t));
175*043036a2SApple OSS Distributions 	T_QUIET; T_EXPECT_POSIX_SUCCESS(ret, "Band sorted and order copied out");
176*043036a2SApple OSS Distributions 
177*043036a2SApple OSS Distributions 	/* Check that the order we got was what we expected */
178*043036a2SApple OSS Distributions 	group_idx = 0; /* idx of pid that starts current reorderable group */
179*043036a2SApple OSS Distributions 	for (i = 0; i < n_pids; i++) {
180*043036a2SApple OSS Distributions 		/*
181*043036a2SApple OSS Distributions 		 * Check if the current pid in actual_order is in the current group.
182*043036a2SApple OSS Distributions 		 * If not, advance to the next group until we find it. This is essentially
183*043036a2SApple OSS Distributions 		 * a ratcheting mechanism - we can move our search group forwards, but not
184*043036a2SApple OSS Distributions 		 * backwards.
185*043036a2SApple OSS Distributions 		 */
186*043036a2SApple OSS Distributions 		for (j = group_idx; j < expected_order_len; j++) {
187*043036a2SApple OSS Distributions 			if (expected_order[j] == -1) {
188*043036a2SApple OSS Distributions 				/* Made it to the end of a group w/o finding the pid */
189*043036a2SApple OSS Distributions 				group_idx = j + 1;
190*043036a2SApple OSS Distributions 				continue;
191*043036a2SApple OSS Distributions 			} else if (expected_order[j] == actual_order[i]) {
192*043036a2SApple OSS Distributions 				/* Found our pid. Mark it found */
193*043036a2SApple OSS Distributions 				expected_order[j] = 0;
194*043036a2SApple OSS Distributions 				break;
195*043036a2SApple OSS Distributions 			}
196*043036a2SApple OSS Distributions 		}
197*043036a2SApple OSS Distributions 	}
198*043036a2SApple OSS Distributions 
199*043036a2SApple OSS Distributions 
200*043036a2SApple OSS Distributions 	/* Check that all pids were actually found */
201*043036a2SApple OSS Distributions 	in_order = true;
202*043036a2SApple OSS Distributions 	for (i = 0; i < expected_order_len; i++) {
203*043036a2SApple OSS Distributions 		if ((expected_order[i] != -1) && (expected_order[i] != 0)) {
204*043036a2SApple OSS Distributions 			in_order = false;
205*043036a2SApple OSS Distributions 			break;
206*043036a2SApple OSS Distributions 		}
207*043036a2SApple OSS Distributions 	}
208*043036a2SApple OSS Distributions 
209*043036a2SApple OSS Distributions 	T_EXPECT_TRUE(in_order, "Band in correct order when sorted in order (%d)", order);
210*043036a2SApple OSS Distributions 
211*043036a2SApple OSS Distributions 	if (!in_order) {
212*043036a2SApple OSS Distributions 		char *exp_str = pids_str(original_expected_order, expected_order_len);
213*043036a2SApple OSS Distributions 		char *actual_str = pids_str(actual_order, n_pids);
214*043036a2SApple OSS Distributions 		T_LOG("Out of order! Expected:\n%s\nbut got\n%s\n", exp_str, actual_str);
215*043036a2SApple OSS Distributions 		free(exp_str);
216*043036a2SApple OSS Distributions 		free(actual_str);
217*043036a2SApple OSS Distributions 	}
218*043036a2SApple OSS Distributions 
219*043036a2SApple OSS Distributions 	free(actual_order);
220*043036a2SApple OSS Distributions 	free(original_expected_order);
221*043036a2SApple OSS Distributions }
222*043036a2SApple OSS Distributions 
223*043036a2SApple OSS Distributions /*
224*043036a2SApple OSS Distributions  * Background process that will munch some memory, signal its parent, and
225*043036a2SApple OSS Distributions  * then sit in a loop.
226*043036a2SApple OSS Distributions  */
227*043036a2SApple OSS Distributions T_HELPER_DECL(coalition_member, "Mock coalition member") {
228*043036a2SApple OSS Distributions 	int num_pages = 0;
229*043036a2SApple OSS Distributions 	if (argc == 1) {
230*043036a2SApple OSS Distributions 		num_pages = atoi(argv[0]);
231*043036a2SApple OSS Distributions 	}
232*043036a2SApple OSS Distributions 	allocate_pages(num_pages);
233*043036a2SApple OSS Distributions 	if (num_pages) {
234*043036a2SApple OSS Distributions 		printf("%d has %d\n", getpid(), num_pages);
235*043036a2SApple OSS Distributions 	}
236*043036a2SApple OSS Distributions 	// Signal to the parent that we've touched all of our pages.
237*043036a2SApple OSS Distributions 	if (kill(getppid(), SIGUSR1) != 0) {
238*043036a2SApple OSS Distributions 		T_LOG("Unable to signal to parent process!");
239*043036a2SApple OSS Distributions 		exit(1);
240*043036a2SApple OSS Distributions 	}
241*043036a2SApple OSS Distributions 	while (true) {
242*043036a2SApple OSS Distributions 		sleep(100);
243*043036a2SApple OSS Distributions 	}
244*043036a2SApple OSS Distributions }
245*043036a2SApple OSS Distributions 
246*043036a2SApple OSS Distributions static void
random_order(int * arr,int size)247*043036a2SApple OSS Distributions random_order(int *arr, int size)
248*043036a2SApple OSS Distributions {
249*043036a2SApple OSS Distributions 	int i, a, b, s;
250*043036a2SApple OSS Distributions 	for (i = 0; i < size; i++) {
251*043036a2SApple OSS Distributions 		arr[i] = i;
252*043036a2SApple OSS Distributions 	}
253*043036a2SApple OSS Distributions 	for (i = 0; i < size; i++) {
254*043036a2SApple OSS Distributions 		a = rand() % size;
255*043036a2SApple OSS Distributions 		b = rand() % size;
256*043036a2SApple OSS Distributions 		s = arr[a];
257*043036a2SApple OSS Distributions 		arr[a] = arr[b];
258*043036a2SApple OSS Distributions 		arr[b] = s;
259*043036a2SApple OSS Distributions 	}
260*043036a2SApple OSS Distributions }
261*043036a2SApple OSS Distributions 
262*043036a2SApple OSS Distributions static void
add_coalition_to_order(pid_t * order,coalition_info_t * coal,int coal_idx)263*043036a2SApple OSS Distributions add_coalition_to_order(pid_t *order, coalition_info_t *coal, int coal_idx)
264*043036a2SApple OSS Distributions {
265*043036a2SApple OSS Distributions 	int order_idx = coal_idx * (COAL_ORDER_NUM_PIDS + 1);
266*043036a2SApple OSS Distributions 	memcpy(&order[order_idx], &coal->expected_order, sizeof(coal->expected_order));
267*043036a2SApple OSS Distributions 	if (coal_idx != 0) {
268*043036a2SApple OSS Distributions 		order[order_idx - 1] = -1;
269*043036a2SApple OSS Distributions 	}
270*043036a2SApple OSS Distributions }
271*043036a2SApple OSS Distributions 
272*043036a2SApple OSS Distributions /*
273*043036a2SApple OSS Distributions  * Test that sorting the fg bucket in coalition order works properly.
274*043036a2SApple OSS Distributions  * Spawns children in the same coalition in the fg band. Each child
275*043036a2SApple OSS Distributions  * has a different coalition role. Verifies that the coalition
276*043036a2SApple OSS Distributions  * is sorted properly by role.
277*043036a2SApple OSS Distributions  */
278*043036a2SApple OSS Distributions #define COALS_EXPECTED_ORDER_LEN ((COAL_ORDER_NUM_PIDS * NUM_COALITIONS) + (NUM_COALITIONS - 1))
279*043036a2SApple OSS Distributions T_DECL(memorystatus_sort_coalitions_footprint, "Sort coalitions by leader footprint",
280*043036a2SApple OSS Distributions     T_META_ASROOT(true),
281*043036a2SApple OSS Distributions     T_META_TAG_VM_PREFERRED)
282*043036a2SApple OSS Distributions {
283*043036a2SApple OSS Distributions 	int i;
284*043036a2SApple OSS Distributions 	coalition_info_t *coalitions;
285*043036a2SApple OSS Distributions 	int coalition_order[NUM_COALITIONS];
286*043036a2SApple OSS Distributions 	pid_t *expected_order; /* Expected order of all pids in all coalitions */
287*043036a2SApple OSS Distributions 
288*043036a2SApple OSS Distributions 	if (!has_unrestrict_coalitions()) {
289*043036a2SApple OSS Distributions 		T_SKIP("Unable to test coalitions on this kernel.");
290*043036a2SApple OSS Distributions 	}
291*043036a2SApple OSS Distributions 	unrestrict_coalitions();
292*043036a2SApple OSS Distributions 
293*043036a2SApple OSS Distributions 	T_ATEND(cleanup_children);
294*043036a2SApple OSS Distributions 	T_ATEND(restrict_coalitions);
295*043036a2SApple OSS Distributions 
296*043036a2SApple OSS Distributions 	/* Initialize our coalitions */
297*043036a2SApple OSS Distributions 	coalitions = malloc(sizeof(coalition_info_t) * NUM_COALITIONS);
298*043036a2SApple OSS Distributions 	expected_order = malloc(sizeof(pid_t) * COALS_EXPECTED_ORDER_LEN);
299*043036a2SApple OSS Distributions 
300*043036a2SApple OSS Distributions 	/* Spawn the coalitions in random order */
301*043036a2SApple OSS Distributions 	random_order(coalition_order, NUM_COALITIONS);
302*043036a2SApple OSS Distributions 
303*043036a2SApple OSS Distributions 	/* Spawn coalitions, each with a different leader footprint */
304*043036a2SApple OSS Distributions 	for (i = 0; i < NUM_COALITIONS; i++) {
305*043036a2SApple OSS Distributions 		int coal = coalition_order[i];
306*043036a2SApple OSS Distributions 		init_coalition(&coalitions[coal], (NUM_COALITIONS - coal) * 50);
307*043036a2SApple OSS Distributions 		add_coalition_to_order(expected_order, &coalitions[coal], coal);
308*043036a2SApple OSS Distributions 		place_coalition_in_band(&coalitions[coal], JETSAM_PRIORITY_FOREGROUND);
309*043036a2SApple OSS Distributions 	}
310*043036a2SApple OSS Distributions 
311*043036a2SApple OSS Distributions 	/* Sort by leader footprint and verify coalitions are sorted by leader footprint */
312*043036a2SApple OSS Distributions 	sort_and_verify(JETSAM_PRIORITY_FOREGROUND, JETSAM_SORT_FOOTPRINT, expected_order, COALS_EXPECTED_ORDER_LEN);
313*043036a2SApple OSS Distributions 
314*043036a2SApple OSS Distributions 	free(coalitions);
315*043036a2SApple OSS Distributions 	free(expected_order);
316*043036a2SApple OSS Distributions }
317*043036a2SApple OSS Distributions 
318*043036a2SApple OSS Distributions T_DECL(memorystatus_sort_coalitions_lru, "Sort coalitions by leader LRU",
319*043036a2SApple OSS Distributions     T_META_ASROOT(true),
320*043036a2SApple OSS Distributions     T_META_TAG_VM_PREFERRED)
321*043036a2SApple OSS Distributions {
322*043036a2SApple OSS Distributions 	int i;
323*043036a2SApple OSS Distributions 	coalition_info_t *coalitions;
324*043036a2SApple OSS Distributions 	int coalition_order[NUM_COALITIONS];
325*043036a2SApple OSS Distributions 	pid_t *expected_order; /* Expected order of all pids in all coalitions */
326*043036a2SApple OSS Distributions 
327*043036a2SApple OSS Distributions 	if (!has_unrestrict_coalitions()) {
328*043036a2SApple OSS Distributions 		T_SKIP("Unable to test coalitions on this kernel.");
329*043036a2SApple OSS Distributions 	}
330*043036a2SApple OSS Distributions 	unrestrict_coalitions();
331*043036a2SApple OSS Distributions 
332*043036a2SApple OSS Distributions 	T_ATEND(cleanup_children);
333*043036a2SApple OSS Distributions 	T_ATEND(restrict_coalitions);
334*043036a2SApple OSS Distributions 
335*043036a2SApple OSS Distributions 	/* Initialize our coalitions */
336*043036a2SApple OSS Distributions 	coalitions = malloc(sizeof(coalition_info_t) * NUM_COALITIONS);
337*043036a2SApple OSS Distributions 	expected_order = malloc(sizeof(pid_t) * COALS_EXPECTED_ORDER_LEN);
338*043036a2SApple OSS Distributions 
339*043036a2SApple OSS Distributions 	/* Spawn coalitions */
340*043036a2SApple OSS Distributions 	for (i = 0; i < NUM_COALITIONS; i++) {
341*043036a2SApple OSS Distributions 		init_coalition(&coalitions[i], 0);
342*043036a2SApple OSS Distributions 	}
343*043036a2SApple OSS Distributions 
344*043036a2SApple OSS Distributions 	/* Add coalitions to foreground in random order*/
345*043036a2SApple OSS Distributions 	random_order(coalition_order, NUM_COALITIONS);
346*043036a2SApple OSS Distributions 	for (i = 0; i < NUM_COALITIONS; i++) {
347*043036a2SApple OSS Distributions 		int coal = coalition_order[i];
348*043036a2SApple OSS Distributions 		place_coalition_in_band(&coalitions[coal], JETSAM_PRIORITY_FOREGROUND);
349*043036a2SApple OSS Distributions 		add_coalition_to_order(expected_order, &coalitions[coal], i);
350*043036a2SApple OSS Distributions 	}
351*043036a2SApple OSS Distributions 
352*043036a2SApple OSS Distributions 
353*043036a2SApple OSS Distributions 	/* Sort by leader LRU and verify coalitions are sorted by leader LRU */
354*043036a2SApple OSS Distributions 	sort_and_verify(JETSAM_PRIORITY_FOREGROUND, JETSAM_SORT_LRU, expected_order, COALS_EXPECTED_ORDER_LEN);
355*043036a2SApple OSS Distributions 
356*043036a2SApple OSS Distributions 	free(coalitions);
357*043036a2SApple OSS Distributions 	free(expected_order);
358*043036a2SApple OSS Distributions }
359*043036a2SApple OSS Distributions 
360*043036a2SApple OSS Distributions 
361*043036a2SApple OSS Distributions /*
362*043036a2SApple OSS Distributions  * Test that sorting the idle bucket in footprint order works properly.
363*043036a2SApple OSS Distributions  *
364*043036a2SApple OSS Distributions  * Spawns some children with very different footprints in the idle band,
365*043036a2SApple OSS Distributions  * and then ensures that they get sorted properly.
366*043036a2SApple OSS Distributions  */
367*043036a2SApple OSS Distributions T_DECL(memorystatus_sort_footprint, "Footprint sort order",
368*043036a2SApple OSS Distributions     T_META_ASROOT(true), T_META_TAG_VM_PREFERRED) {
369*043036a2SApple OSS Distributions #define kNumChildren 3
370*043036a2SApple OSS Distributions 	static const int kChildrenFootprints[kNumChildren] = {500, 0, 2500};
371*043036a2SApple OSS Distributions 	/*
372*043036a2SApple OSS Distributions 	 * The expected sort order of the children in the order that they were launched.
373*043036a2SApple OSS Distributions 	 * Used to construct the expected_order pid array.
374*043036a2SApple OSS Distributions 	 * Note that procs should be sorted in descending footprint order.
375*043036a2SApple OSS Distributions 	 */
376*043036a2SApple OSS Distributions 	static const int kExpectedOrder[kNumChildren] = {2, 0, 1};
377*043036a2SApple OSS Distributions 	static const int kJetsamBand = JETSAM_PRIORITY_BACKGROUND;
378*043036a2SApple OSS Distributions 	__block pid_t pid;
379*043036a2SApple OSS Distributions 	sig_t res;
380*043036a2SApple OSS Distributions 	dispatch_source_t ds_allocated;
381*043036a2SApple OSS Distributions 	T_ATEND(cleanup_children);
382*043036a2SApple OSS Distributions 
383*043036a2SApple OSS Distributions 	// After we spawn the children, they'll signal that they've touched their pages.
384*043036a2SApple OSS Distributions 	res = signal(SIGUSR1, SIG_IGN);
385*043036a2SApple OSS Distributions 	T_WITH_ERRNO; T_ASSERT_NE(res, SIG_ERR, "SIG_IGN SIGUSR1");
386*043036a2SApple OSS Distributions 	ds_allocated = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue());
387*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(ds_allocated, "dispatch_source_create (ds_allocated)");
388*043036a2SApple OSS Distributions 
389*043036a2SApple OSS Distributions 	dispatch_source_set_event_handler(ds_allocated, ^{
390*043036a2SApple OSS Distributions 		if (num_children < kNumChildren) {
391*043036a2SApple OSS Distributions 			pid = launch_proc_in_coalition(NULL, 0, kChildrenFootprints[num_children]);
392*043036a2SApple OSS Distributions 			place_proc_in_band(pid, kJetsamBand);
393*043036a2SApple OSS Distributions 		} else {
394*043036a2SApple OSS Distributions 			pid_t expected_order[kNumChildren] = {0};
395*043036a2SApple OSS Distributions 			for (int i = 0; i < kNumChildren; i++) {
396*043036a2SApple OSS Distributions 				expected_order[i] = children_pids[kExpectedOrder[i]];
397*043036a2SApple OSS Distributions 			}
398*043036a2SApple OSS Distributions 			sort_and_verify(kJetsamBand, JETSAM_SORT_FOOTPRINT_NOCOAL, expected_order, kNumChildren);
399*043036a2SApple OSS Distributions 			T_END;
400*043036a2SApple OSS Distributions 		}
401*043036a2SApple OSS Distributions 	});
402*043036a2SApple OSS Distributions 	dispatch_activate(ds_allocated);
403*043036a2SApple OSS Distributions 
404*043036a2SApple OSS Distributions 	pid = launch_proc_in_coalition(NULL, 0, kChildrenFootprints[num_children]);
405*043036a2SApple OSS Distributions 	place_proc_in_band(pid, kJetsamBand);
406*043036a2SApple OSS Distributions 
407*043036a2SApple OSS Distributions 	dispatch_main();
408*043036a2SApple OSS Distributions 
409*043036a2SApple OSS Distributions #undef kNumChildren
410*043036a2SApple OSS Distributions }
411*043036a2SApple OSS Distributions 
412*043036a2SApple OSS Distributions static pid_t
launch_proc_in_coalition(uint64_t * coalition_ids,int role,int num_pages)413*043036a2SApple OSS Distributions launch_proc_in_coalition(uint64_t *coalition_ids, int role, int num_pages)
414*043036a2SApple OSS Distributions {
415*043036a2SApple OSS Distributions 	int ret;
416*043036a2SApple OSS Distributions 	posix_spawnattr_t attr;
417*043036a2SApple OSS Distributions 	pid_t pid;
418*043036a2SApple OSS Distributions 	char testpath[PATH_MAX];
419*043036a2SApple OSS Distributions 	uint32_t testpath_buf_size = PATH_MAX;
420*043036a2SApple OSS Distributions 	char num_pages_str[32] = {0};
421*043036a2SApple OSS Distributions 	char *argv[5] = {testpath, "-n", "coalition_member", num_pages_str, NULL};
422*043036a2SApple OSS Distributions 	extern char **environ;
423*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_LT(num_children + 1, (size_t) kMaxChildrenProcs, "Don't create too many children.");
424*043036a2SApple OSS Distributions 	ret = posix_spawnattr_init(&attr);
425*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_init");
426*043036a2SApple OSS Distributions 	if (coalition_ids != NULL) {
427*043036a2SApple OSS Distributions 		for (int i = 0; i < COALITION_NUM_TYPES; i++) {
428*043036a2SApple OSS Distributions 			ret = posix_spawnattr_setcoalition_np(&attr, coalition_ids[i], i, role);
429*043036a2SApple OSS Distributions 			T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_setcoalition_np");
430*043036a2SApple OSS Distributions 		}
431*043036a2SApple OSS Distributions 	}
432*043036a2SApple OSS Distributions 
433*043036a2SApple OSS Distributions 	ret = snprintf(num_pages_str, sizeof(num_pages_str), "%d", num_pages);
434*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_LE((size_t) ret, sizeof(num_pages_str), "Don't allocate too many pages.");
435*043036a2SApple OSS Distributions 	ret = _NSGetExecutablePath(testpath, &testpath_buf_size);
436*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_EQ(ret, 0, "_NSGetExecutablePath");
437*043036a2SApple OSS Distributions 	ret = posix_spawn(&pid, argv[0], NULL, &attr, argv, environ);
438*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawn");
439*043036a2SApple OSS Distributions 	ret = posix_spawnattr_destroy(&attr);
440*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_destroy");
441*043036a2SApple OSS Distributions 	children_pids[num_children++] = pid;
442*043036a2SApple OSS Distributions 	return pid;
443*043036a2SApple OSS Distributions }
444*043036a2SApple OSS Distributions 
445*043036a2SApple OSS Distributions static void
init_coalition(coalition_info_t * coalition,size_t leader_fp)446*043036a2SApple OSS Distributions init_coalition(coalition_info_t *coalition, size_t leader_fp)
447*043036a2SApple OSS Distributions {
448*043036a2SApple OSS Distributions 	/* This code will need updating if we add a role */
449*043036a2SApple OSS Distributions 	static_assert(COALITION_NUM_TASKROLES == 4);
450*043036a2SApple OSS Distributions 
451*043036a2SApple OSS Distributions 	sigset_t set;
452*043036a2SApple OSS Distributions 	int ret, i, sig;
453*043036a2SApple OSS Distributions 	uint32_t flags = 0;
454*043036a2SApple OSS Distributions 	memset(coalition, 0, sizeof(coalition_info_t));
455*043036a2SApple OSS Distributions 	for (int i = 0; i < COALITION_NUM_TYPES; i++) {
456*043036a2SApple OSS Distributions 		COALITION_CREATE_FLAGS_SET_TYPE(flags, i);
457*043036a2SApple OSS Distributions 		ret = coalition_create(&coalition->ids[i], flags);
458*043036a2SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_ZERO(ret, "coalition_create");
459*043036a2SApple OSS Distributions 	}
460*043036a2SApple OSS Distributions 
461*043036a2SApple OSS Distributions 	sigemptyset(&set);
462*043036a2SApple OSS Distributions 	ret = sigaddset(&set, SIGUSR1);
463*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sigaddset(SIGUSR1)");
464*043036a2SApple OSS Distributions 
465*043036a2SApple OSS Distributions 	coalition->leader_footprint = leader_fp;
466*043036a2SApple OSS Distributions 
467*043036a2SApple OSS Distributions 	/*
468*043036a2SApple OSS Distributions 	 * Spawn procs for each coalition role, and construct the expected
469*043036a2SApple OSS Distributions 	 * sorted order.
470*043036a2SApple OSS Distributions 	 */
471*043036a2SApple OSS Distributions 	int n_roles[COALITION_NUM_TASKROLES] = {0};
472*043036a2SApple OSS Distributions 	int role_order_idx[COALITION_NUM_TASKROLES] = {
473*043036a2SApple OSS Distributions 		/* COALITION_TASKROLE_UNDEF  */ 0,
474*043036a2SApple OSS Distributions 		/* COALITION_TASKROLE_LEADER */ (NUM_PER_ROLE + 1) * 3,
475*043036a2SApple OSS Distributions 		/* COALITION_TASKROLE_XPC    */ (NUM_PER_ROLE + 1) * 2,
476*043036a2SApple OSS Distributions 		/* COALITION_TASKROLE_EXT    */ NUM_PER_ROLE + 1
477*043036a2SApple OSS Distributions 	};
478*043036a2SApple OSS Distributions 	for (i = 1; i < COALITION_NUM_TASKROLES; i++) {
479*043036a2SApple OSS Distributions 		coalition->expected_order[role_order_idx[i] - 1] = -1;
480*043036a2SApple OSS Distributions 	}
481*043036a2SApple OSS Distributions 	for (size_t i = 0; i < NUM_PROCS_IN_COALITION; i++) {
482*043036a2SApple OSS Distributions 		int role;
483*043036a2SApple OSS Distributions 		size_t pages = 0;
484*043036a2SApple OSS Distributions 
485*043036a2SApple OSS Distributions 		while (true) {
486*043036a2SApple OSS Distributions 			role = rand() % COALITION_NUM_TASKROLES;
487*043036a2SApple OSS Distributions 			if ((role == COALITION_TASKROLE_LEADER) && n_roles[role]) {
488*043036a2SApple OSS Distributions 				continue; /* Already have a leader */
489*043036a2SApple OSS Distributions 			} else if (n_roles[role] == NUM_PER_ROLE) {
490*043036a2SApple OSS Distributions 				continue; /* Already have all of this role */
491*043036a2SApple OSS Distributions 			}
492*043036a2SApple OSS Distributions 			n_roles[role]++;
493*043036a2SApple OSS Distributions 			break;
494*043036a2SApple OSS Distributions 		}
495*043036a2SApple OSS Distributions 
496*043036a2SApple OSS Distributions 		if (role == COALITION_TASKROLE_LEADER) {
497*043036a2SApple OSS Distributions 			pages = leader_fp;
498*043036a2SApple OSS Distributions 		}
499*043036a2SApple OSS Distributions 
500*043036a2SApple OSS Distributions 		pid_t pid = launch_proc_in_coalition(coalition->ids, role, pages);
501*043036a2SApple OSS Distributions 		ret = sigwait(&set, &sig);
502*043036a2SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sigwait");
503*043036a2SApple OSS Distributions 		T_QUIET; T_ASSERT_EQ(sig, SIGUSR1, "sigwait == SIGUSR1");
504*043036a2SApple OSS Distributions 		coalition->pids[i] = pid;
505*043036a2SApple OSS Distributions 		coalition->expected_order[role_order_idx[role]] = pid;
506*043036a2SApple OSS Distributions 		role_order_idx[role]++;
507*043036a2SApple OSS Distributions 	}
508*043036a2SApple OSS Distributions }
509*043036a2SApple OSS Distributions 
510*043036a2SApple OSS Distributions static void
place_proc_in_band(pid_t pid,int band)511*043036a2SApple OSS Distributions place_proc_in_band(pid_t pid, int band)
512*043036a2SApple OSS Distributions {
513*043036a2SApple OSS Distributions 	memorystatus_priority_properties_t props = {0};
514*043036a2SApple OSS Distributions 	int ret;
515*043036a2SApple OSS Distributions 	props.priority = band;
516*043036a2SApple OSS Distributions 	props.user_data = 0;
517*043036a2SApple OSS Distributions 	ret = memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES, pid, 0, &props, sizeof(props));
518*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "move proc to band");
519*043036a2SApple OSS Distributions }
520*043036a2SApple OSS Distributions 
521*043036a2SApple OSS Distributions 
522*043036a2SApple OSS Distributions static void
place_coalition_in_band(const coalition_info_t * coalition,int band)523*043036a2SApple OSS Distributions place_coalition_in_band(const coalition_info_t *coalition, int band)
524*043036a2SApple OSS Distributions {
525*043036a2SApple OSS Distributions 	for (size_t i = 0; i < NUM_PROCS_IN_COALITION; i++) {
526*043036a2SApple OSS Distributions 		pid_t curr = coalition->pids[i];
527*043036a2SApple OSS Distributions 		place_proc_in_band(curr, band);
528*043036a2SApple OSS Distributions 	}
529*043036a2SApple OSS Distributions }
530*043036a2SApple OSS Distributions 
531*043036a2SApple OSS Distributions static void
cleanup_children(void)532*043036a2SApple OSS Distributions cleanup_children(void)
533*043036a2SApple OSS Distributions {
534*043036a2SApple OSS Distributions 	int ret, status;
535*043036a2SApple OSS Distributions 	for (size_t i = 0; i < num_children; i++) {
536*043036a2SApple OSS Distributions 		pid_t exited_pid = 0;
537*043036a2SApple OSS Distributions 		pid_t curr = children_pids[i];
538*043036a2SApple OSS Distributions 		ret = kill(curr, SIGKILL);
539*043036a2SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kill");
540*043036a2SApple OSS Distributions 		while (exited_pid == 0) {
541*043036a2SApple OSS Distributions 			exited_pid = waitpid(curr, &status, 0);
542*043036a2SApple OSS Distributions 		}
543*043036a2SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_SUCCESS(exited_pid, "waitpid");
544*043036a2SApple OSS Distributions 		T_QUIET; T_ASSERT_TRUE(WIFSIGNALED(status), "proc was signaled.");
545*043036a2SApple OSS Distributions 		T_QUIET; T_ASSERT_EQ(WTERMSIG(status), SIGKILL, "proc was killed");
546*043036a2SApple OSS Distributions 	}
547*043036a2SApple OSS Distributions }
548*043036a2SApple OSS Distributions 
549*043036a2SApple OSS Distributions static bool
has_unrestrict_coalitions()550*043036a2SApple OSS Distributions has_unrestrict_coalitions()
551*043036a2SApple OSS Distributions {
552*043036a2SApple OSS Distributions 	int ret, val;
553*043036a2SApple OSS Distributions 	size_t val_sz;
554*043036a2SApple OSS Distributions 
555*043036a2SApple OSS Distributions 	val = 0;
556*043036a2SApple OSS Distributions 	val_sz = sizeof(val);
557*043036a2SApple OSS Distributions 	ret = sysctlbyname("kern.unrestrict_coalitions", &val, &val_sz, NULL, 0);
558*043036a2SApple OSS Distributions 	return ret >= 0;
559*043036a2SApple OSS Distributions }
560*043036a2SApple OSS Distributions 
561*043036a2SApple OSS Distributions static void
unrestrict_coalitions()562*043036a2SApple OSS Distributions unrestrict_coalitions()
563*043036a2SApple OSS Distributions {
564*043036a2SApple OSS Distributions 	int ret, val = 1;
565*043036a2SApple OSS Distributions 	size_t val_sz;
566*043036a2SApple OSS Distributions 	val_sz = sizeof(val);
567*043036a2SApple OSS Distributions 	ret = sysctlbyname("kern.unrestrict_coalitions", NULL, 0, &val, val_sz);
568*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.unrestrict_coalitions <- 1");
569*043036a2SApple OSS Distributions }
570*043036a2SApple OSS Distributions 
571*043036a2SApple OSS Distributions static void
restrict_coalitions()572*043036a2SApple OSS Distributions restrict_coalitions()
573*043036a2SApple OSS Distributions {
574*043036a2SApple OSS Distributions 	int ret, val = 0;
575*043036a2SApple OSS Distributions 	size_t val_sz;
576*043036a2SApple OSS Distributions 	val_sz = sizeof(val);
577*043036a2SApple OSS Distributions 	ret = sysctlbyname("kern.unrestrict_coalitions", NULL, 0, &val, val_sz);
578*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kern.unrestrict_coalitions <- 0");
579*043036a2SApple OSS Distributions }
580*043036a2SApple OSS Distributions 
581*043036a2SApple OSS Distributions static void *
allocate_pages(int num_pages)582*043036a2SApple OSS Distributions allocate_pages(int num_pages)
583*043036a2SApple OSS Distributions {
584*043036a2SApple OSS Distributions 	int page_size, i;
585*043036a2SApple OSS Distributions 	unsigned char *buf;
586*043036a2SApple OSS Distributions 
587*043036a2SApple OSS Distributions 	page_size = get_vmpage_size();
588*043036a2SApple OSS Distributions 	buf = malloc((unsigned long)(num_pages * page_size));
589*043036a2SApple OSS Distributions 	for (i = 0; i < num_pages; i++) {
590*043036a2SApple OSS Distributions 		((volatile unsigned char *)buf)[i * page_size] = 1;
591*043036a2SApple OSS Distributions 	}
592*043036a2SApple OSS Distributions 	return buf;
593*043036a2SApple OSS Distributions }
594*043036a2SApple OSS Distributions 
595*043036a2SApple OSS Distributions static int
get_vmpage_size()596*043036a2SApple OSS Distributions get_vmpage_size()
597*043036a2SApple OSS Distributions {
598*043036a2SApple OSS Distributions 	int vmpage_size;
599*043036a2SApple OSS Distributions 	size_t size = sizeof(vmpage_size);
600*043036a2SApple OSS Distributions 	int ret = sysctlbyname("vm.pagesize", &vmpage_size, &size, NULL, 0);
601*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "failed to query vm.pagesize");
602*043036a2SApple OSS Distributions 	T_QUIET; T_ASSERT_GT(vmpage_size, 0, "vm.pagesize is not > 0");
603*043036a2SApple OSS Distributions 	return vmpage_size;
604*043036a2SApple OSS Distributions }
605