]> git.saurik.com Git - apple/xnu.git/blob - tests/vm_phys_footprint.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / tests / vm_phys_footprint.c
1 #include <darwintest.h>
2 #include <darwintest_utils.h>
3
4 #include <mach/mach_error.h>
5 #include <mach/mach_init.h>
6 #include <mach/mach_port.h>
7 #include <mach/mach_vm.h>
8 #include <mach/task.h>
9 #include <mach/task_info.h>
10 #include <mach/vm_map.h>
11
12 #include <sys/mman.h>
13 #include <sys/types.h>
14 #include <sys/sysctl.h>
15
16 #include <TargetConditionals.h>
17
18 #include <Kernel/kern/ledger.h>
19 extern int ledger(int cmd, caddr_t arg1, caddr_t arg2, caddr_t arg3);
20
21 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));
22 boolean_t legacy_footprint;
23
24 #if LEGACY_FOOTPRINT_ENTITLED && defined(__arm64__)
25 #define TEST_VM_NAMESPACE "xnu.vm_legacy"
26 #else /* ENTITLED && __arm64__ */
27 #define TEST_VM_NAMESPACE "xnu.vm"
28 #endif /* ENTITLED && __arm64__ */
29
30 #define MEM_SIZE (100 * 1024 * 1024) /* 100 MB */
31
32 static int64_t ledger_count = -1;
33 static int footprint_index = -1;
34 static int pagetable_index = -1;
35 static struct ledger_entry_info *lei = NULL;
36
37 static void
38 ledger_init(void)
39 {
40 static int ledger_inited = 0;
41 struct ledger_info li;
42 struct ledger_template_info *templateInfo;
43 int64_t templateCnt;
44 int i;
45 int legacy_footprint_entitlement_mode;
46 size_t oldlen;
47
48 if (ledger_inited) {
49 return;
50 }
51 ledger_inited = 1;
52
53 T_SETUPBEGIN;
54
55 legacy_footprint = FALSE;
56 #if LEGACY_FOOTPRINT_ENTITLED
57 int ret;
58
59 T_QUIET;
60 T_WITH_ERRNO;
61 oldlen = sizeof(legacy_footprint_entitlement_mode);
62 ret = sysctlbyname("kern.legacy_footprint_entitlement_mode",
63 &legacy_footprint_entitlement_mode,
64 &oldlen,
65 NULL,
66 0);
67 if (ret == 0 && legacy_footprint_entitlement_mode == 2) {
68 legacy_footprint = TRUE;
69 }
70 #endif /* LEGACY_FOOTPRINT_ENTITLED */
71
72 T_QUIET;
73 T_WITH_ERRNO;
74 T_ASSERT_EQ(ledger(LEDGER_INFO,
75 (caddr_t)(uintptr_t)getpid(),
76 (caddr_t)&li,
77 NULL),
78 0,
79 "ledger(LEDGER_INFO)");
80
81 templateCnt = li.li_entries;
82 templateInfo = malloc((size_t)li.li_entries * sizeof(struct ledger_template_info));
83 T_QUIET;
84 T_WITH_ERRNO;
85 T_ASSERT_NE(templateInfo, NULL, "malloc()");
86
87 ledger_count = li.li_entries;
88 footprint_index = -1;
89 pagetable_index = -1;
90 T_QUIET;
91 T_WITH_ERRNO;
92 T_ASSERT_GE(ledger(LEDGER_TEMPLATE_INFO,
93 (caddr_t)templateInfo,
94 (caddr_t)&templateCnt,
95 NULL),
96 0,
97 "ledger(LEDGER_TEMPLATE_INFO)");
98 for (i = 0; i < templateCnt; i++) {
99 if (!strncmp(templateInfo[i].lti_name,
100 "phys_footprint",
101 strlen("phys_footprint"))) {
102 footprint_index = i;
103 } else if (!strncmp(templateInfo[i].lti_name,
104 "page_table",
105 strlen("page_table"))) {
106 pagetable_index = i;
107 }
108 }
109 free(templateInfo);
110
111 lei = (struct ledger_entry_info *)
112 malloc((size_t)ledger_count * sizeof(*lei));
113 T_QUIET;
114 T_WITH_ERRNO;
115 T_ASSERT_NE(lei, NULL, "malloc(ledger_entry_info)");
116
117 T_QUIET;
118 T_ASSERT_NE(footprint_index, -1, "no footprint_index");
119 T_QUIET;
120 T_ASSERT_NE(pagetable_index, -1, "no pagetable_index");
121
122 T_SETUPEND;
123 }
124
125 static void
126 get_ledger_info(
127 uint64_t *phys_footprint,
128 uint64_t *page_table)
129 {
130 int64_t count;
131
132 count = ledger_count;
133 T_QUIET;
134 T_WITH_ERRNO;
135 T_ASSERT_GE(ledger(LEDGER_ENTRY_INFO,
136 (caddr_t)(uintptr_t)getpid(),
137 (caddr_t)lei,
138 (caddr_t)&count),
139 0,
140 "ledger(LEDGER_ENTRY_INFO)");
141 T_QUIET;
142 T_ASSERT_GT(count, (int64_t)footprint_index, "no entry for footprint");
143 T_QUIET;
144 T_ASSERT_GT(count, (int64_t)pagetable_index, "no entry for pagetable");
145 if (phys_footprint) {
146 *phys_footprint = (uint64_t)(lei[footprint_index].lei_balance);
147 }
148 if (page_table) {
149 *page_table = (uint64_t)(lei[pagetable_index].lei_balance);
150 }
151 }
152
153 static mach_vm_address_t
154 pre_warm(
155 mach_vm_size_t vm_size)
156 {
157 kern_return_t kr;
158 mach_vm_address_t vm_addr;
159 unsigned char BigBufOnStack[100 * 1024];
160 uint64_t footprint, page_table;
161
162 /* make sure ledgers are ready to be queried */
163 ledger_init();
164
165 T_SETUPBEGIN;
166
167 /*
168 * Touch a few pages ahead on the stack, to make
169 * sure we don't see a footprint increase due to
170 * an extra stack page later.
171 */
172 memset(BigBufOnStack, 0xb, sizeof(BigBufOnStack));
173 T_QUIET;
174 T_EXPECT_EQ(BigBufOnStack[0], 0xb,
175 "BigBufOnStack[0] == 0x%x",
176 BigBufOnStack[0]);
177 T_QUIET;
178 T_EXPECT_EQ(BigBufOnStack[sizeof(BigBufOnStack) - 1], 0xb,
179 "BigBufOnStack[%lu] == 0x%x",
180 sizeof(BigBufOnStack),
181 BigBufOnStack[sizeof(BigBufOnStack) - 1]);
182
183 /*
184 * Pre-allocate, touch and then release the same amount
185 * of memory we'll be allocating later during the test,
186 * to account for any memory overhead (page tables, global
187 * variables, ...).
188 */
189 vm_addr = 0;
190 kr = mach_vm_allocate(mach_task_self(),
191 &vm_addr,
192 vm_size,
193 VM_FLAGS_ANYWHERE);
194 T_QUIET;
195 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_allocate(%lld) error 0x%x (%s)",
196 vm_size, kr, mach_error_string(kr));
197 memset((char *)(uintptr_t)vm_addr, 'p', (size_t)vm_size);
198 kr = mach_vm_deallocate(mach_task_self(),
199 vm_addr,
200 vm_size);
201 T_QUIET;
202 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_deallocate() error 0x%x (%s)",
203 kr, mach_error_string(kr));
204
205 /*
206 * Exercise the ledger code to make sure it's ready to run
207 * without any extra memory overhead later.
208 */
209 get_ledger_info(&footprint, &page_table);
210
211 T_SETUPEND;
212
213 /*
214 * Return the start of the virtual range we pre-warmed, so that the
215 * test can check that it's using the same range.
216 */
217 return vm_addr;
218 }
219
220 T_DECL(phys_footprint_anonymous,
221 "phys_footprint for anonymous memory",
222 T_META_NAMESPACE(TEST_VM_NAMESPACE),
223 T_META_LTEPHASE(LTE_POSTINIT))
224 {
225 uint64_t footprint_before, pagetable_before;
226 uint64_t footprint_after, pagetable_after;
227 uint64_t footprint_expected;
228 kern_return_t kr;
229 mach_vm_address_t pre_vm_addr, vm_addr;
230 mach_vm_size_t vm_size, dirty_size;
231
232 /* pre-warm to account for page table expansion */
233 pre_vm_addr = pre_warm(MEM_SIZE);
234
235 /* allocating virtual memory... */
236 get_ledger_info(&footprint_before, &pagetable_before);
237 vm_addr = 0;
238 vm_size = MEM_SIZE;
239 kr = mach_vm_allocate(mach_task_self(), &vm_addr, vm_size,
240 VM_FLAGS_ANYWHERE);
241 T_QUIET;
242 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_allocate() error 0x%x (%s)",
243 kr, mach_error_string(kr));
244 T_QUIET;
245 T_EXPECT_EQ(vm_addr, pre_vm_addr, "pre-warm mishap");
246 /* ... should not change footprint */
247 get_ledger_info(&footprint_after, &pagetable_after);
248 footprint_expected = footprint_before;
249 footprint_expected += (pagetable_after - pagetable_before);
250 T_LOG("virtual allocation does not change phys_footprint");
251 T_EXPECT_EQ(footprint_after, footprint_expected,
252 "virtual allocation of %lld bytes: "
253 "footprint %lld -> %lld expected %lld delta %lld",
254 vm_size, footprint_before, footprint_after,
255 footprint_expected, footprint_after - footprint_expected);
256
257 /* touching memory... */
258 get_ledger_info(&footprint_before, &pagetable_before);
259 dirty_size = vm_size / 2;
260 memset((char *)(uintptr_t)vm_addr, 'x', (size_t)dirty_size);
261 /* ... should increase footprint */
262 get_ledger_info(&footprint_after, &pagetable_after);
263 footprint_expected = footprint_before + dirty_size;
264 footprint_expected += (pagetable_after - pagetable_before);
265 T_LOG("modifying anonymous memory increases phys_footprint");
266 T_EXPECT_EQ(footprint_after, footprint_expected,
267 "touched %lld bytes: "
268 "footprint %lld -> %lld expected %lld delta %lld",
269 dirty_size, footprint_before, footprint_after,
270 footprint_expected, footprint_after - footprint_expected);
271
272 /* deallocating memory... */
273 get_ledger_info(&footprint_before, &pagetable_before);
274 kr = mach_vm_deallocate(mach_task_self(), vm_addr, vm_size);
275 T_QUIET;
276 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_deallocate() error 0x%x (%s)",
277 kr, mach_error_string(kr));
278 /* ... should decrease footprint */
279 get_ledger_info(&footprint_after, &pagetable_after);
280 footprint_expected = footprint_before - dirty_size;
281 footprint_expected += (pagetable_after - pagetable_before);
282 T_LOG("deallocating dirty anonymous memory decreases phys_footprint");
283 T_EXPECT_EQ(footprint_after, footprint_expected,
284 "deallocated %lld dirty bytes: "
285 "footprint %lld -> %lld expected %lld delta %lld",
286 dirty_size, footprint_before, footprint_after,
287 footprint_expected, footprint_after - footprint_expected);
288 }
289
290 #define TEMP_FILE_TEMPLATE "/tmp/phys_footprint_data.XXXXXXXX"
291 #define TEMP_FILE_SIZE (1 * 1024 * 1024)
292
293 T_DECL(phys_footprint_file,
294 "phys_footprint for mapped file",
295 T_META_NAMESPACE(TEST_VM_NAMESPACE),
296 T_META_LTEPHASE(LTE_POSTINIT))
297 {
298 uint64_t footprint_before, pagetable_before;
299 uint64_t footprint_after, pagetable_after;
300 uint64_t footprint_expected;
301 mach_vm_address_t pre_vm_addr;
302 int fd;
303 char *map_addr;
304 size_t map_size, dirty_size;
305 ssize_t nbytes;
306 char tmp_file_name[PATH_MAX] = TEMP_FILE_TEMPLATE;
307 char *buf;
308 size_t buf_size;
309
310 T_SETUPBEGIN;
311 buf_size = TEMP_FILE_SIZE;
312 T_QUIET;
313 T_ASSERT_NOTNULL(buf = (char *)malloc(buf_size),
314 "allocate %zu-byte buffer", buf_size);
315 memset(buf, 'f', buf_size);
316 T_WITH_ERRNO;
317 T_QUIET;
318 T_ASSERT_NOTNULL(mktemp(tmp_file_name),
319 "create temporary file name");
320 T_WITH_ERRNO;
321 T_QUIET;
322 T_ASSERT_GE(fd = open(tmp_file_name, O_CREAT | O_RDWR),
323 0,
324 "create temp file");
325 T_WITH_ERRNO;
326 T_QUIET;
327 T_ASSERT_EQ(nbytes = write(fd, buf, buf_size),
328 (ssize_t)buf_size,
329 "write %zu bytes", buf_size);
330 free(buf);
331 T_SETUPEND;
332
333 /* pre-warm to account for page table expansion */
334 pre_vm_addr = pre_warm(TEMP_FILE_SIZE);
335
336 /* mapping a file does not impact footprint... */
337 get_ledger_info(&footprint_before, &pagetable_before);
338 map_size = TEMP_FILE_SIZE;
339 T_WITH_ERRNO;
340 T_QUIET;
341 T_ASSERT_NOTNULL(map_addr = (char *)mmap(NULL, map_size,
342 PROT_READ | PROT_WRITE,
343 MAP_FILE | MAP_SHARED, fd, 0),
344 "mmap()");
345 T_QUIET;
346 T_EXPECT_EQ((mach_vm_address_t)map_addr, pre_vm_addr,
347 "pre-warm mishap");
348 /* ... should not change footprint */
349 get_ledger_info(&footprint_after, &pagetable_after);
350 footprint_expected = footprint_before;
351 footprint_expected += (pagetable_after - pagetable_before);
352 T_LOG("mapping file does not change phys_footprint");
353 T_EXPECT_EQ(footprint_after, footprint_expected,
354 "mapping file with %zu bytes: "
355 "footprint %lld -> %lld expected %lld delta %lld",
356 map_size, footprint_before, footprint_after,
357 footprint_expected, footprint_after - footprint_expected);
358
359 /* touching file-backed memory... */
360 get_ledger_info(&footprint_before, &pagetable_before);
361 dirty_size = map_size / 2;
362 memset(map_addr, 'F', dirty_size);
363 /* ... should not impact footprint */
364 get_ledger_info(&footprint_after, &pagetable_after);
365 footprint_expected = footprint_before;
366 footprint_expected += (pagetable_after - pagetable_before);
367 T_LOG("modifying file-backed memory does not impact phys_footprint");
368 T_EXPECT_EQ(footprint_after, footprint_expected,
369 "touched %zu bytes: "
370 "footprint %lld -> %lld expected %lld delta %lld",
371 dirty_size, footprint_before, footprint_after,
372 footprint_expected, footprint_after - footprint_expected);
373
374 /* deallocating file-backed memory... */
375 get_ledger_info(&footprint_before, &pagetable_before);
376 T_WITH_ERRNO;
377 T_QUIET;
378 T_ASSERT_EQ(munmap(map_addr, map_size),
379 0,
380 "unmap file");
381 /* ... should not impact footprint */
382 get_ledger_info(&footprint_after, &pagetable_after);
383 footprint_expected = footprint_before;
384 footprint_expected += (pagetable_after - pagetable_before);
385 T_LOG("unmapping file-backed memory does not impact phys_footprint");
386 T_EXPECT_EQ(footprint_after, footprint_expected,
387 "unmapped %zu dirty bytes: "
388 "footprint %lld -> %lld expected %lld delta %lld",
389 dirty_size, footprint_before, footprint_after,
390 footprint_expected, footprint_after - footprint_expected);
391 }
392
393 T_DECL(phys_footprint_purgeable,
394 "phys_footprint for purgeable memory",
395 T_META_NAMESPACE(TEST_VM_NAMESPACE),
396 T_META_LTEPHASE(LTE_POSTINIT))
397 {
398 uint64_t footprint_before, pagetable_before;
399 uint64_t footprint_after, pagetable_after;
400 uint64_t footprint_expected;
401 kern_return_t kr;
402 mach_vm_address_t pre_vm_addr, vm_addr;
403 mach_vm_size_t vm_size, dirty_size;
404 int state;
405
406 /* pre-warm to account for page table expansion */
407 pre_vm_addr = pre_warm(MEM_SIZE);
408
409 /* allocating purgeable virtual memory... */
410 get_ledger_info(&footprint_before, &pagetable_before);
411 vm_addr = 0;
412 vm_size = MEM_SIZE;
413 kr = mach_vm_allocate(mach_task_self(), &vm_addr, vm_size,
414 VM_FLAGS_ANYWHERE | VM_FLAGS_PURGABLE);
415 T_QUIET;
416 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_allocate() error 0x%x (%s)",
417 kr, mach_error_string(kr));
418 T_QUIET;
419 T_EXPECT_EQ(vm_addr, pre_vm_addr, "pre-warm mishap");
420 /* ... should not change footprint */
421 get_ledger_info(&footprint_after, &pagetable_after);
422 footprint_expected = footprint_before;
423 footprint_expected += (pagetable_after - pagetable_before);
424 T_LOG("purgeable virtual allocation does not change phys_footprint");
425 T_EXPECT_EQ(footprint_after, footprint_expected,
426 "purgeable virtual allocation of %lld bytes: "
427 "footprint %lld -> %lld expected %lld delta %lld",
428 vm_size, footprint_before, footprint_after,
429 footprint_expected, footprint_after - footprint_expected);
430
431 /* touching memory... */
432 get_ledger_info(&footprint_before, &pagetable_before);
433 dirty_size = vm_size / 2;
434 memset((char *)(uintptr_t)vm_addr, 'x', (size_t)dirty_size);
435 /* ... should increase footprint */
436 get_ledger_info(&footprint_after, &pagetable_after);
437 footprint_expected = footprint_before + dirty_size;
438 footprint_expected += (pagetable_after - pagetable_before);
439 T_LOG("modifying anonymous memory increases phys_footprint");
440 T_EXPECT_EQ(footprint_after, footprint_expected,
441 "touched %lld bytes: "
442 "footprint %lld -> %lld expected %lld delta %lld",
443 dirty_size, footprint_before, footprint_after,
444 footprint_expected, footprint_after - footprint_expected);
445
446 /* making it volatile... */
447 get_ledger_info(&footprint_before, &pagetable_before);
448 state = VM_PURGABLE_VOLATILE;
449 T_QUIET;
450 T_ASSERT_EQ(mach_vm_purgable_control(mach_task_self(),
451 vm_addr,
452 VM_PURGABLE_SET_STATE,
453 &state),
454 KERN_SUCCESS,
455 "vm_purgable_control(VOLATILE)");
456 T_QUIET;
457 T_ASSERT_EQ(state, VM_PURGABLE_NONVOLATILE,
458 "memory was non-volatile");
459 /* ... should decrease footprint */
460 get_ledger_info(&footprint_after, &pagetable_after);
461 footprint_expected = footprint_before - dirty_size;
462 footprint_expected += (pagetable_after - pagetable_before);
463 T_LOG("making volatile decreases phys_footprint");
464 T_EXPECT_EQ(footprint_after, footprint_expected,
465 "made volatile %lld dirty bytes: "
466 "footprint %lld -> %lld expected %lld delta %lld",
467 dirty_size, footprint_before, footprint_after,
468 footprint_expected, footprint_after - footprint_expected);
469
470 /* making it non-volatile... */
471 get_ledger_info(&footprint_before, &pagetable_before);
472 state = VM_PURGABLE_NONVOLATILE;
473 T_QUIET;
474 T_ASSERT_EQ(mach_vm_purgable_control(mach_task_self(),
475 vm_addr,
476 VM_PURGABLE_SET_STATE,
477 &state),
478 KERN_SUCCESS,
479 "vm_purgable_control(NONVOLATILE)");
480 T_QUIET;
481 T_ASSERT_EQ(state, VM_PURGABLE_VOLATILE,
482 "memory was volatile");
483 /* ... should increase footprint */
484 get_ledger_info(&footprint_after, &pagetable_after);
485 footprint_expected = footprint_before + dirty_size;
486 footprint_expected += (pagetable_after - pagetable_before);
487 T_LOG("making non-volatile increases phys_footprint");
488 T_EXPECT_EQ(footprint_after, footprint_expected,
489 "made non-volatile %lld dirty bytes: "
490 "footprint %lld -> %lld expected %lld delta %lld",
491 dirty_size, footprint_before, footprint_after,
492 footprint_expected, footprint_after - footprint_expected);
493
494 /* deallocating memory... */
495 get_ledger_info(&footprint_before, &pagetable_before);
496 kr = mach_vm_deallocate(mach_task_self(), vm_addr, vm_size);
497 T_QUIET;
498 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_deallocate() error 0x%x (%s)",
499 kr, mach_error_string(kr));
500 /* ... should decrease footprint */
501 get_ledger_info(&footprint_after, &pagetable_after);
502 footprint_expected = footprint_before - dirty_size;
503 footprint_expected += (pagetable_after - pagetable_before);
504 T_LOG("deallocating memory decreases phys_footprint");
505 T_EXPECT_EQ(footprint_after, footprint_expected,
506 "deallocated %lld dirty bytes: "
507 "footprint %lld -> %lld expected %lld delta %lld",
508 dirty_size, footprint_before, footprint_after,
509 footprint_expected, footprint_after - footprint_expected);
510 }
511
512 T_DECL(phys_footprint_purgeable_ownership,
513 "phys_footprint for owned purgeable memory",
514 T_META_NAMESPACE(TEST_VM_NAMESPACE),
515 T_META_LTEPHASE(LTE_POSTINIT))
516 {
517 uint64_t footprint_before, pagetable_before;
518 uint64_t footprint_after, pagetable_after;
519 uint64_t footprint_expected;
520 kern_return_t kr;
521 mach_vm_address_t pre_vm_addr, vm_addr;
522 mach_vm_size_t vm_size, dirty_size, me_size;
523 int state;
524 mach_port_t me_port;
525
526 /* pre-warm to account for page table expansion */
527 pre_vm_addr = pre_warm(MEM_SIZE);
528
529 /* allocating purgeable virtual memory... */
530 get_ledger_info(&footprint_before, &pagetable_before);
531 vm_addr = 0;
532 vm_size = MEM_SIZE;
533 kr = mach_vm_allocate(mach_task_self(), &vm_addr, vm_size,
534 VM_FLAGS_ANYWHERE | VM_FLAGS_PURGABLE);
535 T_QUIET;
536 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_allocate() error 0x%x (%s)",
537 kr, mach_error_string(kr));
538 T_QUIET;
539 T_EXPECT_EQ(vm_addr, pre_vm_addr, "pre-warm mishap");
540 /* ... should not change footprint */
541 get_ledger_info(&footprint_after, &pagetable_after);
542 footprint_expected = footprint_before;
543 footprint_expected += (pagetable_after - pagetable_before);
544 T_LOG("purgeable virtual allocation does not change phys_footprint");
545 T_EXPECT_EQ(footprint_after, footprint_expected,
546 "purgeable virtual allocation of %lld bytes: "
547 "footprint %lld -> %lld expected %lld delta %lld",
548 vm_size, footprint_before, footprint_after,
549 footprint_expected, footprint_after - footprint_expected);
550
551 /* touching memory... */
552 get_ledger_info(&footprint_before, &pagetable_before);
553 dirty_size = vm_size / 2;
554 memset((char *)(uintptr_t)vm_addr, 'x', (size_t)dirty_size);
555 /* ... should increase footprint */
556 get_ledger_info(&footprint_after, &pagetable_after);
557 footprint_expected = footprint_before + dirty_size;
558 footprint_expected += (pagetable_after - pagetable_before);
559 T_LOG("modifying anonymous memory increases phys_footprint");
560 T_EXPECT_EQ(footprint_after, footprint_expected,
561 "touched %lld bytes: "
562 "footprint %lld -> %lld expected %lld delta %lld",
563 dirty_size, footprint_before, footprint_after,
564 footprint_expected, footprint_after - footprint_expected);
565
566 /* making it volatile... */
567 get_ledger_info(&footprint_before, &pagetable_before);
568 state = VM_PURGABLE_VOLATILE;
569 T_QUIET;
570 T_ASSERT_EQ(mach_vm_purgable_control(mach_task_self(),
571 vm_addr,
572 VM_PURGABLE_SET_STATE,
573 &state),
574 KERN_SUCCESS,
575 "vm_purgable_control(VOLATILE)");
576 T_QUIET;
577 T_ASSERT_EQ(state, VM_PURGABLE_NONVOLATILE,
578 "memory was non-volatile");
579 /* ... should decrease footprint */
580 get_ledger_info(&footprint_after, &pagetable_after);
581 footprint_expected = footprint_before - dirty_size;
582 footprint_expected += (pagetable_after - pagetable_before);
583 T_LOG("making volatile decreases phys_footprint");
584 T_EXPECT_EQ(footprint_after, footprint_expected,
585 "made volatile %lld dirty bytes: "
586 "footprint %lld -> %lld expected %lld delta %lld",
587 dirty_size, footprint_before, footprint_after,
588 footprint_expected, footprint_after - footprint_expected);
589
590 /* making it non-volatile... */
591 get_ledger_info(&footprint_before, &pagetable_before);
592 state = VM_PURGABLE_NONVOLATILE;
593 T_QUIET;
594 T_ASSERT_EQ(mach_vm_purgable_control(mach_task_self(),
595 vm_addr,
596 VM_PURGABLE_SET_STATE,
597 &state),
598 KERN_SUCCESS,
599 "vm_purgable_control(NONVOLATILE)");
600 T_QUIET;
601 T_ASSERT_EQ(state, VM_PURGABLE_VOLATILE,
602 "memory was volatile");
603 /* ... should increase footprint */
604 get_ledger_info(&footprint_after, &pagetable_after);
605 footprint_expected = footprint_before + dirty_size;
606 footprint_expected += (pagetable_after - pagetable_before);
607 T_LOG("making non-volatile increases phys_footprint");
608 T_EXPECT_EQ(footprint_after, footprint_expected,
609 "made non-volatile %lld dirty bytes: "
610 "footprint %lld -> %lld expected %lld delta %lld",
611 dirty_size, footprint_before, footprint_after,
612 footprint_expected, footprint_after - footprint_expected);
613
614 /* making a memory entry... */
615 get_ledger_info(&footprint_before, &pagetable_before);
616 me_size = vm_size;
617 me_port = MACH_PORT_NULL;
618 kr = mach_make_memory_entry_64(mach_task_self(),
619 &me_size,
620 vm_addr,
621 VM_PROT_READ | VM_PROT_WRITE,
622 &me_port,
623 MACH_PORT_NULL);
624 T_QUIET;
625 T_EXPECT_EQ(kr, KERN_SUCCESS, "make_memory_entry() error 0x%x (%s)",
626 kr, mach_error_string(kr));
627 T_QUIET;
628 T_EXPECT_EQ(me_size, vm_size, "memory entry size mismatch");
629 /* ... should not change footprint */
630 get_ledger_info(&footprint_after, &pagetable_after);
631 footprint_expected = footprint_before;
632 footprint_expected += (pagetable_after - pagetable_before);
633 T_LOG("making a memory entry does not change phys_footprint");
634 T_EXPECT_EQ(footprint_after, footprint_expected,
635 "making a memory entry of %lld bytes: "
636 "footprint %lld -> %lld expected %lld delta %lld",
637 vm_size, footprint_before, footprint_after,
638 footprint_expected, footprint_after - footprint_expected);
639
640 /* deallocating memory while holding memory entry... */
641 get_ledger_info(&footprint_before, &pagetable_before);
642 kr = mach_vm_deallocate(mach_task_self(), vm_addr, vm_size);
643 T_QUIET;
644 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_deallocate() error 0x%x (%s)",
645 kr, mach_error_string(kr));
646 /* ... should not change footprint */
647 get_ledger_info(&footprint_after, &pagetable_after);
648 footprint_expected = footprint_before;
649 footprint_expected += (pagetable_after - pagetable_before);
650 T_LOG("deallocating owned memory while holding memory entry "
651 "does not change phys_footprint");
652 T_EXPECT_EQ(footprint_after, footprint_expected,
653 "deallocated %lld dirty bytes: "
654 "footprint %lld -> %lld expected %lld delta %lld",
655 dirty_size, footprint_before, footprint_after,
656 footprint_expected, footprint_after - footprint_expected);
657
658 /* releasing the memory entry... */
659 kr = mach_port_deallocate(mach_task_self(), me_port);
660 T_QUIET;
661 T_EXPECT_EQ(kr, KERN_SUCCESS, "mach_port_deallocate() error 0x%x (%s)",
662 kr, mach_error_string(kr));
663 /* ... should decrease footprint */
664 get_ledger_info(&footprint_after, &pagetable_after);
665 footprint_expected = footprint_before - dirty_size;
666 footprint_expected += (pagetable_after - pagetable_before);
667 T_LOG("releasing memory entry decreases phys_footprint");
668 T_EXPECT_EQ(footprint_after, footprint_expected,
669 "made volatile %lld dirty bytes: "
670 "footprint %lld -> %lld expected %lld delta %lld",
671 dirty_size, footprint_before, footprint_after,
672 footprint_expected, footprint_after - footprint_expected);
673 }
674
675 #ifdef MAP_MEM_LEDGER_TAGGED
676 T_DECL(phys_footprint_ledger_purgeable_owned,
677 "phys_footprint for ledger-tagged purgeable memory ownership",
678 T_META_NAMESPACE(TEST_VM_NAMESPACE),
679 T_META_LTEPHASE(LTE_POSTINIT))
680 {
681 uint64_t footprint_before, pagetable_before;
682 uint64_t footprint_after, pagetable_after;
683 uint64_t footprint_expected;
684 kern_return_t kr;
685 mach_vm_address_t pre_vm_addr, vm_addr;
686 mach_vm_size_t vm_size, dirty_size, me_size;
687 int state;
688 mach_port_t me_port;
689
690 /* pre-warm to account for page table expansion */
691 pre_vm_addr = pre_warm(MEM_SIZE);
692
693 /* making a memory entry... */
694 get_ledger_info(&footprint_before, &pagetable_before);
695 vm_size = MEM_SIZE;
696 me_size = vm_size;
697 me_port = MACH_PORT_NULL;
698 kr = mach_make_memory_entry_64(mach_task_self(),
699 &me_size,
700 0,
701 (MAP_MEM_NAMED_CREATE |
702 MAP_MEM_LEDGER_TAGGED |
703 MAP_MEM_PURGABLE |
704 VM_PROT_READ | VM_PROT_WRITE),
705 &me_port,
706 MACH_PORT_NULL);
707 T_QUIET;
708 T_EXPECT_EQ(kr, KERN_SUCCESS, "make_memory_entry() error 0x%x (%s)",
709 kr, mach_error_string(kr));
710 T_QUIET;
711 T_EXPECT_EQ(me_size, vm_size, "memory entry size mismatch");
712 /* ... should not change footprint */
713 get_ledger_info(&footprint_after, &pagetable_after);
714 footprint_expected = footprint_before;
715 footprint_expected += (pagetable_after - pagetable_before);
716 T_LOG("making a memory entry does not change phys_footprint");
717 T_EXPECT_EQ(footprint_after, footprint_expected,
718 "making a memory entry of %lld bytes: "
719 "footprint %lld -> %lld expected %lld delta %lld",
720 vm_size, footprint_before, footprint_after,
721 footprint_expected, footprint_after - footprint_expected);
722
723 /* mapping ledger-tagged virtual memory... */
724 get_ledger_info(&footprint_before, &pagetable_before);
725 vm_addr = 0;
726 kr = mach_vm_map(mach_task_self(), &vm_addr, vm_size,
727 0, /* mask */
728 VM_FLAGS_ANYWHERE,
729 me_port,
730 0, /* offset */
731 FALSE, /* copy */
732 VM_PROT_READ | VM_PROT_WRITE,
733 VM_PROT_READ | VM_PROT_WRITE,
734 VM_INHERIT_DEFAULT);
735 T_QUIET;
736 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_map() error 0x%x (%s)",
737 kr, mach_error_string(kr));
738 T_QUIET;
739 T_EXPECT_EQ(vm_addr, pre_vm_addr, "pre-warm mishap");
740 /* ... should not change footprint */
741 get_ledger_info(&footprint_after, &pagetable_after);
742 footprint_expected = footprint_before;
743 footprint_expected += (pagetable_after - pagetable_before);
744 T_LOG("mapping ledger-tagged memory does not change phys_footprint");
745 T_EXPECT_EQ(footprint_after, footprint_expected,
746 "ledger-tagged mapping of %lld bytes: "
747 "footprint %lld -> %lld expected %lld delta %lld",
748 vm_size, footprint_before, footprint_after,
749 footprint_expected, footprint_after - footprint_expected);
750
751 /* touching memory... */
752 get_ledger_info(&footprint_before, &pagetable_before);
753 dirty_size = vm_size / 2;
754 memset((char *)(uintptr_t)vm_addr, 'x', (size_t)dirty_size);
755 /* ... should increase footprint */
756 get_ledger_info(&footprint_after, &pagetable_after);
757 footprint_expected = footprint_before + dirty_size;
758 footprint_expected += (pagetable_after - pagetable_before);
759 T_LOG("modifying ledger-tagged memory increases phys_footprint");
760 T_EXPECT_EQ(footprint_after, footprint_expected,
761 "touched %lld bytes: "
762 "footprint %lld -> %lld expected %lld delta %lld",
763 dirty_size, footprint_before, footprint_after,
764 footprint_expected, footprint_after - footprint_expected);
765
766 /* making it volatile... */
767 get_ledger_info(&footprint_before, &pagetable_before);
768 state = VM_PURGABLE_VOLATILE;
769 T_QUIET;
770 T_ASSERT_EQ(mach_vm_purgable_control(mach_task_self(),
771 vm_addr,
772 VM_PURGABLE_SET_STATE,
773 &state),
774 KERN_SUCCESS,
775 "vm_purgable_control(VOLATILE)");
776 T_QUIET;
777 T_ASSERT_EQ(state, VM_PURGABLE_NONVOLATILE,
778 "memory was non-volatile");
779 /* ... should decrease footprint */
780 get_ledger_info(&footprint_after, &pagetable_after);
781 footprint_expected = footprint_before - dirty_size;
782 footprint_expected += (pagetable_after - pagetable_before);
783 T_LOG("making volatile decreases phys_footprint");
784 T_EXPECT_EQ(footprint_after, footprint_expected,
785 "made volatile %lld dirty bytes: "
786 "footprint %lld -> %lld expected %lld delta %lld",
787 dirty_size, footprint_before, footprint_after,
788 footprint_expected, footprint_after - footprint_expected);
789
790 /* making it non-volatile... */
791 get_ledger_info(&footprint_before, &pagetable_before);
792 state = VM_PURGABLE_NONVOLATILE;
793 T_QUIET;
794 T_ASSERT_EQ(mach_vm_purgable_control(mach_task_self(),
795 vm_addr,
796 VM_PURGABLE_SET_STATE,
797 &state),
798 KERN_SUCCESS,
799 "vm_purgable_control(NONVOLATILE)");
800 T_QUIET;
801 T_ASSERT_EQ(state, VM_PURGABLE_VOLATILE,
802 "memory was volatile");
803 /* ... should increase footprint */
804 get_ledger_info(&footprint_after, &pagetable_after);
805 footprint_expected = footprint_before + dirty_size;
806 footprint_expected += (pagetable_after - pagetable_before);
807 T_LOG("making non-volatile increases phys_footprint");
808 T_EXPECT_EQ(footprint_after, footprint_expected,
809 "made non-volatile %lld dirty bytes: "
810 "footprint %lld -> %lld expected %lld delta %lld",
811 dirty_size, footprint_before, footprint_after,
812 footprint_expected, footprint_after - footprint_expected);
813
814 /* deallocating memory while holding memory entry... */
815 get_ledger_info(&footprint_before, &pagetable_before);
816 kr = mach_vm_deallocate(mach_task_self(), vm_addr, vm_size);
817 T_QUIET;
818 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_deallocate() error 0x%x (%s)",
819 kr, mach_error_string(kr));
820 /* ... should not change footprint */
821 get_ledger_info(&footprint_after, &pagetable_after);
822 footprint_expected = footprint_before;
823 footprint_expected += (pagetable_after - pagetable_before);
824 T_LOG("deallocating owned memory while holding memory entry "
825 "does not change phys_footprint");
826 T_EXPECT_EQ(footprint_after, footprint_expected,
827 "deallocated %lld dirty bytes: "
828 "footprint %lld -> %lld expected %lld delta %lld",
829 dirty_size, footprint_before, footprint_after,
830 footprint_expected, footprint_after - footprint_expected);
831
832 /* releasing the memory entry... */
833 kr = mach_port_deallocate(mach_task_self(), me_port);
834 T_QUIET;
835 T_EXPECT_EQ(kr, KERN_SUCCESS, "mach_port_deallocate() error 0x%x (%s)",
836 kr, mach_error_string(kr));
837 /* ... should decrease footprint */
838 get_ledger_info(&footprint_after, &pagetable_after);
839 footprint_expected = footprint_before - dirty_size;
840 footprint_expected += (pagetable_after - pagetable_before);
841 T_LOG("releasing memory entry decreases phys_footprint");
842 T_EXPECT_EQ(footprint_after, footprint_expected,
843 "made volatile %lld dirty bytes: "
844 "footprint %lld -> %lld expected %lld delta %lld",
845 dirty_size, footprint_before, footprint_after,
846 footprint_expected, footprint_after - footprint_expected);
847 }
848
849 T_DECL(phys_footprint_ledger_owned,
850 "phys_footprint for ledger-tagged memory ownership",
851 T_META_NAMESPACE(TEST_VM_NAMESPACE),
852 T_META_LTEPHASE(LTE_POSTINIT))
853 {
854 uint64_t footprint_before, pagetable_before;
855 uint64_t footprint_after, pagetable_after;
856 uint64_t footprint_expected;
857 kern_return_t kr;
858 mach_vm_address_t pre_vm_addr, vm_addr;
859 mach_vm_size_t vm_size, dirty_size, me_size;
860 mach_port_t me_port;
861
862 /* pre-warm to account for page table expansion */
863 pre_vm_addr = pre_warm(MEM_SIZE);
864
865 /* making a memory entry... */
866 get_ledger_info(&footprint_before, &pagetable_before);
867 vm_size = MEM_SIZE;
868 me_size = vm_size;
869 me_port = MACH_PORT_NULL;
870 kr = mach_make_memory_entry_64(mach_task_self(),
871 &me_size,
872 0,
873 (MAP_MEM_NAMED_CREATE |
874 MAP_MEM_LEDGER_TAGGED |
875 VM_PROT_READ | VM_PROT_WRITE),
876 &me_port,
877 MACH_PORT_NULL);
878 T_QUIET;
879 T_EXPECT_EQ(kr, KERN_SUCCESS, "make_memory_entry() error 0x%x (%s)",
880 kr, mach_error_string(kr));
881 T_QUIET;
882 T_EXPECT_EQ(me_size, vm_size, "memory entry size mismatch");
883 /* ... should not change footprint */
884 get_ledger_info(&footprint_after, &pagetable_after);
885 footprint_expected = footprint_before;
886 footprint_expected += (pagetable_after - pagetable_before);
887 T_LOG("making a memory entry does not change phys_footprint");
888 T_EXPECT_EQ(footprint_after, footprint_expected,
889 "making a memory entry of %lld bytes: "
890 "footprint %lld -> %lld expected %lld delta %lld",
891 vm_size, footprint_before, footprint_after,
892 footprint_expected, footprint_after - footprint_expected);
893
894 /* mapping ledger-tagged virtual memory... */
895 get_ledger_info(&footprint_before, &pagetable_before);
896 vm_addr = 0;
897 kr = mach_vm_map(mach_task_self(), &vm_addr, vm_size,
898 0, /* mask */
899 VM_FLAGS_ANYWHERE,
900 me_port,
901 0, /* offset */
902 FALSE, /* copy */
903 VM_PROT_READ | VM_PROT_WRITE,
904 VM_PROT_READ | VM_PROT_WRITE,
905 VM_INHERIT_DEFAULT);
906 T_QUIET;
907 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_map() error 0x%x (%s)",
908 kr, mach_error_string(kr));
909 T_QUIET;
910 T_EXPECT_EQ(vm_addr, pre_vm_addr, "pre-warm mishap");
911 /* ... should not change footprint */
912 get_ledger_info(&footprint_after, &pagetable_after);
913 footprint_expected = footprint_before;
914 footprint_expected += (pagetable_after - pagetable_before);
915 T_LOG("mapping ledger-tagged memory does not change phys_footprint");
916 T_EXPECT_EQ(footprint_after, footprint_expected,
917 "ledger-tagged mapping of %lld bytes: "
918 "footprint %lld -> %lld expected %lld delta %lld",
919 vm_size, footprint_before, footprint_after,
920 footprint_expected, footprint_after - footprint_expected);
921
922 /* touching memory... */
923 get_ledger_info(&footprint_before, &pagetable_before);
924 dirty_size = vm_size / 2;
925 memset((char *)(uintptr_t)vm_addr, 'x', (size_t)dirty_size);
926 /* ... should increase footprint */
927 get_ledger_info(&footprint_after, &pagetable_after);
928 footprint_expected = footprint_before + dirty_size;
929 footprint_expected += (pagetable_after - pagetable_before);
930 T_LOG("modifying ledger-tagged memory increases phys_footprint");
931 T_EXPECT_EQ(footprint_after, footprint_expected,
932 "touched %lld bytes: "
933 "footprint %lld -> %lld expected %lld delta %lld",
934 dirty_size, footprint_before, footprint_after,
935 footprint_expected, footprint_after - footprint_expected);
936
937 /* deallocating memory while holding memory entry... */
938 get_ledger_info(&footprint_before, &pagetable_before);
939 kr = mach_vm_deallocate(mach_task_self(), vm_addr, vm_size);
940 T_QUIET;
941 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_deallocate() error 0x%x (%s)",
942 kr, mach_error_string(kr));
943 /* ... should not change footprint */
944 get_ledger_info(&footprint_after, &pagetable_after);
945 footprint_expected = footprint_before;
946 footprint_expected += (pagetable_after - pagetable_before);
947 T_LOG("deallocating owned memory while holding memory entry "
948 "does not change phys_footprint");
949 T_EXPECT_EQ(footprint_after, footprint_expected,
950 "deallocated %lld dirty bytes: "
951 "footprint %lld -> %lld expected %lld delta %lld",
952 dirty_size, footprint_before, footprint_after,
953 footprint_expected, footprint_after - footprint_expected);
954
955 /* releasing the memory entry... */
956 kr = mach_port_deallocate(mach_task_self(), me_port);
957 T_QUIET;
958 T_EXPECT_EQ(kr, KERN_SUCCESS, "mach_port_deallocate() error 0x%x (%s)",
959 kr, mach_error_string(kr));
960 /* ... should decrease footprint */
961 get_ledger_info(&footprint_after, &pagetable_after);
962 footprint_expected = footprint_before - dirty_size;
963 footprint_expected += (pagetable_after - pagetable_before);
964 T_LOG("releasing memory entry decreases phys_footprint");
965 T_EXPECT_EQ(footprint_after, footprint_expected,
966 "made volatile %lld dirty bytes: "
967 "footprint %lld -> %lld expected %lld delta %lld",
968 dirty_size, footprint_before, footprint_after,
969 footprint_expected, footprint_after - footprint_expected);
970 }
971 #endif /* MAP_MEM_LEDGER_TAGGED */
972
973 /* IOSurface code from: CoreImage/CoreImageTests/CIRender/SurfaceUtils.c */
974 #include <CoreFoundation/CoreFoundation.h>
975 #include <IOSurface/IOSurface.h>
976 #include <IOSurface/IOSurfacePrivate.h>
977 static size_t
978 bytes_per_element(uint32_t format)
979 {
980 size_t bpe = 0;
981 switch (format) {
982 case 32: // kCVPixelFormatType_32ARGB (ARGB8)
983 bpe = 4;
984 break;
985 default:
986 bpe = 0;
987 break;
988 }
989 return bpe;
990 }
991 static size_t
992 bytes_per_pixel(uint32_t format)
993 {
994 size_t bpe = 0;
995 switch (format) {
996 case 32: // kCVPixelFormatType_32ARGB (ARGB8)
997 bpe = 4;
998 break;
999 default:
1000 bpe = 0;
1001 break;
1002 }
1003 return bpe;
1004 }
1005 static inline size_t
1006 roundSizeToMultiple(size_t size, size_t mult)
1007 {
1008 return ((size + mult - 1) / mult) * mult;
1009 }
1010 static inline void
1011 setIntValue(CFMutableDictionaryRef dict, const CFStringRef key, int value)
1012 {
1013 CFNumberRef number = CFNumberCreate(0, kCFNumberIntType, &value);
1014 CFDictionarySetValue(dict, key, number);
1015 CFRelease(number);
1016 }
1017 static inline void
1018 setBoolValue(CFMutableDictionaryRef dict, const CFStringRef key, bool value)
1019 {
1020 CFDictionarySetValue(dict, key, value ? kCFBooleanTrue : kCFBooleanFalse);
1021 }
1022 typedef void (^SurfacePlaneBlock)(void *data, size_t planeIndex, size_t width, size_t height, size_t rowbytes);
1023 static IOReturn
1024 SurfaceApplyPlaneBlock(IOSurfaceRef surface, SurfacePlaneBlock block)
1025 {
1026 if (surface == nil || block == nil) {
1027 return kIOReturnBadArgument;
1028 }
1029
1030 IOReturn result = kIOReturnSuccess;
1031 size_t planeCount = IOSurfaceGetPlaneCount(surface);
1032
1033 if (planeCount == 0) {
1034 result = IOSurfaceLock(surface, 0, NULL);
1035 if (result != kIOReturnSuccess) {
1036 return result;
1037 }
1038
1039 void* base = IOSurfaceGetBaseAddress(surface);
1040 size_t rb = IOSurfaceGetBytesPerRow(surface);
1041 size_t w = IOSurfaceGetWidth(surface);
1042 size_t h = IOSurfaceGetHeight(surface);
1043
1044 if (base && rb && w && h) {
1045 block(base, 0, w, h, rb);
1046 }
1047
1048 IOSurfaceUnlock(surface, 0, NULL);
1049 } else if (planeCount == 2) {
1050 for (size_t i = 0; i < planeCount; i++) {
1051 result = IOSurfaceLock(surface, 0, NULL);
1052 if (result != kIOReturnSuccess) {
1053 return result;
1054 }
1055
1056 void* base = IOSurfaceGetBaseAddressOfPlane(surface, i);
1057 size_t rb = IOSurfaceGetBytesPerRowOfPlane(surface, i);
1058 size_t w = IOSurfaceGetWidthOfPlane(surface, i);
1059 size_t h = IOSurfaceGetHeightOfPlane(surface, i);
1060
1061 if (base && rb && w && h) {
1062 block(base, i, w, h, rb);
1063 }
1064
1065 IOSurfaceUnlock(surface, 0, NULL);
1066 }
1067 }
1068 return result;
1069 }
1070 static void
1071 ClearSurface(IOSurfaceRef surface)
1072 {
1073 const int zero = 0;
1074 (void) SurfaceApplyPlaneBlock(surface, ^(void *p, size_t i, __unused size_t w, size_t h, size_t rb)
1075 {
1076 if (i == 0) {
1077 memset(p, zero, rb * h);
1078 } else {
1079 memset(p, 128, rb * h);
1080 }
1081 });
1082 }
1083 static size_t
1084 SurfaceGetMemorySize(IOSurfaceRef surface)
1085 {
1086 size_t planeCount = IOSurfaceGetPlaneCount(surface);
1087
1088 if (planeCount == 0) {
1089 size_t rb = IOSurfaceGetBytesPerRow(surface);
1090 size_t h = IOSurfaceGetHeight(surface);
1091 return rb * h;
1092 } else if (planeCount == 2) {
1093 size_t rb0 = IOSurfaceGetBytesPerRowOfPlane(surface, 0);
1094 size_t h0 = IOSurfaceGetHeightOfPlane(surface, 0);
1095 size_t rb1 = IOSurfaceGetBytesPerRowOfPlane(surface, 1);
1096 size_t h1 = IOSurfaceGetHeightOfPlane(surface, 1);
1097 return rb0 * h0 + rb1 * h1;
1098 }
1099 return 0;
1100 }
1101 static IOSurfaceRef
1102 CreateSurface(uint32_t pixelsWide, uint32_t pixelsHigh, uint32_t rowBytesAlignment, uint32_t fmt, bool purgeable, bool clear)
1103 {
1104 IOSurfaceRef surface = nil;
1105
1106 if (pixelsWide < 1 || pixelsHigh < 1 || fmt == 0) {
1107 return nil;
1108 }
1109
1110 size_t bpp = bytes_per_pixel(fmt);
1111 size_t bpe = bytes_per_element(fmt);
1112 if (bpp == 0 || bpe == 0) {
1113 return nil;
1114 }
1115
1116 size_t rowbytes = pixelsWide * bpp;
1117 if (rowBytesAlignment == 0) {
1118 rowBytesAlignment = 16;
1119 }
1120 rowbytes = roundSizeToMultiple(rowbytes, rowBytesAlignment);
1121
1122 CFMutableDictionaryRef props = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1123 setIntValue(props, kIOSurfaceBytesPerRow, (int)rowbytes);
1124 setIntValue(props, kIOSurfaceWidth, (int)pixelsWide);
1125 setIntValue(props, kIOSurfaceHeight, (int)pixelsHigh);
1126 setIntValue(props, kIOSurfacePixelFormat, (int)fmt);
1127 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1128 setBoolValue(props, kIOSurfaceNonPurgeable, !purgeable);
1129 #else /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
1130 (void)purgeable;
1131 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
1132 {
1133 if (bpe != bpp) { // i.e. a 422 format such as 'yuvf' etc.
1134 setIntValue(props, kIOSurfaceElementWidth, 2);
1135 setIntValue(props, kIOSurfaceElementHeight, 1);
1136 }
1137 setIntValue(props, kIOSurfaceBytesPerElement, (int)bpe);
1138 }
1139
1140 surface = IOSurfaceCreate(props);
1141
1142 if (clear) {
1143 ClearSurface(surface);
1144 }
1145
1146 CFRelease(props);
1147 return surface;
1148 }
1149 T_DECL(phys_footprint_purgeable_iokit,
1150 "phys_footprint for purgeable IOKit memory",
1151 T_META_NAMESPACE(TEST_VM_NAMESPACE),
1152 T_META_LTEPHASE(LTE_POSTINIT))
1153 {
1154 uint64_t footprint_before, pagetable_before;
1155 uint64_t footprint_after, pagetable_after;
1156 uint64_t footprint_expected, footprint_delta_slop;
1157 int64_t footprint_delta;
1158 IOSurfaceRef surface;
1159 uint32_t old_state;
1160 uint64_t surface_size;
1161
1162 T_SETUPBEGIN;
1163 footprint_delta_slop = 8 * vm_kernel_page_size;
1164 ledger_init();
1165 surface = CreateSurface(1024, 1024, 0, 32, true, true);
1166 IOSurfaceSetPurgeable(surface, kIOSurfacePurgeableVolatile, &old_state);
1167 IOSurfaceSetPurgeable(surface, kIOSurfacePurgeableNonVolatile, &old_state);
1168 CFRelease(surface);
1169 T_SETUPEND;
1170
1171 surface_size = 1024 * 1024 * 4;
1172
1173 /* create IOsurface: footprint grows */
1174 get_ledger_info(&footprint_before, &pagetable_before);
1175 surface = CreateSurface(1024, 1024, 0, 32, true, true);
1176 get_ledger_info(&footprint_after, &pagetable_after);
1177 if (legacy_footprint) {
1178 footprint_expected = footprint_before;
1179 footprint_expected += (pagetable_after - pagetable_before);
1180 footprint_delta = (int64_t)(footprint_after - footprint_expected);
1181 T_LOG("LEGACY FOOTPRINT: creating purgeable IOSurface: no footprint impact");
1182 T_EXPECT_LE((uint64_t)llabs(footprint_delta), footprint_delta_slop,
1183 "create purgeable IOSurface %lld bytes: "
1184 "footprint %lld -> %lld expected %lld delta %lld",
1185 surface_size, footprint_before, footprint_after,
1186 footprint_expected, footprint_delta);
1187 } else {
1188 footprint_expected = footprint_before + surface_size;
1189 footprint_expected += (pagetable_after - pagetable_before);
1190 footprint_delta = (int64_t)(footprint_after - footprint_expected);
1191 T_LOG("creating purgeable IOSurface increases phys_footprint");
1192 T_EXPECT_LE((uint64_t)llabs(footprint_delta), footprint_delta_slop,
1193 "create purgeable IOSurface %lld bytes: "
1194 "footprint %lld -> %lld expected %lld delta %lld",
1195 surface_size, footprint_before, footprint_after,
1196 footprint_expected, footprint_delta);
1197 }
1198
1199 /* make IOSurface volatile: footprint shrinks */
1200 get_ledger_info(&footprint_before, &pagetable_before);
1201 IOSurfaceSetPurgeable(surface, kIOSurfacePurgeableVolatile, &old_state);
1202 get_ledger_info(&footprint_after, &pagetable_after);
1203 if (legacy_footprint) {
1204 footprint_expected = footprint_before;
1205 footprint_expected += (pagetable_after - pagetable_before);
1206 T_LOG("LEGACY FOOTPRINT: volatile IOSurface: no footprint impact");
1207 T_EXPECT_EQ(footprint_after, footprint_expected,
1208 "volatile IOSurface %lld bytes: "
1209 "footprint %lld -> %lld expected %lld delta %lld",
1210 surface_size, footprint_before, footprint_after,
1211 footprint_expected, footprint_after - footprint_expected);
1212 } else {
1213 footprint_expected = footprint_before - surface_size;
1214 footprint_expected += (pagetable_after - pagetable_before);
1215 T_LOG("making IOSurface volatile decreases phys_footprint");
1216 T_EXPECT_EQ(footprint_after, footprint_expected,
1217 "made volatile %lld bytes: "
1218 "footprint %lld -> %lld expected %lld delta %lld",
1219 surface_size, footprint_before, footprint_after,
1220 footprint_expected, footprint_after - footprint_expected);
1221 }
1222
1223 /* make IOSurface non-volatile: footprint grows */
1224 get_ledger_info(&footprint_before, &pagetable_before);
1225 IOSurfaceSetPurgeable(surface, kIOSurfacePurgeableNonVolatile, &old_state);
1226 get_ledger_info(&footprint_after, &pagetable_after);
1227 if (legacy_footprint) {
1228 footprint_expected = footprint_before;
1229 footprint_expected += (pagetable_after - pagetable_before);
1230 T_LOG("LEGACY FOOTPRINT: non-volatile IOSurface: no footprint impact");
1231 T_EXPECT_EQ(footprint_after, footprint_expected,
1232 "non-volatile IOSurface %lld bytes: "
1233 "footprint %lld -> %lld expected %lld delta %lld",
1234 surface_size, footprint_before, footprint_after,
1235 footprint_expected, footprint_after - footprint_expected);
1236 } else {
1237 footprint_expected = footprint_before + surface_size;
1238 footprint_expected += (pagetable_after - pagetable_before);
1239 T_LOG("making IOSurface non-volatile increases phys_footprint");
1240 T_EXPECT_EQ(footprint_after, footprint_expected,
1241 "made non-volatile %lld bytes: "
1242 "footprint %lld -> %lld expected %lld delta %lld",
1243 surface_size, footprint_before, footprint_after,
1244 footprint_expected, footprint_after - footprint_expected);
1245 }
1246
1247 /* accessing IOSurface re-mapping: no footprint impact */
1248
1249 /* deallocating IOSurface re-mapping: no footprint impact */
1250
1251 /* release IOSurface: footprint shrinks */
1252 get_ledger_info(&footprint_before, &pagetable_before);
1253 CFRelease(surface);
1254 get_ledger_info(&footprint_after, &pagetable_after);
1255 if (legacy_footprint) {
1256 footprint_expected = footprint_before;
1257 footprint_expected += (pagetable_after - pagetable_before);
1258 T_LOG("LEGACY FOOTPRINT: release IOSurface: no footprint impact");
1259 T_EXPECT_EQ(footprint_after, footprint_expected,
1260 "releasing IOSurface %lld bytes: "
1261 "footprint %lld -> %lld expected %lld delta %lld",
1262 surface_size, footprint_before, footprint_after,
1263 footprint_expected, footprint_after - footprint_expected);
1264 } else {
1265 footprint_expected = footprint_before - surface_size;
1266 footprint_expected += (pagetable_after - pagetable_before);
1267 T_LOG("releasing IOSurface decreases phys_footprint");
1268 T_EXPECT_EQ(footprint_after, footprint_expected,
1269 "released IOSurface %lld bytes: "
1270 "footprint %lld -> %lld expected %lld delta %lld",
1271 surface_size, footprint_before, footprint_after,
1272 footprint_expected, footprint_after - footprint_expected);
1273 }
1274 }
1275
1276 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1277 T_DECL(phys_footprint_nonpurgeable_iokit,
1278 "phys_footprint for non-purgeable IOKit memory",
1279 T_META_NAMESPACE(TEST_VM_NAMESPACE),
1280 T_META_LTEPHASE(LTE_POSTINIT))
1281 {
1282 uint64_t footprint_before, pagetable_before;
1283 uint64_t footprint_after, pagetable_after;
1284 uint64_t footprint_expected, footprint_delta_slop;
1285 int64_t footprint_delta;
1286 IOSurfaceRef surface;
1287 uint64_t surface_size;
1288 void *map_base;
1289 size_t map_size;
1290 mach_vm_address_t remap_addr;
1291 kern_return_t kr;
1292 vm_prot_t cur_prot, max_prot;
1293 uint32_t old_state;
1294
1295
1296 T_SETUPBEGIN;
1297 ledger_init();
1298 surface = CreateSurface(1024, 1024, 0, 32, false, true);
1299 CFRelease(surface);
1300 footprint_delta_slop = 8 * vm_kernel_page_size;
1301 T_SETUPEND;
1302
1303 surface_size = 1024 * 1024 * 4;
1304
1305 /* create IOsurface: footprint grows */
1306 get_ledger_info(&footprint_before, &pagetable_before);
1307 surface = CreateSurface(1024, 1024, 0, 32, false, true);
1308 get_ledger_info(&footprint_after, &pagetable_after);
1309 footprint_expected = footprint_before + surface_size;
1310 footprint_expected += (pagetable_after - pagetable_before);
1311 footprint_delta = (int64_t)(footprint_after - footprint_expected);
1312 T_LOG("creating non-purgeable IOSurface increases phys_footprint");
1313 T_EXPECT_LE((uint64_t)llabs(footprint_delta), footprint_delta_slop,
1314 "create non-purgeable IOSurface %lld bytes: "
1315 "footprint %lld -> %lld expected %lld delta %lld",
1316 surface_size, footprint_before, footprint_after,
1317 footprint_expected, footprint_delta);
1318
1319 /* make IOSurface volatile: fail and no footprint impact */
1320 get_ledger_info(&footprint_before, &pagetable_before);
1321 IOSurfaceSetPurgeable(surface, kIOSurfacePurgeableVolatile, &old_state);
1322 get_ledger_info(&footprint_after, &pagetable_after);
1323 footprint_expected = footprint_before;
1324 footprint_expected += (pagetable_after - pagetable_before);
1325 T_LOG("making non-purgeable IOSurface volatile: no footprint impact");
1326 T_EXPECT_EQ(footprint_after, footprint_expected,
1327 "made volatile %lld non-purgeable bytes: "
1328 "footprint %lld -> %lld expected %lld delta %lld",
1329 surface_size, footprint_before, footprint_after,
1330 footprint_expected, footprint_after - footprint_expected);
1331
1332 /* re-mapping IOSurface: no footprint impact */
1333 get_ledger_info(&footprint_before, &pagetable_before);
1334 map_base = IOSurfaceGetBaseAddress(surface);
1335 map_size = SurfaceGetMemorySize(surface);
1336 // T_EXPECT_EQ(map_size, surface_size, "map_size %lld surface_size %lld",
1337 // map_size, surface_size);
1338 remap_addr = 0;
1339 kr = mach_vm_remap(mach_task_self(),
1340 &remap_addr,
1341 (mach_vm_size_t)surface_size,
1342 0,
1343 VM_FLAGS_ANYWHERE,
1344 mach_task_self(),
1345 (mach_vm_address_t)map_base,
1346 FALSE, /* copy */
1347 &cur_prot,
1348 &max_prot,
1349 VM_INHERIT_DEFAULT);
1350 T_QUIET;
1351 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_remap() error 0x%x (%s)",
1352 kr, mach_error_string(kr));
1353 get_ledger_info(&footprint_after, &pagetable_after);
1354 footprint_expected = footprint_before;
1355 footprint_expected += (pagetable_after - pagetable_before);
1356 T_LOG("re-mapping IOSurface does not impact phys_footprint");
1357 T_EXPECT_EQ(footprint_after, footprint_expected,
1358 "remapping IOSurface %lld bytes: "
1359 "footprint %lld -> %lld expected %lld delta %lld",
1360 surface_size, footprint_before, footprint_after,
1361 footprint_expected, footprint_after - footprint_expected);
1362
1363 /* accessing IOSurface re-mapping: footprint grows */
1364 get_ledger_info(&footprint_before, &pagetable_before);
1365 memset((char *)(uintptr_t)remap_addr, 'p', (size_t)surface_size);
1366 get_ledger_info(&footprint_after, &pagetable_after);
1367 footprint_expected = footprint_before + surface_size;
1368 footprint_expected += (pagetable_after - pagetable_before);
1369 T_LOG("accessing re-mapped IOSurface grows phys_footprint");
1370 T_EXPECT_EQ(footprint_after, footprint_expected,
1371 "accessing remapped IOSurface %lld bytes: "
1372 "footprint %lld -> %lld expected %lld delta %lld",
1373 surface_size, footprint_before, footprint_after,
1374 footprint_expected, footprint_after - footprint_expected);
1375
1376 /* deallocating IOSurface re-mapping: footprint shrinks */
1377 get_ledger_info(&footprint_before, &pagetable_before);
1378 kr = mach_vm_deallocate(mach_task_self(),
1379 remap_addr,
1380 (mach_vm_size_t)surface_size);
1381 T_QUIET;
1382 T_EXPECT_EQ(kr, KERN_SUCCESS, "vm_deallocate() error 0x%x (%s)",
1383 kr, mach_error_string(kr));
1384 get_ledger_info(&footprint_after, &pagetable_after);
1385 footprint_expected = footprint_before - surface_size;
1386 footprint_expected += (pagetable_after - pagetable_before);
1387 T_LOG("deallocating re-mapping of IOSurface shrinks phys_footprint");
1388 T_EXPECT_EQ(footprint_after, footprint_expected,
1389 "deallocating remapped IOSurface %lld bytes: "
1390 "footprint %lld -> %lld expected %lld delta %lld",
1391 surface_size, footprint_before, footprint_after,
1392 footprint_expected, footprint_after - footprint_expected);
1393
1394 /* release IOSurface: footprint shrinks */
1395 get_ledger_info(&footprint_before, &pagetable_before);
1396 CFRelease(surface);
1397 get_ledger_info(&footprint_after, &pagetable_after);
1398 footprint_expected = footprint_before - surface_size;
1399 footprint_expected += (pagetable_after - pagetable_before);
1400 T_LOG("releasing IOSurface decreases phys_footprint");
1401 T_EXPECT_EQ(footprint_after, footprint_expected,
1402 "released IOSurface %lld bytes: "
1403 "footprint %lld -> %lld expected %lld delta %lld",
1404 surface_size, footprint_before, footprint_after,
1405 footprint_expected, footprint_after - footprint_expected);
1406 }
1407 #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */