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