]> git.cameronkatri.com Git - apple_cmds.git/blob - system_cmds/memory_pressure.tproj/memory_pressure.c
shell_cmds: Fix compilation for lower targets
[apple_cmds.git] / system_cmds / memory_pressure.tproj / memory_pressure.c
1 /*
2 * Copyright (c) 2013-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <getopt.h>
28 #include <string.h>
29 #include <mach/i386/vm_param.h>
30 #include <sys/kern_memorystatus.h>
31 #include <sys/sysctl.h>
32 #include <mach/mach.h>
33 #include <mach/task.h>
34 #include <mach/thread_act.h>
35 #include <mach/thread_policy.h>
36 #include <sys/mman.h>
37 #include <pthread.h>
38 #include <assert.h>
39 #include <dispatch/private.h>
40
41 long long phys_mem = 0; /* amount of physical memory in bytes */
42 unsigned int phys_pages = 0; /* number of physical memory pages */
43 int sleep_seconds = 1;
44 int requested_hysteresis_seconds = 0;
45 boolean_t quiet_mode_on = FALSE;
46 boolean_t simulate_mode_on = FALSE;
47
48 void *range_start_addr = NULL;
49 void *range_end_addr = NULL;
50 void *range_current_addr = NULL;
51
52 int start_referencing_pages = 0;
53 int start_allocing_pages = 0;
54 pthread_cond_t reference_pages_condvar = PTHREAD_COND_INITIALIZER;
55 pthread_mutex_t reference_pages_mutex = PTHREAD_MUTEX_INITIALIZER;
56 unsigned int desired_level = 0, desired_percent = 0;
57 unsigned int percent_for_level = 0;
58 int tool_mode = 0;
59
60 #define TOOL_MODE_FOR_PERCENT 1
61 #define TOOL_MODE_FOR_LEVEL 2
62
63
64 char random_data[] = "ffd8ffe000104a46494600010101002400240000ffe100744578696600004d4d002a000000080004011a0005000000010000003e011b0005000000010000004601280003000000010002000087690004000000010000004e00000000000000240000000100000024000000010002a002000400000001000003c0a003000400000001000001ff00000000ffdb00430002020202020102020202020202030306040303030307050504060807080808070808090a0d0b09090c0a08080b0f0b0c0d0e0e0e0e090b10110f0e110d0e0e0effdb004301020202030303060404060e0908090e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0effc000110801ff03c003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00f9e74fbd37baa2db99e6506391f28371f9519ba67fd9fcabd46cbc1315de8d6776752d7419e049084b152a37283c1dfc8e6bc02db4af18d9df79c9e1bd59a40ae9b65b1761f32953c63ae09c7a1c57656fe24f8896da7c16c9e0bb3748a358d5a4d04b31006324f73c75a00935f7fec9f165ee98b7372e2ddc05795763f2a0f20138ebeb590bac3e70d2b6e1fed1ac6d4ecbc65aa6b973a85c7867528a6998168edec1a38c1c01c2f61c550fec1f16ff00d0bdade4f5ff00447ff0a00eaffb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe66b4f45fed1f1078bb4cd0f4bdf3ea37f7296f6d1ee3f33b1c0cfb7a9f4ae0bfb07c5bff42f6b7ff808ff00e15a9a1c5e3bf0f78c74bd774cd0f5a8751d3ee52e2ddcd9be03a9c8cfb7623d2803d5bc6fe0df11780e0d3ae354bab1bdb1bd678e1bab1b8f36312c6079b112380cac4ae3b9535e7ffdaedff3d5ff00335b3e3af177c4af1f5be9b6da8f8567d36c2c59e486d74fd35e28ccb260c92b003966605b3db71af3cfec1f16ff00d0bdadff00e023ff0085007acf83743d57c6baddd59e9d7062fb3c68d248519c0324ab120c2f3cb38c9ec013dab95b8d427b5bfb8b59a4759a195a3917278652411f98ad9f86fe30f88bf0cb57d56f348f095f5ebdfc2914ab716b200a118918c0ff0068d709a9d878cb55f12ea3aa4fe1cd6127bdba92e2455b47c06772c40e3a64d007d13f09fe1d37c41d135ad7b52d52ef4fd074eba86cff00d1537cd7171310123504e0751927d6be8ed17f65bf0aebde19b4d5ac7c5fe2236b700eddf12060558ab03ee1948fc2be41f84bf123e20fc2d1acd87fc20d77e24f0eeabb1af74cbcb4902974fbb22b0190c3fa0f4afa1748fdad3c51a1691fd9fa57c10b8b4b212bc8b0ac931542c7240c8e067271ef401e9bff000c89a07fd0dbafff00dfb4a3fe191340ff00a1b75fff00bf695c2ffc366f8e7fe88c5cff00df72ff00851ff0d9be39ff00a23173ff007dcbfe1401dd7fc322681ff436ebff00f7ed28ff008644d03fe86dd7ff00efda570bff000d9be39ffa23173ff7dcbfe147fc366f8e7fe88c5cff00df72ff008500775ff0c89a07fd0dbaff00fdfb4a3fe191340ffa1b75ff00fbf695c2ff00c366f8e7fe88c5cffdf72ff851ff000d9be39ffa23173ff7dcbfe1401dd7fc322681ff00436ebfff007ed2be52f8d1f0eeff00e11fc42b2d325d49f51d3afedccf637046d6215b6b2b0f5071f98af733fb6678eb071f062e73fefcbfe15f2a7c5af1cfc4bf8bff001262d7758f0bea76515bc1f67b1b1b7b490a4099c9edcb13c93f4a00fe891bc2fe1c672cda1e96589c92";
65
66 #define PAGE_OP_ALLOC 0x1
67 #define PAGE_OP_FREE 0x2
68
69 #define USE_WIRED_PAGES_FOR_PERCENT_MODE FALSE
70
71 #define MAX_RANGE_SIZE 64 * 1024 * 1024 * 1024ULL
72
73 void print_vm_statistics(void);
74 void munch_for_level(unsigned int, unsigned int);
75 void munch_for_percentage(unsigned int, unsigned int, unsigned int);
76
77 static void
78 usage(void)
79 {
80 fprintf(stderr, "Usage: memory_pressure [options] [<pages>]\n"
81 " Allocate memory and wait forever.\n"
82 " Options include:\n"
83 " -l <level> - allocate memory until a low memory notification is received (warn OR critical)\n"
84 " -p <percent-free> - allocate memory until percent free is this (or less)\n"
85 " -s <seconds> - how long to sleep between checking for a set percent level\n"
86 " -w <percent-free> - don't allocate, just wait until percent free is this then exit\n"
87 " -y <seconds> - Hysteresis Interval: how long to wait after requested percntage free is reached, before exiting program. Requires the usage of the -p option\n"
88 " -v <print VM stats> - print VM statistics every sampling interval\n"
89 " -Q <quiet mode> - reduces the tool's output\n"
90 " -S - simulate the system's memory pressure level without applying any real pressure\n"
91 " \n"
92 );
93 exit(0);
94 }
95
96 static unsigned int
97 read_sysctl_int(const char* name)
98 {
99 unsigned int var;
100 size_t var_size;
101 int error;
102
103 var_size = sizeof(var);
104 error = sysctlbyname(name, &var, &var_size, NULL, 0);
105 if( error ) {
106 perror(name);
107 exit(-1);
108 }
109 return var;
110 }
111
112 static long long
113 read_sysctl_long_long(const char* name)
114 {
115 long long var;
116 size_t var_size;
117 int error;
118
119 var_size = sizeof(var);
120 error = sysctlbyname(name, &var, &var_size, NULL, 0);
121 if( error ) {
122 perror(name);
123 exit(-1);
124 }
125 return var;
126 }
127
128 static int
129 get_percent_free(unsigned int* level)
130 {
131 int error;
132
133 error = memorystatus_get_level((user_addr_t) level);
134
135 if( error ) {
136 perror("memorystatus_get_level failed:");
137 exit(-1);
138 }
139 return error;
140 }
141
142 void
143 print_vm_statistics(void)
144 {
145 unsigned int count = HOST_VM_INFO64_COUNT;
146 kern_return_t ret = 0;
147 vm_statistics64_data_t vm_stat;;
148
149 if (quiet_mode_on == TRUE) {
150 return;
151 }
152
153 if ((ret = host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vm_stat, &count) != KERN_SUCCESS)) {
154 fprintf(stderr, "Failed to get statistics. Error %d\n", ret);
155 } else {
156 printf("\nStats: \n");
157 printf("Pages free: %llu \n", (uint64_t) (vm_stat.free_count - vm_stat.speculative_count));
158 printf("Pages purgeable: %llu \n", (uint64_t) (vm_stat.purgeable_count));
159 printf("Pages purged: %llu \n",(uint64_t) (vm_stat.purges));
160
161 printf("\nSwap I/O:\n");
162 printf("Swapins: %llu \n", (uint64_t) (vm_stat.swapins));
163 printf("Swapouts: %llu \n", (uint64_t) (vm_stat.swapouts));
164
165 printf("\nPage Q counts:\n");
166 printf("Pages active: %llu \n", (uint64_t) (vm_stat.active_count));
167 printf("Pages inactive: %llu \n", (uint64_t) (vm_stat.inactive_count));
168 printf("Pages speculative: %llu \n", (uint64_t) (vm_stat.speculative_count));
169 printf("Pages throttled: %llu \n", (uint64_t) (vm_stat.throttled_count));
170 printf("Pages wired down: %llu \n", (uint64_t) (vm_stat.wire_count));
171
172 printf("\nCompressor Stats:\n");
173 printf("Pages used by compressor: %llu \n", (uint64_t) (vm_stat.compressor_page_count));
174 printf("Pages decompressed: %llu \n", (uint64_t) (vm_stat.decompressions));
175 printf("Pages compressed: %llu \n", (uint64_t) (vm_stat.compressions));
176
177 printf("\nFile I/O:\n");
178 printf("Pageins: %llu \n", (uint64_t) (vm_stat.pageins));
179 printf("Pageouts: %llu \n", (uint64_t) (vm_stat.pageouts));
180
181 #if 0
182 printf("\"Translation faults\": %llu \n", (uint64_t) (vm_stat.faults));
183 printf("Pages copy-on-write: %llu \n", (uint64_t) (vm_stat.cow_faults));
184 printf("Pages zero filled: %llu \n", (uint64_t) (vm_stat.zero_fill_count));
185 printf("Pages reactivated: %llu \n", (uint64_t) (vm_stat.reactivations));
186 #endif
187 printf("\n");
188 }
189 }
190
191 /*
192 this will work for up to 64 TB of RAM -- beyond that we exceed Intel's max for VRAM (48 bits of addressable space).
193 By the time we get there Intel probably will have increased this
194 */
195 static unsigned long long
196 get_max_range_size()
197 {
198 unsigned long long the_max_range_size = MAX_RANGE_SIZE;
199
200 if (phys_mem * 4 > the_max_range_size) {
201 the_max_range_size = phys_mem * 4;
202 }
203
204 return the_max_range_size;
205 }
206
207 static int
208 reached_or_bypassed_desired_result(void)
209 {
210 if (tool_mode == TOOL_MODE_FOR_LEVEL) {
211
212 unsigned int current_level = 0;
213
214 current_level = read_sysctl_int("kern.memorystatus_vm_pressure_level");
215
216 if (desired_level > 0 && current_level >= desired_level) {
217 return 1;
218 }
219
220 return 0;
221 }
222
223 if (tool_mode == TOOL_MODE_FOR_PERCENT) {
224
225 unsigned int current_percent = 0;
226
227 get_percent_free(&current_percent);
228
229 if (desired_percent > 0 && current_percent <= desired_percent) {
230 return 1;
231 }
232
233 return 0;
234 }
235
236 return 0;
237 }
238
239 static void
240 reference_pages(int level)
241 {
242 int error;
243 void *addr = NULL;
244 int num_pages = 0;
245
246 error = pthread_mutex_lock(&reference_pages_mutex);
247 addr = range_start_addr;
248 again:
249 while(start_referencing_pages == 0) {
250 error = pthread_cond_wait(&reference_pages_condvar, &reference_pages_mutex);
251 }
252
253 start_allocing_pages = 0;
254 pthread_mutex_unlock(&reference_pages_mutex);
255
256 num_pages = 0;
257 for(; addr < range_current_addr;) {
258
259 char p;
260
261 if (reached_or_bypassed_desired_result()) {
262 //printf("stopped referencing after %d pages\n", num_pages);
263 break;
264 }
265
266 p = *(char*) addr;
267 addr += PAGE_SIZE;
268 num_pages++;
269
270 }
271
272 //if (num_pages) {
273 // printf("Referenced %d\n", num_pages);
274 //}
275 error = pthread_mutex_lock(&reference_pages_mutex);
276 start_referencing_pages = 0;
277 start_allocing_pages = 1;
278
279 goto again;
280 }
281
282 static void
283 process_pages(int num_pages, int page_op)
284 {
285 if (num_pages > 0) {
286
287 int error = 0, i = 0;
288 size_t size = num_pages * PAGE_SIZE;
289
290 if (page_op == PAGE_OP_ALLOC) {
291
292 if (tool_mode == TOOL_MODE_FOR_PERCENT && USE_WIRED_PAGES_FOR_PERCENT_MODE) {
293 error = mlock(range_current_addr, size);
294 if (error == -1) {
295 perror("Failed to lock memory!");
296 exit(-1);
297 }
298
299 memset(range_current_addr, 0xFF, size);
300 range_current_addr += size;
301
302 } else {
303
304 pthread_mutex_lock(&reference_pages_mutex);
305 while (start_allocing_pages == 0) {
306 pthread_mutex_unlock(&reference_pages_mutex);
307 sleep(1);
308 pthread_mutex_lock(&reference_pages_mutex);
309 }
310 pthread_mutex_unlock(&reference_pages_mutex);
311
312 for (i=0; i < num_pages; i++) {
313
314 if (reached_or_bypassed_desired_result()) {
315 //printf("stopped faulting after %d pages\n", i);
316 break;
317 }
318 if ((uintptr_t)range_current_addr < get_max_range_size()) {
319 memcpy(range_current_addr, random_data, PAGE_SIZE);
320 range_current_addr += PAGE_SIZE;
321 } else {
322 printf("\nRun out of allocable memory\n");
323 exit(0);
324 }
325 }
326
327 pthread_mutex_lock(&reference_pages_mutex);
328 start_referencing_pages = 1;
329 pthread_cond_signal(&reference_pages_condvar);
330 pthread_mutex_unlock(&reference_pages_mutex);
331 }
332 } else {
333 if (tool_mode == TOOL_MODE_FOR_PERCENT && USE_WIRED_PAGES_FOR_PERCENT_MODE) {
334 error = munlock(range_current_addr, size);
335 if (error == -1) {
336 perror("Failed to unlock memory!");
337 exit(-1);
338 }
339
340 error = madvise(range_current_addr, size, MADV_FREE);
341 if (error == -1) {
342 perror("Failed to madv_free memory!");
343 exit(-1);
344 }
345
346 range_current_addr -= size;
347
348 } else {
349 pthread_mutex_lock(&reference_pages_mutex);
350 while (start_referencing_pages == 1) {
351 pthread_mutex_unlock(&reference_pages_mutex);
352 sleep(1);
353 pthread_mutex_lock(&reference_pages_mutex);
354 }
355
356 error = madvise(range_current_addr, size, MADV_FREE);
357 if (error == -1) {
358 perror("Failed to madv_free memory!");
359 exit(-1);
360 }
361 range_current_addr -= size;
362 start_referencing_pages = 1;
363 pthread_cond_signal(&reference_pages_condvar);
364 pthread_mutex_unlock(&reference_pages_mutex);
365 }
366 }
367 }
368 }
369
370 void
371 munch_for_level(unsigned int sleep_seconds, unsigned int print_vm_stats)
372 {
373
374 unsigned int current_level = 0;
375 unsigned int desired_percent = 0;
376 unsigned int current_percent = 0;
377 unsigned int page_op = PAGE_OP_ALLOC;
378 unsigned int previous_page_op = PAGE_OP_ALLOC;
379 unsigned int pages_to_process = 0;
380 unsigned int stabilized_percentage = 0;
381 boolean_t print_vm_stats_on_page_processing = FALSE;
382 boolean_t ok_to_print_stablity_message = TRUE;
383
384 current_level = read_sysctl_int("kern.memorystatus_vm_pressure_level");
385
386 if (current_level >= desired_level) {
387 return;
388 }
389
390 get_percent_free(&current_percent);
391
392 if (print_vm_stats) {
393 print_vm_stats_on_page_processing = TRUE;
394 }
395
396 page_op = PAGE_OP_ALLOC;
397 previous_page_op = 0;
398
399 while (1) {
400
401 if (current_percent > percent_for_level) {
402 desired_percent = current_percent - percent_for_level;
403 } else {
404 desired_percent = 1;
405 }
406
407 pages_to_process = (desired_percent * phys_pages) / 100;
408
409 page_op = PAGE_OP_ALLOC;
410
411 if (previous_page_op != page_op) {
412 //printf("%s %d pages.\n", (page_op == PAGE_OP_ALLOC) ? "Allocating" : "Freeing", pages_to_process);
413 printf("\nCMD: %s pages to go from level: %d to level: %d", (page_op == PAGE_OP_ALLOC) ? "Allocating" : "Freeing", current_level, desired_level);
414 previous_page_op = page_op;
415 fflush(stdout);
416 } else {
417 printf(".");
418 fflush(stdout);
419 }
420
421 if (print_vm_stats_on_page_processing == TRUE) {
422 print_vm_statistics();
423 }
424 process_pages(pages_to_process, page_op);
425 ok_to_print_stablity_message = TRUE;
426
427 current_level = read_sysctl_int("kern.memorystatus_vm_pressure_level");
428
429 if (current_level >= desired_level) {
430
431 while(1) {
432 current_level = read_sysctl_int("kern.memorystatus_vm_pressure_level");
433 if (current_level < desired_level) {
434 break;
435 }
436
437 if (current_level > desired_level) {
438 page_op = PAGE_OP_FREE;
439
440 get_percent_free(&current_percent);
441
442 if (stabilized_percentage > current_percent) {
443 pages_to_process = ((stabilized_percentage - current_percent) * phys_pages) / 100;
444
445 if (previous_page_op != page_op) {
446 printf("\nCMD: %s pages to go from %d to %d level", (page_op == PAGE_OP_ALLOC) ? "Allocating" : "Freeing", current_level, desired_level);
447 previous_page_op = page_op;
448 fflush(stdout);
449 } else {
450 printf(".");
451 fflush(stdout);
452 }
453
454 if (print_vm_stats_on_page_processing == TRUE) {
455 print_vm_statistics();
456 }
457 process_pages(pages_to_process, page_op);
458 ok_to_print_stablity_message = TRUE;
459 }
460 }
461
462 while (current_level == desired_level) {
463 get_percent_free(&current_percent);
464 if (ok_to_print_stablity_message == TRUE) {
465 print_vm_statistics();
466 printf("\nStabilizing at Percent: %d Level: %d", current_percent, current_level);
467 fflush(stdout);
468 ok_to_print_stablity_message = FALSE;
469 previous_page_op = 0;
470 } else {
471 printf(".");
472 fflush(stdout);
473 }
474
475 stabilized_percentage = current_percent;
476 sleep(sleep_seconds);
477 current_level = read_sysctl_int("kern.memorystatus_vm_pressure_level");
478 }
479 }
480 }
481
482 get_percent_free(&current_percent);
483 //printf("Percent: %d Level: %d\n", current_percent, current_level);
484 sleep(1);
485
486 if (print_vm_stats) {
487 print_vm_stats_on_page_processing = TRUE;
488 }
489
490 } /* while */
491 }
492
493 void
494 munch_for_percentage(unsigned int sleep_seconds, unsigned int wait_percent_free, unsigned int print_vm_stats)
495 {
496
497 int total_pages_allocated = 0;
498 int current_stable_timer = 0; /* in seconds */
499 unsigned int current_percent = 0;
500 boolean_t page_op = PAGE_OP_FREE;
501 unsigned int pages_to_process = 0;
502 boolean_t print_vm_stats_on_page_processing = FALSE;
503 boolean_t previous_page_op = 0;
504 boolean_t ok_to_print_stablity_message = TRUE;
505
506 /* Allocate until memory level is hit. */
507
508 get_percent_free(&current_percent);
509
510 /*
511 * "wait" mode doesn't alloc, it just waits and exits. This is used
512 * while waiting for *other* processes to allocate memory.
513 */
514 if (wait_percent_free) {
515 while (current_percent > wait_percent_free) {
516 sleep(sleep_seconds);
517 get_percent_free (&current_percent);
518 }
519 return;
520 }
521
522 page_op = PAGE_OP_ALLOC;
523 previous_page_op = 0;
524
525 while (1) {
526
527 if (current_percent > desired_percent) {
528 pages_to_process = ((current_percent - desired_percent) * phys_pages) / 100;
529 page_op = PAGE_OP_ALLOC;
530 } else {
531 pages_to_process = ((desired_percent - current_percent) * phys_pages) / 100;
532 page_op = PAGE_OP_FREE;
533 }
534
535 if (pages_to_process > 0) {
536
537 if (page_op != previous_page_op) {
538 //printf("\n%s %d pages to go from %d%% to %d%% pages free\n", (page_op == PAGE_OP_ALLOC) ? "Allocating" : "Freeing", pages_to_process, current_percent, desired_percent);
539 printf("\nCMD: %s pages to go from %d%% to %d%% percent free", (page_op == PAGE_OP_ALLOC) ? "Allocating" : "Freeing", current_percent, desired_percent);
540 fflush(stdout);
541 previous_page_op = page_op;
542 } else {
543 printf(".");
544 fflush(stdout);
545 }
546
547 if (page_op == PAGE_OP_ALLOC) {
548 total_pages_allocated += pages_to_process;
549 process_pages(pages_to_process, page_op);
550 ok_to_print_stablity_message = TRUE;
551 } else {
552
553 if (total_pages_allocated >= pages_to_process) {
554 total_pages_allocated -= pages_to_process;
555 process_pages(pages_to_process, page_op);
556 ok_to_print_stablity_message = TRUE;
557 } else {
558 get_percent_free(&current_percent);
559 if (ok_to_print_stablity_message == TRUE) {
560 printf("\nDesired Percent: %d, Current Percent: %d. No pages to free so waiting", desired_percent, current_percent);
561 fflush(stdout);
562 ok_to_print_stablity_message = FALSE;
563 }
564 }
565 }
566
567 //printf("kernel memorystatus: %d%% free, allocated %d pages total. Requested: %d\n", current_percent, total_pages_allocated, desired_percent);
568 if (print_vm_stats) {
569 print_vm_stats_on_page_processing = TRUE;
570 }
571 } else {
572 if (ok_to_print_stablity_message == TRUE) {
573 print_vm_statistics();
574 printf("\nStable at percent free: %d", current_percent);
575 fflush(stdout);
576 ok_to_print_stablity_message = FALSE;
577 } else {
578 printf(".");
579 fflush(stdout);
580 }
581
582 /* Stability has been reached; Increment current_stable_timer by sleep_seconds */
583
584 if (current_stable_timer <= requested_hysteresis_seconds){
585 current_stable_timer += sleep_seconds;
586 /* Debug only */
587 /* printf("\n Percentage Free stable for %d seconds", current_stable_timer); */
588 } else {
589 printf ("\n Maintained memory pressure to %d percent free for more than %d seconds. Stopping pressure now.", current_percent, requested_hysteresis_seconds);
590 return;
591 }
592
593 print_vm_stats_on_page_processing = FALSE;
594 }
595
596 if (print_vm_stats_on_page_processing) {
597
598 print_vm_statistics();
599
600 if (print_vm_stats_on_page_processing == TRUE) {
601 print_vm_stats_on_page_processing = FALSE;
602 }
603 }
604
605 sleep(sleep_seconds);
606
607 get_percent_free(&current_percent);
608 } /* while */
609 }
610
611 int
612 main(int argc, char * const argv[])
613 {
614 int opt;
615 unsigned int wait_percent_free = 0;
616 unsigned int current_percent = 0;
617 unsigned int print_vm_stats = 0;
618 char level[10];
619
620 while ((opt = getopt(argc, argv, "hl:p:s:w:y:vQS")) != -1) {
621 switch (opt) {
622 case 'h':
623 usage();
624 break;
625 case 'l':
626 strlcpy(level, optarg, 9);
627
628 if (strncasecmp(level, "normal", 6) == 0) {
629 desired_level = DISPATCH_MEMORYPRESSURE_NORMAL;
630 percent_for_level = 90;
631 } else if (strncasecmp(level, "warn", 4) == 0) {
632 desired_level = DISPATCH_MEMORYPRESSURE_WARN;
633 percent_for_level = 60;
634
635 } else if (strncasecmp(level, "critical", 8) == 0) {
636 desired_level = DISPATCH_MEMORYPRESSURE_CRITICAL;
637 percent_for_level = 30;
638
639 } else {
640 printf("Incorrect level. Allowed \"normal\" or \"warn\" or \"critical\". Specified: %s\n", level);
641 exit(0);
642 }
643 break;
644 case 'p':
645 desired_percent = atoi(optarg);
646 break;
647 case 's':
648 sleep_seconds = atoi(optarg);
649 break;
650 case 'w':
651 wait_percent_free = atoi(optarg);
652 break;
653 case 'y':
654 requested_hysteresis_seconds = atoi(optarg);
655 break;
656 case 'v':
657 print_vm_stats = 1;
658 break;
659 case 'Q':
660 quiet_mode_on = TRUE;
661 break;
662 case 'S':
663 simulate_mode_on = TRUE;
664 break;
665 default:
666 usage();
667 }
668 }
669
670 if (simulate_mode_on == TRUE && desired_level == 0) {
671 printf("Expected level with -l along with \"simulated\" mode.\n");
672 return 0;
673 }
674
675 if (requested_hysteresis_seconds > 0) {
676 if (desired_percent == 0) {
677 printf("Hysteresis time may only be specified in conjunction with a non-zero value for the -p option. \n");
678 usage();
679 }
680 }
681
682 phys_mem = read_sysctl_long_long("hw.memsize");
683 phys_pages = (unsigned int) (phys_mem / PAGE_SIZE);
684
685 printf("The system has %lld (%d pages with a page size of %lu).\n", phys_mem, phys_pages, PAGE_SIZE);
686
687 print_vm_statistics();
688
689 get_percent_free(&current_percent);
690 printf("System-wide memory free percentage: %d%%\n", current_percent);
691
692 if (desired_percent == 0 && wait_percent_free == 0 && desired_level == 0) {
693 return 0;
694 }
695
696 if (simulate_mode_on == TRUE) {
697
698 /*
699 We use the sysctl "kern.memorypressure_manual_trigger" for this mode. Here's a blurb:
700
701 Supported behaviors when using the manual trigger tests.
702
703 #define TEST_LOW_MEMORY_TRIGGER_ONE 1 most suitable app is notified
704 #define TEST_LOW_MEMORY_TRIGGER_ALL 2 all apps are notified
705 #define TEST_PURGEABLE_TRIGGER_ONE 3
706 #define TEST_PURGEABLE_TRIGGER_ALL 4
707 #define TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ONE 5
708 #define TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ALL 6
709
710 So, for example, to simulate one app getting a poke when the "pressure" reaches critical levels: "sudo sysctl -w kern.memorypressure_manual_trigger = level"
711 where level is calculated as: ((TEST_LOW_MEMORY_TRIGGER_ONE << 16) | NOTE_MEMORYSTATUS_PRESSURE_CRITICAL), which will be "65540".
712
713 For this tool, currently, we only support the "TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ALL" options.
714 */
715
716 #define TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ALL 6
717
718 unsigned int var = 0;
719 size_t var_size = 0;
720 int error = 0;
721
722 var_size = sizeof(var);
723
724 var = ((TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ALL << 16) | desired_level);
725
726 error = sysctlbyname("kern.memorypressure_manual_trigger", NULL, 0, &var, var_size);
727
728 if(error) {
729 perror("sysctl: kern.memorypressure_manual_trigger failed ");
730 exit(-1);
731 }
732
733 printf("Waiting %d seconds before resetting system state\n", sleep_seconds);
734
735 sleep(sleep_seconds);
736
737 var_size = sizeof(var);
738
739 var = ((TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ALL << 16) | DISPATCH_MEMORYPRESSURE_NORMAL);
740
741 error = sysctlbyname("kern.memorypressure_manual_trigger", NULL, 0, &var, var_size);
742
743 if(error) {
744 perror("sysctl: kern.memorypressure_manual_trigger failed ");
745 exit(-1);
746 }
747
748 printf("Reset system state\n");
749
750 } else {
751 range_start_addr = mmap(NULL, get_max_range_size(), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
752
753 if (range_start_addr == MAP_FAILED) {
754 perror("mmap failed");
755 } else {
756
757 int error = 0;
758 pthread_t thread = NULL;
759
760 error = pthread_create(&thread, NULL, (void*) reference_pages, NULL);
761
762 range_current_addr = range_start_addr;
763 range_end_addr = range_start_addr + get_max_range_size();
764 start_allocing_pages = 1;
765
766 if (desired_level) {
767 tool_mode = TOOL_MODE_FOR_LEVEL;
768 munch_for_level(sleep_seconds, print_vm_stats);
769 } else {
770 tool_mode = TOOL_MODE_FOR_PERCENT;
771 munch_for_percentage(sleep_seconds, wait_percent_free, print_vm_stats);
772 }
773 }
774 }
775
776 return 0;
777 }