/*
- * Copyright (c) 2013 Apple Inc. All rights reserved.
+ * 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,
* 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@
*/
void munch_for_level(unsigned int, unsigned int);
void munch_for_percentage(unsigned int, unsigned int, unsigned int);
-static void
+static void
usage(void)
{
fprintf(stderr, "Usage: memory_pressure [options] [<pages>]\n"
" Allocate memory and wait forever.\n"
" Options include:\n"
- " -l <level> - allocate memory until a low memory notification is received (warn OR critical)\n"
+ " -l <level> - allocate memory until a low memory notification is received (warn OR critical)\n"
" -p <percent-free> - allocate memory until percent free is this (or less)\n"
" -s <seconds> - how long to sleep between checking for a set percent level\n"
" -w <percent-free> - don't allocate, just wait until percent free is this then exit\n"
" -v <print VM stats> - print VM statistics every sampling interval\n"
- " -Q <quiet mode> - reduces the tool's output\n"
+ " -Q <quiet mode> - reduces the tool's output\n"
" -S - simulate the system's memory pressure level without applying any real pressure\n"
" \n"
);
}
static unsigned int
-read_sysctl_int(const char* name)
+read_sysctl_int(const char* name)
{
unsigned int var;
size_t var_size;
}
static int
-get_percent_free(unsigned int* level)
+get_percent_free(unsigned int* level)
{
int error;
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));
}
}
-
static int
reached_or_bypassed_desired_result(void)
{
}
if (tool_mode == TOOL_MODE_FOR_PERCENT) {
-
+
unsigned int current_percent = 0;
get_percent_free(¤t_percent);
error = pthread_mutex_lock(&reference_pages_mutex);
addr = range_start_addr;
again:
- while(start_referencing_pages == 0) {
+ 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);
//}
start_referencing_pages = 0;
start_allocing_pages = 1;
- goto again;
-
+ goto again;
}
static void
-process_pages(int num_pages, int page_op)
+process_pages(int num_pages, int page_op)
{
if (num_pages > 0) {
-
- int error = 0, i = 0;
+
+ int error = 0, i = 0;
size_t size = num_pages * PAGE_SIZE;
if (page_op == PAGE_OP_ALLOC) {
if (error == -1) {
perror("Failed to lock memory!");
exit(-1);
- }
+ }
memset(range_current_addr, 0xFF, size);
range_current_addr += size;
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 (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!");
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!");
}
void
-munch_for_level(unsigned int sleep_seconds, unsigned int print_vm_stats)
+munch_for_level(unsigned int sleep_seconds, unsigned int print_vm_stats)
{
unsigned int current_level = 0;
}
get_percent_free(¤t_percent);
-
+
if (print_vm_stats) {
print_vm_stats_on_page_processing = TRUE;
}
} else {
desired_percent = 1;
}
-
+
pages_to_process = (desired_percent * phys_pages) / 100;
page_op = PAGE_OP_ALLOC;
printf(".");
fflush(stdout);
}
-
+
if (print_vm_stats_on_page_processing == TRUE) {
print_vm_statistics();
}
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;
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)
+void
+munch_for_percentage(unsigned int sleep_seconds, unsigned int wait_percent_free, unsigned int print_vm_stats)
{
int total_pages_allocated = 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.
*/
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);
}
}
}
-
+
//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;
}
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
+int
main(int argc, char * const argv[])
{
int opt;
strlcpy(level, optarg, 9);
if (strncasecmp(level, "normal", 6) == 0) {
- desired_level = DISPATCH_MEMORYSTATUS_PRESSURE_NORMAL;
+ desired_level = DISPATCH_MEMORYPRESSURE_NORMAL;
percent_for_level = 90;
} else if (strncasecmp(level, "warn", 4) == 0) {
- desired_level = DISPATCH_MEMORYSTATUS_PRESSURE_WARN;
+ desired_level = DISPATCH_MEMORYPRESSURE_WARN;
percent_for_level = 60;
} else if (strncasecmp(level, "critical", 8) == 0) {
- desired_level = DISPATCH_MEMORYSTATUS_PRESSURE_CRITICAL;
+ desired_level = DISPATCH_MEMORYPRESSURE_CRITICAL;
percent_for_level = 30;
} else {
break;
case 'v':
print_vm_stats = 1;
- break;
+ break;
case 'Q':
quiet_mode_on = TRUE;
break;
phys_mem = read_sysctl_int("hw.physmem");
phys_pages = (unsigned int) (phys_mem / PAGE_SIZE);
- printf("The system has %ld (%d pages with a page size of %d).\n", phys_mem, phys_pages, PAGE_SIZE);
+ printf("The system has %lu (%d pages with a page size of %d).\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_PURGEABLE_TRIGGER_ALL 6
-
+
unsigned int var = 0;
size_t var_size = 0;
int error = 0;
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_MEMORYSTATUS_PRESSURE_NORMAL);
+ 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);
} else {
range_start_addr = mmap(NULL, MAX_RANGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
-
+
if (range_start_addr == MAP_FAILED) {
perror("mmap failed");
} else {