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