#include <asl.h>
#include <assert.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <libproc.h>
#include <mach/mach.h>
#include <mach/mach_types.h>
#include <mach/mach_vm.h>
#include <mach/shared_region.h>
#include <mach/task_info.h>
#include <mach/vm_map.h>
#include <mach/vm_page_size.h>
#include <sys/event.h>
#include <sys/ipc.h>
#include <sys/kern_memorystatus.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <xpc/xpc.h>
#include <xpc/private.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#include <ServiceManagement/ServiceManagement.h>
#include <ServiceManagement/SMErrors.h>
#include <Kernel/kern/ledger.h>
#include <sys/spawn_internal.h>
#include <spawn_private.h>
#define CR_JOB "com.apple.ReportCrash.Jetsam"
#define CR_JOB_PLIST_PATH "/System/Library/LaunchDaemons/com.apple.ReportCrash.Jetsam.plist"
#define ERR_BUF_LEN 1024
#ifndef VM_PAGE_SIZE
#define VM_PAGE_SIZE 4096
#endif
#define TASK_LIMIT_MB 75
#define HWM_LIMIT_MB 8
#define RANDOM_DATA_SIZE 4096
char random_data[] = "ffd8ffe000104a46494600010101002400240000ffe100744578696600004d4d002a000000080004011a0005000000010000003e011b0005000000010000004601280003000000010002000087690004000000010000004e00000000000000240000000100000024000000010002a002000400000001000003c0a003000400000001000001ff00000000ffdb00430002020202020102020202020202030306040303030307050504060807080808070808090a0d0b09090c0a08080b0f0b0c0d0e0e0e0e090b10110f0e110d0e0e0effdb004301020202030303060404060e0908090e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0effc000110801ff03c003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00f9e74fbd37baa2db99e6506391f28371f9519ba67fd9fcabd46cbc1315de8d6776752d7419e049084b152a37283c1dfc8e6bc02db4af18d9df79c9e1bd59a40ae9b65b1761f32953c63ae09c7a1c57656fe24f8896da7c16c9e0bb3748a358d5a4d04b31006324f73c75a00935f7fec9f165ee98b7372e2ddc05795763f2a0f20138ebeb590bac3e70d2b6e1fed1ac6d4ecbc65aa6b973a85c7867528a6998168edec1a38c1c01c2f61c550fec1f16ff00d0bdade4f5ff00447ff0a00eaffb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f";
#ifndef VM_PAGER_FREEZER_DEFAULT
#define VM_PAGER_FREEZER_DEFAULT 0x8
#endif
#define CAUSE_HIWAT_OR_PERPROC -1
typedef enum jetsam_test {
kSimpleJetsamTest = 1,
kCustomTaskLimitTest,
kPressureJetsamTestFG,
kPressureJetsamTestBG,
kHighwaterJetsamTest,
kVnodeJetsamTest,
kBackgroundJetsamTest
} jetsam_test_t;
typedef enum idle_exit_test {
kDeferTimeoutCleanTest = 1,
kDeferTimeoutDirtyTest,
kCancelTimeoutCleanTest,
kCancelTimeoutDirtyTest
} idle_exit_test_t;
typedef struct shared_mem_t {
pthread_mutex_t mutex;
pthread_cond_t cv;
boolean_t completed;
boolean_t pressure_event_fired;
boolean_t child_failed;
} shared_mem_t;
shared_mem_t *g_shared = NULL;
unsigned long g_physmem = 0;
int g_compressor_mode=0;
int g_ledger_count = -1, g_footprint_index = -1;
int64_t g_per_process_limit = -1;
int g_exit_status = 0;
extern int ledger(int cmd, caddr_t arg1, caddr_t arg2, caddr_t arg3);
static boolean_t check_properties(pid_t pid, int32_t requested_priority, int32_t requested_limit_mb, uint64_t requested_user_data, const char *test);
static void
printTestHeader(pid_t testPid, const char *testName, ...)
{
va_list va;
printf("========================================\n");
printf("[TEST] ");
va_start(va, testName);
vprintf(testName, va);
va_end(va);
printf("\n");
printf("[PID] %d\n", testPid);
printf("========================================\n");
printf("[BEGIN]\n");
fflush(stdout);
}
static void
printTestResult(const char *testName, boolean_t didPass, const char *msg, ...)
{
if (msg != NULL) {
va_list va;
printf("\t\t");
va_start(va, msg);
vprintf(msg, va);
va_end(va);
printf("\n");
}
if (didPass) {
printf("[PASS]\t%s\n\n", testName);
} else {
printf("[FAIL]\t%s\n\n", testName);
g_exit_status = -1;
}
fflush(stdout);
}
static int
_get_munch_interval(int given_interval)
{
int res;
int new_interval=0;
char *slow_device;
char model_name_buf[1025];
size_t mnb_size = 1024;
res = sysctlbyname("hw.model", model_name_buf, &mnb_size, NULL, 0);
if (res) {
perror("\t\tsysctlbyname(hw.model...)");
}
else {
slow_device = strstr(model_name_buf, "N90");
if (slow_device == NULL) {
slow_device = strstr(model_name_buf, "K66");
}
if (slow_device == NULL) {
slow_device = strstr(model_name_buf, "J33");
}
if (slow_device != NULL) {
printf("\t\tRunning on a slow device...\n");
}
if (given_interval == 0) {
if (slow_device != NULL) {
new_interval = 500 * 1000;
}
else {
new_interval = 100 * 1000;
}
}
else {
new_interval = given_interval * USEC_PER_SEC;
}
}
return new_interval;
}
static CFDictionaryRef create_dictionary_from_plist(const char *path) {
void *bytes = NULL;
CFDataRef data = NULL;
CFDictionaryRef options = NULL;
size_t bufLen;
int fd = open(path, O_RDONLY, 0);
if (fd == -1) {
goto exit;
}
struct stat sb;
if (fstat(fd, &sb) == -1) {
goto exit;
}
bufLen = (size_t)sb.st_size;
bytes = malloc(bufLen);
if (bytes == NULL) {
goto exit;
}
if (read(fd, bytes, bufLen) != bufLen) {
goto exit;
}
data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *) bytes, bufLen, kCFAllocatorNull);
if (data == NULL) {
goto exit;
}
options = (CFDictionaryRef) CFPropertyListCreateWithData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL, NULL);
if (options == NULL) {
goto exit;
}
exit:
if (data != NULL) {
CFRelease(data);
}
if (bytes != NULL) {
free(bytes);
}
if (fd != -1) {
close(fd);
}
return options;
}
static void cleanup_and_exit(int status) {
exit(status);
}
static void child_ready() {
pthread_mutex_lock(&g_shared->mutex);
pthread_cond_signal(&g_shared->cv);
pthread_mutex_unlock(&g_shared->mutex);
}
static pid_t init_and_fork() {
int pid;
g_shared->completed = 0;
g_shared->pressure_event_fired = 0;
pthread_mutex_lock(&g_shared->mutex);
pid = fork();
if (pid == 0) {
return 0;
} else if (pid == -1) {
printTestResult(__func__, false, "Fork error!");
cleanup_and_exit(-1);
}
pthread_cond_wait(&g_shared->cv, &g_shared->mutex);
pthread_mutex_unlock(&g_shared->mutex);
return (pid_t)pid;
}
static memorystatus_priority_entry_t *get_priority_list(int *size) {
memorystatus_priority_entry_t *list = NULL;
assert(size);
*size = memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST, 0, 0, NULL, 0);
if (*size <= 0) {
printf("\t\tCan't get list size: %d!\n", *size);
goto exit;
}
list = (memorystatus_priority_entry_t*)malloc(*size);
if (!list) {
printf("\t\tCan't allocate list!\n");
goto exit;
}
*size = memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST, 0, 0, list, *size);
if (*size <= 0) {
printf("\t\tCan't retrieve list!\n");
goto exit;
}
exit:
return list;
}
static boolean_t get_ledger_info(pid_t pid, int64_t *balance_mb, int64_t *limit_mb) {
struct ledger_entry_info *lei;
uint64_t count;
boolean_t res = false;
lei = (struct ledger_entry_info *)malloc((size_t)(g_ledger_count * sizeof (*lei)));
if (lei) {
void *arg;
arg = (void *)(long)pid;
count = g_ledger_count;
if ((ledger(LEDGER_ENTRY_INFO, arg, (caddr_t)lei, (caddr_t)&count) >= 0) && (g_footprint_index < count)) {
if (balance_mb) {
*balance_mb = lei[g_footprint_index].lei_balance;
}
if (limit_mb) {
*limit_mb = lei[g_footprint_index].lei_limit;
}
res = true;
}
free(lei);
}
return res;
}
static boolean_t get_priority_props(pid_t pid, int32_t *priority, int32_t *limit_mb, uint64_t *user_data) {
int size;
memorystatus_priority_entry_t *entries = NULL;
int i;
boolean_t res = false;
entries = get_priority_list(&size);
if (!entries) {
goto exit;
}
for (i = 0; i < size/sizeof(memorystatus_priority_entry_t); i++ ){
if (entries[i].pid == pid) {
int64_t limit;
*priority = entries[i].priority;
*user_data = entries[i].user_data;
#if 1
*limit_mb = entries[i].limit;
res = true;
#else
res = get_ledger_info(entries[i].pid, NULL, &limit);
if (false == res) {
printf("Failed to get highwater!\n");
}
*limit_mb = (int32_t)(limit/(1024 * 1024));
#endif
goto exit;
}
}
printf("\t\tCan't find pid: %d!\n", pid);
exit:
if (entries)
free(entries);
return res;
}
static boolean_t check_properties(pid_t pid, int32_t requested_priority, int32_t requested_limit_mb, uint64_t requested_user_data, const char *test) {
const char *PROP_GET_ERROR_STRING = "failed to get properties";
const char *PROP_CHECK_ERROR_STRING = "property mismatch";
int32_t actual_priority, actual_hiwat;
uint64_t actual_user_data;
if (!get_priority_props(pid, &actual_priority, &actual_hiwat, &actual_user_data)) {
printf("\t\t%s test failed: %s\n", test, PROP_GET_ERROR_STRING);
return false;
}
if (requested_limit_mb <= 0) {
requested_limit_mb = (int32_t)g_per_process_limit;
}
if (actual_priority != requested_priority || actual_hiwat != requested_limit_mb || actual_user_data != requested_user_data) {
printf("\t\t%s test failed: %s\n", test, PROP_CHECK_ERROR_STRING);
printf("priority is %d, should be %d\n", actual_priority, requested_priority);
printf("hiwat is %d, should be %d\n", actual_hiwat, requested_limit_mb);
printf("user data is 0x%llx, should be 0x%llx\n", actual_user_data, requested_user_data);
return false;
}
printf("\t\t%s test ok...\n", test);
return true;
}
static void start_list_validation_test() {
int size;
memorystatus_priority_entry_t *entries = NULL;
int i;
boolean_t valid = false;
printTestHeader(getpid(), "List validation test");
entries = get_priority_list(&size);
if (!entries) {
printf("Can't get entries!\n");
goto exit;
}
for (i = 0; i < size/sizeof(memorystatus_priority_entry_t); i++ ) {
int dirty_ret;
uint32_t dirty_flags;
if (entries[i].pid <= 1) {
printf("\t\tBad process (%d) in list!\n", entries[i].pid);
goto exit;
}
dirty_ret = proc_get_dirty(entries[i].pid, &dirty_flags);
if (dirty_ret != 0) {
dirty_flags = 0;
}
if (dirty_flags & PROC_DIRTY_ALLOWS_IDLE_EXIT) {
if ((entries[i].priority == JETSAM_PRIORITY_IDLE) && (dirty_flags & PROC_DIRTY_IS_DIRTY)) {
printf("\t\tProcess %d at idle priority when dirty (priority %d, flags 0x%x)!\n", entries[i].pid, entries[i].priority, dirty_flags);
goto exit;
}
if ((entries[i].priority > JETSAM_PRIORITY_IDLE_DEFERRED) && !(dirty_flags & PROC_DIRTY_IS_DIRTY)) {
printf("\t\tProcess %d not at non-idle priority when clean(priority %d, flags 0x%x)\n", entries[i].pid, entries[i].priority, dirty_flags);
goto exit;
}
}
}
valid = true;
exit:
if (entries)
free(entries);
printTestResult("List validation test", valid, NULL);
}
static void start_general_sanity_test() {
int ret, size;
int i;
boolean_t valid = false;
printTestHeader(getpid(), "Sanity test");
ret = proc_track_dirty(1, PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT | PROC_DIRTY_DEFER);
if (ret != EPERM) {
printf("\t\tNo EPERM tracking launchd (%d/%d)!\n", ret, errno);
goto exit;
} else {
printf("\t\tlaunchd track test OK!\n");
}
ret = proc_set_dirty(1, true);
if (ret != EPERM) {
printf("\t\tNo EPERM setting launchd dirty state (%d/%d)!\n", ret, errno);
goto exit;
} else {
printf("\t\tlaunchd dirty test OK!\n");
}
valid = true;
exit:
printTestResult("Sanity test", valid, NULL);
}
static void idle_exit_deferral_test(idle_exit_test_t test) {
int secs = DEFERRED_IDLE_EXIT_TIME_SECS;
child_ready();
if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#1 - pre xpc_track_activity()")) {
goto exit;
}
proc_track_dirty(getpid(), PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT | PROC_DIRTY_DEFER);
if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#2 - post xpc_track_activity()")) {
goto exit;
}
proc_set_dirty(getpid(), true);
proc_set_dirty(getpid(), false);
proc_set_dirty(getpid(), true);
proc_set_dirty(getpid(), false);
switch (test) {
case kDeferTimeoutCleanTest:
if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#3 - post toggle")) {
goto exit;
}
sleep(secs - 1);
if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#4 - pre timeout")) {
goto exit;
}
sleep(2);
if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#5 - post timeout")) {
goto exit;
}
proc_set_dirty(getpid(), true);
if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#6 - post dirty")) {
goto exit;
}
proc_set_dirty(getpid(), false);
if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#7 - post clean")) {
goto exit;
}
break;
case kDeferTimeoutDirtyTest:
proc_set_dirty(getpid(), true);
if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#3 - post dirty")) {
goto exit;
}
sleep(secs - 1);
if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#4 - pre timeout")) {
goto exit;
}
sleep(2);
if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#5 - post timeout")) {
goto exit;
}
proc_set_dirty(getpid(), false);
if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#6 - post clean")) {
goto exit;
}
break;
case kCancelTimeoutDirtyTest:
proc_set_dirty(getpid(), true);
if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#3 - post toggle")) {
goto exit;
}
proc_clear_dirty(getpid(), PROC_DIRTY_DEFER);
if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#4 - post deferral cancellation")) {
goto exit;
}
proc_set_dirty(getpid(), false);
if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#5 - post toggle")) {
goto exit;
}
break;
case kCancelTimeoutCleanTest:
if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#3 - post toggle")) {
goto exit;
}
proc_clear_dirty(getpid(), PROC_DIRTY_DEFER);
if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#4 - post deferral cancellation")) {
goto exit;
}
proc_set_dirty(getpid(), true);
if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#5 - post dirty")) {
goto exit;
}
proc_set_dirty(getpid(), false);
if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#6 - post clean")) {
goto exit;
}
break;
}
g_shared->completed = 1;
exit(0);
exit:
printTestResult(__func__, false, "Something bad happened...");
exit(-1);
}
static void start_idle_exit_defer_test(idle_exit_test_t test) {
pid_t pid;
int status;
memset(g_shared, 0, sizeof(shared_mem_t));
pid = init_and_fork();
if (pid == 0) {
idle_exit_deferral_test(test);
}
else {
printTestHeader(pid, "Idle exit deferral test: %d", test);
}
waitpid(pid, &status, 0);
printTestResult("Idle exit deferral test", g_shared->completed, NULL);
}
static void ledger_init(void) {
const char *physFootprintName = "phys_footprint";
struct ledger_info li;
int64_t template_cnt;
struct ledger_template_info *templateInfo;
void *arg;
int i;
arg = (void *)(long)getpid();
if (ledger(LEDGER_INFO, arg, (caddr_t)&li, NULL) < 0) {
exit(-1);
}
g_ledger_count = template_cnt = li.li_entries;
templateInfo = malloc(template_cnt * sizeof (struct ledger_template_info));
if (templateInfo == NULL) {
exit (-1);
}
if (!(ledger(LEDGER_TEMPLATE_INFO, (caddr_t)templateInfo, (caddr_t)&template_cnt, NULL) < 0)) {
for (i = 0; i < template_cnt; i++) {
if (!strncmp(templateInfo[i].lti_name, physFootprintName, strlen(physFootprintName))) {
g_footprint_index = i;
break;
}
}
}
free(templateInfo);
}
static void run_tests(const char *path) {
#pragma unused(path)
start_general_sanity_test();
start_list_validation_test();
start_idle_exit_defer_test(kDeferTimeoutCleanTest);
start_idle_exit_defer_test(kDeferTimeoutDirtyTest);
start_idle_exit_defer_test(kCancelTimeoutCleanTest);
start_idle_exit_defer_test(kCancelTimeoutDirtyTest);
}
int main(int argc, char **argv)
{
pthread_mutexattr_t attr;
pthread_condattr_t cattr;
size_t size;
if (getuid() != 0) {
fprintf(stderr, "%s must be run as root.\n", getprogname());
exit(EXIT_FAILURE);
}
size = sizeof(g_physmem);
if (sysctlbyname("hw.physmem", &g_physmem, &size, NULL, 0) != 0 || !g_physmem) {
printTestResult(__func__, false, "Failed to retrieve system memory");
cleanup_and_exit(-1);
}
size = sizeof(g_compressor_mode);
if (sysctlbyname("vm.compressor_mode", &g_compressor_mode, &size, NULL, 0) != 0) {
printTestResult(__func__, false, "Failed to retrieve compressor config");
cleanup_and_exit(-1);
}
ledger_init();
if ((-1 == g_ledger_count) || (-1 == g_footprint_index) || (false == get_ledger_info(getpid(), NULL, &g_per_process_limit))) {
printTestResult("setup", false, "Unable to init ledger!\n");
cleanup_and_exit(-1);
}
if (g_per_process_limit == LEDGER_LIMIT_INFINITY) {
g_per_process_limit = 0;
} else {
g_per_process_limit /= (1024 * 1024);
}
g_shared = mmap(NULL, sizeof(shared_mem_t), PROT_WRITE|PROT_READ, MAP_ANON|MAP_SHARED, 0, 0);
if (!g_shared) {
printTestResult(__func__, false, "Failed mmap");
cleanup_and_exit(-1);
}
if (sizeof(random_data) < RANDOM_DATA_SIZE) {
printTestResult(__func__, false, "Failed to guarantee random_data buffer size [expected %d, actual %d]",
RANDOM_DATA_SIZE, sizeof(random_data));
cleanup_and_exit(-1);
}
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED );
pthread_condattr_init(&cattr);
pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
if (pthread_mutex_init(&g_shared->mutex, &attr) || pthread_cond_init(&g_shared->cv, &cattr)) {
printTestResult("setup", false, "Unable to init condition variable!");
cleanup_and_exit(-1);
}
run_tests(argv[0]);
pthread_mutex_destroy(&g_shared->mutex);
pthread_cond_destroy(&g_shared->cv);
pthread_mutexattr_destroy(&attr);
pthread_condattr_destroy(&cattr);
return (g_exit_status);
}