1#include <asl.h> 2#include <assert.h> 3#include <fcntl.h> 4#include <pthread.h> 5#include <signal.h> 6#include <stdio.h> 7#include <stdlib.h> 8#include <string.h> 9#include <time.h> 10#include <unistd.h> 11 12#include <libproc.h> 13 14#include <mach/mach.h> 15#include <mach/mach_types.h> 16#include <mach/mach_vm.h> 17#include <mach/shared_region.h> 18#include <mach/task_info.h> 19#include <mach/vm_map.h> 20#include <mach/vm_page_size.h> /* Needed for vm_region info */ 21 22#include <sys/event.h> 23#include <sys/ipc.h> 24#include <sys/kern_memorystatus.h> 25#include <sys/mman.h> 26#include <sys/shm.h> 27#include <sys/stat.h> 28#include <sys/sysctl.h> 29#include <sys/wait.h> 30 31#include <xpc/xpc.h> 32#include <xpc/private.h> 33 34#include <CoreFoundation/CoreFoundation.h> 35 36#include <Security/Security.h> 37#include <ServiceManagement/ServiceManagement.h> 38#include <ServiceManagement/SMErrors.h> 39 40#include <Kernel/kern/ledger.h> 41 42#include <sys/spawn_internal.h> 43#include <spawn_private.h> 44 45#define CR_JOB "com.apple.ReportCrash.Jetsam" 46#define CR_JOB_PLIST_PATH "/System/Library/LaunchDaemons/com.apple.ReportCrash.Jetsam.plist" 47 48#define ERR_BUF_LEN 1024 49 50#ifndef VM_PAGE_SIZE 51#define VM_PAGE_SIZE 4096 52#endif 53 54#define TASK_LIMIT_MB 75 55#define HWM_LIMIT_MB 8 56 57/* 58 * Blob of data that is not easily compressed. 59 * Guaranteed during setup to be at least 60 * RANDOM_DATA_SIZE in length. 61 */ 62 63#define RANDOM_DATA_SIZE 4096 64char random_data[] = "ffd8ffe000104a46494600010101002400240000ffe100744578696600004d4d002a000000080004011a0005000000010000003e011b0005000000010000004601280003000000010002000087690004000000010000004e00000000000000240000000100000024000000010002a002000400000001000003c0a003000400000001000001ff00000000ffdb00430002020202020102020202020202030306040303030307050504060807080808070808090a0d0b09090c0a08080b0f0b0c0d0e0e0e0e090b10110f0e110d0e0e0effdb004301020202030303060404060e0908090e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0effc000110801ff03c003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00f9e74fbd37baa2db99e6506391f28371f9519ba67fd9fcabd46cbc1315de8d6776752d7419e049084b152a37283c1dfc8e6bc02db4af18d9df79c9e1bd59a40ae9b65b1761f32953c63ae09c7a1c57656fe24f8896da7c16c9e0bb3748a358d5a4d04b31006324f73c75a00935f7fec9f165ee98b7372e2ddc05795763f2a0f20138ebeb590bac3e70d2b6e1fed1ac6d4ecbc65aa6b973a85c7867528a6998168edec1a38c1c01c2f61c550fec1f16ff00d0bdade4f5ff00447ff0a00eaffb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f16ffd0bdadffe023ff851fd83e2dffa17b5bffc047ff0a00eabfb5dbfe7abfe668fed76ff009eaff99ae57fb07c5bff0042f6b7ff00808ffe147f60f8b7fe85ed6fff00011ffc2803aafed76ff9eaff0099a3fb5dbfe7abfe66b95fec1f16ff00d0bdadff00e023ff00851fd83e2dff00a17b5bff00c047ff000a00eabfb5dbfe7abfe668fed76ff9eaff0099ae57fb07c5bff42f6b7ff808ff00e147f60f8b7fe85ed6ff00f011ff00c2803aafed76ff009eaff99a3fb5dbfe7abfe66b95fec1f"; 65 66/* 67 * TODO: import header (currently vm_pageout.h) without pulling in extraneous definitions; 68 * see <rdar://problem/13374916>. 69 */ 70#ifndef VM_PAGER_FREEZER_DEFAULT 71#define VM_PAGER_FREEZER_DEFAULT 0x8 /* Freezer backed by default pager.*/ 72#endif 73 74/* 75 * Special note to ourselves: the jetsam cause to look out for is *either* 76 * a high watermark kill, *or* a per-process kill. 77 */ 78#define CAUSE_HIWAT_OR_PERPROC -1 79 80typedef enum jetsam_test { 81 kSimpleJetsamTest = 1, 82 kCustomTaskLimitTest, 83 kPressureJetsamTestFG, 84 kPressureJetsamTestBG, 85 kHighwaterJetsamTest, 86 kVnodeJetsamTest, 87 kBackgroundJetsamTest 88} jetsam_test_t; 89 90typedef enum idle_exit_test { 91 kDeferTimeoutCleanTest = 1, 92 kDeferTimeoutDirtyTest, 93 kCancelTimeoutCleanTest, 94 kCancelTimeoutDirtyTest 95} idle_exit_test_t; 96 97typedef struct shared_mem_t { 98 pthread_mutex_t mutex; 99 pthread_cond_t cv; 100 boolean_t completed; 101 boolean_t pressure_event_fired; 102 boolean_t child_failed; 103} shared_mem_t; 104 105shared_mem_t *g_shared = NULL; 106unsigned long g_physmem = 0; 107int g_compressor_mode=0; 108int g_ledger_count = -1, g_footprint_index = -1; 109int64_t g_per_process_limit = -1; 110 111/* 112 * g_exit_status: 113 * Holds the PASS/FAIL status of the memorystatus 114 * test run as a whole. 115 * e.g: If one subtest reports failure, the entire 116 * test run reports failure. 117 * 118 * PASS: returns 0 (default) 119 * FAIL: returns -1 120 * 121 * The only time the g_exit_status changes state 122 * is when printTestResult() reports a FAIL status. 123 */ 124int g_exit_status = 0; 125 126 127extern int ledger(int cmd, caddr_t arg1, caddr_t arg2, caddr_t arg3); 128static boolean_t check_properties(pid_t pid, int32_t requested_priority, int32_t requested_limit_mb, uint64_t requested_user_data, const char *test); 129 130/* Utilities. */ 131 132static void 133printTestHeader(pid_t testPid, const char *testName, ...) 134{ 135 va_list va; 136 printf("========================================\n"); 137 printf("[TEST] "); 138 va_start(va, testName); 139 vprintf(testName, va); 140 va_end(va); 141 printf("\n"); 142 printf("[PID] %d\n", testPid); 143 printf("========================================\n"); 144 printf("[BEGIN]\n"); 145 fflush(stdout); 146} 147 148static void 149printTestResult(const char *testName, boolean_t didPass, const char *msg, ...) 150{ 151 if (msg != NULL) { 152 va_list va; 153 printf("\t\t"); 154 va_start(va, msg); 155 vprintf(msg, va); 156 va_end(va); 157 printf("\n"); 158 } 159 if (didPass) { 160 printf("[PASS]\t%s\n\n", testName); 161 } else { 162 printf("[FAIL]\t%s\n\n", testName); 163 164 /* Any single failure, fails full test run */ 165 g_exit_status = -1; 166 } 167 fflush(stdout); 168} 169 170static int 171_get_munch_interval(int given_interval) 172{ 173 int res; 174 int new_interval=0; 175 char *slow_device; 176 char model_name_buf[1025]; 177 size_t mnb_size = 1024; 178 res = sysctlbyname("hw.model", model_name_buf, &mnb_size, NULL, 0); 179 180 if (res) { 181 perror("\t\tsysctlbyname(hw.model...)"); 182 } 183 else { 184 /* see if we're a slow device (N90, K66, J33) */ 185 slow_device = strstr(model_name_buf, "N90"); 186 if (slow_device == NULL) { 187 slow_device = strstr(model_name_buf, "K66"); 188 } 189 if (slow_device == NULL) { 190 slow_device = strstr(model_name_buf, "J33"); 191 } 192 193 if (slow_device != NULL) { 194 printf("\t\tRunning on a slow device...\n"); 195 } 196 197 if (given_interval == 0) { 198 if (slow_device != NULL) { 199 new_interval = 500 * 1000; /* want sleep time in microseconds */ 200 } 201 else { 202 new_interval = 100 * 1000;/* want sleep time in microseconds */ 203 } 204 } 205 else { 206 new_interval = given_interval * USEC_PER_SEC; 207 } 208 } 209 210 return new_interval; 211} 212 213static CFDictionaryRef create_dictionary_from_plist(const char *path) { 214 void *bytes = NULL; 215 CFDataRef data = NULL; 216 CFDictionaryRef options = NULL; 217 size_t bufLen; 218 int fd = open(path, O_RDONLY, 0); 219 if (fd == -1) { 220 goto exit; 221 } 222 struct stat sb; 223 if (fstat(fd, &sb) == -1) { 224 goto exit; 225 } 226 227 bufLen = (size_t)sb.st_size; 228 bytes = malloc(bufLen); 229 if (bytes == NULL) { 230 goto exit; 231 } 232 233 if (read(fd, bytes, bufLen) != bufLen) { 234 goto exit; 235 } 236 237 data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *) bytes, bufLen, kCFAllocatorNull); 238 if (data == NULL) { 239 goto exit; 240 } 241 242 options = (CFDictionaryRef) CFPropertyListCreateWithData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL, NULL); 243 if (options == NULL) { 244 goto exit; 245 } 246 247exit: 248 if (data != NULL) { 249 CFRelease(data); 250 } 251 if (bytes != NULL) { 252 free(bytes); 253 } 254 if (fd != -1) { 255 close(fd); 256 } 257 258 return options; 259} 260 261 262/* 263 * cleanup_and_exit(): 264 * The parent process can call this routine to exit or abort 265 * the test run at any time. 266 * 267 * The child process on the other hand should not call this routine. 268 * Be mindful about how re-enabling the crashreporter can affect tests 269 * further down the line. 270 */ 271static void cleanup_and_exit(int status) { 272 273 /* Exit. Pretty literal. */ 274 exit(status); 275} 276 277/* 278 * child_ready(): 279 * After a child process takes care of its inital setup, it 280 * synchronizes back to the parent using this call. 281 * 282 * If the child process experiences a failure during its 283 * intial setup, it should abort using a standard exit 284 * routine, leaving crashreporter cleanup to the parent. 285 * 286 * The child should never call cleanup_and_exit(). 287 * That's for the parent only. 288 */ 289static void child_ready() { 290 pthread_mutex_lock(&g_shared->mutex); 291 pthread_cond_signal(&g_shared->cv); 292 pthread_mutex_unlock(&g_shared->mutex); 293} 294 295static pid_t init_and_fork() { 296 int pid; 297 298 g_shared->completed = 0; 299 g_shared->pressure_event_fired = 0; 300 301 pthread_mutex_lock(&g_shared->mutex); 302 303 pid = fork(); 304 if (pid == 0) { 305 return 0; 306 } else if (pid == -1) { 307 printTestResult(__func__, false, "Fork error!"); 308 cleanup_and_exit(-1); 309 } 310 311 /* Wait for child's signal */ 312 pthread_cond_wait(&g_shared->cv, &g_shared->mutex); 313 pthread_mutex_unlock(&g_shared->mutex); 314 return (pid_t)pid; 315} 316 317static memorystatus_priority_entry_t *get_priority_list(int *size) { 318 memorystatus_priority_entry_t *list = NULL; 319 320 assert(size); 321 322 *size = memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST, 0, 0, NULL, 0); 323 if (*size <= 0) { 324 printf("\t\tCan't get list size: %d!\n", *size); 325 goto exit; 326 } 327 328 list = (memorystatus_priority_entry_t*)malloc(*size); 329 if (!list) { 330 printf("\t\tCan't allocate list!\n"); 331 goto exit; 332 } 333 334 *size = memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST, 0, 0, list, *size); 335 if (*size <= 0) { 336 printf("\t\tCan't retrieve list!\n"); 337 goto exit; 338 } 339 340exit: 341 return list; 342} 343 344/* Tests */ 345 346 347static boolean_t get_ledger_info(pid_t pid, int64_t *balance_mb, int64_t *limit_mb) { 348 struct ledger_entry_info *lei; 349 uint64_t count; 350 boolean_t res = false; 351 352 lei = (struct ledger_entry_info *)malloc((size_t)(g_ledger_count * sizeof (*lei))); 353 if (lei) { 354 void *arg; 355 356 arg = (void *)(long)pid; 357 count = g_ledger_count; 358 359 if ((ledger(LEDGER_ENTRY_INFO, arg, (caddr_t)lei, (caddr_t)&count) >= 0) && (g_footprint_index < count)) { 360 if (balance_mb) { 361 *balance_mb = lei[g_footprint_index].lei_balance; 362 } 363 if (limit_mb) { 364 *limit_mb = lei[g_footprint_index].lei_limit; 365 } 366 res = true; 367 } 368 369 free(lei); 370 } 371 372 return res; 373} 374 375static boolean_t get_priority_props(pid_t pid, int32_t *priority, int32_t *limit_mb, uint64_t *user_data) { 376 int size; 377 memorystatus_priority_entry_t *entries = NULL; 378 int i; 379 boolean_t res = false; 380 381 entries = get_priority_list(&size); 382 if (!entries) { 383 goto exit; 384 } 385 386 /* Locate */ 387 for (i = 0; i < size/sizeof(memorystatus_priority_entry_t); i++ ){ 388 if (entries[i].pid == pid) { 389 int64_t limit; 390 391 *priority = entries[i].priority; 392 *user_data = entries[i].user_data; 393#if 1 394 *limit_mb = entries[i].limit; 395 res = true; 396#else 397 res = get_ledger_info(entries[i].pid, NULL, &limit); 398 if (false == res) { 399 printf("Failed to get highwater!\n"); 400 } 401 /* The limit is retrieved in bytes, but set in MB, so rescale */ 402 *limit_mb = (int32_t)(limit/(1024 * 1024)); 403#endif 404 goto exit; 405 } 406 } 407 408 printf("\t\tCan't find pid: %d!\n", pid); 409 410exit: 411 if (entries) 412 free(entries); 413 414 return res; 415} 416 417static boolean_t check_properties(pid_t pid, int32_t requested_priority, int32_t requested_limit_mb, uint64_t requested_user_data, const char *test) { 418 const char *PROP_GET_ERROR_STRING = "failed to get properties"; 419 const char *PROP_CHECK_ERROR_STRING = "property mismatch"; 420 421 int32_t actual_priority, actual_hiwat; 422 uint64_t actual_user_data; 423 424 if (!get_priority_props(pid, &actual_priority, &actual_hiwat, &actual_user_data)) { 425 printf("\t\t%s test failed: %s\n", test, PROP_GET_ERROR_STRING); 426 return false; 427 } 428 429 /* -1 really means the default per-process limit, which varies per device */ 430 if (requested_limit_mb <= 0) { 431 requested_limit_mb = (int32_t)g_per_process_limit; 432 } 433 434 if (actual_priority != requested_priority || actual_hiwat != requested_limit_mb || actual_user_data != requested_user_data) { 435 printf("\t\t%s test failed: %s\n", test, PROP_CHECK_ERROR_STRING); 436 printf("priority is %d, should be %d\n", actual_priority, requested_priority); 437 printf("hiwat is %d, should be %d\n", actual_hiwat, requested_limit_mb); 438 printf("user data is 0x%llx, should be 0x%llx\n", actual_user_data, requested_user_data); 439 return false; 440 } 441 442 printf("\t\t%s test ok...\n", test); 443 444 return true; 445} 446 447 448static void start_list_validation_test() { 449 int size; 450 memorystatus_priority_entry_t *entries = NULL; 451 int i; 452 boolean_t valid = false; 453 454 printTestHeader(getpid(), "List validation test"); 455 456 entries = get_priority_list(&size); 457 if (!entries) { 458 printf("Can't get entries!\n"); 459 goto exit; 460 } 461 462 /* Validate */ 463 for (i = 0; i < size/sizeof(memorystatus_priority_entry_t); i++ ) { 464 int dirty_ret; 465 uint32_t dirty_flags; 466 467 /* Make sure launchd isn't in the list - <rdar://problem/13168754> */ 468 if (entries[i].pid <= 1) { 469 printf("\t\tBad process (%d) in list!\n", entries[i].pid); 470 goto exit; 471 } 472 473 /* Sanity check idle exit state */ 474 dirty_ret = proc_get_dirty(entries[i].pid, &dirty_flags); 475 if (dirty_ret != 0) { 476 dirty_flags = 0; 477 } 478 479 if (dirty_flags & PROC_DIRTY_ALLOWS_IDLE_EXIT) { 480 /* Check that the process isn't at idle priority when dirty */ 481 if ((entries[i].priority == JETSAM_PRIORITY_IDLE) && (dirty_flags & PROC_DIRTY_IS_DIRTY)) { 482 printf("\t\tProcess %d at idle priority when dirty (priority %d, flags 0x%x)!\n", entries[i].pid, entries[i].priority, dirty_flags); 483 goto exit; 484 } 485 /* Check that the process is at idle (or deferred) priority when clean. */ 486 if ((entries[i].priority > JETSAM_PRIORITY_IDLE_DEFERRED) && !(dirty_flags & PROC_DIRTY_IS_DIRTY)) { 487 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); 488 goto exit; 489 } 490 } 491 } 492 493 valid = true; 494 495exit: 496 if (entries) 497 free(entries); 498 499 printTestResult("List validation test", valid, NULL); 500} 501 502/* Random individual tests */ 503static void start_general_sanity_test() { 504 int ret, size; 505 int i; 506 boolean_t valid = false; 507 508 /* 509 * The sanity test checks for permission failures 510 * against P_MEMSTAT_INTERNAL processes. 511 * Currently only launchd (pid==1) qualifies. 512 */ 513 514 printTestHeader(getpid(), "Sanity test"); 515 516 517 /* Ensure that launchd's transaction state is fixed */ 518 ret = proc_track_dirty(1, PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT | PROC_DIRTY_DEFER); 519 if (ret != EPERM) { 520 printf("\t\tNo EPERM tracking launchd (%d/%d)!\n", ret, errno); 521 goto exit; 522 } else { 523 printf("\t\tlaunchd track test OK!\n"); 524 } 525 526 ret = proc_set_dirty(1, true); 527 if (ret != EPERM) { 528 printf("\t\tNo EPERM setting launchd dirty state (%d/%d)!\n", ret, errno); 529 goto exit; 530 } else { 531 printf("\t\tlaunchd dirty test OK!\n"); 532 } 533 534 535 valid = true; 536 537exit: 538 printTestResult("Sanity test", valid, NULL); 539} 540 541static void idle_exit_deferral_test(idle_exit_test_t test) { 542 int secs = DEFERRED_IDLE_EXIT_TIME_SECS; 543 544 child_ready(); 545 546 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#1 - pre xpc_track_activity()")) { 547 goto exit; 548 } 549 550 proc_track_dirty(getpid(), PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT | PROC_DIRTY_DEFER); 551 552 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#2 - post xpc_track_activity()")) { 553 goto exit; 554 } 555 556 /* Toggle */ 557 proc_set_dirty(getpid(), true); 558 proc_set_dirty(getpid(), false); 559 proc_set_dirty(getpid(), true); 560 proc_set_dirty(getpid(), false); 561 562 switch (test) { 563 case kDeferTimeoutCleanTest: 564 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#3 - post toggle")) { 565 goto exit; 566 } 567 568 /* Approximate transition check */ 569 sleep(secs - 1); 570 571 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#4 - pre timeout")) { 572 goto exit; 573 } 574 575 sleep(2); 576 577 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#5 - post timeout")) { 578 goto exit; 579 } 580 581 proc_set_dirty(getpid(), true); 582 583 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#6 - post dirty")) { 584 goto exit; 585 } 586 587 proc_set_dirty(getpid(), false); 588 589 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#7 - post clean")) { 590 goto exit; 591 } 592 593 break; 594 case kDeferTimeoutDirtyTest: 595 proc_set_dirty(getpid(), true); 596 597 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#3 - post dirty")) { 598 goto exit; 599 } 600 601 /* Approximate transition check */ 602 sleep(secs - 1); 603 604 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#4 - pre timeout")) { 605 goto exit; 606 } 607 608 sleep(2); 609 610 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#5 - post timeout")) { 611 goto exit; 612 } 613 614 proc_set_dirty(getpid(), false); 615 616 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#6 - post clean")) { 617 goto exit; 618 } 619 620 break; 621 case kCancelTimeoutDirtyTest: 622 proc_set_dirty(getpid(), true); 623 624 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#3 - post toggle")) { 625 goto exit; 626 } 627 628 proc_clear_dirty(getpid(), PROC_DIRTY_DEFER); 629 630 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#4 - post deferral cancellation")) { 631 goto exit; 632 } 633 634 proc_set_dirty(getpid(), false); 635 636 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#5 - post toggle")) { 637 goto exit; 638 } 639 640 break; 641 case kCancelTimeoutCleanTest: 642 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#3 - post toggle")) { 643 goto exit; 644 } 645 646 proc_clear_dirty(getpid(), PROC_DIRTY_DEFER); 647 648 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#4 - post deferral cancellation")) { 649 goto exit; 650 } 651 652 proc_set_dirty(getpid(), true); 653 654 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#5 - post dirty")) { 655 goto exit; 656 } 657 658 proc_set_dirty(getpid(), false); 659 660 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#6 - post clean")) { 661 goto exit; 662 } 663 664 break; 665 } 666 667 g_shared->completed = 1; 668 exit(0); 669 670exit: 671 printTestResult(__func__, false, "Something bad happened..."); 672 exit(-1); 673} 674 675static void start_idle_exit_defer_test(idle_exit_test_t test) { 676 pid_t pid; 677 int status; 678 679 /* Reset */ 680 memset(g_shared, 0, sizeof(shared_mem_t)); 681 682 pid = init_and_fork(); 683 if (pid == 0) { 684 idle_exit_deferral_test(test); 685 } 686 else { 687 printTestHeader(pid, "Idle exit deferral test: %d", test); 688 } 689 690 /* Wait for exit */ 691 waitpid(pid, &status, 0); 692 /* Idle exit not reported on embedded */ 693 // wait_for_exit_event(pid, kMemorystatusKilledIdleExit); 694 695 printTestResult("Idle exit deferral test", g_shared->completed, NULL); 696} 697 698static void ledger_init(void) { 699 const char *physFootprintName = "phys_footprint"; 700 struct ledger_info li; 701 int64_t template_cnt; 702 struct ledger_template_info *templateInfo; 703 void *arg; 704 int i; 705 706 /* Grab ledger entries */ 707 arg = (void *)(long)getpid(); 708 if (ledger(LEDGER_INFO, arg, (caddr_t)&li, NULL) < 0) { 709 exit(-1); 710 } 711 712 g_ledger_count = template_cnt = li.li_entries; 713 714 templateInfo = malloc(template_cnt * sizeof (struct ledger_template_info)); 715 if (templateInfo == NULL) { 716 exit (-1); 717 } 718 719 if (!(ledger(LEDGER_TEMPLATE_INFO, (caddr_t)templateInfo, (caddr_t)&template_cnt, NULL) < 0)) { 720 for (i = 0; i < template_cnt; i++) { 721 if (!strncmp(templateInfo[i].lti_name, physFootprintName, strlen(physFootprintName))) { 722 g_footprint_index = i; 723 break; 724 } 725 } 726 } 727 728 free(templateInfo); 729} 730 731static void run_tests(const char *path) { 732 /* Embedded-only */ 733#pragma unused(path) 734 735 /* Generic */ 736 start_general_sanity_test(); 737 start_list_validation_test(); 738 start_idle_exit_defer_test(kDeferTimeoutCleanTest); 739 start_idle_exit_defer_test(kDeferTimeoutDirtyTest); 740 start_idle_exit_defer_test(kCancelTimeoutCleanTest); 741 start_idle_exit_defer_test(kCancelTimeoutDirtyTest); 742} 743 744 745int main(int argc, char **argv) 746{ 747 pthread_mutexattr_t attr; 748 pthread_condattr_t cattr; 749 size_t size; 750 751 /* Must be run as root for priority retrieval */ 752 if (getuid() != 0) { 753 fprintf(stderr, "%s must be run as root.\n", getprogname()); 754 exit(EXIT_FAILURE); 755 } 756 757 758 /* Memory */ 759 size = sizeof(g_physmem); 760 if (sysctlbyname("hw.physmem", &g_physmem, &size, NULL, 0) != 0 || !g_physmem) { 761 printTestResult(__func__, false, "Failed to retrieve system memory"); 762 cleanup_and_exit(-1); 763 } 764 765 /* VM Compressor Mode */ 766 size = sizeof(g_compressor_mode); 767 if (sysctlbyname("vm.compressor_mode", &g_compressor_mode, &size, NULL, 0) != 0) { 768 printTestResult(__func__, false, "Failed to retrieve compressor config"); 769 cleanup_and_exit(-1); 770 } 771 772 /* Ledger; default limit applies to this process, so grab it here */ 773 ledger_init(); 774 if ((-1 == g_ledger_count) || (-1 == g_footprint_index) || (false == get_ledger_info(getpid(), NULL, &g_per_process_limit))) { 775 printTestResult("setup", false, "Unable to init ledger!\n"); 776 cleanup_and_exit(-1); 777 } 778 779 if (g_per_process_limit == LEDGER_LIMIT_INFINITY) { 780 g_per_process_limit = 0; 781 } else { 782 /* Rescale to MB */ 783 g_per_process_limit /= (1024 * 1024); 784 } 785 786 /* Shared memory */ 787 g_shared = mmap(NULL, sizeof(shared_mem_t), PROT_WRITE|PROT_READ, MAP_ANON|MAP_SHARED, 0, 0); 788 if (!g_shared) { 789 printTestResult(__func__, false, "Failed mmap"); 790 cleanup_and_exit(-1); 791 } 792 793 /* Guarantee size of random_data buffer */ 794 if (sizeof(random_data) < RANDOM_DATA_SIZE) { 795 printTestResult(__func__, false, "Failed to guarantee random_data buffer size [expected %d, actual %d]", 796 RANDOM_DATA_SIZE, sizeof(random_data)); 797 cleanup_and_exit(-1); 798 } 799 800 pthread_mutexattr_init(&attr); 801 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED ); 802 803 pthread_condattr_init(&cattr); 804 pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); 805 806 if (pthread_mutex_init(&g_shared->mutex, &attr) || pthread_cond_init(&g_shared->cv, &cattr)) { 807 printTestResult("setup", false, "Unable to init condition variable!"); 808 cleanup_and_exit(-1); 809 } 810 811 run_tests(argv[0]); 812 813 /* Teardown */ 814 pthread_mutex_destroy(&g_shared->mutex); 815 pthread_cond_destroy(&g_shared->cv); 816 817 pthread_mutexattr_destroy(&attr); 818 pthread_condattr_destroy(&cattr); 819 820 821 return (g_exit_status); /* exit status 0 on success, -1 on failure */ 822} 823