From 5fd83771641d15c418f747bd343ba6738d3875f7 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sun, 9 May 2021 14:20:58 -0400 Subject: Import macOS userland adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106 --- .../memory_pressure.tproj/memory_pressure.c | 777 +++++++++++++++++++++ 1 file changed, 777 insertions(+) create mode 100644 system_cmds/memory_pressure.tproj/memory_pressure.c (limited to 'system_cmds/memory_pressure.tproj/memory_pressure.c') diff --git a/system_cmds/memory_pressure.tproj/memory_pressure.c b/system_cmds/memory_pressure.tproj/memory_pressure.c new file mode 100644 index 0000000..1713fcb --- /dev/null +++ b/system_cmds/memory_pressure.tproj/memory_pressure.c @@ -0,0 +1,777 @@ +/* + * Copyright (c) 2013-2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +long long phys_mem = 0; /* amount of physical memory in bytes */ +unsigned int phys_pages = 0; /* number of physical memory pages */ +int sleep_seconds = 1; +int requested_hysteresis_seconds = 0; +boolean_t quiet_mode_on = FALSE; +boolean_t simulate_mode_on = FALSE; + +void *range_start_addr = NULL; +void *range_end_addr = NULL; +void *range_current_addr = NULL; + +int start_referencing_pages = 0; +int start_allocing_pages = 0; +pthread_cond_t reference_pages_condvar = PTHREAD_COND_INITIALIZER; +pthread_mutex_t reference_pages_mutex = PTHREAD_MUTEX_INITIALIZER; +unsigned int desired_level = 0, desired_percent = 0; +unsigned int percent_for_level = 0; +int tool_mode = 0; + +#define TOOL_MODE_FOR_PERCENT 1 +#define TOOL_MODE_FOR_LEVEL 2 + + +char random_data[] = "ffd8ffe000104a46494600010101002400240000ffe100744578696600004d4d002a000000080004011a0005000000010000003e011b0005000000010000004601280003000000010002000087690004000000010000004e00000000000000240000000100000024000000010002a002000400000001000003c0a003000400000001000001ff00000000ffdb00430002020202020102020202020202030306040303030307050504060807080808070808090a0d0b09090c0a08080b0f0b0c0d0e0e0e0e090b10110f0e110d0e0e0effdb004301020202030303060404060e0908090e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0effc000110801ff03c003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00f9e74fbd37baa2db99e6506391f28371f9519ba67fd9fcabd46cbc1315de8d6776752d7419e049084b152a37283c1dfc8e6bc02db4af18d9df79c9e1bd59a40ae9b65b1761f32953c63ae09c7a1c57656fe24f8896da7c16c9e0bb3748a358d5a4d04b31006324f73c75a00935f7fec9f165ee98b7372e2ddc05795763f2a0f20138ebeb590bac3e70d2b6e1fed1ac6d4ecbc65aa6b973a85c7867528a6998168edec1a38c1c01c2f61c550fec1f16ff00d0bdade4f5ff00447ff0a00eaffb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe66b4f45fed1f1078bb4cd0f4bdf3ea37f7296f6d1ee3f33b1c0cfb7a9f4ae0bfb07c5bff42f6b7ff808ff00e15a9a1c5e3bf0f78c74bd774cd0f5a8751d3ee52e2ddcd9be03a9c8cfb7623d2803d5bc6fe0df11780e0d3ae354bab1bdb1bd678e1bab1b8f36312c6079b112380cac4ae3b9535e7ffdaedff3d5ff00335b3e3af177c4af1f5be9b6da8f8567d36c2c59e486d74fd35e28ccb260c92b003966605b3db71af3cfec1f16ff00d0bdadff00e023ff0085007acf83743d57c6baddd59e9d7062fb3c68d248519c0324ab120c2f3cb38c9ec013dab95b8d427b5bfb8b59a4759a195a3917278652411f98ad9f86fe30f88bf0cb57d56f348f095f5ebdfc2914ab716b200a118918c0ff0068d709a9d878cb55f12ea3aa4fe1cd6127bdba92e2455b47c06772c40e3a64d007d13f09fe1d37c41d135ad7b52d52ef4fd074eba86cff00d1537cd7171310123504e0751927d6be8ed17f65bf0aebde19b4d5ac7c5fe2236b700eddf12060558ab03ee1948fc2be41f84bf123e20fc2d1acd87fc20d77e24f0eeabb1af74cbcb4902974fbb22b0190c3fa0f4afa1748fdad3c51a1691fd9fa57c10b8b4b212bc8b0ac931542c7240c8e067271ef401e9bff000c89a07fd0dbafff00dfb4a3fe191340ff00a1b75fff00bf695c2ffc366f8e7fe88c5cff00df72ff00851ff0d9be39ff00a23173ff007dcbfe1401dd7fc322681ff436ebff00f7ed28ff008644d03fe86dd7ff00efda570bff000d9be39ffa23173ff7dcbfe147fc366f8e7fe88c5cff00df72ff008500775ff0c89a07fd0dbaff00fdfb4a3fe191340ffa1b75ff00fbf695c2ff00c366f8e7fe88c5cffdf72ff851ff000d9be39ffa23173ff7dcbfe1401dd7fc322681ff00436ebfff007ed2be52f8d1f0eeff00e11fc42b2d325d49f51d3afedccf637046d6215b6b2b0f5071f98af733fb6678eb071f062e73fefcbfe15f2a7c5af1cfc4bf8bff001262d7758f0bea76515bc1f67b1b1b7b490a4099c9edcb13c93f4a00fe891bc2fe1c672cda1e96589c92"; + +#define PAGE_OP_ALLOC 0x1 +#define PAGE_OP_FREE 0x2 + +#define USE_WIRED_PAGES_FOR_PERCENT_MODE FALSE + +#define MAX_RANGE_SIZE 64 * 1024 * 1024 * 1024ULL + +void print_vm_statistics(void); +void munch_for_level(unsigned int, unsigned int); +void munch_for_percentage(unsigned int, unsigned int, unsigned int); + +static void +usage(void) +{ + fprintf(stderr, "Usage: memory_pressure [options] []\n" + " Allocate memory and wait forever.\n" + " Options include:\n" + " -l - allocate memory until a low memory notification is received (warn OR critical)\n" + " -p - allocate memory until percent free is this (or less)\n" + " -s - how long to sleep between checking for a set percent level\n" + " -w - don't allocate, just wait until percent free is this then exit\n" + " -y - Hysteresis Interval: how long to wait after requested percntage free is reached, before exiting program. Requires the usage of the -p option\n" + " -v - print VM statistics every sampling interval\n" + " -Q - reduces the tool's output\n" + " -S - simulate the system's memory pressure level without applying any real pressure\n" + " \n" + ); + exit(0); +} + +static unsigned int +read_sysctl_int(const char* name) +{ + unsigned int var; + size_t var_size; + int error; + + var_size = sizeof(var); + error = sysctlbyname(name, &var, &var_size, NULL, 0); + if( error ) { + perror(name); + exit(-1); + } + return var; +} + +static long long +read_sysctl_long_long(const char* name) +{ + long long var; + size_t var_size; + int error; + + var_size = sizeof(var); + error = sysctlbyname(name, &var, &var_size, NULL, 0); + if( error ) { + perror(name); + exit(-1); + } + return var; +} + +static int +get_percent_free(unsigned int* level) +{ + int error; + + error = memorystatus_get_level((user_addr_t) level); + + if( error ) { + perror("memorystatus_get_level failed:"); + exit(-1); + } + return error; +} + +void +print_vm_statistics(void) +{ + unsigned int count = HOST_VM_INFO64_COUNT; + kern_return_t ret = 0; + vm_statistics64_data_t vm_stat;; + + if (quiet_mode_on == TRUE) { + return; + } + + if ((ret = host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vm_stat, &count) != KERN_SUCCESS)) { + fprintf(stderr, "Failed to get statistics. Error %d\n", ret); + } else { + printf("\nStats: \n"); + printf("Pages free: %llu \n", (uint64_t) (vm_stat.free_count - vm_stat.speculative_count)); + printf("Pages purgeable: %llu \n", (uint64_t) (vm_stat.purgeable_count)); + printf("Pages purged: %llu \n",(uint64_t) (vm_stat.purges)); + + printf("\nSwap I/O:\n"); + printf("Swapins: %llu \n", (uint64_t) (vm_stat.swapins)); + printf("Swapouts: %llu \n", (uint64_t) (vm_stat.swapouts)); + + printf("\nPage Q counts:\n"); + printf("Pages active: %llu \n", (uint64_t) (vm_stat.active_count)); + printf("Pages inactive: %llu \n", (uint64_t) (vm_stat.inactive_count)); + printf("Pages speculative: %llu \n", (uint64_t) (vm_stat.speculative_count)); + printf("Pages throttled: %llu \n", (uint64_t) (vm_stat.throttled_count)); + printf("Pages wired down: %llu \n", (uint64_t) (vm_stat.wire_count)); + + printf("\nCompressor Stats:\n"); + printf("Pages used by compressor: %llu \n", (uint64_t) (vm_stat.compressor_page_count)); + printf("Pages decompressed: %llu \n", (uint64_t) (vm_stat.decompressions)); + printf("Pages compressed: %llu \n", (uint64_t) (vm_stat.compressions)); + + printf("\nFile I/O:\n"); + printf("Pageins: %llu \n", (uint64_t) (vm_stat.pageins)); + printf("Pageouts: %llu \n", (uint64_t) (vm_stat.pageouts)); + +#if 0 + printf("\"Translation faults\": %llu \n", (uint64_t) (vm_stat.faults)); + printf("Pages copy-on-write: %llu \n", (uint64_t) (vm_stat.cow_faults)); + printf("Pages zero filled: %llu \n", (uint64_t) (vm_stat.zero_fill_count)); + printf("Pages reactivated: %llu \n", (uint64_t) (vm_stat.reactivations)); +#endif + printf("\n"); + } +} + +/* + this will work for up to 64 TB of RAM -- beyond that we exceed Intel's max for VRAM (48 bits of addressable space). + By the time we get there Intel probably will have increased this + */ +static unsigned long long +get_max_range_size() +{ + unsigned long long the_max_range_size = MAX_RANGE_SIZE; + + if (phys_mem * 4 > the_max_range_size) { + the_max_range_size = phys_mem * 4; + } + + return the_max_range_size; +} + +static int +reached_or_bypassed_desired_result(void) +{ + if (tool_mode == TOOL_MODE_FOR_LEVEL) { + + unsigned int current_level = 0; + + current_level = read_sysctl_int("kern.memorystatus_vm_pressure_level"); + + if (desired_level > 0 && current_level >= desired_level) { + return 1; + } + + return 0; + } + + if (tool_mode == TOOL_MODE_FOR_PERCENT) { + + unsigned int current_percent = 0; + + get_percent_free(¤t_percent); + + if (desired_percent > 0 && current_percent <= desired_percent) { + return 1; + } + + return 0; + } + + return 0; +} + +static void +reference_pages(int level) +{ + int error; + void *addr = NULL; + int num_pages = 0; + + error = pthread_mutex_lock(&reference_pages_mutex); + addr = range_start_addr; +again: + while(start_referencing_pages == 0) { + error = pthread_cond_wait(&reference_pages_condvar, &reference_pages_mutex); + } + + start_allocing_pages = 0; + pthread_mutex_unlock(&reference_pages_mutex); + + num_pages = 0; + for(; addr < range_current_addr;) { + + char p; + + if (reached_or_bypassed_desired_result()) { + //printf("stopped referencing after %d pages\n", num_pages); + break; + } + + p = *(char*) addr; + addr += PAGE_SIZE; + num_pages++; + + } + + //if (num_pages) { + // printf("Referenced %d\n", num_pages); + //} + error = pthread_mutex_lock(&reference_pages_mutex); + start_referencing_pages = 0; + start_allocing_pages = 1; + + goto again; +} + +static void +process_pages(int num_pages, int page_op) +{ + if (num_pages > 0) { + + int error = 0, i = 0; + size_t size = num_pages * PAGE_SIZE; + + if (page_op == PAGE_OP_ALLOC) { + + if (tool_mode == TOOL_MODE_FOR_PERCENT && USE_WIRED_PAGES_FOR_PERCENT_MODE) { + error = mlock(range_current_addr, size); + if (error == -1) { + perror("Failed to lock memory!"); + exit(-1); + } + + memset(range_current_addr, 0xFF, size); + range_current_addr += size; + + } else { + + pthread_mutex_lock(&reference_pages_mutex); + while (start_allocing_pages == 0) { + pthread_mutex_unlock(&reference_pages_mutex); + sleep(1); + pthread_mutex_lock(&reference_pages_mutex); + } + pthread_mutex_unlock(&reference_pages_mutex); + + for (i=0; i < num_pages; i++) { + + if (reached_or_bypassed_desired_result()) { + //printf("stopped faulting after %d pages\n", i); + break; + } + if ((uintptr_t)range_current_addr < get_max_range_size()) { + memcpy(range_current_addr, random_data, PAGE_SIZE); + range_current_addr += PAGE_SIZE; + } else { + printf("\nRun out of allocable memory\n"); + exit(0); + } + } + + pthread_mutex_lock(&reference_pages_mutex); + start_referencing_pages = 1; + pthread_cond_signal(&reference_pages_condvar); + pthread_mutex_unlock(&reference_pages_mutex); + } + } else { + if (tool_mode == TOOL_MODE_FOR_PERCENT && USE_WIRED_PAGES_FOR_PERCENT_MODE) { + error = munlock(range_current_addr, size); + if (error == -1) { + perror("Failed to unlock memory!"); + exit(-1); + } + + error = madvise(range_current_addr, size, MADV_FREE); + if (error == -1) { + perror("Failed to madv_free memory!"); + exit(-1); + } + + range_current_addr -= size; + + } else { + pthread_mutex_lock(&reference_pages_mutex); + while (start_referencing_pages == 1) { + pthread_mutex_unlock(&reference_pages_mutex); + sleep(1); + pthread_mutex_lock(&reference_pages_mutex); + } + + error = madvise(range_current_addr, size, MADV_FREE); + if (error == -1) { + perror("Failed to madv_free memory!"); + exit(-1); + } + range_current_addr -= size; + start_referencing_pages = 1; + pthread_cond_signal(&reference_pages_condvar); + pthread_mutex_unlock(&reference_pages_mutex); + } + } + } +} + +void +munch_for_level(unsigned int sleep_seconds, unsigned int print_vm_stats) +{ + + unsigned int current_level = 0; + unsigned int desired_percent = 0; + unsigned int current_percent = 0; + unsigned int page_op = PAGE_OP_ALLOC; + unsigned int previous_page_op = PAGE_OP_ALLOC; + unsigned int pages_to_process = 0; + unsigned int stabilized_percentage = 0; + boolean_t print_vm_stats_on_page_processing = FALSE; + boolean_t ok_to_print_stablity_message = TRUE; + + current_level = read_sysctl_int("kern.memorystatus_vm_pressure_level"); + + if (current_level >= desired_level) { + return; + } + + get_percent_free(¤t_percent); + + if (print_vm_stats) { + print_vm_stats_on_page_processing = TRUE; + } + + page_op = PAGE_OP_ALLOC; + previous_page_op = 0; + + while (1) { + + if (current_percent > percent_for_level) { + desired_percent = current_percent - percent_for_level; + } else { + desired_percent = 1; + } + + pages_to_process = (desired_percent * phys_pages) / 100; + + page_op = PAGE_OP_ALLOC; + + if (previous_page_op != page_op) { + //printf("%s %d pages.\n", (page_op == PAGE_OP_ALLOC) ? "Allocating" : "Freeing", pages_to_process); + printf("\nCMD: %s pages to go from level: %d to level: %d", (page_op == PAGE_OP_ALLOC) ? "Allocating" : "Freeing", current_level, desired_level); + previous_page_op = page_op; + fflush(stdout); + } else { + printf("."); + fflush(stdout); + } + + if (print_vm_stats_on_page_processing == TRUE) { + print_vm_statistics(); + } + process_pages(pages_to_process, page_op); + ok_to_print_stablity_message = TRUE; + + current_level = read_sysctl_int("kern.memorystatus_vm_pressure_level"); + + if (current_level >= desired_level) { + + while(1) { + current_level = read_sysctl_int("kern.memorystatus_vm_pressure_level"); + if (current_level < desired_level) { + break; + } + + if (current_level > desired_level) { + page_op = PAGE_OP_FREE; + + get_percent_free(¤t_percent); + + if (stabilized_percentage > current_percent) { + pages_to_process = ((stabilized_percentage - current_percent) * phys_pages) / 100; + + if (previous_page_op != page_op) { + printf("\nCMD: %s pages to go from %d to %d level", (page_op == PAGE_OP_ALLOC) ? "Allocating" : "Freeing", current_level, desired_level); + previous_page_op = page_op; + fflush(stdout); + } else { + printf("."); + fflush(stdout); + } + + if (print_vm_stats_on_page_processing == TRUE) { + print_vm_statistics(); + } + process_pages(pages_to_process, page_op); + ok_to_print_stablity_message = TRUE; + } + } + + while (current_level == desired_level) { + get_percent_free(¤t_percent); + if (ok_to_print_stablity_message == TRUE) { + print_vm_statistics(); + printf("\nStabilizing at Percent: %d Level: %d", current_percent, current_level); + fflush(stdout); + ok_to_print_stablity_message = FALSE; + previous_page_op = 0; + } else { + printf("."); + fflush(stdout); + } + + stabilized_percentage = current_percent; + sleep(sleep_seconds); + current_level = read_sysctl_int("kern.memorystatus_vm_pressure_level"); + } + } + } + + get_percent_free(¤t_percent); + //printf("Percent: %d Level: %d\n", current_percent, current_level); + sleep(1); + + if (print_vm_stats) { + print_vm_stats_on_page_processing = TRUE; + } + + } /* while */ +} + +void +munch_for_percentage(unsigned int sleep_seconds, unsigned int wait_percent_free, unsigned int print_vm_stats) +{ + + int total_pages_allocated = 0; + int current_stable_timer = 0; /* in seconds */ + unsigned int current_percent = 0; + boolean_t page_op = PAGE_OP_FREE; + unsigned int pages_to_process = 0; + boolean_t print_vm_stats_on_page_processing = FALSE; + boolean_t previous_page_op = 0; + boolean_t ok_to_print_stablity_message = TRUE; + + /* Allocate until memory level is hit. */ + + get_percent_free(¤t_percent); + + /* + * "wait" mode doesn't alloc, it just waits and exits. This is used + * while waiting for *other* processes to allocate memory. + */ + if (wait_percent_free) { + while (current_percent > wait_percent_free) { + sleep(sleep_seconds); + get_percent_free (¤t_percent); + } + return; + } + + page_op = PAGE_OP_ALLOC; + previous_page_op = 0; + + while (1) { + + if (current_percent > desired_percent) { + pages_to_process = ((current_percent - desired_percent) * phys_pages) / 100; + page_op = PAGE_OP_ALLOC; + } else { + pages_to_process = ((desired_percent - current_percent) * phys_pages) / 100; + page_op = PAGE_OP_FREE; + } + + if (pages_to_process > 0) { + + if (page_op != previous_page_op) { + //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); + printf("\nCMD: %s pages to go from %d%% to %d%% percent free", (page_op == PAGE_OP_ALLOC) ? "Allocating" : "Freeing", current_percent, desired_percent); + fflush(stdout); + previous_page_op = page_op; + } else { + printf("."); + fflush(stdout); + } + + if (page_op == PAGE_OP_ALLOC) { + total_pages_allocated += pages_to_process; + process_pages(pages_to_process, page_op); + ok_to_print_stablity_message = TRUE; + } else { + + if (total_pages_allocated >= pages_to_process) { + total_pages_allocated -= pages_to_process; + process_pages(pages_to_process, page_op); + ok_to_print_stablity_message = TRUE; + } else { + get_percent_free(¤t_percent); + if (ok_to_print_stablity_message == TRUE) { + printf("\nDesired Percent: %d, Current Percent: %d. No pages to free so waiting", desired_percent, current_percent); + fflush(stdout); + ok_to_print_stablity_message = FALSE; + } + } + } + + //printf("kernel memorystatus: %d%% free, allocated %d pages total. Requested: %d\n", current_percent, total_pages_allocated, desired_percent); + if (print_vm_stats) { + print_vm_stats_on_page_processing = TRUE; + } + } else { + if (ok_to_print_stablity_message == TRUE) { + print_vm_statistics(); + printf("\nStable at percent free: %d", current_percent); + fflush(stdout); + ok_to_print_stablity_message = FALSE; + } else { + printf("."); + fflush(stdout); + } + + /* Stability has been reached; Increment current_stable_timer by sleep_seconds */ + + if (current_stable_timer <= requested_hysteresis_seconds){ + current_stable_timer += sleep_seconds; + /* Debug only */ + /* printf("\n Percentage Free stable for %d seconds", current_stable_timer); */ + } else { + printf ("\n Maintained memory pressure to %d percent free for more than %d seconds. Stopping pressure now.", current_percent, requested_hysteresis_seconds); + return; + } + + print_vm_stats_on_page_processing = FALSE; + } + + if (print_vm_stats_on_page_processing) { + + print_vm_statistics(); + + if (print_vm_stats_on_page_processing == TRUE) { + print_vm_stats_on_page_processing = FALSE; + } + } + + sleep(sleep_seconds); + + get_percent_free(¤t_percent); + } /* while */ +} + +int +main(int argc, char * const argv[]) +{ + int opt; + unsigned int wait_percent_free = 0; + unsigned int current_percent = 0; + unsigned int print_vm_stats = 0; + char level[10]; + + while ((opt = getopt(argc, argv, "hl:p:s:w:y:vQS")) != -1) { + switch (opt) { + case 'h': + usage(); + break; + case 'l': + strlcpy(level, optarg, 9); + + if (strncasecmp(level, "normal", 6) == 0) { + desired_level = DISPATCH_MEMORYPRESSURE_NORMAL; + percent_for_level = 90; + } else if (strncasecmp(level, "warn", 4) == 0) { + desired_level = DISPATCH_MEMORYPRESSURE_WARN; + percent_for_level = 60; + + } else if (strncasecmp(level, "critical", 8) == 0) { + desired_level = DISPATCH_MEMORYPRESSURE_CRITICAL; + percent_for_level = 30; + + } else { + printf("Incorrect level. Allowed \"normal\" or \"warn\" or \"critical\". Specified: %s\n", level); + exit(0); + } + break; + case 'p': + desired_percent = atoi(optarg); + break; + case 's': + sleep_seconds = atoi(optarg); + break; + case 'w': + wait_percent_free = atoi(optarg); + break; + case 'y': + requested_hysteresis_seconds = atoi(optarg); + break; + case 'v': + print_vm_stats = 1; + break; + case 'Q': + quiet_mode_on = TRUE; + break; + case 'S': + simulate_mode_on = TRUE; + break; + default: + usage(); + } + } + + if (simulate_mode_on == TRUE && desired_level == 0) { + printf("Expected level with -l along with \"simulated\" mode.\n"); + return 0; + } + + if (requested_hysteresis_seconds > 0) { + if (desired_percent == 0) { + printf("Hysteresis time may only be specified in conjunction with a non-zero value for the -p option. \n"); + usage(); + } + } + + phys_mem = read_sysctl_long_long("hw.memsize"); + phys_pages = (unsigned int) (phys_mem / PAGE_SIZE); + + printf("The system has %lld (%d pages with a page size of %lu).\n", phys_mem, phys_pages, PAGE_SIZE); + + print_vm_statistics(); + + get_percent_free(¤t_percent); + printf("System-wide memory free percentage: %d%%\n", current_percent); + + if (desired_percent == 0 && wait_percent_free == 0 && desired_level == 0) { + return 0; + } + + if (simulate_mode_on == TRUE) { + + /* + We use the sysctl "kern.memorypressure_manual_trigger" for this mode. Here's a blurb: + + Supported behaviors when using the manual trigger tests. + + #define TEST_LOW_MEMORY_TRIGGER_ONE 1 most suitable app is notified + #define TEST_LOW_MEMORY_TRIGGER_ALL 2 all apps are notified + #define TEST_PURGEABLE_TRIGGER_ONE 3 + #define TEST_PURGEABLE_TRIGGER_ALL 4 + #define TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ONE 5 + #define TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ALL 6 + + So, for example, to simulate one app getting a poke when the "pressure" reaches critical levels: "sudo sysctl -w kern.memorypressure_manual_trigger = level" + where level is calculated as: ((TEST_LOW_MEMORY_TRIGGER_ONE << 16) | NOTE_MEMORYSTATUS_PRESSURE_CRITICAL), which will be "65540". + + For this tool, currently, we only support the "TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ALL" options. + */ + +#define TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ALL 6 + + unsigned int var = 0; + size_t var_size = 0; + int error = 0; + + var_size = sizeof(var); + + var = ((TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ALL << 16) | desired_level); + + error = sysctlbyname("kern.memorypressure_manual_trigger", NULL, 0, &var, var_size); + + if(error) { + perror("sysctl: kern.memorypressure_manual_trigger failed "); + exit(-1); + } + + printf("Waiting %d seconds before resetting system state\n", sleep_seconds); + + sleep(sleep_seconds); + + var_size = sizeof(var); + + var = ((TEST_LOW_MEMORY_PURGEABLE_TRIGGER_ALL << 16) | DISPATCH_MEMORYPRESSURE_NORMAL); + + error = sysctlbyname("kern.memorypressure_manual_trigger", NULL, 0, &var, var_size); + + if(error) { + perror("sysctl: kern.memorypressure_manual_trigger failed "); + exit(-1); + } + + printf("Reset system state\n"); + + } else { + range_start_addr = mmap(NULL, get_max_range_size(), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0); + + if (range_start_addr == MAP_FAILED) { + perror("mmap failed"); + } else { + + int error = 0; + pthread_t thread = NULL; + + error = pthread_create(&thread, NULL, (void*) reference_pages, NULL); + + range_current_addr = range_start_addr; + range_end_addr = range_start_addr + get_max_range_size(); + start_allocing_pages = 1; + + if (desired_level) { + tool_mode = TOOL_MODE_FOR_LEVEL; + munch_for_level(sleep_seconds, print_vm_stats); + } else { + tool_mode = TOOL_MODE_FOR_PERCENT; + munch_for_percentage(sleep_seconds, wait_percent_free, print_vm_stats); + } + } + } + + return 0; +} -- cgit v1.2.3-56-ge451