]> git.saurik.com Git - apple/xnu.git/blame - tools/tests/memorystatus/memorystatus.c
xnu-2782.10.72.tar.gz
[apple/xnu.git] / tools / tests / memorystatus / memorystatus.c
CommitLineData
39236c6e
A
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>
fe8ab488 20#include <mach/vm_page_size.h> /* Needed for vm_region info */
39236c6e
A
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
fe8ab488
A
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
39236c6e
A
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,
fe8ab488 82 kCustomTaskLimitTest,
39236c6e
A
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;
fe8ab488 102 boolean_t child_failed;
39236c6e
A
103} shared_mem_t;
104
105shared_mem_t *g_shared = NULL;
106unsigned long g_physmem = 0;
fe8ab488 107int g_compressor_mode=0;
39236c6e
A
108int g_ledger_count = -1, g_footprint_index = -1;
109int64_t g_per_process_limit = -1;
110
fe8ab488
A
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
39236c6e
A
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");
fe8ab488 145 fflush(stdout);
39236c6e
A
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);
fe8ab488
A
163
164 /* Any single failure, fails full test run */
165 g_exit_status = -1;
39236c6e 166 }
fe8ab488
A
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;
39236c6e
A
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
fe8ab488 242 options = (CFDictionaryRef) CFPropertyListCreateWithData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL, NULL);
39236c6e 243 if (options == NULL) {
fe8ab488 244 goto exit;
39236c6e
A
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
39236c6e 261
fe8ab488
A
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 */
39236c6e 271static void cleanup_and_exit(int status) {
39236c6e
A
272
273 /* Exit. Pretty literal. */
274 exit(status);
275}
276
fe8ab488
A
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 */
39236c6e
A
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;
fe8ab488 297
39236c6e
A
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) {
fe8ab488 307 printTestResult(__func__, false, "Fork error!");
39236c6e
A
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
39236c6e
A
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:
fe8ab488
A
411 if (entries)
412 free(entries);
39236c6e
A
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) {
fe8ab488 431 requested_limit_mb = (int32_t)g_per_process_limit;
39236c6e
A
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
39236c6e
A
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:
fe8ab488
A
496 if (entries)
497 free(entries);
39236c6e
A
498
499 printTestResult("List validation test", valid, NULL);
500}
501
502/* Random individual tests */
503static void start_general_sanity_test() {
504 int ret, size;
39236c6e
A
505 int i;
506 boolean_t valid = false;
fe8ab488
A
507
508 /*
509 * The sanity test checks for permission failures
510 * against P_MEMSTAT_INTERNAL processes.
511 * Currently only launchd (pid==1) qualifies.
512 */
39236c6e
A
513
514 printTestHeader(getpid(), "Sanity test");
515
fe8ab488
A
516
517 /* Ensure that launchd's transaction state is fixed */
39236c6e
A
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
fe8ab488 534
39236c6e
A
535 valid = true;
536
537exit:
fe8ab488 538 printTestResult("Sanity test", valid, NULL);
39236c6e
A
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
fe8ab488 628 proc_clear_dirty(getpid(), PROC_DIRTY_DEFER);
39236c6e
A
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
fe8ab488 646 proc_clear_dirty(getpid(), PROC_DIRTY_DEFER);
39236c6e
A
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;
fe8ab488 668 exit(0);
39236c6e
A
669
670exit:
671 printTestResult(__func__, false, "Something bad happened...");
fe8ab488 672 exit(-1);
39236c6e
A
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 {
fe8ab488 687 printTestHeader(pid, "Idle exit deferral test: %d", test);
39236c6e
A
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 */
39236c6e 733#pragma unused(path)
39236c6e
A
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
39236c6e
A
744
745int main(int argc, char **argv)
746{
747 pthread_mutexattr_t attr;
748 pthread_condattr_t cattr;
749 size_t size;
39236c6e
A
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
39236c6e
A
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
fe8ab488
A
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
39236c6e
A
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
fe8ab488
A
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 }
39236c6e
A
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
fe8ab488
A
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
39236c6e
A
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)) {
fe8ab488 807 printTestResult("setup", false, "Unable to init condition variable!");
39236c6e
A
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
39236c6e 820
fe8ab488 821 return (g_exit_status); /* exit status 0 on success, -1 on failure */
39236c6e 822}