xref: /xnu-11215.1.10/tests/memorystatus_zone_test.c (revision 8d741a5de7ff4191bf97d57b9f54c2f6d4a15585)
1*8d741a5dSApple OSS Distributions #include <stdio.h>
2*8d741a5dSApple OSS Distributions #include <mach/mach_vm.h>
3*8d741a5dSApple OSS Distributions #include <mach/mach_port.h>
4*8d741a5dSApple OSS Distributions #include <mach/mach_host.h>
5*8d741a5dSApple OSS Distributions #include <mach/mach_error.h>
6*8d741a5dSApple OSS Distributions #include <mach-o/dyld.h>
7*8d741a5dSApple OSS Distributions #include <sys/sysctl.h>
8*8d741a5dSApple OSS Distributions #include <sys/kdebug.h>
9*8d741a5dSApple OSS Distributions #include <sys/mman.h>
10*8d741a5dSApple OSS Distributions #include <sys/kern_memorystatus.h>
11*8d741a5dSApple OSS Distributions #include <ktrace/session.h>
12*8d741a5dSApple OSS Distributions #include <dispatch/private.h>
13*8d741a5dSApple OSS Distributions 
14*8d741a5dSApple OSS Distributions #ifdef T_NAMESPACE
15*8d741a5dSApple OSS Distributions #undef T_NAMESPACE
16*8d741a5dSApple OSS Distributions #endif
17*8d741a5dSApple OSS Distributions #include <darwintest.h>
18*8d741a5dSApple OSS Distributions #include <darwintest_utils.h>
19*8d741a5dSApple OSS Distributions 
20*8d741a5dSApple OSS Distributions T_GLOBAL_META(
21*8d741a5dSApple OSS Distributions 	T_META_NAMESPACE("xnu.vm"),
22*8d741a5dSApple OSS Distributions 	T_META_RADAR_COMPONENT_NAME("xnu"),
23*8d741a5dSApple OSS Distributions 	T_META_RADAR_COMPONENT_VERSION("VM"),
24*8d741a5dSApple OSS Distributions 	T_META_CHECK_LEAKS(false)
25*8d741a5dSApple OSS Distributions 	);
26*8d741a5dSApple OSS Distributions 
27*8d741a5dSApple OSS Distributions #define TIMEOUT_SECS                                    10 * 60 /* abort if test takes > 10 minutes */
28*8d741a5dSApple OSS Distributions 
29*8d741a5dSApple OSS Distributions #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
30*8d741a5dSApple OSS Distributions #define ALLOCATION_SIZE_VM_REGION                       (16*1024)               /* 16 KB */
31*8d741a5dSApple OSS Distributions #define ALLOCATION_SIZE_VM_OBJECT                       ALLOCATION_SIZE_VM_REGION
32*8d741a5dSApple OSS Distributions #else
33*8d741a5dSApple OSS Distributions #define ALLOCATION_SIZE_VM_REGION                       (1024*1024*100) /* 100 MB */
34*8d741a5dSApple OSS Distributions #define ALLOCATION_SIZE_VM_OBJECT                       (16*1024)               /* 16 KB */
35*8d741a5dSApple OSS Distributions #endif
36*8d741a5dSApple OSS Distributions #define MAX_CHILD_PROCS                                 100
37*8d741a5dSApple OSS Distributions 
38*8d741a5dSApple OSS Distributions #define NUM_GIVE_BACK                                   5
39*8d741a5dSApple OSS Distributions #define NUM_GIVE_BACK_PORTS                             20
40*8d741a5dSApple OSS Distributions 
41*8d741a5dSApple OSS Distributions /* 60% is too high on bridgeOS to achieve without vm-pageshortage jetsams. Set it to 40%. */
42*8d741a5dSApple OSS Distributions #if TARGET_OS_BRIDGE
43*8d741a5dSApple OSS Distributions #define ZONEMAP_JETSAM_LIMIT_SYSCTL                     "kern.zone_map_jetsam_limit=40"
44*8d741a5dSApple OSS Distributions #else
45*8d741a5dSApple OSS Distributions #define ZONEMAP_JETSAM_LIMIT_SYSCTL                     "kern.zone_map_jetsam_limit=60"
46*8d741a5dSApple OSS Distributions #endif
47*8d741a5dSApple OSS Distributions 
48*8d741a5dSApple OSS Distributions #define VME_ZONE_TEST_OPT                               "allocate_vm_regions"
49*8d741a5dSApple OSS Distributions #define VM_OBJECTS_ZONE_TEST_OPT                        "allocate_vm_objects"
50*8d741a5dSApple OSS Distributions #define GENERIC_ZONE_TEST_OPT                           "allocate_from_generic_zone"
51*8d741a5dSApple OSS Distributions 
52*8d741a5dSApple OSS Distributions #define VME_ZONE                                        "VM map entries"
53*8d741a5dSApple OSS Distributions #define VMOBJECTS_ZONE                                  "vm objects"
54*8d741a5dSApple OSS Distributions #define VMENTRY_TO_VMOBJECT_COMPARISON_RATIO            98
55*8d741a5dSApple OSS Distributions 
56*8d741a5dSApple OSS Distributions #define VM_TAG1                                         100
57*8d741a5dSApple OSS Distributions #define VM_TAG2                                         101
58*8d741a5dSApple OSS Distributions 
59*8d741a5dSApple OSS Distributions enum {
60*8d741a5dSApple OSS Distributions 	VME_ZONE_TEST = 0,
61*8d741a5dSApple OSS Distributions 	VM_OBJECTS_ZONE_TEST,
62*8d741a5dSApple OSS Distributions 	GENERIC_ZONE_TEST,
63*8d741a5dSApple OSS Distributions };
64*8d741a5dSApple OSS Distributions 
65*8d741a5dSApple OSS Distributions typedef struct test_config_struct {
66*8d741a5dSApple OSS Distributions 	int test_index;
67*8d741a5dSApple OSS Distributions 	int num_zones;
68*8d741a5dSApple OSS Distributions 	const char *helper_func;
69*8d741a5dSApple OSS Distributions 	mach_zone_name_array_t zone_names;
70*8d741a5dSApple OSS Distributions } test_config_struct;
71*8d741a5dSApple OSS Distributions 
72*8d741a5dSApple OSS Distributions static test_config_struct current_test;
73*8d741a5dSApple OSS Distributions static dispatch_source_t ds_signal = NULL;
74*8d741a5dSApple OSS Distributions static dispatch_source_t ds_timer = NULL;
75*8d741a5dSApple OSS Distributions static dispatch_queue_t dq_spawn = NULL;
76*8d741a5dSApple OSS Distributions static ktrace_session_t session = NULL;
77*8d741a5dSApple OSS Distributions 
78*8d741a5dSApple OSS Distributions static mach_zone_info_array_t zone_info_array = NULL;
79*8d741a5dSApple OSS Distributions static mach_zone_name_t largest_zone_name;
80*8d741a5dSApple OSS Distributions static mach_zone_info_t largest_zone_info;
81*8d741a5dSApple OSS Distributions 
82*8d741a5dSApple OSS Distributions static pthread_mutex_t test_mtx = PTHREAD_MUTEX_INITIALIZER;   /* protects the next 3 things */
83*8d741a5dSApple OSS Distributions static bool test_ending = false;
84*8d741a5dSApple OSS Distributions static int num_children = 0;
85*8d741a5dSApple OSS Distributions static pid_t child_pids[MAX_CHILD_PROCS];
86*8d741a5dSApple OSS Distributions 
87*8d741a5dSApple OSS Distributions static char testpath[PATH_MAX];
88*8d741a5dSApple OSS Distributions static void allocate_vm_stuff(int);
89*8d741a5dSApple OSS Distributions static void allocate_from_generic_zone(void);
90*8d741a5dSApple OSS Distributions static void begin_test_teardown(void);
91*8d741a5dSApple OSS Distributions static void cleanup_and_end_test(void);
92*8d741a5dSApple OSS Distributions static void setup_ktrace_session(void);
93*8d741a5dSApple OSS Distributions static void spawn_child_process(void);
94*8d741a5dSApple OSS Distributions static void run_test(void);
95*8d741a5dSApple OSS Distributions static bool verify_generic_jetsam_criteria(void);
96*8d741a5dSApple OSS Distributions static bool vme_zone_compares_to_vm_objects(void);
97*8d741a5dSApple OSS Distributions static void query_zone_info(void);
98*8d741a5dSApple OSS Distributions static void print_zone_info(mach_zone_name_t *zn, mach_zone_info_t *zi);
99*8d741a5dSApple OSS Distributions 
100*8d741a5dSApple OSS Distributions extern void mach_zone_force_gc(host_t host);
101*8d741a5dSApple OSS Distributions extern kern_return_t mach_zone_info_for_largest_zone(
102*8d741a5dSApple OSS Distributions 	host_priv_t host,
103*8d741a5dSApple OSS Distributions 	mach_zone_name_t *name,
104*8d741a5dSApple OSS Distributions 	mach_zone_info_t *info
105*8d741a5dSApple OSS Distributions 	);
106*8d741a5dSApple OSS Distributions 
107*8d741a5dSApple OSS Distributions static bool
check_time(time_t start,int timeout)108*8d741a5dSApple OSS Distributions check_time(time_t start, int timeout)
109*8d741a5dSApple OSS Distributions {
110*8d741a5dSApple OSS Distributions 	return start + timeout < time(NULL);
111*8d741a5dSApple OSS Distributions }
112*8d741a5dSApple OSS Distributions 
113*8d741a5dSApple OSS Distributions /*
114*8d741a5dSApple OSS Distributions  * flag values for allocate_vm_stuff()
115*8d741a5dSApple OSS Distributions  */
116*8d741a5dSApple OSS Distributions #define REGIONS 1
117*8d741a5dSApple OSS Distributions #define OBJECTS 2
118*8d741a5dSApple OSS Distributions 
119*8d741a5dSApple OSS Distributions static void
allocate_vm_stuff(int flags)120*8d741a5dSApple OSS Distributions allocate_vm_stuff(int flags)
121*8d741a5dSApple OSS Distributions {
122*8d741a5dSApple OSS Distributions 	uint64_t alloc_size, i;
123*8d741a5dSApple OSS Distributions 	time_t start = time(NULL);
124*8d741a5dSApple OSS Distributions 	mach_vm_address_t give_back[NUM_GIVE_BACK];
125*8d741a5dSApple OSS Distributions 	char *msg;
126*8d741a5dSApple OSS Distributions 
127*8d741a5dSApple OSS Distributions 	if (flags == REGIONS) {
128*8d741a5dSApple OSS Distributions 		alloc_size = ALLOCATION_SIZE_VM_REGION;
129*8d741a5dSApple OSS Distributions 		msg = "";
130*8d741a5dSApple OSS Distributions 	} else {
131*8d741a5dSApple OSS Distributions 		alloc_size = ALLOCATION_SIZE_VM_OBJECT;
132*8d741a5dSApple OSS Distributions 		msg = " each region backed by a VM object";
133*8d741a5dSApple OSS Distributions 	}
134*8d741a5dSApple OSS Distributions 
135*8d741a5dSApple OSS Distributions 	printf("[%d] Allocating VM regions, each of size %lld KB%s\n", getpid(), (alloc_size >> 10), msg);
136*8d741a5dSApple OSS Distributions 
137*8d741a5dSApple OSS Distributions 	for (i = 0;; i++) {
138*8d741a5dSApple OSS Distributions 		mach_vm_address_t addr = (mach_vm_address_t)NULL;
139*8d741a5dSApple OSS Distributions 
140*8d741a5dSApple OSS Distributions 		/* Alternate VM tags between consecutive regions to prevent coalescing */
141*8d741a5dSApple OSS Distributions 		int vmflags = VM_MAKE_TAG((i % 2)? VM_TAG1: VM_TAG2) | VM_FLAGS_ANYWHERE;
142*8d741a5dSApple OSS Distributions 
143*8d741a5dSApple OSS Distributions 		if ((mach_vm_allocate(mach_task_self(), &addr, (mach_vm_size_t)alloc_size, vmflags)) != KERN_SUCCESS) {
144*8d741a5dSApple OSS Distributions 			break;
145*8d741a5dSApple OSS Distributions 		}
146*8d741a5dSApple OSS Distributions 
147*8d741a5dSApple OSS Distributions 		/*
148*8d741a5dSApple OSS Distributions 		 * If interested in objects, touch the region so the VM object is created,
149*8d741a5dSApple OSS Distributions 		 * then free this page. Keeps us from holding a lot of dirty pages.
150*8d741a5dSApple OSS Distributions 		 */
151*8d741a5dSApple OSS Distributions 		if (flags == OBJECTS) {
152*8d741a5dSApple OSS Distributions 			*((int *)addr) = 0;
153*8d741a5dSApple OSS Distributions 			madvise((void *)addr, (size_t)alloc_size, MADV_FREE);
154*8d741a5dSApple OSS Distributions 		}
155*8d741a5dSApple OSS Distributions 
156*8d741a5dSApple OSS Distributions 		if (check_time(start, TIMEOUT_SECS)) {
157*8d741a5dSApple OSS Distributions 			printf("[%d] child timeout during allocations\n", getpid());
158*8d741a5dSApple OSS Distributions 			exit(0);
159*8d741a5dSApple OSS Distributions 		}
160*8d741a5dSApple OSS Distributions 
161*8d741a5dSApple OSS Distributions 		if (i < NUM_GIVE_BACK) {
162*8d741a5dSApple OSS Distributions 			give_back[i] = addr;
163*8d741a5dSApple OSS Distributions 		}
164*8d741a5dSApple OSS Distributions 	}
165*8d741a5dSApple OSS Distributions 
166*8d741a5dSApple OSS Distributions 	/* return some of the resource to avoid O-O-M problems */
167*8d741a5dSApple OSS Distributions 	for (uint64_t j = 0; j < NUM_GIVE_BACK && j < i; ++j) {
168*8d741a5dSApple OSS Distributions 		mach_vm_deallocate(mach_task_self(), give_back[j], (mach_vm_size_t)alloc_size);
169*8d741a5dSApple OSS Distributions 	}
170*8d741a5dSApple OSS Distributions 
171*8d741a5dSApple OSS Distributions 	printf("[%d] Number of allocations: %lld\n", getpid(), i);
172*8d741a5dSApple OSS Distributions 
173*8d741a5dSApple OSS Distributions 	/* Signal to the parent that we're done allocating */
174*8d741a5dSApple OSS Distributions 	kill(getppid(), SIGUSR1);
175*8d741a5dSApple OSS Distributions 
176*8d741a5dSApple OSS Distributions 	while (1) {
177*8d741a5dSApple OSS Distributions 		usleep(500 * 1000);
178*8d741a5dSApple OSS Distributions 		/* Exit if parent has exited. Ensures child processes don't linger around after the test exits */
179*8d741a5dSApple OSS Distributions 		if (getppid() == 1) {
180*8d741a5dSApple OSS Distributions 			exit(0);
181*8d741a5dSApple OSS Distributions 		}
182*8d741a5dSApple OSS Distributions 		if (check_time(start, TIMEOUT_SECS)) {
183*8d741a5dSApple OSS Distributions 			printf("[%d] child timeout while waiting\n", getpid());
184*8d741a5dSApple OSS Distributions 			exit(0);
185*8d741a5dSApple OSS Distributions 		}
186*8d741a5dSApple OSS Distributions 	}
187*8d741a5dSApple OSS Distributions }
188*8d741a5dSApple OSS Distributions 
189*8d741a5dSApple OSS Distributions 
190*8d741a5dSApple OSS Distributions static void
allocate_from_generic_zone(void)191*8d741a5dSApple OSS Distributions allocate_from_generic_zone(void)
192*8d741a5dSApple OSS Distributions {
193*8d741a5dSApple OSS Distributions 	uint64_t i = 0;
194*8d741a5dSApple OSS Distributions 	time_t start = time(NULL);
195*8d741a5dSApple OSS Distributions 	mach_port_t give_back[NUM_GIVE_BACK_PORTS];
196*8d741a5dSApple OSS Distributions 	int old_limit = 0;
197*8d741a5dSApple OSS Distributions 
198*8d741a5dSApple OSS Distributions 	printf("[%d] Allocating mach_ports\n", getpid());
199*8d741a5dSApple OSS Distributions 
200*8d741a5dSApple OSS Distributions 	size_t size = sizeof(old_limit);
201*8d741a5dSApple OSS Distributions 	int kr = sysctlbyname("machdep.max_port_table_size", &old_limit, &size, NULL, 0);
202*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kr, "sysctl kern.max_port_table_size failed");
203*8d741a5dSApple OSS Distributions 	T_LOG("machdep.max_port_table_size = %d", old_limit);
204*8d741a5dSApple OSS Distributions 
205*8d741a5dSApple OSS Distributions 	/* Avoid hitting the resource limit exception */
206*8d741a5dSApple OSS Distributions 	uint64_t limit = (uint64_t)(old_limit * 7 / 8);
207*8d741a5dSApple OSS Distributions 
208*8d741a5dSApple OSS Distributions 	for (i = 0; i < limit; i++) {
209*8d741a5dSApple OSS Distributions 		mach_port_t port;
210*8d741a5dSApple OSS Distributions 
211*8d741a5dSApple OSS Distributions 		if ((mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port)) != KERN_SUCCESS) {
212*8d741a5dSApple OSS Distributions 			break;
213*8d741a5dSApple OSS Distributions 		}
214*8d741a5dSApple OSS Distributions 
215*8d741a5dSApple OSS Distributions 		if (check_time(start, TIMEOUT_SECS)) {
216*8d741a5dSApple OSS Distributions 			printf("[%d] child timeout during allocations\n", getpid());
217*8d741a5dSApple OSS Distributions 			exit(0);
218*8d741a5dSApple OSS Distributions 		}
219*8d741a5dSApple OSS Distributions 
220*8d741a5dSApple OSS Distributions 		if (i < NUM_GIVE_BACK_PORTS) {
221*8d741a5dSApple OSS Distributions 			give_back[i] = port;
222*8d741a5dSApple OSS Distributions 		}
223*8d741a5dSApple OSS Distributions 	}
224*8d741a5dSApple OSS Distributions 
225*8d741a5dSApple OSS Distributions 	/* return some of the resource to avoid O-O-M problems */
226*8d741a5dSApple OSS Distributions 	for (uint64_t j = 0; j < NUM_GIVE_BACK_PORTS && j < i; ++j) {
227*8d741a5dSApple OSS Distributions 		int ret;
228*8d741a5dSApple OSS Distributions 		ret = mach_port_mod_refs(mach_task_self(), give_back[j], MACH_PORT_RIGHT_RECEIVE, -1);
229*8d741a5dSApple OSS Distributions 		T_ASSERT_MACH_SUCCESS(ret, "mach_port_mod_refs(RECV_RIGHT, -1)");
230*8d741a5dSApple OSS Distributions 	}
231*8d741a5dSApple OSS Distributions 	printf("[%d] Number of allocations: %lld\n", getpid(), i);
232*8d741a5dSApple OSS Distributions 
233*8d741a5dSApple OSS Distributions 	/* Signal to the parent that we're done allocating */
234*8d741a5dSApple OSS Distributions 	kill(getppid(), SIGUSR1);
235*8d741a5dSApple OSS Distributions 
236*8d741a5dSApple OSS Distributions 	while (1) {
237*8d741a5dSApple OSS Distributions 		usleep(500 * 1000);
238*8d741a5dSApple OSS Distributions 		/* Exit if parent has exited. Ensures child processes don't linger around after the test exits */
239*8d741a5dSApple OSS Distributions 		if (getppid() == 1) {
240*8d741a5dSApple OSS Distributions 			exit(0);
241*8d741a5dSApple OSS Distributions 		}
242*8d741a5dSApple OSS Distributions 
243*8d741a5dSApple OSS Distributions 		if (check_time(start, TIMEOUT_SECS)) {
244*8d741a5dSApple OSS Distributions 			printf("[%d] child timeout while waiting\n", getpid());
245*8d741a5dSApple OSS Distributions 			exit(0);
246*8d741a5dSApple OSS Distributions 		}
247*8d741a5dSApple OSS Distributions 	}
248*8d741a5dSApple OSS Distributions }
249*8d741a5dSApple OSS Distributions 
250*8d741a5dSApple OSS Distributions static void
print_zone_info(mach_zone_name_t * zn,mach_zone_info_t * zi)251*8d741a5dSApple OSS Distributions print_zone_info(mach_zone_name_t *zn, mach_zone_info_t *zi)
252*8d741a5dSApple OSS Distributions {
253*8d741a5dSApple OSS Distributions 	T_LOG("ZONE NAME: %-35sSIZE: %-25lluELEMENTS: %llu",
254*8d741a5dSApple OSS Distributions 	    zn->mzn_name, zi->mzi_cur_size, zi->mzi_count);
255*8d741a5dSApple OSS Distributions }
256*8d741a5dSApple OSS Distributions 
257*8d741a5dSApple OSS Distributions static time_t main_start;
258*8d741a5dSApple OSS Distributions 
259*8d741a5dSApple OSS Distributions static void
query_zone_info(void)260*8d741a5dSApple OSS Distributions query_zone_info(void)
261*8d741a5dSApple OSS Distributions {
262*8d741a5dSApple OSS Distributions 	int i;
263*8d741a5dSApple OSS Distributions 	kern_return_t kr;
264*8d741a5dSApple OSS Distributions 	static uint64_t num_calls = 0;
265*8d741a5dSApple OSS Distributions 
266*8d741a5dSApple OSS Distributions 	if (check_time(main_start, TIMEOUT_SECS)) {
267*8d741a5dSApple OSS Distributions 		T_ASSERT_FAIL("Global timeout expired");
268*8d741a5dSApple OSS Distributions 	}
269*8d741a5dSApple OSS Distributions 	for (i = 0; i < current_test.num_zones; i++) {
270*8d741a5dSApple OSS Distributions 		kr = mach_zone_info_for_zone(mach_host_self(), current_test.zone_names[i], &(zone_info_array[i]));
271*8d741a5dSApple OSS Distributions 		T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_zone_info_for_zone(%s) returned %d [%s]", current_test.zone_names[i].mzn_name, kr, mach_error_string(kr));
272*8d741a5dSApple OSS Distributions 	}
273*8d741a5dSApple OSS Distributions 	kr = mach_zone_info_for_largest_zone(mach_host_self(), &largest_zone_name, &largest_zone_info);
274*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "mach_zone_info_for_largest_zone returned %d [%s]", kr, mach_error_string(kr));
275*8d741a5dSApple OSS Distributions 
276*8d741a5dSApple OSS Distributions 	num_calls++;
277*8d741a5dSApple OSS Distributions 	if (num_calls % 5 != 0) {
278*8d741a5dSApple OSS Distributions 		return;
279*8d741a5dSApple OSS Distributions 	}
280*8d741a5dSApple OSS Distributions 
281*8d741a5dSApple OSS Distributions 	/* Print out size and element count for zones relevant to the test */
282*8d741a5dSApple OSS Distributions 	for (i = 0; i < current_test.num_zones; i++) {
283*8d741a5dSApple OSS Distributions 		print_zone_info(&(current_test.zone_names[i]), &(zone_info_array[i]));
284*8d741a5dSApple OSS Distributions 	}
285*8d741a5dSApple OSS Distributions }
286*8d741a5dSApple OSS Distributions 
287*8d741a5dSApple OSS Distributions static bool
vme_zone_compares_to_vm_objects(void)288*8d741a5dSApple OSS Distributions vme_zone_compares_to_vm_objects(void)
289*8d741a5dSApple OSS Distributions {
290*8d741a5dSApple OSS Distributions 	int i;
291*8d741a5dSApple OSS Distributions 	uint64_t vm_object_element_count = 0, vm_map_entry_element_count = 0;
292*8d741a5dSApple OSS Distributions 
293*8d741a5dSApple OSS Distributions 	T_LOG("Comparing element counts of \"VM map entries\" and \"vm objects\" zones");
294*8d741a5dSApple OSS Distributions 	for (i = 0; i < current_test.num_zones; i++) {
295*8d741a5dSApple OSS Distributions 		if (!strcmp(current_test.zone_names[i].mzn_name, VME_ZONE)) {
296*8d741a5dSApple OSS Distributions 			vm_map_entry_element_count = zone_info_array[i].mzi_count;
297*8d741a5dSApple OSS Distributions 		} else if (!strcmp(current_test.zone_names[i].mzn_name, VMOBJECTS_ZONE)) {
298*8d741a5dSApple OSS Distributions 			vm_object_element_count = zone_info_array[i].mzi_count;
299*8d741a5dSApple OSS Distributions 		}
300*8d741a5dSApple OSS Distributions 		print_zone_info(&(current_test.zone_names[i]), &(zone_info_array[i]));
301*8d741a5dSApple OSS Distributions 	}
302*8d741a5dSApple OSS Distributions 
303*8d741a5dSApple OSS Distributions 	T_LOG("# VM map entries as percentage of # vm objects = %llu", (vm_map_entry_element_count * 100) / vm_object_element_count);
304*8d741a5dSApple OSS Distributions 	if (vm_map_entry_element_count >= ((vm_object_element_count * VMENTRY_TO_VMOBJECT_COMPARISON_RATIO) / 100)) {
305*8d741a5dSApple OSS Distributions 		T_LOG("Number of VM map entries is comparable to vm objects\n\n");
306*8d741a5dSApple OSS Distributions 		return true;
307*8d741a5dSApple OSS Distributions 	}
308*8d741a5dSApple OSS Distributions 	T_LOG("Number of VM map entries is NOT comparable to vm objects\n\n");
309*8d741a5dSApple OSS Distributions 	return false;
310*8d741a5dSApple OSS Distributions }
311*8d741a5dSApple OSS Distributions 
312*8d741a5dSApple OSS Distributions static bool
verify_generic_jetsam_criteria(void)313*8d741a5dSApple OSS Distributions verify_generic_jetsam_criteria(void)
314*8d741a5dSApple OSS Distributions {
315*8d741a5dSApple OSS Distributions 	T_LOG("Largest zone info");
316*8d741a5dSApple OSS Distributions 	print_zone_info(&largest_zone_name, &largest_zone_info);
317*8d741a5dSApple OSS Distributions 
318*8d741a5dSApple OSS Distributions 	/* If VM map entries is not the largest zone */
319*8d741a5dSApple OSS Distributions 	if (strcmp(largest_zone_name.mzn_name, VME_ZONE)) {
320*8d741a5dSApple OSS Distributions 		/* If vm objects is the largest zone and the VM map entries zone had comparable # of elements, return false */
321*8d741a5dSApple OSS Distributions 		if (!strcmp(largest_zone_name.mzn_name, VMOBJECTS_ZONE) && vme_zone_compares_to_vm_objects()) {
322*8d741a5dSApple OSS Distributions 			return false;
323*8d741a5dSApple OSS Distributions 		}
324*8d741a5dSApple OSS Distributions 		return true;
325*8d741a5dSApple OSS Distributions 	}
326*8d741a5dSApple OSS Distributions 	return false;
327*8d741a5dSApple OSS Distributions }
328*8d741a5dSApple OSS Distributions 
329*8d741a5dSApple OSS Distributions static void
begin_test_teardown(void)330*8d741a5dSApple OSS Distributions begin_test_teardown(void)
331*8d741a5dSApple OSS Distributions {
332*8d741a5dSApple OSS Distributions 	int ret, old_limit = 95;
333*8d741a5dSApple OSS Distributions 
334*8d741a5dSApple OSS Distributions 	/*
335*8d741a5dSApple OSS Distributions 	 * Restore kern.zone_map_jetsam_limit to the default high value, to prevent further jetsams.
336*8d741a5dSApple OSS Distributions 	 * We should change the value of old_limit if ZONE_MAP_JETSAM_LIMIT_DEFAULT changes in the kernel.
337*8d741a5dSApple OSS Distributions 	 * We don't have a way to capture what the original value was before the test, because the
338*8d741a5dSApple OSS Distributions 	 * T_META_SYSCTL_INT macro will have changed the value before the test starts running.
339*8d741a5dSApple OSS Distributions 	 */
340*8d741a5dSApple OSS Distributions 	ret = sysctlbyname("kern.zone_map_jetsam_limit", NULL, NULL, &old_limit, sizeof(old_limit));
341*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.zone_map_jetsam_limit failed");
342*8d741a5dSApple OSS Distributions 	T_LOG("kern.zone_map_jetsam_limit set to %d%%", old_limit);
343*8d741a5dSApple OSS Distributions 
344*8d741a5dSApple OSS Distributions 
345*8d741a5dSApple OSS Distributions 	/* End ktrace session */
346*8d741a5dSApple OSS Distributions 	if (session != NULL) {
347*8d741a5dSApple OSS Distributions 		T_LOG("Ending ktrace session...");
348*8d741a5dSApple OSS Distributions 		ktrace_end(session, 1);
349*8d741a5dSApple OSS Distributions 	}
350*8d741a5dSApple OSS Distributions 
351*8d741a5dSApple OSS Distributions 	dispatch_sync(dq_spawn, ^{
352*8d741a5dSApple OSS Distributions 		T_LOG("Cancelling dispatch sources...");
353*8d741a5dSApple OSS Distributions 
354*8d741a5dSApple OSS Distributions 		/* Disable the timer that queries and prints zone info periodically */
355*8d741a5dSApple OSS Distributions 		if (ds_timer != NULL) {
356*8d741a5dSApple OSS Distributions 		        dispatch_source_cancel(ds_timer);
357*8d741a5dSApple OSS Distributions 		}
358*8d741a5dSApple OSS Distributions 
359*8d741a5dSApple OSS Distributions 		/* Disable signal handler that spawns child processes */
360*8d741a5dSApple OSS Distributions 		if (ds_signal != NULL) {
361*8d741a5dSApple OSS Distributions 		        /*
362*8d741a5dSApple OSS Distributions 		         * No need for a dispatch_source_cancel_and_wait here.
363*8d741a5dSApple OSS Distributions 		         * We're queueing this on the spawn queue, so no further
364*8d741a5dSApple OSS Distributions 		         * processes will be spawned after the source is cancelled.
365*8d741a5dSApple OSS Distributions 		         */
366*8d741a5dSApple OSS Distributions 		        dispatch_source_cancel(ds_signal);
367*8d741a5dSApple OSS Distributions 		}
368*8d741a5dSApple OSS Distributions 	});
369*8d741a5dSApple OSS Distributions }
370*8d741a5dSApple OSS Distributions 
371*8d741a5dSApple OSS Distributions static void
cleanup_and_end_test(void)372*8d741a5dSApple OSS Distributions cleanup_and_end_test(void)
373*8d741a5dSApple OSS Distributions {
374*8d741a5dSApple OSS Distributions 	int i;
375*8d741a5dSApple OSS Distributions 
376*8d741a5dSApple OSS Distributions 	/*
377*8d741a5dSApple OSS Distributions 	 * The atend handler executes on a different dispatch queue.
378*8d741a5dSApple OSS Distributions 	 * We want to do the cleanup only once.
379*8d741a5dSApple OSS Distributions 	 */
380*8d741a5dSApple OSS Distributions 	pthread_mutex_lock(&test_mtx);
381*8d741a5dSApple OSS Distributions 	if (test_ending) {
382*8d741a5dSApple OSS Distributions 		pthread_mutex_unlock(&test_mtx);
383*8d741a5dSApple OSS Distributions 		return;
384*8d741a5dSApple OSS Distributions 	}
385*8d741a5dSApple OSS Distributions 	test_ending = TRUE;
386*8d741a5dSApple OSS Distributions 	pthread_mutex_unlock(&test_mtx);
387*8d741a5dSApple OSS Distributions 
388*8d741a5dSApple OSS Distributions 	dispatch_async(dq_spawn, ^{
389*8d741a5dSApple OSS Distributions 		/*
390*8d741a5dSApple OSS Distributions 		 * If the test succeeds, we will call dispatch_source_cancel twice, which is fine since
391*8d741a5dSApple OSS Distributions 		 * the operation is idempotent. Just make sure to not drop all references to the dispatch sources
392*8d741a5dSApple OSS Distributions 		 * (in this case we're not, we have globals holding references to them), or we can end up with
393*8d741a5dSApple OSS Distributions 		 * use-after-frees which would be a problem.
394*8d741a5dSApple OSS Distributions 		 */
395*8d741a5dSApple OSS Distributions 		/* Disable the timer that queries and prints zone info periodically */
396*8d741a5dSApple OSS Distributions 		if (ds_timer != NULL) {
397*8d741a5dSApple OSS Distributions 		        dispatch_source_cancel(ds_timer);
398*8d741a5dSApple OSS Distributions 		}
399*8d741a5dSApple OSS Distributions 
400*8d741a5dSApple OSS Distributions 		/* Disable signal handler that spawns child processes */
401*8d741a5dSApple OSS Distributions 		if (ds_signal != NULL) {
402*8d741a5dSApple OSS Distributions 		        dispatch_source_cancel(ds_signal);
403*8d741a5dSApple OSS Distributions 		}
404*8d741a5dSApple OSS Distributions 	});
405*8d741a5dSApple OSS Distributions 
406*8d741a5dSApple OSS Distributions 	pthread_mutex_lock(&test_mtx);
407*8d741a5dSApple OSS Distributions 	T_LOG("Number of processes spawned: %d", num_children);
408*8d741a5dSApple OSS Distributions 	T_LOG("Killing child processes...");
409*8d741a5dSApple OSS Distributions 
410*8d741a5dSApple OSS Distributions 	/* Kill all the child processes that were spawned */
411*8d741a5dSApple OSS Distributions 	for (i = 0; i < num_children; i++) {
412*8d741a5dSApple OSS Distributions 		pid_t pid = child_pids[i];
413*8d741a5dSApple OSS Distributions 		int status = 0;
414*8d741a5dSApple OSS Distributions 
415*8d741a5dSApple OSS Distributions 		/*
416*8d741a5dSApple OSS Distributions 		 * Kill and wait for each child to exit
417*8d741a5dSApple OSS Distributions 		 * Without this we were seeing hw_lock_bit timeouts in BATS.
418*8d741a5dSApple OSS Distributions 		 */
419*8d741a5dSApple OSS Distributions 		kill(pid, SIGKILL);
420*8d741a5dSApple OSS Distributions 		pthread_mutex_unlock(&test_mtx);
421*8d741a5dSApple OSS Distributions 		if (waitpid(pid, &status, 0) < 0) {
422*8d741a5dSApple OSS Distributions 			T_LOG("waitpid returned status %d", status);
423*8d741a5dSApple OSS Distributions 		}
424*8d741a5dSApple OSS Distributions 		pthread_mutex_lock(&test_mtx);
425*8d741a5dSApple OSS Distributions 	}
426*8d741a5dSApple OSS Distributions 	usleep(500 * 1000);
427*8d741a5dSApple OSS Distributions 
428*8d741a5dSApple OSS Distributions 	/* Force zone_gc before starting test for another zone or exiting */
429*8d741a5dSApple OSS Distributions 	mach_zone_force_gc(mach_host_self());
430*8d741a5dSApple OSS Distributions 
431*8d741a5dSApple OSS Distributions 	/* End ktrace session */
432*8d741a5dSApple OSS Distributions 	if (session != NULL) {
433*8d741a5dSApple OSS Distributions 		ktrace_end(session, 1);
434*8d741a5dSApple OSS Distributions 	}
435*8d741a5dSApple OSS Distributions 
436*8d741a5dSApple OSS Distributions 	if (current_test.num_zones > 0) {
437*8d741a5dSApple OSS Distributions 		T_LOG("Relevant zone info at the end of the test:");
438*8d741a5dSApple OSS Distributions 		for (i = 0; i < current_test.num_zones; i++) {
439*8d741a5dSApple OSS Distributions 			print_zone_info(&(current_test.zone_names[i]), &(zone_info_array[i]));
440*8d741a5dSApple OSS Distributions 		}
441*8d741a5dSApple OSS Distributions 	}
442*8d741a5dSApple OSS Distributions }
443*8d741a5dSApple OSS Distributions 
444*8d741a5dSApple OSS Distributions static void
setup_ktrace_session(void)445*8d741a5dSApple OSS Distributions setup_ktrace_session(void)
446*8d741a5dSApple OSS Distributions {
447*8d741a5dSApple OSS Distributions 	int ret = 0;
448*8d741a5dSApple OSS Distributions 
449*8d741a5dSApple OSS Distributions 	T_LOG("Setting up ktrace session...");
450*8d741a5dSApple OSS Distributions 	session = ktrace_session_create();
451*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(session, "ktrace_session_create");
452*8d741a5dSApple OSS Distributions 
453*8d741a5dSApple OSS Distributions 	ktrace_set_interactive(session);
454*8d741a5dSApple OSS Distributions 
455*8d741a5dSApple OSS Distributions 	ktrace_set_dropped_events_handler(session, ^{
456*8d741a5dSApple OSS Distributions 		T_FAIL("Dropped ktrace events; might have missed an expected jetsam event. Terminating early.");
457*8d741a5dSApple OSS Distributions 	});
458*8d741a5dSApple OSS Distributions 
459*8d741a5dSApple OSS Distributions 	ktrace_set_completion_handler(session, ^{
460*8d741a5dSApple OSS Distributions 		ktrace_session_destroy(session);
461*8d741a5dSApple OSS Distributions 		T_END;
462*8d741a5dSApple OSS Distributions 	});
463*8d741a5dSApple OSS Distributions 
464*8d741a5dSApple OSS Distributions 	/* Listen for memorystatus_do_kill trace events */
465*8d741a5dSApple OSS Distributions 	ret = ktrace_events_single(session, (BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_DO_KILL)), ^(ktrace_event_t event) {
466*8d741a5dSApple OSS Distributions 		int i;
467*8d741a5dSApple OSS Distributions 		bool received_jetsam_event = false;
468*8d741a5dSApple OSS Distributions 
469*8d741a5dSApple OSS Distributions 		/*
470*8d741a5dSApple OSS Distributions 		 * libktrace does not support DBG_FUNC_START/END in the event filter. It simply ignores it.
471*8d741a5dSApple OSS Distributions 		 * So we need to explicitly check for the end event (a successful jetsam kill) here,
472*8d741a5dSApple OSS Distributions 		 * instead of passing in ((BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_DO_KILL)) | DBG_FUNC_START).
473*8d741a5dSApple OSS Distributions 		 */
474*8d741a5dSApple OSS Distributions 		if (!(event->debugid & DBG_FUNC_START)) {
475*8d741a5dSApple OSS Distributions 		        return;
476*8d741a5dSApple OSS Distributions 		}
477*8d741a5dSApple OSS Distributions 
478*8d741a5dSApple OSS Distributions 		/* Check for zone-map-exhaustion jetsam. */
479*8d741a5dSApple OSS Distributions 		if (event->arg2 == kMemorystatusKilledZoneMapExhaustion) {
480*8d741a5dSApple OSS Distributions 		        begin_test_teardown();
481*8d741a5dSApple OSS Distributions 		        T_LOG("[memorystatus_do_kill] jetsam reason: zone-map-exhaustion, pid: %d\n\n", (int)event->arg1);
482*8d741a5dSApple OSS Distributions 		        if (current_test.test_index == VME_ZONE_TEST || current_test.test_index == VM_OBJECTS_ZONE_TEST) {
483*8d741a5dSApple OSS Distributions 		                /*
484*8d741a5dSApple OSS Distributions 		                 * For the VM map entries zone we try to kill the leaking process.
485*8d741a5dSApple OSS Distributions 		                 * Verify that we jetsammed one of the processes we spawned.
486*8d741a5dSApple OSS Distributions 		                 *
487*8d741a5dSApple OSS Distributions 		                 * For the vm objects zone we pick the leaking process via the VM map entries
488*8d741a5dSApple OSS Distributions 		                 * zone, if the number of vm objects and VM map entries are comparable.
489*8d741a5dSApple OSS Distributions 		                 * The test simulates this scenario, we should see a targeted jetsam for the
490*8d741a5dSApple OSS Distributions 		                 * vm objects zone too.
491*8d741a5dSApple OSS Distributions 		                 */
492*8d741a5dSApple OSS Distributions 		                pthread_mutex_lock(&test_mtx);
493*8d741a5dSApple OSS Distributions 		                for (i = 0; i < num_children; i++) {
494*8d741a5dSApple OSS Distributions 		                        if (child_pids[i] == (pid_t)event->arg1) {
495*8d741a5dSApple OSS Distributions 		                                received_jetsam_event = true;
496*8d741a5dSApple OSS Distributions 		                                T_LOG("Received jetsam event for a child");
497*8d741a5dSApple OSS Distributions 		                                break;
498*8d741a5dSApple OSS Distributions 					}
499*8d741a5dSApple OSS Distributions 				}
500*8d741a5dSApple OSS Distributions 		                pthread_mutex_unlock(&test_mtx);
501*8d741a5dSApple OSS Distributions 		                /*
502*8d741a5dSApple OSS Distributions 		                 * If we didn't see a targeted jetsam, verify that the largest zone actually
503*8d741a5dSApple OSS Distributions 		                 * fulfilled the criteria for generic jetsams.
504*8d741a5dSApple OSS Distributions 		                 */
505*8d741a5dSApple OSS Distributions 		                if (!received_jetsam_event && verify_generic_jetsam_criteria()) {
506*8d741a5dSApple OSS Distributions 		                        received_jetsam_event = true;
507*8d741a5dSApple OSS Distributions 		                        T_LOG("Did not receive jetsam event for a child, but generic jetsam criteria holds");
508*8d741a5dSApple OSS Distributions 				}
509*8d741a5dSApple OSS Distributions 			} else {
510*8d741a5dSApple OSS Distributions 		                received_jetsam_event = true;
511*8d741a5dSApple OSS Distributions 		                T_LOG("Received generic jetsam event");
512*8d741a5dSApple OSS Distributions 			}
513*8d741a5dSApple OSS Distributions 
514*8d741a5dSApple OSS Distributions 		        T_QUIET; T_ASSERT_TRUE(received_jetsam_event, "Jetsam event not as expected");
515*8d741a5dSApple OSS Distributions 		} else {
516*8d741a5dSApple OSS Distributions 		        /*
517*8d741a5dSApple OSS Distributions 		         * The test relies on the children being able to send a signal to the parent, to continue spawning new processes
518*8d741a5dSApple OSS Distributions 		         * that leak more zone memory. If a child is jetsammed for some other reason, the parent can get stuck waiting for
519*8d741a5dSApple OSS Distributions 		         * a signal from the child, never being able to make progress (We spawn only a single process at a time to rate-limit
520*8d741a5dSApple OSS Distributions 		         * the zone memory bloat.). If this happens, the test eventually times out. So if a child is jetsammed for some
521*8d741a5dSApple OSS Distributions 		         * reason other than zone-map-exhaustion, end the test early.
522*8d741a5dSApple OSS Distributions 		         *
523*8d741a5dSApple OSS Distributions 		         * This typically happens when we end up triggering vm-pageshortage jetsams before zone-map-exhaustion jetsams.
524*8d741a5dSApple OSS Distributions 		         * Lowering the zone_map_jetsam_limit if the zone map size was initially low should help with this too.
525*8d741a5dSApple OSS Distributions 		         * See sysctlbyname("kern.zone_map_jetsam_limit"...) in run_test() below.
526*8d741a5dSApple OSS Distributions 		         */
527*8d741a5dSApple OSS Distributions 		        pthread_mutex_lock(&test_mtx);
528*8d741a5dSApple OSS Distributions 		        for (i = 0; i < num_children; i++) {
529*8d741a5dSApple OSS Distributions 		                if (child_pids[i] == (pid_t)event->arg1) {
530*8d741a5dSApple OSS Distributions 		                        begin_test_teardown();
531*8d741a5dSApple OSS Distributions 		                        T_PASS("Child pid %d was jetsammed due to reason %d. Terminating early.",
532*8d741a5dSApple OSS Distributions 		                        (int)event->arg1, (int)event->arg2);
533*8d741a5dSApple OSS Distributions 				}
534*8d741a5dSApple OSS Distributions 			}
535*8d741a5dSApple OSS Distributions 		        pthread_mutex_unlock(&test_mtx);
536*8d741a5dSApple OSS Distributions 		}
537*8d741a5dSApple OSS Distributions 	});
538*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "ktrace_events_single");
539*8d741a5dSApple OSS Distributions 
540*8d741a5dSApple OSS Distributions 	ret = ktrace_start(session, dispatch_get_main_queue());
541*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "ktrace_start");
542*8d741a5dSApple OSS Distributions }
543*8d741a5dSApple OSS Distributions 
544*8d741a5dSApple OSS Distributions static void
query_zone_map_size(uint64_t * current,uint64_t * total)545*8d741a5dSApple OSS Distributions query_zone_map_size(uint64_t *current, uint64_t *total)
546*8d741a5dSApple OSS Distributions {
547*8d741a5dSApple OSS Distributions 	int ret;
548*8d741a5dSApple OSS Distributions 	uint64_t zstats[2];
549*8d741a5dSApple OSS Distributions 	size_t zstats_size = sizeof(zstats);
550*8d741a5dSApple OSS Distributions 
551*8d741a5dSApple OSS Distributions 	ret = sysctlbyname("kern.zone_map_size_and_capacity", &zstats, &zstats_size, NULL, 0);
552*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.zone_map_size_and_capacity failed");
553*8d741a5dSApple OSS Distributions 
554*8d741a5dSApple OSS Distributions 	T_LOG("Zone map capacity: %-30lldZone map size: %lld [%lld%% full]", zstats[1], zstats[0], (zstats[0] * 100) / zstats[1]);
555*8d741a5dSApple OSS Distributions 
556*8d741a5dSApple OSS Distributions #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
557*8d741a5dSApple OSS Distributions 	int memstat_level;
558*8d741a5dSApple OSS Distributions 	size_t memstat_level_size = sizeof(memstat_level);
559*8d741a5dSApple OSS Distributions 	ret = sysctlbyname("kern.memorystatus_level", &memstat_level, &memstat_level_size, NULL, 0);
560*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.memorystatus_level failed");
561*8d741a5dSApple OSS Distributions 
562*8d741a5dSApple OSS Distributions 	T_LOG("kern.memorystatus_level = %d%%", memstat_level);
563*8d741a5dSApple OSS Distributions #endif
564*8d741a5dSApple OSS Distributions 	if (current) {
565*8d741a5dSApple OSS Distributions 		*current = zstats[0];
566*8d741a5dSApple OSS Distributions 	}
567*8d741a5dSApple OSS Distributions 	if (total) {
568*8d741a5dSApple OSS Distributions 		*total = zstats[1];
569*8d741a5dSApple OSS Distributions 	}
570*8d741a5dSApple OSS Distributions }
571*8d741a5dSApple OSS Distributions 
572*8d741a5dSApple OSS Distributions static void
spawn_child_process(void)573*8d741a5dSApple OSS Distributions spawn_child_process(void)
574*8d741a5dSApple OSS Distributions {
575*8d741a5dSApple OSS Distributions 	pid_t pid = -1;
576*8d741a5dSApple OSS Distributions 	char helper_func[50];
577*8d741a5dSApple OSS Distributions 	char *launch_tool_args[4];
578*8d741a5dSApple OSS Distributions 
579*8d741a5dSApple OSS Distributions 	pthread_mutex_lock(&test_mtx);
580*8d741a5dSApple OSS Distributions 	if (!test_ending) {
581*8d741a5dSApple OSS Distributions 		if (num_children == MAX_CHILD_PROCS) {
582*8d741a5dSApple OSS Distributions 			pthread_mutex_unlock(&test_mtx);
583*8d741a5dSApple OSS Distributions 			T_ASSERT_FAIL("Spawned too many children. Aborting test");
584*8d741a5dSApple OSS Distributions 			/* not reached */
585*8d741a5dSApple OSS Distributions 		}
586*8d741a5dSApple OSS Distributions 
587*8d741a5dSApple OSS Distributions 		strlcpy(helper_func, current_test.helper_func, sizeof(helper_func));
588*8d741a5dSApple OSS Distributions 		launch_tool_args[0] = testpath;
589*8d741a5dSApple OSS Distributions 		launch_tool_args[1] = "-n";
590*8d741a5dSApple OSS Distributions 		launch_tool_args[2] = helper_func;
591*8d741a5dSApple OSS Distributions 		launch_tool_args[3] = NULL;
592*8d741a5dSApple OSS Distributions 
593*8d741a5dSApple OSS Distributions 		/* Spawn the child process */
594*8d741a5dSApple OSS Distributions 		int rc = dt_launch_tool(&pid, launch_tool_args, false, NULL, NULL);
595*8d741a5dSApple OSS Distributions 		if (rc != 0) {
596*8d741a5dSApple OSS Distributions 			T_LOG("dt_launch tool returned %d with error code %d", rc, errno);
597*8d741a5dSApple OSS Distributions 		}
598*8d741a5dSApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "dt_launch_tool");
599*8d741a5dSApple OSS Distributions 
600*8d741a5dSApple OSS Distributions 		child_pids[num_children++] = pid;
601*8d741a5dSApple OSS Distributions 	}
602*8d741a5dSApple OSS Distributions 	pthread_mutex_unlock(&test_mtx);
603*8d741a5dSApple OSS Distributions }
604*8d741a5dSApple OSS Distributions 
605*8d741a5dSApple OSS Distributions static void
run_test(void)606*8d741a5dSApple OSS Distributions run_test(void)
607*8d741a5dSApple OSS Distributions {
608*8d741a5dSApple OSS Distributions 	uint64_t mem;
609*8d741a5dSApple OSS Distributions 	uint32_t testpath_buf_size, pages;
610*8d741a5dSApple OSS Distributions 	int ret, pgsz, old_limit, new_limit = 0;
611*8d741a5dSApple OSS Distributions 	size_t sysctl_size;
612*8d741a5dSApple OSS Distributions 	uint64_t zone_cur, zone_tot, zone_target;
613*8d741a5dSApple OSS Distributions 
614*8d741a5dSApple OSS Distributions 	T_ATEND(cleanup_and_end_test);
615*8d741a5dSApple OSS Distributions 	T_SETUPBEGIN;
616*8d741a5dSApple OSS Distributions 
617*8d741a5dSApple OSS Distributions 	main_start = time(NULL);
618*8d741a5dSApple OSS Distributions 
619*8d741a5dSApple OSS Distributions 	testpath_buf_size = sizeof(testpath);
620*8d741a5dSApple OSS Distributions 	ret = _NSGetExecutablePath(testpath, &testpath_buf_size);
621*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "_NSGetExecutablePath");
622*8d741a5dSApple OSS Distributions 	T_LOG("Executable path: %s", testpath);
623*8d741a5dSApple OSS Distributions 
624*8d741a5dSApple OSS Distributions 	sysctl_size = sizeof(mem);
625*8d741a5dSApple OSS Distributions 	ret = sysctlbyname("hw.memsize", &mem, &sysctl_size, NULL, 0);
626*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl hw.memsize failed");
627*8d741a5dSApple OSS Distributions 	T_LOG("hw.memsize: %llu", mem);
628*8d741a5dSApple OSS Distributions 
629*8d741a5dSApple OSS Distributions 	sysctl_size = sizeof(pgsz);
630*8d741a5dSApple OSS Distributions 	ret = sysctlbyname("vm.pagesize", &pgsz, &sysctl_size, NULL, 0);
631*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl vm.pagesize failed");
632*8d741a5dSApple OSS Distributions 	T_LOG("vm.pagesize: %d", pgsz);
633*8d741a5dSApple OSS Distributions 
634*8d741a5dSApple OSS Distributions 	sysctl_size = sizeof(pages);
635*8d741a5dSApple OSS Distributions 	ret = sysctlbyname("vm.pages", &pages, &sysctl_size, NULL, 0);
636*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl vm.pages failed");
637*8d741a5dSApple OSS Distributions 	T_LOG("vm.pages: %d", pages);
638*8d741a5dSApple OSS Distributions 
639*8d741a5dSApple OSS Distributions 	sysctl_size = sizeof(old_limit);
640*8d741a5dSApple OSS Distributions 	ret = sysctlbyname("kern.zone_map_jetsam_limit", &old_limit, &sysctl_size, NULL, 0);
641*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.zone_map_jetsam_limit failed");
642*8d741a5dSApple OSS Distributions 	T_LOG("kern.zone_map_jetsam_limit: %d", old_limit);
643*8d741a5dSApple OSS Distributions 
644*8d741a5dSApple OSS Distributions 	/*
645*8d741a5dSApple OSS Distributions 	 * In order to start jetsamming "quickly",
646*8d741a5dSApple OSS Distributions 	 * set up the limit to be about 2x of what the current usage is.
647*8d741a5dSApple OSS Distributions 	 */
648*8d741a5dSApple OSS Distributions 	query_zone_map_size(&zone_cur, &zone_tot);
649*8d741a5dSApple OSS Distributions 	zone_target = zone_cur * 2;
650*8d741a5dSApple OSS Distributions 
651*8d741a5dSApple OSS Distributions 	new_limit = (int)howmany(zone_target * 100, zone_tot);
652*8d741a5dSApple OSS Distributions 
653*8d741a5dSApple OSS Distributions 	if (new_limit < old_limit) {
654*8d741a5dSApple OSS Distributions 		/*
655*8d741a5dSApple OSS Distributions 		 * We should be fine messing with the zone_map_jetsam_limit here, i.e. outside of T_META_SYSCTL_INT.
656*8d741a5dSApple OSS Distributions 		 * When the test ends, T_META_SYSCTL_INT will restore the zone_map_jetsam_limit to what it was
657*8d741a5dSApple OSS Distributions 		 * before the test anyway.
658*8d741a5dSApple OSS Distributions 		 */
659*8d741a5dSApple OSS Distributions 		ret = sysctlbyname("kern.zone_map_jetsam_limit", NULL, NULL, &new_limit, sizeof(new_limit));
660*8d741a5dSApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.zone_map_jetsam_limit failed");
661*8d741a5dSApple OSS Distributions 		T_LOG("kern.zone_map_jetsam_limit set to %d%%", new_limit);
662*8d741a5dSApple OSS Distributions 	}
663*8d741a5dSApple OSS Distributions 
664*8d741a5dSApple OSS Distributions 	zone_info_array = (mach_zone_info_array_t) calloc((unsigned long)current_test.num_zones, sizeof *zone_info_array);
665*8d741a5dSApple OSS Distributions 
666*8d741a5dSApple OSS Distributions 	/*
667*8d741a5dSApple OSS Distributions 	 * If the timeout specified by T_META_TIMEOUT is hit, the atend handler does not get called.
668*8d741a5dSApple OSS Distributions 	 * So we're queueing a dispatch block to fire after TIMEOUT_SECS seconds, so we can exit cleanly.
669*8d741a5dSApple OSS Distributions 	 */
670*8d741a5dSApple OSS Distributions 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, TIMEOUT_SECS * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
671*8d741a5dSApple OSS Distributions 		T_ASSERT_FAIL("Timed out after %d seconds", TIMEOUT_SECS);
672*8d741a5dSApple OSS Distributions 	});
673*8d741a5dSApple OSS Distributions 
674*8d741a5dSApple OSS Distributions 	/*
675*8d741a5dSApple OSS Distributions 	 * Create a dispatch source for the signal SIGUSR1. When a child is done allocating zone memory, it
676*8d741a5dSApple OSS Distributions 	 * sends SIGUSR1 to the parent. Only then does the parent spawn another child. This prevents us from
677*8d741a5dSApple OSS Distributions 	 * spawning many children at once and creating a lot of memory pressure.
678*8d741a5dSApple OSS Distributions 	 */
679*8d741a5dSApple OSS Distributions 	signal(SIGUSR1, SIG_IGN);
680*8d741a5dSApple OSS Distributions 	dq_spawn = dispatch_queue_create("spawn_queue", DISPATCH_QUEUE_SERIAL);
681*8d741a5dSApple OSS Distributions 	ds_signal = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dq_spawn);
682*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(ds_signal, "dispatch_source_create: signal");
683*8d741a5dSApple OSS Distributions 
684*8d741a5dSApple OSS Distributions 	dispatch_source_set_event_handler(ds_signal, ^{
685*8d741a5dSApple OSS Distributions 		uint64_t cur, tot;
686*8d741a5dSApple OSS Distributions 
687*8d741a5dSApple OSS Distributions 		query_zone_map_size(&cur, &tot);
688*8d741a5dSApple OSS Distributions 
689*8d741a5dSApple OSS Distributions 		if (cur + cur / 20 >= zone_target) {
690*8d741a5dSApple OSS Distributions 		        /*
691*8d741a5dSApple OSS Distributions 		         * Slow down allocation pace when nearing target.
692*8d741a5dSApple OSS Distributions 		         */
693*8d741a5dSApple OSS Distributions 		        sleep(1);
694*8d741a5dSApple OSS Distributions 		}
695*8d741a5dSApple OSS Distributions 		spawn_child_process();
696*8d741a5dSApple OSS Distributions 	});
697*8d741a5dSApple OSS Distributions 	dispatch_activate(ds_signal);
698*8d741a5dSApple OSS Distributions 
699*8d741a5dSApple OSS Distributions 	/* Timer to query jetsam-relevant zone info every second. Print it every 5 seconds. */
700*8d741a5dSApple OSS Distributions 	ds_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_queue_create("timer_queue", NULL));
701*8d741a5dSApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(ds_timer, "dispatch_source_create: timer");
702*8d741a5dSApple OSS Distributions 	dispatch_source_set_timer(ds_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), NSEC_PER_SEC, 0);
703*8d741a5dSApple OSS Distributions 
704*8d741a5dSApple OSS Distributions 	dispatch_source_set_event_handler(ds_timer, ^{
705*8d741a5dSApple OSS Distributions 		query_zone_info();
706*8d741a5dSApple OSS Distributions 	});
707*8d741a5dSApple OSS Distributions 	dispatch_activate(ds_timer);
708*8d741a5dSApple OSS Distributions 
709*8d741a5dSApple OSS Distributions 	/* Set up a ktrace session to listen for jetsam events */
710*8d741a5dSApple OSS Distributions 	setup_ktrace_session();
711*8d741a5dSApple OSS Distributions 
712*8d741a5dSApple OSS Distributions 	T_SETUPEND;
713*8d741a5dSApple OSS Distributions 
714*8d741a5dSApple OSS Distributions 	/* Spawn the first child process */
715*8d741a5dSApple OSS Distributions 	T_LOG("Spawning child processes to allocate zone memory...\n\n");
716*8d741a5dSApple OSS Distributions 	spawn_child_process();
717*8d741a5dSApple OSS Distributions 
718*8d741a5dSApple OSS Distributions 	dispatch_main();
719*8d741a5dSApple OSS Distributions }
720*8d741a5dSApple OSS Distributions 
721*8d741a5dSApple OSS Distributions static void
move_to_idle_band(void)722*8d741a5dSApple OSS Distributions move_to_idle_band(void)
723*8d741a5dSApple OSS Distributions {
724*8d741a5dSApple OSS Distributions 	memorystatus_priority_properties_t props;
725*8d741a5dSApple OSS Distributions 
726*8d741a5dSApple OSS Distributions 	/*
727*8d741a5dSApple OSS Distributions 	 * We want to move the processes we spawn into the idle band, so that jetsam can target them first.
728*8d741a5dSApple OSS Distributions 	 * This prevents other important BATS tasks from getting killed, specially in LTE where we have very few
729*8d741a5dSApple OSS Distributions 	 * processes running.
730*8d741a5dSApple OSS Distributions 	 *
731*8d741a5dSApple OSS Distributions 	 * This is only needed for tests which (are likely to) lead us down the generic jetsam path.
732*8d741a5dSApple OSS Distributions 	 */
733*8d741a5dSApple OSS Distributions 	props.priority = JETSAM_PRIORITY_IDLE;
734*8d741a5dSApple OSS Distributions 	props.user_data = 0;
735*8d741a5dSApple OSS Distributions 
736*8d741a5dSApple OSS Distributions 	if (memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES, getpid(), 0, &props, sizeof(props))) {
737*8d741a5dSApple OSS Distributions 		printf("memorystatus call to change jetsam priority failed\n");
738*8d741a5dSApple OSS Distributions 		exit(-1);
739*8d741a5dSApple OSS Distributions 	}
740*8d741a5dSApple OSS Distributions }
741*8d741a5dSApple OSS Distributions 
742*8d741a5dSApple OSS Distributions T_HELPER_DECL(allocate_vm_regions, "allocates VM regions")
743*8d741a5dSApple OSS Distributions {
744*8d741a5dSApple OSS Distributions 	move_to_idle_band();
745*8d741a5dSApple OSS Distributions 	allocate_vm_stuff(REGIONS);
746*8d741a5dSApple OSS Distributions }
747*8d741a5dSApple OSS Distributions 
748*8d741a5dSApple OSS Distributions T_HELPER_DECL(allocate_vm_objects, "allocates VM objects and VM regions")
749*8d741a5dSApple OSS Distributions {
750*8d741a5dSApple OSS Distributions 	move_to_idle_band();
751*8d741a5dSApple OSS Distributions 	allocate_vm_stuff(OBJECTS);
752*8d741a5dSApple OSS Distributions }
753*8d741a5dSApple OSS Distributions 
754*8d741a5dSApple OSS Distributions T_HELPER_DECL(allocate_from_generic_zone, "allocates from a generic zone")
755*8d741a5dSApple OSS Distributions {
756*8d741a5dSApple OSS Distributions 	move_to_idle_band();
757*8d741a5dSApple OSS Distributions 	allocate_from_generic_zone();
758*8d741a5dSApple OSS Distributions }
759*8d741a5dSApple OSS Distributions 
760*8d741a5dSApple OSS Distributions /*
761*8d741a5dSApple OSS Distributions  * T_META_SYSCTL_INT(ZONEMAP_JETSAM_LIMIT_SYSCTL) changes the zone_map_jetsam_limit to a
762*8d741a5dSApple OSS Distributions  * lower value, so that the test can complete faster.
763*8d741a5dSApple OSS Distributions  * The test allocates zone memory pretty aggressively which can cause the system to panic
764*8d741a5dSApple OSS Distributions  * if the jetsam limit is quite high; a lower value keeps us from panicking.
765*8d741a5dSApple OSS Distributions  */
766*8d741a5dSApple OSS Distributions T_DECL( memorystatus_vme_zone_test,
767*8d741a5dSApple OSS Distributions     "allocates elements from the VM map entries zone, verifies zone-map-exhaustion jetsams",
768*8d741a5dSApple OSS Distributions     T_META_ASROOT(true),
769*8d741a5dSApple OSS Distributions     T_META_TIMEOUT(1800),
770*8d741a5dSApple OSS Distributions /*		T_META_LTEPHASE(LTE_POSTINIT),
771*8d741a5dSApple OSS Distributions  */
772*8d741a5dSApple OSS Distributions     T_META_REQUIRES_SYSCTL_NE("kern.kasan.available", 1),
773*8d741a5dSApple OSS Distributions     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
774*8d741a5dSApple OSS Distributions     T_META_SYSCTL_INT(ZONEMAP_JETSAM_LIMIT_SYSCTL),
775*8d741a5dSApple OSS Distributions     T_META_TAG_VM_PREFERRED)
776*8d741a5dSApple OSS Distributions {
777*8d741a5dSApple OSS Distributions 	current_test = (test_config_struct) {
778*8d741a5dSApple OSS Distributions 		.test_index = VME_ZONE_TEST,
779*8d741a5dSApple OSS Distributions 		.helper_func = VME_ZONE_TEST_OPT,
780*8d741a5dSApple OSS Distributions 		.num_zones = 1,
781*8d741a5dSApple OSS Distributions 		.zone_names = (mach_zone_name_t[]){
782*8d741a5dSApple OSS Distributions 			{ .mzn_name = VME_ZONE }
783*8d741a5dSApple OSS Distributions 		}
784*8d741a5dSApple OSS Distributions 	};
785*8d741a5dSApple OSS Distributions 	run_test();
786*8d741a5dSApple OSS Distributions }
787*8d741a5dSApple OSS Distributions 
788*8d741a5dSApple OSS Distributions T_DECL( memorystatus_vm_objects_zone_test,
789*8d741a5dSApple OSS Distributions     "allocates elements from the VM objects and the VM map entries zones, verifies zone-map-exhaustion jetsams",
790*8d741a5dSApple OSS Distributions     T_META_ASROOT(true),
791*8d741a5dSApple OSS Distributions     T_META_TIMEOUT(1800),
792*8d741a5dSApple OSS Distributions /*		T_META_LTEPHASE(LTE_POSTINIT),
793*8d741a5dSApple OSS Distributions  */
794*8d741a5dSApple OSS Distributions     T_META_REQUIRES_SYSCTL_NE("kern.kasan.available", 1),
795*8d741a5dSApple OSS Distributions     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
796*8d741a5dSApple OSS Distributions     T_META_SYSCTL_INT(ZONEMAP_JETSAM_LIMIT_SYSCTL),
797*8d741a5dSApple OSS Distributions     T_META_TAG_VM_PREFERRED)
798*8d741a5dSApple OSS Distributions {
799*8d741a5dSApple OSS Distributions 	current_test = (test_config_struct) {
800*8d741a5dSApple OSS Distributions 		.test_index = VM_OBJECTS_ZONE_TEST,
801*8d741a5dSApple OSS Distributions 		.helper_func = VM_OBJECTS_ZONE_TEST_OPT,
802*8d741a5dSApple OSS Distributions 		.num_zones = 2,
803*8d741a5dSApple OSS Distributions 		.zone_names = (mach_zone_name_t[]){
804*8d741a5dSApple OSS Distributions 			{ .mzn_name = VME_ZONE },
805*8d741a5dSApple OSS Distributions 			{ .mzn_name = VMOBJECTS_ZONE}
806*8d741a5dSApple OSS Distributions 		}
807*8d741a5dSApple OSS Distributions 	};
808*8d741a5dSApple OSS Distributions 	run_test();
809*8d741a5dSApple OSS Distributions }
810*8d741a5dSApple OSS Distributions 
811*8d741a5dSApple OSS Distributions T_DECL( memorystatus_generic_zone_test,
812*8d741a5dSApple OSS Distributions     "allocates elements from a zone that doesn't have an optimized jetsam path, verifies zone-map-exhaustion jetsams",
813*8d741a5dSApple OSS Distributions     T_META_ASROOT(true),
814*8d741a5dSApple OSS Distributions     T_META_TIMEOUT(1800),
815*8d741a5dSApple OSS Distributions /*		T_META_LTEPHASE(LTE_POSTINIT),
816*8d741a5dSApple OSS Distributions  */
817*8d741a5dSApple OSS Distributions     T_META_REQUIRES_SYSCTL_NE("kern.kasan.available", 1),
818*8d741a5dSApple OSS Distributions     T_META_REQUIRES_SYSCTL_EQ("kern.development", 1),
819*8d741a5dSApple OSS Distributions     T_META_SYSCTL_INT(ZONEMAP_JETSAM_LIMIT_SYSCTL),
820*8d741a5dSApple OSS Distributions     T_META_TAG_VM_PREFERRED)
821*8d741a5dSApple OSS Distributions {
822*8d741a5dSApple OSS Distributions 	current_test = (test_config_struct) {
823*8d741a5dSApple OSS Distributions 		.test_index = GENERIC_ZONE_TEST,
824*8d741a5dSApple OSS Distributions 		.helper_func = GENERIC_ZONE_TEST_OPT,
825*8d741a5dSApple OSS Distributions 		.num_zones = 0,
826*8d741a5dSApple OSS Distributions 		.zone_names = NULL
827*8d741a5dSApple OSS Distributions 	};
828*8d741a5dSApple OSS Distributions 	run_test();
829*8d741a5dSApple OSS Distributions }
830