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