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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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 (¤t_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(¤t_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(¤t_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(¤t_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