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>
21 #include <sys/event.h>
23 #include <sys/kern_memorystatus.h>
27 #include <sys/sysctl.h>
31 #include <xpc/private.h>
33 #include <CoreFoundation/CoreFoundation.h>
35 #include <Security/Security.h>
36 #include <ServiceManagement/ServiceManagement.h>
37 #include <ServiceManagement/SMErrors.h>
39 #include <Kernel/kern/ledger.h>
41 #include <sys/spawn_internal.h>
42 #include <spawn_private.h>
44 #define CR_JOB "com.apple.ReportCrash.Jetsam"
45 #define CR_JOB_PLIST_PATH "/System/Library/LaunchDaemons/com.apple.ReportCrash.Jetsam.plist"
47 #define ERR_BUF_LEN 1024
50 #define VM_PAGE_SIZE 4096
54 * TODO: import header (currently vm_pageout.h) without pulling in extraneous definitions;
55 * see <rdar://problem/13374916>.
57 #ifndef VM_PAGER_FREEZER_DEFAULT
58 #define VM_PAGER_FREEZER_DEFAULT 0x8 /* Freezer backed by default pager.*/
62 * Special note to ourselves: the jetsam cause to look out for is *either*
63 * a high watermark kill, *or* a per-process kill.
65 #define CAUSE_HIWAT_OR_PERPROC -1
67 typedef enum jetsam_test
{
68 kSimpleJetsamTest
= 1,
69 kPressureJetsamTestFG
,
70 kPressureJetsamTestBG
,
76 typedef enum idle_exit_test
{
77 kDeferTimeoutCleanTest
= 1,
78 kDeferTimeoutDirtyTest
,
79 kCancelTimeoutCleanTest
,
80 kCancelTimeoutDirtyTest
83 typedef struct shared_mem_t
{
84 pthread_mutex_t mutex
;
87 boolean_t pressure_event_fired
;
90 shared_mem_t
*g_shared
= NULL
;
91 unsigned long g_physmem
= 0;
92 int g_ledger_count
= -1, g_footprint_index
= -1;
93 int64_t g_per_process_limit
= -1;
95 #if TARGET_OS_EMBEDDED
96 static boolean_t
set_priority(pid_t pid
, int32_t priority
, uint64_t user_data
);
99 extern int ledger(int cmd
, caddr_t arg1
, caddr_t arg2
, caddr_t arg3
);
100 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
);
105 printTestHeader(pid_t testPid
, const char *testName
, ...)
108 printf("========================================\n");
110 va_start(va
, testName
);
111 vprintf(testName
, va
);
114 printf("[PID] %d\n", testPid
);
115 printf("========================================\n");
120 printTestResult(const char *testName
, boolean_t didPass
, const char *msg
, ...)
131 printf("[PASS]\t%s\n\n", testName
);
133 printf("[FAIL]\t%s\n\n", testName
);
137 static CFDictionaryRef
create_dictionary_from_plist(const char *path
) {
139 CFDataRef data
= NULL
;
140 CFDictionaryRef options
= NULL
;
142 int fd
= open(path
, O_RDONLY
, 0);
147 if (fstat(fd
, &sb
) == -1) {
151 bufLen
= (size_t)sb
.st_size
;
152 bytes
= malloc(bufLen
);
157 if (read(fd
, bytes
, bufLen
) != bufLen
) {
161 data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8
*) bytes
, bufLen
, kCFAllocatorNull
);
166 options
= (CFDictionaryRef
) CFPropertyListCreateFromXMLData(kCFAllocatorDefault
, data
, kCFPropertyListImmutable
, NULL
);
167 if (options
== NULL
) {
184 #if TARGET_OS_EMBEDDED
186 static void disable_crashreporter(void) {
187 if (!SMJobRemove(kSMDomainSystemLaunchd
, CFSTR(CR_JOB
), NULL
, true, NULL
)) {
188 printf ("\t\tCould not unload %s\n", CR_JOB
);
192 static void enable_crashreporter(void) {
193 CFDictionaryRef job_dict
;
195 job_dict
= create_dictionary_from_plist(CR_JOB_PLIST_PATH
);
197 printf("\t\tCould not create dictionary from %s\n", CR_JOB_PLIST_PATH
);
200 if (!SMJobSubmit(kSMDomainSystemLaunchd
, job_dict
, NULL
, NULL
)) {
201 printf ("\t\tCould not submit %s\n", CR_JOB
);
207 static boolean_t
verify_snapshot(pid_t pid
, int32_t priority
, uint32_t kill_cause
, uint64_t user_data
, bool expecting_snapshot
) {
209 memorystatus_jetsam_snapshot_t
*snapshot
= NULL
;
211 boolean_t res
= false;
213 if (kill_cause
== CAUSE_HIWAT_OR_PERPROC
) {
214 kill_cause
= kMemorystatusKilledHiwat
|kMemorystatusKilledVMPageShortage
;
217 size
= memorystatus_control(MEMORYSTATUS_CMD_GET_JETSAM_SNAPSHOT
, 0, 0, NULL
, 0);
219 if (expecting_snapshot
) {
220 printf("\t\tCan't get snapshot size: %d!\n", size
);
225 snapshot
= (memorystatus_jetsam_snapshot_t
*)malloc(size
);
227 printf("\t\tCan't allocate snapshot!\n");
231 size
= memorystatus_control(MEMORYSTATUS_CMD_GET_JETSAM_SNAPSHOT
, 0, 0, snapshot
, size
);
233 printf("\t\tCan't retrieve snapshot (%d)!\n", size
);
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
)));
245 /* Just flushing the buffer */
251 for (i
= 0; i
< snapshot
->entry_count
; i
++) {
252 if (snapshot
->entries
[i
].pid
== pid
) {
254 if ((priority
== snapshot
->entries
[i
].priority
) && ((kill_cause
| snapshot
->entries
[i
].killed
) == kill_cause
) && (user_data
== snapshot
->entries
[i
].user_data
)) {
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
);
270 #endif /* TARGET_OS_EMBEDDED */
272 static void cleanup_and_exit(int status
) {
273 #if TARGET_OS_EMBEDDED
275 enable_crashreporter();
278 /* Exit. Pretty literal. */
282 static void child_ready() {
283 pthread_mutex_lock(&g_shared
->mutex
);
284 pthread_cond_signal(&g_shared
->cv
);
285 pthread_mutex_unlock(&g_shared
->mutex
);
288 static pid_t
init_and_fork() {
291 g_shared
->completed
= 0;
292 g_shared
->pressure_event_fired
= 0;
294 pthread_mutex_lock(&g_shared
->mutex
);
299 } else if (pid
== -1) {
300 printTestResult(__func__
, false, "Fork error!\n");
301 cleanup_and_exit(-1);
304 /* Wait for child's signal */
305 pthread_cond_wait(&g_shared
->cv
, &g_shared
->mutex
);
306 pthread_mutex_unlock(&g_shared
->mutex
);
310 static memorystatus_priority_entry_t
*get_priority_list(int *size
) {
311 memorystatus_priority_entry_t
*list
= NULL
;
315 *size
= memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST
, 0, 0, NULL
, 0);
317 printf("\t\tCan't get list size: %d!\n", *size
);
321 list
= (memorystatus_priority_entry_t
*)malloc(*size
);
323 printf("\t\tCan't allocate list!\n");
327 *size
= memorystatus_control(MEMORYSTATUS_CMD_GET_PRIORITY_LIST
, 0, 0, list
, *size
);
329 printf("\t\tCan't retrieve list!\n");
339 #if TARGET_OS_EMBEDDED
343 static void spawn_test() {
344 int page_delta
= 32768; /* 128MB */
346 unsigned long total
= 0;
350 /* Priority will be shifted during this time... */
353 /* ...then process will be backgrounded and hopefully killed by the memory limit */
356 mem
= malloc(page_delta
* VM_PAGE_SIZE
);
358 fprintf(stderr
, "Failed to allocate memory!\n");
365 memset(mem
, 0xFF, page_delta
* VM_PAGE_SIZE
);
367 set_priority(getpid(), JETSAM_PRIORITY_BACKGROUND
, 0);
378 static boolean_t
get_ledger_info(pid_t pid
, int64_t *balance_mb
, int64_t *limit_mb
) {
379 struct ledger_entry_info
*lei
;
381 boolean_t res
= false;
383 lei
= (struct ledger_entry_info
*)malloc((size_t)(g_ledger_count
* sizeof (*lei
)));
387 arg
= (void *)(long)pid
;
388 count
= g_ledger_count
;
390 if ((ledger(LEDGER_ENTRY_INFO
, arg
, (caddr_t
)lei
, (caddr_t
)&count
) >= 0) && (g_footprint_index
< count
)) {
392 *balance_mb
= lei
[g_footprint_index
].lei_balance
;
395 *limit_mb
= lei
[g_footprint_index
].lei_limit
;
406 static boolean_t
get_priority_props(pid_t pid
, int32_t *priority
, int32_t *limit_mb
, uint64_t *user_data
) {
408 memorystatus_priority_entry_t
*entries
= NULL
;
410 boolean_t res
= false;
412 entries
= get_priority_list(&size
);
418 for (i
= 0; i
< size
/sizeof(memorystatus_priority_entry_t
); i
++ ){
419 if (entries
[i
].pid
== pid
) {
422 *priority
= entries
[i
].priority
;
423 *user_data
= entries
[i
].user_data
;
425 *limit_mb
= entries
[i
].limit
;
428 res
= get_ledger_info(entries
[i
].pid
, NULL
, &limit
);
430 printf("Failed to get highwater!\n");
432 /* The limit is retrieved in bytes, but set in MB, so rescale */
433 *limit_mb
= (int32_t)(limit
/(1024 * 1024));
439 printf("\t\tCan't find pid: %d!\n", pid
);
447 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
) {
448 const char *PROP_GET_ERROR_STRING
= "failed to get properties";
449 const char *PROP_CHECK_ERROR_STRING
= "property mismatch";
451 int32_t actual_priority
, actual_hiwat
;
452 uint64_t actual_user_data
;
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
);
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
;
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
);
472 printf("\t\t%s test ok...\n", test
);
477 #if TARGET_OS_EMBEDDED
490 static boolean_t
set_priority(pid_t pid
, int32_t priority
, uint64_t user_data
) {
492 memorystatus_priority_properties_t props
;
494 props
.priority
= priority
;
495 props
.user_data
= (uint32_t)user_data
;
497 return memorystatus_control(MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES
, pid
, 0, &props
, sizeof(props
));
500 static 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);
504 static 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
) {
507 ret
= set_priority(pid
, priority
, user_data
);
509 ret
= set_memlimit(pid
, limit_mb
);
514 printf("\t\t%s stage: failed to set properties!\n", stage
);
523 static 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";
537 pid
= init_and_fork();
541 printTestHeader(pid
, "Priority test");
544 /* Check the default properties */
545 if (!check_properties(pid
, JETSAM_PRIORITY_DEFAULT
, -1, 0, DEFAULT_TEST_STR
)) {
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
)) {
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
)) {
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
)) {
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
)) {
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
)) {
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
)) {
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
)) {
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
)) {
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
)) {
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
)) {
607 waitpid(pid
, &status
, 0);
609 printTestResult("Priority test", success
, NULL
);
614 static boolean_t
check_reorder_priorities(pid_t pid1
, pid_t pid2
, int priority
) {
616 memorystatus_priority_entry_t
*entries
= NULL
;
618 boolean_t res
= false;
620 entries
= get_priority_list(&size
);
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
) {
633 /* There should be one more daemon to follow... */
634 if ((i
+ 1) >= size
) {
638 /* The next process should be pid2 */
639 if (entries
[i
+ 1].pid
!= pid2
) {
643 /* The priority should also match... */
644 if (entries
[i
+ 1].priority
!= priority
) {
659 static void start_fs_priority_test() {
660 const char *REORDER_TEST_STR
= "Reorder";
661 const int test_priority
= JETSAM_PRIORITY_FOREGROUND_SUPPORT
;
667 pid1
= init_and_fork();
672 pid2
= init_and_fork();
677 printTestHeader(pid1
, "Reorder test");
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");
685 /* Check relative priorities */
686 if (!check_reorder_priorities(pid1
, pid2
, test_priority
)) {
687 printf("Bad pid1 -> pid2 priorities - #2!\n");
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");
698 if (!check_reorder_priorities(pid2
, pid1
, test_priority
)) {
699 printf("Bad pid2 -> pid1 priorities - #4!\n");
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");
709 /* ...so validate for the last time */
710 if (!check_reorder_priorities(pid1
, pid2
, test_priority
)) {
711 printf("Bad pid1 -> pid2 priorities - #6!\n");
724 waitpid(pid1
, &status
, 0);
725 waitpid(pid2
, &status
, 0);
727 printTestResult("Reorder test", success
, NULL
);
737 Message is ReadGID 80
738 Message is ASLMessageID 703
740 Message is Time 1333155901
741 Message is Sender kernel
742 Message is Facility kern
746 static void vnode_test(int page_delta
, int interval
, int verbose
, int32_t priority
, uint64_t user_data
) {
747 memorystatus_priority_properties_t props
;
749 props
.priority
= priority
;
750 props
.user_data
= user_data
;
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);
761 /* ...so start stealing vnodes */
767 static void *wait_for_pressure_event(void *s
) {
770 struct kevent event
, mevent
;
771 char errMsg
[ERR_BUF_LEN
+ 1];
775 EV_SET(&mevent
, 0, EVFILT_VM
, EV_ADD
, NOTE_VM_PRESSURE
, 0, 0);
777 res
= kevent(kq
, &mevent
, 1, NULL
, 0, NULL
);
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);
786 memset(&event
, 0, sizeof(struct kevent
));
787 res
= kevent(kq
, NULL
, 0, &event
, 1, NULL
);
788 g_shared
->pressure_event_fired
= 1;
792 static void wait_for_exit_event(int pid
, uint32_t kill_cause
) {
795 uint32_t expected_flag
, received_flag
;
796 struct kevent event
, mevent
;
797 char errMsg
[ERR_BUF_LEN
+ 1];
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;
811 EV_SET(&mevent
, pid
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
| NOTE_EXIT_DETAIL
, 0, 0);
813 res
= kevent(kq
, &mevent
, 1, NULL
, 0, NULL
);
815 snprintf(errMsg
,ERR_BUF_LEN
,"Exit kevent registration failed - returning: %d!",res
);
816 printTestResult(__func__
, false, errMsg
);
817 cleanup_and_exit(-1);
820 res
= kevent(kq
, NULL
, 0, &event
, 1, NULL
);
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);
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);
836 static 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";
839 unsigned long total
= 0;
843 /* Start thread to watch for pressure messages */
844 res
= pthread_create(&pe_thread
, NULL
, wait_for_pressure_event
, (void*)g_shared
);
846 printTestResult(__func__
, false, "Error creating pressure event thread!\n");
847 cleanup_and_exit(-1);
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);
864 /* ...so start munch */
867 mem
= malloc(page_delta
* VM_PAGE_SIZE
);
869 fprintf(stderr
, "Failed to allocate memory!\n");
876 memset(mem
, 0xFF, page_delta
* VM_PAGE_SIZE
);
879 printf("\t\t%lu pages dirtied...\n", total
);
886 static bool is_pressure_test(test
) {
887 return ((test
== kPressureJetsamTestFG
) || (test
== kPressureJetsamTestBG
));
890 static 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";
894 aslresponse response
;
899 bool got_snapshot
= 0;
903 wait_for_exit_event(pid
, kill_cause
);
905 /* Let the messages filter through to the log - arbitrary */
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
);
916 while (NULL
!= (message
= aslresponse_next(response
)))
918 val
= asl_get(message
, time_key
);
920 uint32_t msg_time
= atoi(val
);
921 if (msg_time
> start_time
) {
922 val
= asl_get(message
, msg_key
);
924 printf("\t\tFound: %s\n", val
);
932 got_snapshot
= verify_snapshot(pid
, test_pri
, kill_cause
, test_user_data
, expecting_snapshot
);
934 printf("\t\tCouldn't find jetsam message in log!\n");
937 aslresponse_free(response
);
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
);
945 static void start_jetsam_test(jetsam_test_t test
, const char *description
) {
946 const char *msg_key
= "Message";
947 const char *time_key
= "Time";
950 aslresponse response
;
957 int got_snapshot
= 0;
958 uint32_t test_pri
= 0;
959 uint64_t test_user_data
= 0;
962 boolean_t expecting_snapshot
= TRUE
;
963 boolean_t big_mem
= (g_physmem
> 512 * 1024 * 1024);
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.
972 kill_cause
= CAUSE_HIWAT_OR_PERPROC
;
973 } else if (test
== kHighwaterJetsamTest
) {
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.
979 kill_cause
= kMemorystatusKilledHiwat
;
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
986 kill_cause
= kMemorystatusKilledVMPageShortage
;
989 start_time
= time(NULL
);
992 case kPressureJetsamTestFG
:
993 test_pri
= JETSAM_PRIORITY_FOREGROUND
; /* Test that FG processes get pressure events */
994 test_user_data
= 0xDEADBEEF;
996 case kPressureJetsamTestBG
:
997 test_pri
= JETSAM_PRIORITY_UI_SUPPORT
; /* Test that BG processes get pressure events */
998 test_user_data
= 0xFADEBEEF;
1000 case kSimpleJetsamTest
:
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.
1005 expecting_snapshot
= big_mem
? TRUE
: FALSE
;
1006 test_pri
= JETSAM_PRIORITY_IDLE
; /* Suspended */
1007 test_user_data
= 0xFACEF00D;
1010 test_pri
= JETSAM_PRIORITY_IDLE
; /* Suspended */
1011 test_user_data
= 0xCAFEF00D;
1015 pid
= init_and_fork();
1019 case kVnodeJetsamTest
:
1020 vnode_test(0, 0, 0, test_pri
, test_user_data
);
1022 case kHighwaterJetsamTest
:
1023 munch_test(0, 0, 0, test_pri
, 8, test_user_data
);
1026 munch_test(0, 0, 0, test_pri
, -1, test_user_data
);
1031 printTestHeader(pid
, "%s test", description
);
1034 verify_exit(pid
, kill_cause
, start_time
, test_pri
, test_user_data
, test
, expecting_snapshot
);
1037 static void start_jetsam_test_background(const char *path
) {
1038 const char *argv
[] = {
1044 const uint32_t memlimit
= 100; /* 100 MB */
1049 uint32_t test_pri
= 0;
1050 posix_spawnattr_t spattr
;
1054 start_time
= time(NULL
);
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);
1062 if (posix_spawn(&pid
, path
, NULL
, &spattr
, (char *const *)argv
, NULL
) < 0) {
1063 printf("posix_spawn() failed!\n");
1067 printTestHeader(pid
, "Background memory limit test");
1069 /* Starts in background */
1070 if (!check_properties(pid
, JETSAM_PRIORITY_UI_SUPPORT
, memlimit
, 0x0, "jetsam_test_background - #1 BG")) {
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")) {
1081 set_priority(pid
, JETSAM_PRIORITY_BACKGROUND
, 0);
1082 if (!check_properties(pid
, JETSAM_PRIORITY_BACKGROUND
, memlimit
, 0x0, "jetsam_test_background - #3 BG")) {
1087 set_priority(pid
, JETSAM_PRIORITY_FOREGROUND
, 0);
1088 if (!check_properties(pid
, JETSAM_PRIORITY_FOREGROUND
, 0, 0x0, "jetsam_test_background - #4 FG")) {
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.
1100 /* Let the process dirty 128MB of memory, then background itself */
1101 verify_exit(pid
, kMemorystatusKilledPerProcessLimit
, start_time
, test_pri
, 0, kBackgroundJetsamTest
);
1112 waitpid(pid
, &status
, 0);
1114 printTestResult("Background test", success
, NULL
);
1119 /* Cribbed from 'top'... */
1121 in_shared_region(mach_vm_address_t addr
, cpu_type_t type
) {
1122 mach_vm_address_t base
= 0, size
= 0;
1126 base
= SHARED_REGION_BASE_ARM
;
1127 size
= SHARED_REGION_SIZE_ARM
;
1130 case CPU_TYPE_X86_64
:
1131 base
= SHARED_REGION_BASE_X86_64
;
1132 size
= SHARED_REGION_SIZE_X86_64
;
1136 base
= SHARED_REGION_BASE_I386
;
1137 size
= SHARED_REGION_SIZE_I386
;
1140 case CPU_TYPE_POWERPC
:
1141 base
= SHARED_REGION_BASE_PPC
;
1142 size
= SHARED_REGION_SIZE_PPC
;
1145 case CPU_TYPE_POWERPC64
:
1146 base
= SHARED_REGION_BASE_PPC64
;
1147 size
= SHARED_REGION_SIZE_PPC64
;
1153 fprintf(stderr
, "unknown CPU type: 0x%x\n", t
);
1159 return(addr
>= base
&& addr
< (base
+ size
));
1162 static unsigned long long get_rprvt(mach_port_t task
, pid_t pid
) {
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;
1171 mach_vm_address_t addr
;
1172 mach_vm_size_t size
;
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
;
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;
1184 if (in_shared_region(addr
, CPU_TYPE_ARM
)) {
1186 fw_private
+= info
.private_pages_resident
* pagesize
;
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
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
;
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;
1203 if (b_info
.reserved
) {
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
1214 if (info
.share_mode
!= SM_PRIVATE
) {
1222 * Update counters according to the region type.
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
;
1230 switch (info
.share_mode
) {
1232 // Treat SM_LARGE_PAGE the same as SM_PRIVATE
1233 // since they are not shareable and are wired.
1235 rprvt
+= info
.private_pages_resident
* pagesize
;
1236 rprvt
+= info
.shared_pages_resident
* pagesize
;
1246 // Treat kernel_task specially
1247 if (info
.share_mode
== SM_COW
) {
1248 rprvt
+= info
.private_pages_resident
* pagesize
;
1253 if (info
.share_mode
== SM_COW
) {
1254 rprvt
+= info
.private_pages_resident
* pagesize
;
1267 static void freeze_test() {
1268 const unsigned long DIRTY_ALLOC
= 16 * 1024 * 1024;
1270 task_port_t task
= mach_task_self();
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
);
1279 unsigned long long baseline_rprvt
, half_rprvt
, rprvt
;
1282 baseline_rprvt
= get_rprvt(task
, pid
);
1285 memset(ptr
, 0xAB, DIRTY_ALLOC
/ 2);
1288 half_rprvt
= get_rprvt(task
, pid
);
1289 printf("\t\trprvt is %llu\n", half_rprvt
);
1291 if (half_rprvt
!= (baseline_rprvt
+ (DIRTY_ALLOC
/ 2)))
1293 printTestResult(__func__
, false, "Failed to dirty memory");
1294 cleanup_and_exit(-1);
1298 sysctlbyname("kern.memorystatus_freeze", NULL
, 0, &pid
, sizeof(pid
));
1303 rprvt
= get_rprvt(task
, pid
);
1304 printf("\t\trprvt is %llu\n", rprvt
);
1306 if ((rprvt
> (half_rprvt
- (DIRTY_ALLOC
/ 2))) || (rprvt
> (64 * 1024)) /* Sanity */)
1308 printTestResult(__func__
, false, "Failed to freeze memory");
1309 cleanup_and_exit(-1);
1313 sysctlbyname("kern.memorystatus_thaw", NULL
, 0, &pid
, sizeof(pid
));
1318 rprvt
= get_rprvt(task
, pid
);
1319 printf("\t\trprvt is %llu\n", rprvt
);
1321 if (rprvt
< (baseline_rprvt
+ (DIRTY_ALLOC
/ 2)))
1323 printTestResult(__func__
, false, "Failed to thaw memory");
1324 cleanup_and_exit(-1);
1327 /* Dirty the rest */
1328 memset(ptr
+ (DIRTY_ALLOC
/ (2 * sizeof(unsigned long))), 0xBC, DIRTY_ALLOC
/ 2);
1331 rprvt
= get_rprvt(task
, pid
);
1332 printf("\t\trprvt is %llu\n", rprvt
);
1334 if (rprvt
< (baseline_rprvt
+ DIRTY_ALLOC
))
1336 printTestResult(__func__
, false, "Failed to dirty memory");
1337 cleanup_and_exit(-1);
1340 g_shared
->completed
= 1;
1341 cleanup_and_exit(0);
1344 printTestResult(__func__
, false, "Something bad happened...");
1345 cleanup_and_exit(-1);
1348 static void start_freeze_test() {
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);
1362 if (mode
!= VM_PAGER_FREEZER_DEFAULT
) {
1363 printTestHeader(getpid(), "Freeze test");
1364 printTestResult(__func__
, true, "Freeze disabled; skipping test");
1369 memset(g_shared
, 0, sizeof(shared_mem_t
));
1371 pid
= init_and_fork();
1375 printTestHeader(pid
, "Freeze test");
1379 waitpid(pid
, &status
, 0);
1381 printTestResult("Freeze test", g_shared
->completed
, NULL
);
1386 static void start_list_validation_test() {
1388 memorystatus_priority_entry_t
*entries
= NULL
;
1390 boolean_t valid
= false;
1392 printTestHeader(getpid(), "List validation test");
1394 entries
= get_priority_list(&size
);
1396 printf("Can't get entries!\n");
1401 for (i
= 0; i
< size
/sizeof(memorystatus_priority_entry_t
); i
++ ) {
1403 uint32_t dirty_flags
;
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
);
1411 /* Sanity check idle exit state */
1412 dirty_ret
= proc_get_dirty(entries
[i
].pid
, &dirty_flags
);
1413 if (dirty_ret
!= 0) {
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
);
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
);
1436 printTestResult("List validation test", valid
, NULL
);
1439 /* Random individual tests */
1440 static void start_general_sanity_test() {
1442 memorystatus_priority_entry_t
*entries
= NULL
;
1444 boolean_t valid
= false;
1446 printTestHeader(getpid(), "Sanity test");
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
);
1454 printf("\t\tlaunchd priority test OK!\n");
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
);
1463 printf("\t\tlaunchd memlimit test OK!\n");
1466 /* ...nor tinker with transactions */
1467 ret
= proc_track_dirty(1, PROC_DIRTY_TRACK
| PROC_DIRTY_ALLOW_IDLE_EXIT
| PROC_DIRTY_DEFER
);
1469 printf("\t\tNo EPERM tracking launchd (%d/%d)!\n", ret
, errno
);
1472 printf("\t\tlaunchd track test OK!\n");
1475 ret
= proc_set_dirty(1, true);
1477 printf("\t\tNo EPERM setting launchd dirty state (%d/%d)!\n", ret
, errno
);
1480 printf("\t\tlaunchd dirty test OK!\n");
1488 printTestResult("Idle exit test", valid
, NULL
);
1491 static void idle_exit_deferral_test(idle_exit_test_t test
) {
1492 int secs
= DEFERRED_IDLE_EXIT_TIME_SECS
;
1496 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT
, -1, 0x0, "#1 - pre xpc_track_activity()")) {
1500 proc_track_dirty(getpid(), PROC_DIRTY_TRACK
| PROC_DIRTY_ALLOW_IDLE_EXIT
| PROC_DIRTY_DEFER
);
1502 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED
, -1, 0x0, "#2 - post xpc_track_activity()")) {
1507 proc_set_dirty(getpid(), true);
1508 proc_set_dirty(getpid(), false);
1509 proc_set_dirty(getpid(), true);
1510 proc_set_dirty(getpid(), false);
1513 case kDeferTimeoutCleanTest
:
1514 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED
, -1, 0x0, "#3 - post toggle")) {
1518 /* Approximate transition check */
1521 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED
, -1, 0x0, "#4 - pre timeout")) {
1527 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE
, -1, 0x0, "#5 - post timeout")) {
1531 proc_set_dirty(getpid(), true);
1533 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT
, -1, 0x0, "#6 - post dirty")) {
1537 proc_set_dirty(getpid(), false);
1539 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE
, -1, 0x0, "#7 - post clean")) {
1544 case kDeferTimeoutDirtyTest
:
1545 proc_set_dirty(getpid(), true);
1547 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT
, -1, 0x0, "#3 - post dirty")) {
1551 /* Approximate transition check */
1554 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT
, -1, 0x0, "#4 - pre timeout")) {
1560 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT
, -1, 0x0, "#5 - post timeout")) {
1564 proc_set_dirty(getpid(), false);
1566 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE
, -1, 0x0, "#6 - post clean")) {
1571 case kCancelTimeoutDirtyTest
:
1572 proc_set_dirty(getpid(), true);
1574 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT
, -1, 0x0, "#3 - post toggle")) {
1578 proc_track_dirty(getpid(), PROC_DIRTY_TRACK
| PROC_DIRTY_ALLOW_IDLE_EXIT
);
1580 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT
, -1, 0x0, "#4 - post deferral cancellation")) {
1584 proc_set_dirty(getpid(), false);
1586 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE
, -1, 0x0, "#5 - post toggle")) {
1591 case kCancelTimeoutCleanTest
:
1592 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE_DEFERRED
, -1, 0x0, "#3 - post toggle")) {
1596 proc_track_dirty(getpid(), PROC_DIRTY_TRACK
| PROC_DIRTY_ALLOW_IDLE_EXIT
);
1598 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE
, -1, 0x0, "#4 - post deferral cancellation")) {
1602 proc_set_dirty(getpid(), true);
1604 if (!check_properties(getpid(), JETSAM_PRIORITY_DEFAULT
, -1, 0x0, "#5 - post dirty")) {
1608 proc_set_dirty(getpid(), false);
1610 if (!check_properties(getpid(), JETSAM_PRIORITY_IDLE
, -1, 0x0, "#6 - post clean")) {
1617 g_shared
->completed
= 1;
1618 cleanup_and_exit(0);
1621 printTestResult(__func__
, false, "Something bad happened...");
1622 cleanup_and_exit(-1);
1625 static void start_idle_exit_defer_test(idle_exit_test_t test
) {
1630 memset(g_shared
, 0, sizeof(shared_mem_t
));
1632 pid
= init_and_fork();
1634 idle_exit_deferral_test(test
);
1637 printTestHeader(pid
, "Idle exit deferral test");
1641 waitpid(pid
, &status
, 0);
1642 /* Idle exit not reported on embedded */
1643 // wait_for_exit_event(pid, kMemorystatusKilledIdleExit);
1645 printTestResult("Idle exit deferral test", g_shared
->completed
, NULL
);
1648 static 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
;
1656 /* Grab ledger entries */
1657 arg
= (void *)(long)getpid();
1658 if (ledger(LEDGER_INFO
, arg
, (caddr_t
)&li
, NULL
) < 0) {
1662 g_ledger_count
= template_cnt
= li
.li_entries
;
1664 templateInfo
= malloc(template_cnt
* sizeof (struct ledger_template_info
));
1665 if (templateInfo
== NULL
) {
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
;
1681 static void run_tests(const char *path
) {
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();
1693 #pragma unused(path)
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
);
1705 #if TARGET_OS_EMBEDDED
1710 /* Reload crash reporter job */
1711 enable_crashreporter();
1713 /* Reset signal handlers and re-raise signal */
1714 signal(SIGTERM
, SIG_DFL
);
1715 signal(SIGINT
, SIG_DFL
);
1717 kill(getpid(), sig
);
1722 int main(int argc
, char **argv
)
1724 pthread_mutexattr_t attr
;
1725 pthread_condattr_t cattr
;
1727 #if TARGET_OS_EMBEDDED
1728 struct sigaction sa
;
1731 /* Must be run as root for priority retrieval */
1732 if (getuid() != 0) {
1733 fprintf(stderr
, "%s must be run as root.\n", getprogname());
1737 #if TARGET_OS_EMBEDDED
1739 if ((argc
== 2) && !strcmp(argv
[1], "-s")) {
1744 sa
.sa_handler
= sigterm
;
1745 sigemptyset(&sa
.sa_mask
);
1747 /* Ensure we can reinstate CrashReporter on exit */
1748 sigaction(SIGTERM
, &sa
, NULL
);
1749 sigaction(SIGINT
, &sa
, NULL
);
1752 disable_crashreporter();
1754 /* Flush the jetsam snapshot */
1755 verify_snapshot(-1, 0, 0, 0, FALSE
);
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);
1765 /* Ledger; default limit applies to this process, so grab it here */
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);
1773 g_per_process_limit
/= (1024 * 1024);
1776 g_shared
= mmap(NULL
, sizeof(shared_mem_t
), PROT_WRITE
|PROT_READ
, MAP_ANON
|MAP_SHARED
, 0, 0);
1778 printTestResult(__func__
, false, "Failed mmap");
1779 cleanup_and_exit(-1);
1782 pthread_mutexattr_init(&attr
);
1783 pthread_mutexattr_setpshared(&attr
, PTHREAD_PROCESS_SHARED
);
1785 pthread_condattr_init(&cattr
);
1786 pthread_condattr_setpshared(&cattr
, PTHREAD_PROCESS_SHARED
);
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);
1796 pthread_mutex_destroy(&g_shared
->mutex
);
1797 pthread_cond_destroy(&g_shared
->cv
);
1799 pthread_mutexattr_destroy(&attr
);
1800 pthread_condattr_destroy(&cattr
);
1802 #if TARGET_OS_EMBEDDED
1803 /* Reload crash reporter */
1804 enable_crashreporter();