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