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 21#include <sys/event.h> 22#include <sys/ipc.h> 23#include <sys/kern_memorystatus.h> 24#include <sys/mman.h> 25#include <sys/shm.h> 26#include <sys/stat.h> 27#include <sys/sysctl.h> 28#include <sys/wait.h> 29 30#include <xpc/xpc.h> 31#include <xpc/private.h> 32 33#include <CoreFoundation/CoreFoundation.h> 34 35#include <Security/Security.h> 36#include <ServiceManagement/ServiceManagement.h> 37#include <ServiceManagement/SMErrors.h> 38 39#include <Kernel/kern/ledger.h> 40 41#include <sys/spawn_internal.h> 42#include <spawn_private.h> 43 44#define CR_JOB "com.apple.ReportCrash.Jetsam" 45#define CR_JOB_PLIST_PATH "/System/Library/LaunchDaemons/com.apple.ReportCrash.Jetsam.plist" 46 47#define ERR_BUF_LEN 1024 48 49#ifndef VM_PAGE_SIZE 50#define VM_PAGE_SIZE 4096 51#endif 52 53/* 54 * TODO: import header (currently vm_pageout.h) without pulling in extraneous definitions; 55 * see <rdar://problem/13374916>. 56 */ 57#ifndef VM_PAGER_FREEZER_DEFAULT 58#define VM_PAGER_FREEZER_DEFAULT 0x8 /* Freezer backed by default pager.*/ 59#endif 60 61/* 62 * Special note to ourselves: the jetsam cause to look out for is *either* 63 * a high watermark kill, *or* a per-process kill. 64 */ 65#define CAUSE_HIWAT_OR_PERPROC -1 66 67typedef enum jetsam_test { 68 kSimpleJetsamTest = 1, 69 kPressureJetsamTestFG, 70 kPressureJetsamTestBG, 71 kHighwaterJetsamTest, 72 kVnodeJetsamTest, 73 kBackgroundJetsamTest 74} jetsam_test_t; 75 76typedef enum idle_exit_test { 77 kDeferTimeoutCleanTest = 1, 78 kDeferTimeoutDirtyTest, 79 kCancelTimeoutCleanTest, 80 kCancelTimeoutDirtyTest 81} idle_exit_test_t; 82 83typedef struct shared_mem_t { 84 pthread_mutex_t mutex; 85 pthread_cond_t cv; 86 boolean_t completed; 87 boolean_t pressure_event_fired; 88} shared_mem_t; 89 90shared_mem_t *g_shared = NULL; 91unsigned long g_physmem = 0; 92int g_ledger_count = -1, g_footprint_index = -1; 93int64_t g_per_process_limit = -1; 94 95#if TARGET_OS_EMBEDDED 96static boolean_t set_priority(pid_t pid, int32_t priority, uint64_t user_data); 97#endif 98 99extern int ledger(int cmd, caddr_t arg1, caddr_t arg2, caddr_t arg3); 100static boolean_t check_properties(pid_t pid, int32_t requested_priority, int32_t requested_limit_mb, uint64_t requested_user_data, const char *test); 101 102/* Utilities. */ 103 104static void 105printTestHeader(pid_t testPid, const char *testName, ...) 106{ 107 va_list va; 108 printf("========================================\n"); 109 printf("[TEST] "); 110 va_start(va, testName); 111 vprintf(testName, va); 112 va_end(va); 113 printf("\n"); 114 printf("[PID] %d\n", testPid); 115 printf("========================================\n"); 116 printf("[BEGIN]\n"); 117} 118 119static void 120printTestResult(const char *testName, boolean_t didPass, const char *msg, ...) 121{ 122 if (msg != NULL) { 123 va_list va; 124 printf("\t\t"); 125 va_start(va, msg); 126 vprintf(msg, va); 127 va_end(va); 128 printf("\n"); 129 } 130 if (didPass) { 131 printf("[PASS]\t%s\n\n", testName); 132 } else { 133 printf("[FAIL]\t%s\n\n", testName); 134 } 135} 136 137static CFDictionaryRef create_dictionary_from_plist(const char *path) { 138 void *bytes = NULL; 139 CFDataRef data = NULL; 140 CFDictionaryRef options = NULL; 141 size_t bufLen; 142 int fd = open(path, O_RDONLY, 0); 143 if (fd == -1) { 144 goto exit; 145 } 146 struct stat sb; 147 if (fstat(fd, &sb) == -1) { 148 goto exit; 149 } 150 151 bufLen = (size_t)sb.st_size; 152 bytes = malloc(bufLen); 153 if (bytes == NULL) { 154 goto exit; 155 } 156 157 if (read(fd, bytes, bufLen) != bufLen) { 158 goto exit; 159 } 160 161 data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *) bytes, bufLen, kCFAllocatorNull); 162 if (data == NULL) { 163 goto exit; 164 } 165 166 options = (CFDictionaryRef) CFPropertyListCreateFromXMLData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL); 167 if (options == NULL) { 168 } 169 170exit: 171 if (data != NULL) { 172 CFRelease(data); 173 } 174 if (bytes != NULL) { 175 free(bytes); 176 } 177 if (fd != -1) { 178 close(fd); 179 } 180 181 return options; 182} 183 184#if TARGET_OS_EMBEDDED 185 186static void disable_crashreporter(void) { 187 if (!SMJobRemove(kSMDomainSystemLaunchd, CFSTR(CR_JOB), NULL, true, NULL)) { 188 printf ("\t\tCould not unload %s\n", CR_JOB); 189 } 190} 191 192static void enable_crashreporter(void) { 193 CFDictionaryRef job_dict; 194 195 job_dict = create_dictionary_from_plist(CR_JOB_PLIST_PATH); 196 if (!job_dict) { 197 printf("\t\tCould not create dictionary from %s\n", CR_JOB_PLIST_PATH); 198 } 199 200 if (!SMJobSubmit(kSMDomainSystemLaunchd, job_dict, NULL, NULL)) { 201 printf ("\t\tCould not submit %s\n", CR_JOB); 202 } 203 204 CFRelease(job_dict); 205} 206 207static boolean_t verify_snapshot(pid_t pid, int32_t priority, uint32_t kill_cause, uint64_t user_data, bool expecting_snapshot) { 208 int size; 209 memorystatus_jetsam_snapshot_t *snapshot = NULL; 210 int i; 211 boolean_t res = false; 212 213 if (kill_cause == CAUSE_HIWAT_OR_PERPROC) { 214 kill_cause = kMemorystatusKilledHiwat|kMemorystatusKilledVMPageShortage; 215 } 216 217 size = memorystatus_control(MEMORYSTATUS_CMD_GET_JETSAM_SNAPSHOT, 0, 0, NULL, 0); 218 if (size <= 0) { 219 if (expecting_snapshot) { 220 printf("\t\tCan't get snapshot size: %d!\n", size); 221 } 222 goto exit; 223 } 224 225 snapshot = (memorystatus_jetsam_snapshot_t*)malloc(size); 226 if (!snapshot) { 227 printf("\t\tCan't allocate snapshot!\n"); 228 goto exit; 229 } 230 231 size = memorystatus_control(MEMORYSTATUS_CMD_GET_JETSAM_SNAPSHOT, 0, 0, snapshot, size); 232 if (size <= 0) { 233 printf("\t\tCan't retrieve snapshot (%d)!\n", size); 234 goto exit; 235 } 236 237 if (((size - sizeof(memorystatus_jetsam_snapshot_t)) / sizeof(memorystatus_jetsam_snapshot_entry_t)) != snapshot->entry_count) { 238 printf("\t\tMalformed snapshot: %d! Expected %ld + %zd x %ld = %ld\n", size, 239 sizeof(memorystatus_jetsam_snapshot_t), snapshot->entry_count, sizeof(memorystatus_jetsam_snapshot_entry_t), 240 sizeof(memorystatus_jetsam_snapshot_t) + (snapshot->entry_count * sizeof(memorystatus_jetsam_snapshot_entry_t))); 241 goto exit; 242 } 243 244 if (pid == -1) { 245 /* Just flushing the buffer */ 246 res = true; 247 goto exit; 248 } 249 250 /* Locate */ 251 for (i = 0; i < snapshot->entry_count; i++) { 252 if (snapshot->entries[i].pid == pid) { 253 res = 0; 254 if ((priority == snapshot->entries[i].priority) && ((kill_cause | snapshot->entries[i].killed) == kill_cause) && (user_data == snapshot->entries[i].user_data)) { 255 res = true; 256 } else { 257 printf("\t\tMismatched snapshot properties for pid %d (expected/actual): priority %d/%d : kill cause 0x%x/0x%x : user data 0x%llx/0x%llx\n", 258 pid, priority, snapshot->entries[i].priority, kill_cause, snapshot->entries[i].killed, user_data, snapshot->entries[i].user_data); 259 } 260 goto exit; 261 } 262 } 263 264exit: 265 free(snapshot); 266 267 return res; 268} 269 270#endif /* TARGET_OS_EMBEDDED */ 271 272static void cleanup_and_exit(int status) { 273#if TARGET_OS_EMBEDDED 274 /* Cleanup */ 275 enable_crashreporter(); 276#endif 277 278 /* Exit. Pretty literal. */ 279 exit(status); 280} 281 282static void child_ready() { 283 pthread_mutex_lock(&g_shared->mutex); 284 pthread_cond_signal(&g_shared->cv); 285 pthread_mutex_unlock(&g_shared->mutex); 286} 287 288static pid_t init_and_fork() { 289 int pid; 290 291 g_shared->completed = 0; 292 g_shared->pressure_event_fired = 0; 293 294 pthread_mutex_lock(&g_shared->mutex); 295 296 pid = fork(); 297 if (pid == 0) { 298 return 0; 299 } else if (pid == -1) { 300 printTestResult(__func__, false, "Fork error!\n"); 301 cleanup_and_exit(-1); 302 } 303 304 /* Wait for child's signal */ 305 pthread_cond_wait(&g_shared->cv, &g_shared->mutex); 306 pthread_mutex_unlock(&g_shared->mutex); 307 return (pid_t)pid; 308} 309 310static memorystatus_priority_entry_t *get_priority_list(int *size) { 311 memorystatus_priority_entry_t *list = NULL; 312 313 assert(size); 314 315 *size = memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST, 0, 0, NULL, 0); 316 if (*size <= 0) { 317 printf("\t\tCan't get list size: %d!\n", *size); 318 goto exit; 319 } 320 321 list = (memorystatus_priority_entry_t*)malloc(*size); 322 if (!list) { 323 printf("\t\tCan't allocate list!\n"); 324 goto exit; 325 } 326 327 *size = memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST, 0, 0, list, *size); 328 if (*size <= 0) { 329 printf("\t\tCan't retrieve list!\n"); 330 goto exit; 331 } 332 333exit: 334 return list; 335} 336 337/* Tests */ 338 339#if TARGET_OS_EMBEDDED 340 341/* Spawn tests */ 342 343static void spawn_test() { 344 int page_delta = 32768; /* 128MB */ 345 char *mem; 346 unsigned long total = 0; 347 348 /* Spin */ 349 while (1) { 350 /* Priority will be shifted during this time... */ 351 sleep(1); 352 353 /* ...then process will be backgrounded and hopefully killed by the memory limit */ 354 while(1) { 355 int i; 356 mem = malloc(page_delta * VM_PAGE_SIZE); 357 if (!mem) { 358 fprintf(stderr, "Failed to allocate memory!\n"); 359 while (1) { 360 sleep(1); 361 } 362 } 363 364 total += page_delta; 365 memset(mem, 0xFF, page_delta * VM_PAGE_SIZE); 366 367 set_priority(getpid(), JETSAM_PRIORITY_BACKGROUND, 0); 368 369 while(1) { 370 sleep(1); 371 } 372 } 373 } 374} 375 376#endif 377 378static boolean_t get_ledger_info(pid_t pid, int64_t *balance_mb, int64_t *limit_mb) { 379 struct ledger_entry_info *lei; 380 uint64_t count; 381 boolean_t res = false; 382 383 lei = (struct ledger_entry_info *)malloc((size_t)(g_ledger_count * sizeof (*lei))); 384 if (lei) { 385 void *arg; 386 387 arg = (void *)(long)pid; 388 count = g_ledger_count; 389 390 if ((ledger(LEDGER_ENTRY_INFO, arg, (caddr_t)lei, (caddr_t)&count) >= 0) && (g_footprint_index < count)) { 391 if (balance_mb) { 392 *balance_mb = lei[g_footprint_index].lei_balance; 393 } 394 if (limit_mb) { 395 *limit_mb = lei[g_footprint_index].lei_limit; 396 } 397 res = true; 398 } 399 400 free(lei); 401 } 402 403 return res; 404} 405 406static boolean_t get_priority_props(pid_t pid, int32_t *priority, int32_t *limit_mb, uint64_t *user_data) { 407 int size; 408 memorystatus_priority_entry_t *entries = NULL; 409 int i; 410 boolean_t res = false; 411 412 entries = get_priority_list(&size); 413 if (!entries) { 414 goto exit; 415 } 416 417 /* Locate */ 418 for (i = 0; i < size/sizeof(memorystatus_priority_entry_t); i++ ){ 419 if (entries[i].pid == pid) { 420 int64_t limit; 421 422 *priority = entries[i].priority; 423 *user_data = entries[i].user_data; 424#if 1 425 *limit_mb = entries[i].limit; 426 res = true; 427#else 428 res = get_ledger_info(entries[i].pid, NULL, &limit); 429 if (false == res) { 430 printf("Failed to get highwater!\n"); 431 } 432 /* The limit is retrieved in bytes, but set in MB, so rescale */ 433 *limit_mb = (int32_t)(limit/(1024 * 1024)); 434#endif 435 goto exit; 436 } 437 } 438 439 printf("\t\tCan't find pid: %d!\n", pid); 440 441exit: 442 free(entries); 443 444 return res; 445} 446 447static boolean_t check_properties(pid_t pid, int32_t requested_priority, int32_t requested_limit_mb, uint64_t requested_user_data, const char *test) { 448 const char *PROP_GET_ERROR_STRING = "failed to get properties"; 449 const char *PROP_CHECK_ERROR_STRING = "property mismatch"; 450 451 int32_t actual_priority, actual_hiwat; 452 uint64_t actual_user_data; 453 454 if (!get_priority_props(pid, &actual_priority, &actual_hiwat, &actual_user_data)) { 455 printf("\t\t%s test failed: %s\n", test, PROP_GET_ERROR_STRING); 456 return false; 457 } 458 459 /* -1 really means the default per-process limit, which varies per device */ 460 if (requested_limit_mb <= 0) { 461 requested_limit_mb = g_per_process_limit; 462 } 463 464 if (actual_priority != requested_priority || actual_hiwat != requested_limit_mb || actual_user_data != requested_user_data) { 465 printf("\t\t%s test failed: %s\n", test, PROP_CHECK_ERROR_STRING); 466 printf("priority is %d, should be %d\n", actual_priority, requested_priority); 467 printf("hiwat is %d, should be %d\n", actual_hiwat, requested_limit_mb); 468 printf("user data is 0x%llx, should be 0x%llx\n", actual_user_data, requested_user_data); 469 return false; 470 } 471 472 printf("\t\t%s test ok...\n", test); 473 474 return true; 475} 476 477#if TARGET_OS_EMBEDDED 478 479static void spin() { 480 child_ready(); 481 482 /* Spin */ 483 while (1) { 484 sleep(10); 485 } 486} 487 488/* Priority tests */ 489 490static boolean_t set_priority(pid_t pid, int32_t priority, uint64_t user_data) { 491 int ret; 492 memorystatus_priority_properties_t props; 493 494 props.priority = priority; 495 props.user_data = (uint32_t)user_data; 496 497 return memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES, pid, 0, &props, sizeof(props)); 498} 499 500static boolean_t set_memlimit(pid_t pid, int32_t limit_mb) { 501 return memorystatus_control(MEMORYSTATUS_CMD_SET_JETSAM_HIGH_WATER_MARK, pid, limit_mb, NULL, 0); 502} 503 504static boolean_t set_priority_properties(pid_t pid, int32_t priority, int32_t limit_mb, uint64_t user_data, const char *stage, boolean_t show_error) { 505 int ret; 506 507 ret = set_priority(pid, priority, user_data); 508 if (ret == 0) { 509 ret = set_memlimit(pid, limit_mb); 510 } 511 512 if (ret) { 513 if (show_error) { 514 printf("\t\t%s stage: failed to set properties!\n", stage); 515 } 516 517 return false; 518 } 519 520 return true; 521} 522 523static void start_priority_test() { 524 const char *DEFAULT_TEST_STR = "Default"; 525 const char *INVALID_NEGATIVE_TEST_STR = "Invalid (Negative)"; 526 const char *INVALID_POSITIVE_TEST_STR = "Invalid (Positive)"; 527 const char *IDLE_ALIAS_TEST_STR = "Idle Alias"; 528 const char *DEFERRED_TEST_STR = "Deferred"; 529 const char *SUSPENDED_TEST_STR = "Suspended"; 530 const char *FOREGROUND_TEST_STR = "Foreground"; 531 const char *HIGHPRI_TEST_STR = "Highpri"; 532 533 pid_t pid; 534 int status; 535 int success = false; 536 537 pid = init_and_fork(); 538 if (pid == 0) { 539 spin(); 540 } else { 541 printTestHeader(pid, "Priority test"); 542 } 543 544 /* Check the default properties */ 545 if (!check_properties(pid, JETSAM_PRIORITY_DEFAULT, -1, 0, DEFAULT_TEST_STR)) { 546 goto exit; 547 } 548 549 /* Check that setting a negative value (other than -1) leaves properties unchanged */ 550 if (set_priority_properties(pid, -100, 0xABABABAB, 0, INVALID_NEGATIVE_TEST_STR, false) || !check_properties(pid, JETSAM_PRIORITY_DEFAULT, -1, 0, INVALID_NEGATIVE_TEST_STR)) { 551 goto exit; 552 } 553 554 /* Check that setting an out-of-range positive value leaves properties unchanged */ 555 if (set_priority_properties(pid, 100, 0xCBCBCBCB, 0, INVALID_POSITIVE_TEST_STR, false) || !check_properties(pid, JETSAM_PRIORITY_DEFAULT, -1, 0, INVALID_POSITIVE_TEST_STR)) { 556 goto exit; 557 } 558 559 /* Idle-deferred - this should be adjusted down to idle */ 560 if (!set_priority_properties(pid, JETSAM_PRIORITY_IDLE_DEFERRED, 0, 0xBEEF, DEFERRED_TEST_STR, true) || !check_properties(pid, JETSAM_PRIORITY_IDLE, 0, 0xBEEF, DEFERRED_TEST_STR)) { 561 goto exit; 562 } 563 564 /* Suspended */ 565 if (!set_priority_properties(pid, JETSAM_PRIORITY_IDLE, 0, 0xCAFE, SUSPENDED_TEST_STR, true) || !check_properties(pid, JETSAM_PRIORITY_IDLE, 0, 0xCAFE, SUSPENDED_TEST_STR)) { 566 goto exit; 567 } 568 569 /* Foreground */ 570 if (!set_priority_properties(pid, JETSAM_PRIORITY_FOREGROUND, 50, 0xBEEFF00D, FOREGROUND_TEST_STR, true) || !check_properties(pid, JETSAM_PRIORITY_FOREGROUND, 50, 0xBEEFF00D, FOREGROUND_TEST_STR)) { 571 goto exit; 572 } 573 574 /* Hipri */ 575 if (!set_priority_properties(pid, JETSAM_PRIORITY_DEFAULT - 1, 0, 0x01234567, HIGHPRI_TEST_STR, true) || !check_properties(pid, JETSAM_PRIORITY_DEFAULT - 1, 0, 0x01234567, HIGHPRI_TEST_STR)) { 576 goto exit; 577 } 578 579 /* Foreground again (to test that the limit is restored) */ 580 if (!set_priority_properties(pid, JETSAM_PRIORITY_FOREGROUND, 50, 0xBEEFF00D, FOREGROUND_TEST_STR, true) || !check_properties(pid, JETSAM_PRIORITY_FOREGROUND, 50, 0xBEEFF00D, FOREGROUND_TEST_STR)) { 581 goto exit; 582 } 583 584 /* Set foreground priority again; this would have caught <rdar://problem/13056007> */ 585 if (!set_priority_properties(pid, JETSAM_PRIORITY_FOREGROUND, 50, 0xFEEDF00D, FOREGROUND_TEST_STR, true) || !check_properties(pid, JETSAM_PRIORITY_FOREGROUND, 50, 0xFEEDF00D, FOREGROUND_TEST_STR)) { 586 goto exit; 587 } 588 589 /* Set foreground priority again but pass a large memory limit; this would have caught <rdar://problem/13116445> */ 590 if (!set_priority_properties(pid, JETSAM_PRIORITY_FOREGROUND, 4096, 0xBEEFF00D, FOREGROUND_TEST_STR, true) || !check_properties(pid, JETSAM_PRIORITY_FOREGROUND, 4096, 0xBEEFF00D, FOREGROUND_TEST_STR)) { 591 goto exit; 592 } 593 594 /* Check that -1 aliases to JETSAM_PRIORITY_DEFAULT */ 595 if (!set_priority_properties(pid, -1, 0, 0xFADEF00D, IDLE_ALIAS_TEST_STR, true) || !check_properties(pid, JETSAM_PRIORITY_DEFAULT, 0, 0xFADEF00D, IDLE_ALIAS_TEST_STR)) { 596 goto exit; 597 } 598 599 success = true; 600 601exit: 602 603 /* Done here... */ 604 kill(pid, SIGKILL); 605 606 /* Wait for exit */ 607 waitpid(pid, &status, 0); 608 609 printTestResult("Priority test", success, NULL); 610} 611 612/* Reordering */ 613 614static boolean_t check_reorder_priorities(pid_t pid1, pid_t pid2, int priority) { 615 int size; 616 memorystatus_priority_entry_t *entries = NULL; 617 int i; 618 boolean_t res = false; 619 620 entries = get_priority_list(&size); 621 if (!entries) { 622 goto exit; 623 } 624 625 /* Check relative priorities */ 626 for (i = 0; i < size/sizeof(memorystatus_priority_entry_t); i++ ){ 627 if (entries[i].pid == pid1) { 628 /* First process. The priority should match... */ 629 if (entries[i].priority != priority) { 630 goto exit; 631 } 632 633 /* There should be one more daemon to follow... */ 634 if ((i + 1) >= size) { 635 goto exit; 636 } 637 638 /* The next process should be pid2 */ 639 if (entries[i + 1].pid != pid2) { 640 goto exit; 641 } 642 643 /* The priority should also match... */ 644 if (entries[i + 1].priority != priority) { 645 goto exit; 646 } 647 648 break; 649 } 650 } 651 652 res = true; 653 654exit: 655 656 return res; 657} 658 659static void start_fs_priority_test() { 660 const char *REORDER_TEST_STR = "Reorder"; 661 const int test_priority = JETSAM_PRIORITY_FOREGROUND_SUPPORT; 662 663 pid_t pid1, pid2; 664 int status; 665 int success = false; 666 667 pid1 = init_and_fork(); 668 if (pid1 == 0) { 669 spin(); 670 } 671 672 pid2 = init_and_fork(); 673 if (pid2 == 0) { 674 spin(); 675 } 676 677 printTestHeader(pid1, "Reorder test"); 678 679 /* pid2 should follow pid1 in the bucket */ 680 if (!set_priority_properties(pid1, test_priority, 0, 0, REORDER_TEST_STR, true) || !set_priority_properties(pid2, test_priority, 0, 0, REORDER_TEST_STR, true)) { 681 printf("Cannot set priorities - #1!\n"); 682 goto exit; 683 } 684 685 /* Check relative priorities */ 686 if (!check_reorder_priorities(pid1, pid2, test_priority)) { 687 printf("Bad pid1 -> pid2 priorities - #2!\n"); 688 goto exit; 689 } 690 691 /* pid 1 should move to the back... */ 692 if (!set_priority_properties(pid1, test_priority, 0, 0, REORDER_TEST_STR, true)) { 693 printf("Cannot set priorities - #3!\n"); 694 goto exit; 695 } 696 697 /* ...so validate */ 698 if (!check_reorder_priorities(pid2, pid1, test_priority)) { 699 printf("Bad pid2 -> pid1 priorities - #4!\n"); 700 goto exit; 701 } 702 703 /* Again, pid 2 should move to the back... */ 704 if (!set_priority_properties(pid2, test_priority, 0, 0, REORDER_TEST_STR, true)) { 705 printf("Cannot set priorities - #5!\n"); 706 goto exit; 707 } 708 709 /* ...so validate for the last time */ 710 if (!check_reorder_priorities(pid1, pid2, test_priority)) { 711 printf("Bad pid1 -> pid2 priorities - #6!\n"); 712 goto exit; 713 } 714 715 success = true; 716 717exit: 718 719 /* Done here... */ 720 kill(pid1, SIGKILL); 721 kill(pid2, SIGKILL); 722 723 /* Wait for exit */ 724 waitpid(pid1, &status, 0); 725 waitpid(pid2, &status, 0); 726 727 printTestResult("Reorder test", success, NULL); 728} 729 730/* Jetsam tests */ 731 732/* 733 734 ASL message format: 735 736 Message is ReadUID 0 737 Message is ReadGID 80 738 Message is ASLMessageID 703 739 Message is Level 7 740 Message is Time 1333155901 741 Message is Sender kernel 742 Message is Facility kern 743 744 */ 745 746static void vnode_test(int page_delta, int interval, int verbose, int32_t priority, uint64_t user_data) { 747 memorystatus_priority_properties_t props; 748 749 props.priority = priority; 750 props.user_data = user_data; 751 752 if (memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES, getpid(), 0, &props, sizeof(props))) { 753 /*printf("\t\tFailed to set jetsam priority!\n");*/ 754 printTestResult(__func__, false, "Failed to set jetsam priority!"); 755 cleanup_and_exit(-1); 756 } 757 758 /* Initialized... */ 759 child_ready(); 760 761 /* ...so start stealing vnodes */ 762 while(1) { 763 sleep(1); 764 } 765} 766 767static void *wait_for_pressure_event(void *s) { 768 int kq; 769 int res; 770 struct kevent event, mevent; 771 char errMsg[ERR_BUF_LEN + 1]; 772 773 kq = kqueue(); 774 775 EV_SET(&mevent, 0, EVFILT_VM, EV_ADD, NOTE_VM_PRESSURE, 0, 0); 776 777 res = kevent(kq, &mevent, 1, NULL, 0, NULL); 778 if (res != 0) { 779 /*printf("\t\tKevent registration failed - returning: %d!\n", res);*/ 780 snprintf(errMsg, ERR_BUF_LEN, "Kevent registration failed - returning: %d!",res); 781 printTestResult(__func__, false, errMsg); 782 cleanup_and_exit(-1); 783 } 784 785 while (1) { 786 memset(&event, 0, sizeof(struct kevent)); 787 res = kevent(kq, NULL, 0, &event, 1, NULL); 788 g_shared->pressure_event_fired = 1; 789 } 790} 791 792static void wait_for_exit_event(int pid, uint32_t kill_cause) { 793 int kq; 794 int res; 795 uint32_t expected_flag, received_flag; 796 struct kevent event, mevent; 797 char errMsg[ERR_BUF_LEN + 1]; 798 799 switch (kill_cause) { 800 case kMemorystatusKilledVnodes: expected_flag = NOTE_EXIT_MEMORY_VNODE; break; 801 case kMemorystatusKilledVMPageShortage: expected_flag = NOTE_EXIT_MEMORY_VMPAGESHORTAGE; break; 802 case kMemorystatusKilledVMThrashing: expected_flag = NOTE_EXIT_MEMORY_VMTHRASHING; break; 803 case kMemorystatusKilledHiwat: expected_flag = NOTE_EXIT_MEMORY_HIWAT; break; 804 case kMemorystatusKilledPerProcessLimit: expected_flag = NOTE_EXIT_MEMORY_PID; break; 805 case kMemorystatusKilledIdleExit: expected_flag = NOTE_EXIT_MEMORY_IDLE; break; 806 case CAUSE_HIWAT_OR_PERPROC: expected_flag = NOTE_EXIT_MEMORY_HIWAT|NOTE_EXIT_MEMORY_PID; break; 807 } 808 809 kq = kqueue(); 810 811 EV_SET(&mevent, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT | NOTE_EXIT_DETAIL, 0, 0); 812 813 res = kevent(kq, &mevent, 1, NULL, 0, NULL); 814 if (res != 0) { 815 snprintf(errMsg,ERR_BUF_LEN,"Exit kevent registration failed - returning: %d!",res); 816 printTestResult(__func__, false, errMsg); 817 cleanup_and_exit(-1); 818 } 819 820 res = kevent(kq, NULL, 0, &event, 1, NULL); 821 822 /* Check if appropriate flags are set */ 823 if (!event.fflags & NOTE_EXIT_MEMORY) { 824 printTestResult(__func__, false, "Exit event fflags do not contain NOTE_EXIT_MEMORY\n"); 825 cleanup_and_exit(-1); 826 } 827 828 received_flag = event.data & NOTE_EXIT_MEMORY_DETAIL_MASK; 829 if ((received_flag | expected_flag) != expected_flag) { 830 printTestResult(__func__, false, "Exit event data does not contain the expected jetsam flag for cause %x.\n" 831 "\t\t(expected %x, got %x)", kill_cause, expected_flag, received_flag); 832 cleanup_and_exit(-1); 833 } 834} 835 836static void munch_test(int page_delta, int interval, int verbose, int32_t priority, int32_t highwater, uint64_t user_data) { 837 const char *MUNCH_TEST_STR = "Munch"; 838 char *mem; 839 unsigned long total = 0; 840 pthread_t pe_thread; 841 int res; 842 843 /* Start thread to watch for pressure messages */ 844 res = pthread_create(&pe_thread, NULL, wait_for_pressure_event, (void*)g_shared); 845 if (res) { 846 printTestResult(__func__, false, "Error creating pressure event thread!\n"); 847 cleanup_and_exit(-1); 848 } 849 850 if (set_priority_properties(getpid(), priority, highwater, user_data, MUNCH_TEST_STR, false) == false) { 851 printTestResult(__func__, false, "Failed to set jetsam priority!"); 852 cleanup_and_exit(-1); 853 } 854 855 if (!page_delta) { 856 page_delta = 4096; 857 } 858 859 sleep(1); 860 861 /* Initialized... */ 862 child_ready(); 863 864 /* ...so start munch */ 865 while(1) { 866 int i; 867 mem = malloc(page_delta * VM_PAGE_SIZE); 868 if (!mem) { 869 fprintf(stderr, "Failed to allocate memory!\n"); 870 while (1) { 871 sleep(1); 872 } 873 } 874 875 total += page_delta; 876 memset(mem, 0xFF, page_delta * VM_PAGE_SIZE); 877 878 if (verbose) { 879 printf("\t\t%lu pages dirtied...\n", total); 880 } 881 882 sleep(interval); 883 } 884} 885 886static bool is_pressure_test(test) { 887 return ((test == kPressureJetsamTestFG) || (test == kPressureJetsamTestBG)); 888} 889 890static bool verify_exit(pid_t pid, uint32_t kill_cause, time_t start_time, uint32_t test_pri, uint64_t test_user_data, jetsam_test_t test, bool expecting_snapshot) { 891 const char *msg_key = "Message"; 892 const char *time_key = "Time"; 893 aslmsg query; 894 aslresponse response; 895 aslmsg message; 896 char pid_buffer[16]; 897 const char *val; 898 int got_jetsam = 0; 899 bool got_snapshot = 0; 900 bool success; 901 902 /* Wait for exit */ 903 wait_for_exit_event(pid, kill_cause); 904 905 /* Let the messages filter through to the log - arbitrary */ 906 sleep(3); 907 908 query = asl_new(ASL_TYPE_QUERY); 909 asl_set_query(query, ASL_KEY_SENDER, "kernel", ASL_QUERY_OP_EQUAL); 910 asl_set_query(query, ASL_KEY_MSG, "memorystatus", ASL_QUERY_OP_EQUAL|ASL_QUERY_OP_SUBSTRING); 911 snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%d", pid); 912 asl_set_query(query, ASL_KEY_MSG, pid_buffer, ASL_QUERY_OP_EQUAL|ASL_QUERY_OP_SUBSTRING); 913 response = asl_search(NULL, query); 914 asl_free(query); 915 916 while (NULL != (message = aslresponse_next(response))) 917 { 918 val = asl_get(message, time_key); 919 if (val) { 920 uint32_t msg_time = atoi(val); 921 if (msg_time > start_time) { 922 val = asl_get(message, msg_key); 923 if (val) { 924 printf("\t\tFound: %s\n", val); 925 got_jetsam = 1; 926 } 927 } 928 } 929 } 930 931 if (got_jetsam) { 932 got_snapshot = verify_snapshot(pid, test_pri, kill_cause, test_user_data, expecting_snapshot); 933 } else { 934 printf("\t\tCouldn't find jetsam message in log!\n"); 935 } 936 937 aslresponse_free(response); 938 939 success = got_jetsam && (expecting_snapshot == got_snapshot) && (!(is_pressure_test(test)) || (is_pressure_test(test) && g_shared->pressure_event_fired)); 940 printTestResult("munch_test", success, "(test: %d, got_jetsam: %d, got_snapshot: %d, fired: %d)", test, got_jetsam, got_snapshot, g_shared->pressure_event_fired); 941 942 return success; 943} 944 945static void start_jetsam_test(jetsam_test_t test, const char *description) { 946 const char *msg_key = "Message"; 947 const char *time_key = "Time"; 948 const char *val; 949 aslmsg query; 950 aslresponse response; 951 aslmsg message; 952 time_t start_time; 953 pid_t pid; 954 char pid_buffer[16]; 955 int status; 956 int got_jetsam = 0; 957 int got_snapshot = 0; 958 uint32_t test_pri = 0; 959 uint64_t test_user_data = 0; 960 uint32_t kill_cause; 961 int success; 962 boolean_t expecting_snapshot = TRUE; 963 boolean_t big_mem = (g_physmem > 512 * 1024 * 1024); 964 965 if (big_mem) { 966 /* 967 * On big memory machines (1GB+), there is a per-task memory limit. 968 * A munch test could fail because of this, if they manage to cross it; 969 * *or* because the high watermark was crossed, and the system was under 970 * enough mem pressure to go looking for a high watermark victim to kill. 971 */ 972 kill_cause = CAUSE_HIWAT_OR_PERPROC; 973 } else if (test == kHighwaterJetsamTest) { 974 /* 975 * On systems without the per-task memory limit, we shouldn't see any 976 * such kills; so that leaves high watermark kills as the only legitimate 977 * reason to kill a munch test that has a high watermark set. 978 */ 979 kill_cause = kMemorystatusKilledHiwat; 980 } else { 981 /* 982 * If this is a standard munch test and we're on a machine without the 983 * per-task memory limit, the only reason for kill should be that we need 984 * memory. 985 */ 986 kill_cause = kMemorystatusKilledVMPageShortage; 987 } 988 989 start_time = time(NULL); 990 991 switch (test) { 992 case kPressureJetsamTestFG: 993 test_pri = JETSAM_PRIORITY_FOREGROUND; /* Test that FG processes get pressure events */ 994 test_user_data = 0xDEADBEEF; 995 break; 996 case kPressureJetsamTestBG: 997 test_pri = JETSAM_PRIORITY_UI_SUPPORT; /* Test that BG processes get pressure events */ 998 test_user_data = 0xFADEBEEF; 999 break; 1000 case kSimpleJetsamTest: 1001 /* 1002 * On 1GB devices, we should see a snapshot as the per-process limit is hit. 1003 * On 512MB devices, we should see a normal jetsam, and no snapshot. 1004 */ 1005 expecting_snapshot = big_mem ? TRUE : FALSE; 1006 test_pri = JETSAM_PRIORITY_IDLE; /* Suspended */ 1007 test_user_data = 0xFACEF00D; 1008 break; 1009 default: 1010 test_pri = JETSAM_PRIORITY_IDLE; /* Suspended */ 1011 test_user_data = 0xCAFEF00D; 1012 break; 1013 } 1014 1015 pid = init_and_fork(); 1016 1017 if (pid == 0) { 1018 switch (test) { 1019 case kVnodeJetsamTest: 1020 vnode_test(0, 0, 0, test_pri, test_user_data); 1021 break; 1022 case kHighwaterJetsamTest: 1023 munch_test(0, 0, 0, test_pri, 8, test_user_data); 1024 break; 1025 default: 1026 munch_test(0, 0, 0, test_pri, -1, test_user_data); 1027 break; 1028 } 1029 } 1030 else { 1031 printTestHeader(pid, "%s test", description); 1032 } 1033 1034 verify_exit(pid, kill_cause, start_time, test_pri, test_user_data, test, expecting_snapshot); 1035} 1036 1037static void start_jetsam_test_background(const char *path) { 1038 const char *argv[] = { 1039 path, 1040 "-s", 1041 NULL 1042 }; 1043 1044 const uint32_t memlimit = 100; /* 100 MB */ 1045 1046 time_t start_time; 1047 pid_t pid = 1; 1048 int status; 1049 uint32_t test_pri = 0; 1050 posix_spawnattr_t spattr; 1051 int32_t pf_balance; 1052 bool success; 1053 1054 start_time = time(NULL); 1055 1056 pid = 1; 1057 status = 1; 1058 1059 posix_spawnattr_init(&spattr); 1060 posix_spawnattr_setjetsam(&spattr, (POSIX_SPAWN_JETSAM_USE_EFFECTIVE_PRIORITY | POSIX_SPAWN_JETSAM_HIWATER_BACKGROUND), JETSAM_PRIORITY_UI_SUPPORT, 100); 1061 1062 if (posix_spawn(&pid, path, NULL, &spattr, (char *const *)argv, NULL) < 0) { 1063 printf("posix_spawn() failed!\n"); 1064 goto exit; 1065 } 1066 1067 printTestHeader(pid, "Background memory limit test"); 1068 1069 /* Starts in background */ 1070 if (!check_properties(pid, JETSAM_PRIORITY_UI_SUPPORT, memlimit, 0x0, "jetsam_test_background - #1 BG")) { 1071 goto exit; 1072 } 1073 1074 /* Set to foreground - priority and memlimit should change */ 1075 set_priority(pid, JETSAM_PRIORITY_FOREGROUND, 0); 1076 if (!check_properties(pid, JETSAM_PRIORITY_FOREGROUND, 0, 0x0, "jetsam_test_background - #2 FG")) { 1077 goto exit; 1078 } 1079 1080 /* ...and back */ 1081 set_priority(pid, JETSAM_PRIORITY_BACKGROUND, 0); 1082 if (!check_properties(pid, JETSAM_PRIORITY_BACKGROUND, memlimit, 0x0, "jetsam_test_background - #3 BG")) { 1083 goto exit; 1084 } 1085 1086 /* ...and again */ 1087 set_priority(pid, JETSAM_PRIORITY_FOREGROUND, 0); 1088 if (!check_properties(pid, JETSAM_PRIORITY_FOREGROUND, 0, 0x0, "jetsam_test_background - #4 FG")) { 1089 goto exit; 1090 } 1091 1092#if 1 1093 /* 1094 * For now, this is all we can do. Limitations of the ledger mean that this process is credited with 1095 * the dirty pages, *not* the child. At least the memory limit is reported to have shifted dynamically 1096 * by this point. Kill the child and continue. 1097 */ 1098 kill(pid, SIGKILL); 1099#else 1100 /* Let the process dirty 128MB of memory, then background itself */ 1101 verify_exit(pid, kMemorystatusKilledPerProcessLimit, start_time, test_pri, 0, kBackgroundJetsamTest); 1102#endif 1103 1104 success = true; 1105 1106exit: 1107 if (pid != -1) { 1108 kill(pid, SIGKILL); 1109 } 1110 1111 /* Wait for exit */ 1112 waitpid(pid, &status, 0); 1113 1114 printTestResult("Background test", success, NULL); 1115} 1116 1117/* Freeze tests */ 1118 1119/* Cribbed from 'top'... */ 1120static int 1121in_shared_region(mach_vm_address_t addr, cpu_type_t type) { 1122 mach_vm_address_t base = 0, size = 0; 1123 1124 switch(type) { 1125 case CPU_TYPE_ARM: 1126 base = SHARED_REGION_BASE_ARM; 1127 size = SHARED_REGION_SIZE_ARM; 1128 break; 1129 1130 case CPU_TYPE_X86_64: 1131 base = SHARED_REGION_BASE_X86_64; 1132 size = SHARED_REGION_SIZE_X86_64; 1133 break; 1134 1135 case CPU_TYPE_I386: 1136 base = SHARED_REGION_BASE_I386; 1137 size = SHARED_REGION_SIZE_I386; 1138 break; 1139 1140 case CPU_TYPE_POWERPC: 1141 base = SHARED_REGION_BASE_PPC; 1142 size = SHARED_REGION_SIZE_PPC; 1143 break; 1144 1145 case CPU_TYPE_POWERPC64: 1146 base = SHARED_REGION_BASE_PPC64; 1147 size = SHARED_REGION_SIZE_PPC64; 1148 break; 1149 1150 default: { 1151 int t = type; 1152 1153 fprintf(stderr, "unknown CPU type: 0x%x\n", t); 1154 abort(); 1155 } 1156 break; 1157 } 1158 1159 return(addr >= base && addr < (base + size)); 1160} 1161 1162static unsigned long long get_rprvt(mach_port_t task, pid_t pid) { 1163 kern_return_t kr; 1164 1165 mach_vm_size_t rprvt = 0; 1166 mach_vm_size_t empty = 0; 1167 mach_vm_size_t fw_private = 0; 1168 mach_vm_size_t pagesize = VM_PAGE_SIZE; 1169 mach_vm_size_t regs = 0; 1170 1171 mach_vm_address_t addr; 1172 mach_vm_size_t size; 1173 1174 int split = 0; 1175 1176 for (addr = 0; ; addr += size) { 1177 vm_region_top_info_data_t info; 1178 mach_msg_type_number_t count = VM_REGION_TOP_INFO_COUNT; 1179 mach_port_t object_name; 1180 1181 kr = mach_vm_region(task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info, &count, &object_name); 1182 if (kr != KERN_SUCCESS) break; 1183 1184 if (in_shared_region(addr, CPU_TYPE_ARM)) { 1185 // Private Shared 1186 fw_private += info.private_pages_resident * pagesize; 1187 1188 /* 1189 * Check if this process has the globally shared 1190 * text and data regions mapped in. If so, set 1191 * split to TRUE and avoid checking 1192 * again. 1193 */ 1194 if (split == FALSE && info.share_mode == SM_EMPTY) { 1195 vm_region_basic_info_data_64_t b_info; 1196 mach_vm_address_t b_addr = addr; 1197 mach_vm_size_t b_size = size; 1198 count = VM_REGION_BASIC_INFO_COUNT_64; 1199 1200 kr = mach_vm_region(task, &b_addr, &b_size, VM_REGION_BASIC_INFO, (vm_region_info_t)&b_info, &count, &object_name); 1201 if (kr != KERN_SUCCESS) break; 1202 1203 if (b_info.reserved) { 1204 split = TRUE; 1205 } 1206 } 1207 1208 /* 1209 * Short circuit the loop if this isn't a shared 1210 * private region, since that's the only region 1211 * type we care about within the current address 1212 * range. 1213 */ 1214 if (info.share_mode != SM_PRIVATE) { 1215 continue; 1216 } 1217 } 1218 1219 regs++; 1220 1221 /* 1222 * Update counters according to the region type. 1223 */ 1224 1225 if (info.share_mode == SM_COW && info.ref_count == 1) { 1226 // Treat single reference SM_COW as SM_PRIVATE 1227 info.share_mode = SM_PRIVATE; 1228 } 1229 1230 switch (info.share_mode) { 1231 case SM_LARGE_PAGE: 1232 // Treat SM_LARGE_PAGE the same as SM_PRIVATE 1233 // since they are not shareable and are wired. 1234 case SM_PRIVATE: 1235 rprvt += info.private_pages_resident * pagesize; 1236 rprvt += info.shared_pages_resident * pagesize; 1237 break; 1238 1239 case SM_EMPTY: 1240 empty += size; 1241 break; 1242 1243 case SM_COW: 1244 case SM_SHARED: 1245 if (pid == 0) { 1246 // Treat kernel_task specially 1247 if (info.share_mode == SM_COW) { 1248 rprvt += info.private_pages_resident * pagesize; 1249 } 1250 break; 1251 } 1252 1253 if (info.share_mode == SM_COW) { 1254 rprvt += info.private_pages_resident * pagesize; 1255 } 1256 break; 1257 1258 default: 1259 assert(0); 1260 break; 1261 } 1262 } 1263 1264 return rprvt; 1265} 1266 1267static void freeze_test() { 1268 const unsigned long DIRTY_ALLOC = 16 * 1024 * 1024; 1269 unsigned long *ptr; 1270 task_port_t task = mach_task_self(); 1271 1272 child_ready(); 1273 1274 /* Needs to be vm_allocate() here; otherwise the compiler will optimize memset away */ 1275 vm_allocate(task, (vm_address_t *)&ptr, DIRTY_ALLOC, TRUE); 1276 if (ptr) { 1277 int i; 1278 int pid = getpid(); 1279 unsigned long long baseline_rprvt, half_rprvt, rprvt; 1280 1281 /* Get baseline */ 1282 baseline_rprvt = get_rprvt(task, pid); 1283 1284 /* Dirty half */ 1285 memset(ptr, 0xAB, DIRTY_ALLOC / 2); 1286 1287 /* Check RPRVT */ 1288 half_rprvt = get_rprvt(task, pid); 1289 printf("\t\trprvt is %llu\n", half_rprvt); 1290 1291 if (half_rprvt != (baseline_rprvt + (DIRTY_ALLOC / 2))) 1292 { 1293 printTestResult(__func__, false, "Failed to dirty memory"); 1294 cleanup_and_exit(-1); 1295 } 1296 1297 /* Freeze */ 1298 sysctlbyname("kern.memorystatus_freeze", NULL, 0, &pid, sizeof(pid)); 1299 1300 sleep(2); 1301 1302 /* Check RPRVT */ 1303 rprvt = get_rprvt(task, pid); 1304 printf("\t\trprvt is %llu\n", rprvt); 1305 1306 if ((rprvt > (half_rprvt - (DIRTY_ALLOC / 2))) || (rprvt > (64 * 1024)) /* Sanity */) 1307 { 1308 printTestResult(__func__, false, "Failed to freeze memory"); 1309 cleanup_and_exit(-1); 1310 } 1311 1312 /* Thaw */ 1313 sysctlbyname("kern.memorystatus_thaw", NULL, 0, &pid, sizeof(pid)); 1314 1315 sleep(2); 1316 1317 /* Check RPRVT */ 1318 rprvt = get_rprvt(task, pid); 1319 printf("\t\trprvt is %llu\n", rprvt); 1320 1321 if (rprvt < (baseline_rprvt + (DIRTY_ALLOC / 2))) 1322 { 1323 printTestResult(__func__, false, "Failed to thaw memory"); 1324 cleanup_and_exit(-1); 1325 } 1326 1327 /* Dirty the rest */ 1328 memset(ptr + (DIRTY_ALLOC / (2 * sizeof(unsigned long))), 0xBC, DIRTY_ALLOC / 2); 1329 1330 /* Check RPRVT */ 1331 rprvt = get_rprvt(task, pid); 1332 printf("\t\trprvt is %llu\n", rprvt); 1333 1334 if (rprvt < (baseline_rprvt + DIRTY_ALLOC)) 1335 { 1336 printTestResult(__func__, false, "Failed to dirty memory"); 1337 cleanup_and_exit(-1); 1338 } 1339 1340 g_shared->completed = 1; 1341 cleanup_and_exit(0); 1342 } 1343 1344 printTestResult(__func__, false, "Something bad happened..."); 1345 cleanup_and_exit(-1); 1346} 1347 1348static void start_freeze_test() { 1349 pid_t pid; 1350 int status; 1351 int mode; 1352 size_t size; 1353 1354 /* Check to see if the test is applicable */ 1355 size = sizeof(mode); 1356 if (sysctlbyname("vm.compressor_mode", &mode, &size, NULL, 0) != 0) { 1357 printTestHeader(getpid(), "Freeze test"); 1358 printTestResult(__func__, false, "Failed to retrieve compressor config"); 1359 cleanup_and_exit(-1); 1360 } 1361 1362 if (mode != VM_PAGER_FREEZER_DEFAULT) { 1363 printTestHeader(getpid(), "Freeze test"); 1364 printTestResult(__func__, true, "Freeze disabled; skipping test"); 1365 return; 1366 } 1367 1368 /* Reset */ 1369 memset(g_shared, 0, sizeof(shared_mem_t)); 1370 1371 pid = init_and_fork(); 1372 if (pid == 0) { 1373 freeze_test(); 1374 } else { 1375 printTestHeader(pid, "Freeze test"); 1376 } 1377 1378 /* Wait for exit */ 1379 waitpid(pid, &status, 0); 1380 1381 printTestResult("Freeze test", g_shared->completed, NULL); 1382} 1383 1384#endif 1385 1386static void start_list_validation_test() { 1387 int size; 1388 memorystatus_priority_entry_t *entries = NULL; 1389 int i; 1390 boolean_t valid = false; 1391 1392 printTestHeader(getpid(), "List validation test"); 1393 1394 entries = get_priority_list(&size); 1395 if (!entries) { 1396 printf("Can't get entries!\n"); 1397 goto exit; 1398 } 1399 1400 /* Validate */ 1401 for (i = 0; i < size/sizeof(memorystatus_priority_entry_t); i++ ) { 1402 int dirty_ret; 1403 uint32_t dirty_flags; 1404 1405 /* Make sure launchd isn't in the list - <rdar://problem/13168754> */ 1406 if (entries[i].pid <= 1) { 1407 printf("\t\tBad process (%d) in list!\n", entries[i].pid); 1408 goto exit; 1409 } 1410 1411 /* Sanity check idle exit state */ 1412 dirty_ret = proc_get_dirty(entries[i].pid, &dirty_flags); 1413 if (dirty_ret != 0) { 1414 dirty_flags = 0; 1415 } 1416 1417 if (dirty_flags & PROC_DIRTY_ALLOWS_IDLE_EXIT) { 1418 /* Check that the process isn't at idle priority when dirty */ 1419 if ((entries[i].priority == JETSAM_PRIORITY_IDLE) && (dirty_flags & PROC_DIRTY_IS_DIRTY)) { 1420 printf("\t\tProcess %d at idle priority when dirty (priority %d, flags 0x%x)!\n", entries[i].pid, entries[i].priority, dirty_flags); 1421 goto exit; 1422 } 1423 /* Check that the process is at idle (or deferred) priority when clean. */ 1424 if ((entries[i].priority > JETSAM_PRIORITY_IDLE_DEFERRED) && !(dirty_flags & PROC_DIRTY_IS_DIRTY)) { 1425 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); 1426 goto exit; 1427 } 1428 } 1429 } 1430 1431 valid = true; 1432 1433exit: 1434 free(entries); 1435 1436 printTestResult("List validation test", valid, NULL); 1437} 1438 1439/* Random individual tests */ 1440static void start_general_sanity_test() { 1441 int ret, size; 1442 memorystatus_priority_entry_t *entries = NULL; 1443 int i; 1444 boolean_t valid = false; 1445 1446 printTestHeader(getpid(), "Sanity test"); 1447 1448 /* Should not be able to set the priority of launchd... */ 1449 ret = set_priority(1, JETSAM_PRIORITY_FOREGROUND, 0); 1450 if (ret != -1 || errno != EPERM) { 1451 printf("\t\tAble to set priority of launchd (%d/%d)!\n", ret, errno); 1452 goto exit; 1453 } else { 1454 printf("\t\tlaunchd priority test OK!\n"); 1455 } 1456 1457 /* ...nor the memory limit... */ 1458 ret = set_memlimit(1, 100); 1459 if (ret != -1 || errno != EPERM) { 1460 printf("\t\tNo EPERM setting launchd memlimit (%d/%d)!\n", ret, errno); 1461 goto exit; 1462 } else { 1463 printf("\t\tlaunchd memlimit test OK!\n"); 1464 } 1465 1466 /* ...nor tinker with transactions */ 1467 ret = proc_track_dirty(1, PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT | PROC_DIRTY_DEFER); 1468 if (ret != EPERM) { 1469 printf("\t\tNo EPERM tracking launchd (%d/%d)!\n", ret, errno); 1470 goto exit; 1471 } else { 1472 printf("\t\tlaunchd track test OK!\n"); 1473 } 1474 1475 ret = proc_set_dirty(1, true); 1476 if (ret != EPERM) { 1477 printf("\t\tNo EPERM setting launchd dirty state (%d/%d)!\n", ret, errno); 1478 goto exit; 1479 } else { 1480 printf("\t\tlaunchd dirty test OK!\n"); 1481 } 1482 1483 valid = true; 1484 1485exit: 1486 free(entries); 1487 1488 printTestResult("Idle exit test", valid, NULL); 1489} 1490 1491static void idle_exit_deferral_test(idle_exit_test_t test) { 1492 int secs = DEFERRED_IDLE_EXIT_TIME_SECS; 1493 1494 child_ready(); 1495 1496 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#1 - pre xpc_track_activity()")) { 1497 goto exit; 1498 } 1499 1500 proc_track_dirty(getpid(), PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT | PROC_DIRTY_DEFER); 1501 1502 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#2 - post xpc_track_activity()")) { 1503 goto exit; 1504 } 1505 1506 /* Toggle */ 1507 proc_set_dirty(getpid(), true); 1508 proc_set_dirty(getpid(), false); 1509 proc_set_dirty(getpid(), true); 1510 proc_set_dirty(getpid(), false); 1511 1512 switch (test) { 1513 case kDeferTimeoutCleanTest: 1514 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#3 - post toggle")) { 1515 goto exit; 1516 } 1517 1518 /* Approximate transition check */ 1519 sleep(secs - 1); 1520 1521 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#4 - pre timeout")) { 1522 goto exit; 1523 } 1524 1525 sleep(2); 1526 1527 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#5 - post timeout")) { 1528 goto exit; 1529 } 1530 1531 proc_set_dirty(getpid(), true); 1532 1533 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#6 - post dirty")) { 1534 goto exit; 1535 } 1536 1537 proc_set_dirty(getpid(), false); 1538 1539 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#7 - post clean")) { 1540 goto exit; 1541 } 1542 1543 break; 1544 case kDeferTimeoutDirtyTest: 1545 proc_set_dirty(getpid(), true); 1546 1547 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#3 - post dirty")) { 1548 goto exit; 1549 } 1550 1551 /* Approximate transition check */ 1552 sleep(secs - 1); 1553 1554 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#4 - pre timeout")) { 1555 goto exit; 1556 } 1557 1558 sleep(2); 1559 1560 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#5 - post timeout")) { 1561 goto exit; 1562 } 1563 1564 proc_set_dirty(getpid(), false); 1565 1566 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#6 - post clean")) { 1567 goto exit; 1568 } 1569 1570 break; 1571 case kCancelTimeoutDirtyTest: 1572 proc_set_dirty(getpid(), true); 1573 1574 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#3 - post toggle")) { 1575 goto exit; 1576 } 1577 1578 proc_track_dirty(getpid(), PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT); 1579 1580 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#4 - post deferral cancellation")) { 1581 goto exit; 1582 } 1583 1584 proc_set_dirty(getpid(), false); 1585 1586 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#5 - post toggle")) { 1587 goto exit; 1588 } 1589 1590 break; 1591 case kCancelTimeoutCleanTest: 1592 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED, -1, 0x0, "#3 - post toggle")) { 1593 goto exit; 1594 } 1595 1596 proc_track_dirty(getpid(), PROC_DIRTY_TRACK | PROC_DIRTY_ALLOW_IDLE_EXIT); 1597 1598 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#4 - post deferral cancellation")) { 1599 goto exit; 1600 } 1601 1602 proc_set_dirty(getpid(), true); 1603 1604 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT, -1, 0x0, "#5 - post dirty")) { 1605 goto exit; 1606 } 1607 1608 proc_set_dirty(getpid(), false); 1609 1610 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE, -1, 0x0, "#6 - post clean")) { 1611 goto exit; 1612 } 1613 1614 break; 1615 } 1616 1617 g_shared->completed = 1; 1618 cleanup_and_exit(0); 1619 1620exit: 1621 printTestResult(__func__, false, "Something bad happened..."); 1622 cleanup_and_exit(-1); 1623} 1624 1625static void start_idle_exit_defer_test(idle_exit_test_t test) { 1626 pid_t pid; 1627 int status; 1628 1629 /* Reset */ 1630 memset(g_shared, 0, sizeof(shared_mem_t)); 1631 1632 pid = init_and_fork(); 1633 if (pid == 0) { 1634 idle_exit_deferral_test(test); 1635 } 1636 else { 1637 printTestHeader(pid, "Idle exit deferral test"); 1638 } 1639 1640 /* Wait for exit */ 1641 waitpid(pid, &status, 0); 1642 /* Idle exit not reported on embedded */ 1643 // wait_for_exit_event(pid, kMemorystatusKilledIdleExit); 1644 1645 printTestResult("Idle exit deferral test", g_shared->completed, NULL); 1646} 1647 1648static void ledger_init(void) { 1649 const char *physFootprintName = "phys_footprint"; 1650 struct ledger_info li; 1651 int64_t template_cnt; 1652 struct ledger_template_info *templateInfo; 1653 void *arg; 1654 int i; 1655 1656 /* Grab ledger entries */ 1657 arg = (void *)(long)getpid(); 1658 if (ledger(LEDGER_INFO, arg, (caddr_t)&li, NULL) < 0) { 1659 exit(-1); 1660 } 1661 1662 g_ledger_count = template_cnt = li.li_entries; 1663 1664 templateInfo = malloc(template_cnt * sizeof (struct ledger_template_info)); 1665 if (templateInfo == NULL) { 1666 exit (-1); 1667 } 1668 1669 if (!(ledger(LEDGER_TEMPLATE_INFO, (caddr_t)templateInfo, (caddr_t)&template_cnt, NULL) < 0)) { 1670 for (i = 0; i < template_cnt; i++) { 1671 if (!strncmp(templateInfo[i].lti_name, physFootprintName, strlen(physFootprintName))) { 1672 g_footprint_index = i; 1673 break; 1674 } 1675 } 1676 } 1677 1678 free(templateInfo); 1679} 1680 1681static void run_tests(const char *path) { 1682 /* Embedded-only */ 1683#if TARGET_OS_EMBEDDED 1684 start_jetsam_test(kSimpleJetsamTest, "Simple munch"); 1685 start_jetsam_test(kHighwaterJetsamTest, "Highwater munch"); 1686 start_jetsam_test(kPressureJetsamTestBG, "Background pressure munch"); 1687 start_jetsam_test(kPressureJetsamTestFG, "Foreground Pressure munch"); 1688 start_jetsam_test_background(path); 1689 start_freeze_test(); 1690 start_priority_test(); 1691 start_fs_priority_test(); 1692#else 1693#pragma unused(path) 1694#endif 1695 1696 /* Generic */ 1697 start_general_sanity_test(); 1698 start_list_validation_test(); 1699 start_idle_exit_defer_test(kDeferTimeoutCleanTest); 1700 start_idle_exit_defer_test(kDeferTimeoutDirtyTest); 1701 start_idle_exit_defer_test(kCancelTimeoutCleanTest); 1702 start_idle_exit_defer_test(kCancelTimeoutDirtyTest); 1703} 1704 1705#if TARGET_OS_EMBEDDED 1706 1707static void 1708sigterm(int sig) 1709{ 1710 /* Reload crash reporter job */ 1711 enable_crashreporter(); 1712 1713 /* Reset signal handlers and re-raise signal */ 1714 signal(SIGTERM, SIG_DFL); 1715 signal(SIGINT, SIG_DFL); 1716 1717 kill(getpid(), sig); 1718} 1719 1720#endif 1721 1722int main(int argc, char **argv) 1723{ 1724 pthread_mutexattr_t attr; 1725 pthread_condattr_t cattr; 1726 size_t size; 1727#if TARGET_OS_EMBEDDED 1728 struct sigaction sa; 1729#endif 1730 1731 /* Must be run as root for priority retrieval */ 1732 if (getuid() != 0) { 1733 fprintf(stderr, "%s must be run as root.\n", getprogname()); 1734 exit(EXIT_FAILURE); 1735 } 1736 1737#if TARGET_OS_EMBEDDED 1738 /* Spawn test */ 1739 if ((argc == 2) && !strcmp(argv[1], "-s")) { 1740 spawn_test(); 1741 } 1742 1743 sa.sa_flags = 0; 1744 sa.sa_handler = sigterm; 1745 sigemptyset(&sa.sa_mask); 1746 1747 /* Ensure we can reinstate CrashReporter on exit */ 1748 sigaction(SIGTERM, &sa, NULL); 1749 sigaction(SIGINT, &sa, NULL); 1750 1751 /* Unload */ 1752 disable_crashreporter(); 1753 1754 /* Flush the jetsam snapshot */ 1755 verify_snapshot(-1, 0, 0, 0, FALSE); 1756#endif 1757 1758 /* Memory */ 1759 size = sizeof(g_physmem); 1760 if (sysctlbyname("hw.physmem", &g_physmem, &size, NULL, 0) != 0 || !g_physmem) { 1761 printTestResult(__func__, false, "Failed to retrieve system memory"); 1762 cleanup_and_exit(-1); 1763 } 1764 1765 /* Ledger; default limit applies to this process, so grab it here */ 1766 ledger_init(); 1767 if ((-1 == g_ledger_count) || (-1 == g_footprint_index) || (false == get_ledger_info(getpid(), NULL, &g_per_process_limit))) { 1768 printTestResult("setup", false, "Unable to init ledger!\n"); 1769 cleanup_and_exit(-1); 1770 } 1771 1772 /* Rescale to MB */ 1773 g_per_process_limit /= (1024 * 1024); 1774 1775 /* Shared memory */ 1776 g_shared = mmap(NULL, sizeof(shared_mem_t), PROT_WRITE|PROT_READ, MAP_ANON|MAP_SHARED, 0, 0); 1777 if (!g_shared) { 1778 printTestResult(__func__, false, "Failed mmap"); 1779 cleanup_and_exit(-1); 1780 } 1781 1782 pthread_mutexattr_init(&attr); 1783 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED ); 1784 1785 pthread_condattr_init(&cattr); 1786 pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); 1787 1788 if (pthread_mutex_init(&g_shared->mutex, &attr) || pthread_cond_init(&g_shared->cv, &cattr)) { 1789 printTestResult("setup", false, "Unable to init condition variable!\n"); 1790 cleanup_and_exit(-1); 1791 } 1792 1793 run_tests(argv[0]); 1794 1795 /* Teardown */ 1796 pthread_mutex_destroy(&g_shared->mutex); 1797 pthread_cond_destroy(&g_shared->cv); 1798 1799 pthread_mutexattr_destroy(&attr); 1800 pthread_condattr_destroy(&cattr); 1801 1802#if TARGET_OS_EMBEDDED 1803 /* Reload crash reporter */ 1804 enable_crashreporter(); 1805#endif 1806 1807 return 0; 1808} 1809