This is xnu-11215.1.10. See this file in:
/**
 *  neural_footprint.c
 *  Neural composite footprint ledger test
 *
 * Test various memory settings to ensure correct accounting
 * Copyright (c) 2023 Apple Inc. All rights reserved.
 */

#include <sys/mman.h>
#include <mach/vm_map.h>
#include <mach/mach_vm.h>
#include <mach/mach_port.h>
#include <mach/mach_init.h>
#include <mach/mach_error.h>
#include <darwintest_utils.h>
#include <libproc_internal.h>
#include <mach/memory_entry.h>
#include <Kernel/kern/ledger.h>


extern int ledger(int cmd, caddr_t arg1, caddr_t arg2, caddr_t arg3);

#define ALLOCATION_SIZE (10 * vm_kernel_page_size)  /* 10 pages */

T_GLOBAL_META(
	T_META_RUN_CONCURRENTLY(true),
	T_META_RADAR_COMPONENT_NAME("xnu"),
	T_META_RADAR_COMPONENT_VERSION("VM"));

static int neural_nofootprint_index = -1;
static int neural_nofootprint_compressed_index = -1;
static int neural_total_index = -1;
static int neural_footprint_index = -1;
static int neural_footprint_compressed_index = -1;

static uint64_t neural_nofootprint_before;
static uint64_t neural_nofootprint_compressed_before;
static uint64_t neural_footprint_before;
static uint64_t neural_footprint_compressed_before;
static uint64_t neural_total_before;
static uint64_t neural_nofootprint_after;
static uint64_t neural_nofootprint_compressed_after;
static uint64_t neural_footprint_after;
static uint64_t neural_footprint_compressed_after;
static uint64_t neural_total_after;

static int64_t  ledger_count = -1;
static struct   ledger_entry_info *lei = NULL;

static uint64_t phys_max_before, phys_max_after;
static struct   rusage_info_v6   ru;
static uint64_t neural_lifetime_max;



static void
ledger_init(void)
{
	kern_return_t                   kr;
	static int                      ledger_inited = 0;
	struct ledger_template_info     *templateInfo;
	int64_t                         templateCnt;
	struct ledger_info              li;
	if (ledger_inited) {
		return;
	}
	ledger_inited = 1;

	T_SETUPBEGIN;

	kr = ledger(LEDGER_INFO,
	    (caddr_t)(uintptr_t)getpid(),
	    (caddr_t)&li,
	    NULL);

	T_ASSERT_MACH_SUCCESS(kr, "ledger() 0x%x (%s)",
	    kr, mach_error_string(kr));

	templateCnt = li.li_entries;
	templateInfo = malloc((size_t)li.li_entries *
	    sizeof(struct ledger_template_info));
	T_QUIET;
	T_WITH_ERRNO;
	T_ASSERT_NOTNULL(templateInfo, "malloc()");

	ledger_count = li.li_entries;

	T_QUIET;
	T_WITH_ERRNO;
	T_ASSERT_POSIX_SUCCESS(ledger(LEDGER_TEMPLATE_INFO,
	    (caddr_t)templateInfo,
	    (caddr_t)&templateCnt,
	    NULL),
	    "ledger(LEDGER_TEMPLATE_INFO)");
	for (int i = 0; i < templateCnt; i++) {
		if (!strncmp(templateInfo[i].lti_name,
		    "neural_nofootprint_compressed",
		    strlen("neural_nofootprint_compressed"))) {
			neural_nofootprint_compressed_index = i;
			T_LOG("Acquired index of neural_nofootprint_compressed");
		} else if (!strncmp(templateInfo[i].lti_name,
		    "neural_footprint_compressed",
		    strlen("neural_footprint_compressed"))) {
			neural_footprint_compressed_index = i;
			T_LOG("Acquired index of neural_footprint_compressed");
		} else if (!strncmp(templateInfo[i].lti_name,
		    "neural_nofootprint_total",
		    strlen("neural_nofootprint_total"))) {
			neural_total_index = i;
			T_LOG("Acquired index of neural_nofootprint_total");
		} else if (!strncmp(templateInfo[i].lti_name,
		    "neural_nofootprint",
		    strlen("neural_nofootprint"))) {
			neural_nofootprint_index = i;
			T_LOG("Acquired index of neural_nofootprint");
		} else if (!strncmp(templateInfo[i].lti_name,
		    "neural_footprint",
		    strlen("neural_footprint"))) {
			neural_footprint_index = i;
			T_LOG("Acquired index of neural_footprint");
		}
	}
	free(templateInfo);

	lei = (struct ledger_entry_info *)
	    malloc((size_t)ledger_count * sizeof(*lei));
	T_QUIET;
	T_WITH_ERRNO;
	T_ASSERT_NE(lei, NULL,
	    "malloc(ledger_entry_info)");

	T_QUIET;
	T_ASSERT_NE(neural_nofootprint_compressed_index, -1,
	    "no nofootprint_compressed_index");
	T_QUIET;
	T_ASSERT_NE(neural_footprint_compressed_index, -1,
	    "no footprint_compressed_index");
	T_QUIET;
	T_ASSERT_NE(neural_total_index, -1,
	    "no nofootprint_total_index");
	T_QUIET;
	T_ASSERT_NE(neural_nofootprint_index, -1,
	    "no nofootprint_index");
	T_QUIET;
	T_ASSERT_NE(neural_footprint_index, -1,
	    "no footprint_index");

	T_SETUPEND;
}

static void
get_ledger_info(
	uint64_t        *neural_nofootprint,
	uint64_t        *neural_nofootprint_compressed,
	uint64_t        *neural_footprint,
	uint64_t        *neural_footprint_compressed,
	uint64_t        *neural_total)
{
	int64_t count;

	count = ledger_count;
	T_QUIET;
	T_WITH_ERRNO;
	T_ASSERT_POSIX_SUCCESS(ledger(LEDGER_ENTRY_INFO,
	    (caddr_t)(uintptr_t)getpid(),
	    (caddr_t)lei,
	    (caddr_t)&count),
	    "ledger(LEDGER_ENTRY_INFO)");
	T_QUIET;
	T_ASSERT_GT(count, (int64_t)neural_nofootprint_index,
	    "no entry for neural_nofootprint");
	T_QUIET;
	T_ASSERT_GT(count, (int64_t)neural_nofootprint_compressed_index,
	    "no entry for neural_nofootprint_compressed");
	T_QUIET;
	T_ASSERT_GT(count, (int64_t)neural_total_index,
	    "no entry for neural_total");
	T_QUIET;
	T_ASSERT_GT(count, (int64_t)neural_footprint_compressed_index,
	    "no entry for neural_footprint_compressed");
	T_QUIET;
	T_ASSERT_GT(count, (int64_t)neural_footprint_index,
	    "no entry for neural_footprint");
	if (neural_footprint_index) {
		*neural_footprint = (uint64_t)(lei[neural_footprint_index].lei_balance);
	}
	if (neural_footprint_compressed_index) {
		*neural_footprint_compressed = (uint64_t)(
			lei[neural_footprint_compressed_index].lei_balance);
	}
	if (neural_nofootprint_index) {
		*neural_nofootprint = (uint64_t)(lei[neural_nofootprint_index].lei_balance);
	}
	if (neural_nofootprint_compressed_index) {
		*neural_nofootprint_compressed = (uint64_t)(
			lei[neural_nofootprint_compressed_index].lei_balance);
	}
	if (neural_total_index) {
		*neural_total = (uint64_t)(lei[neural_total_index].lei_balance);
	}
}

static void
get_ledger_before(void)
{
	get_ledger_info(
		&neural_nofootprint_before,
		&neural_nofootprint_compressed_before,
		&neural_footprint_before,
		&neural_footprint_compressed_before,
		&neural_total_before);
	T_LOG(
		"*** pages before: footprint:%llu compr:%llu nofootprint:%llu nofootprint compr: %llu, total: %llu",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_compressed_before / vm_kernel_page_size,
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_compressed_before / vm_kernel_page_size,
		neural_total_before / vm_kernel_page_size);
}

static void
get_ledger_after(void)
{
	get_ledger_info(
		&neural_nofootprint_after,
		&neural_nofootprint_compressed_after,
		&neural_footprint_after,
		&neural_footprint_compressed_after,
		&neural_total_after);

	T_LOG(
		"*** pages after: footprint:%llu compr:%llu nofootprint:%llu nofootprint compr: %llu, total: %llu",
		neural_footprint_after / vm_kernel_page_size,
		neural_footprint_compressed_after / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size,
		neural_nofootprint_compressed_after / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);
}

static void
compress_pages(mach_vm_address_t vm_addr, mach_vm_size_t vm_size)
{
	int ret;
	T_LOG(">>>> Compress all pages");
	unsigned char *cp;
	cp = (unsigned char *)(uintptr_t)vm_addr;
	T_ASSERT_POSIX_SUCCESS(
		madvise(cp,
		(size_t)vm_size,
		MADV_PAGEOUT),
		"page out all with madvise");

	T_LOG("...> Wait for pages to be compressed");
	unsigned char vec;

	for (size_t idx = 0; idx < vm_size; idx += vm_kernel_page_size) {
		do {
			ret = mincore(&cp[idx], 1, (char *)&vec);
			if (ret != 0) {
				T_ASSERT_POSIX_SUCCESS(ret, "failed on mincore check");
			}
		} while (vec & MINCORE_INCORE);
	}
}

static void
uncompress_pages(mach_vm_address_t vm_addr, mach_vm_size_t vm_size)
{
	T_LOG("<<<< Reading pages to bring back from compressor");
	unsigned char *cp;
	cp = (unsigned char *)(uintptr_t)vm_addr;
	memset(cp, 0xff, vm_size);
}

static void
check_phys_footprint_rusage(void)
{
	int ret;
	T_LOG("---? Check phys footprint lifetime max and max interval");

	ret = proc_pid_rusage(getpid(), RUSAGE_INFO_V6, (rusage_info_t *)&ru);
	T_QUIET;
	T_ASSERT_POSIX_SUCCESS(ret, "proc_pid_rusage");
	phys_max_before = ru.ri_lifetime_max_phys_footprint;
	T_LOG("Phys max: %llu", ru.ri_lifetime_max_phys_footprint);
}

static void
check_phys_footprint_rusage_after(void)
{
	int ret;

	T_LOG("---? Check phys footprint lifetime max and max interval");

	ret = proc_pid_rusage(getpid(), RUSAGE_INFO_V6, (rusage_info_t *)&ru);
	T_QUIET;
	T_ASSERT_POSIX_SUCCESS(ret, "proc_pid_rusage");
	phys_max_after = ru.ri_lifetime_max_phys_footprint;;
	T_LOG("Phys_footprint max: %llu  before: %llu  diff: %llu",
	    phys_max_after,
	    phys_max_before,
	    phys_max_after - phys_max_before);

	T_ASSERT_EQ(0ULL, phys_max_after - phys_max_before,
	    "Phys footprint lifetime max shouldn't change");
}

static void
make_volatile(mach_vm_address_t vm_addr)
{
	kern_return_t kr;
	vm_purgable_t state;
	char *vm_purgable_state[4] = {
		"nonvolatile",
		"volatile",
		"empty",
		"deny"
	};
	state = VM_PURGABLE_VOLATILE;
	kr = mach_vm_purgable_control(
		mach_task_self(),
		vm_addr,
		VM_PURGABLE_SET_STATE,
		&state);
	T_ASSERT_MACH_SUCCESS(kr, "vm_purgable_control(volatile)");
	T_ASSERT_EQ(state, VM_PURGABLE_NONVOLATILE,
	    "nonvolatile -> volatile: state was %s",
	    vm_purgable_state[state]);
}

static void
make_nonvolatile(mach_vm_address_t vm_addr)
{
	kern_return_t kr;
	vm_purgable_t state;
	char *vm_purgable_state[4] = {
		"nonvolatile",
		"volatile",
		"empty",
		"deny"
	};
	state = VM_PURGABLE_NONVOLATILE;
	kr = mach_vm_purgable_control(
		mach_task_self(),
		vm_addr,
		VM_PURGABLE_SET_STATE,
		&state);
	T_ASSERT_MACH_SUCCESS(kr, "vm_purgable_control(nonvolatile)");
	T_ASSERT_EQ(state, VM_PURGABLE_VOLATILE,
	    "volatile -> nonvolatile: state was %s",
	    vm_purgable_state[state]);
}

static void
reset_max_interval(uint64_t check)
{
	int ret;
	T_LOG("---> Reset interval max");
	ret = proc_reset_footprint_interval(getpid());
	T_ASSERT_POSIX_SUCCESS(ret, "proc_reset_footprint_interval()");

	ret = proc_pid_rusage(getpid(), RUSAGE_INFO_V6, (rusage_info_t *)&ru);
	T_QUIET;
	T_ASSERT_POSIX_SUCCESS(ret, "proc_pid_rusage");
	T_EXPECT_EQ(check, ru.ri_interval_max_neural_footprint,
	    "Neural max interval footprint is %llu pages", check / vm_kernel_page_size);
}

T_DECL(check_neural_total_ledger,
    "Check neural totall ledger",
    T_META_LTEPHASE(LTE_POSTINIT))
{
	int                     ret;
	kern_return_t           kr;
	mach_vm_size_t          vm_size, me_size;
	mach_port_t             footprint_port, nofootprint_port;
	mach_port_t             footprint_port2, nofootprint_port2;
	mach_port_t             file_mem_entry;
	vm_prot_t               permissions;

	mach_vm_address_t       footprint_vm_addr = 0, nofootprint_vm_addr = 0;
	mach_vm_address_t       footprint_vm_addr2 = 0, nofootprint_vm_addr2 = 0;
	mach_vm_size_t          dirty_size = ALLOCATION_SIZE;

	ledger_init();

	get_ledger_before();

	vm_size = ALLOCATION_SIZE;
	me_size = vm_size;
	footprint_port = MACH_PORT_NULL;
	nofootprint_port = MACH_PORT_NULL;

	permissions = MAP_MEM_NAMED_CREATE |
	    MAP_MEM_PURGABLE |
	    MAP_MEM_LEDGER_TAGGED |
	    VM_PROT_DEFAULT;

	T_LOG("---> Allocate for footprint");
	kr = mach_make_memory_entry_64(
		mach_task_self(),
		&me_size,
		0,
		permissions,
		&footprint_port,
		MACH_PORT_NULL);
	T_ASSERT_MACH_SUCCESS(kr, "make_memory_entry()");
	T_ASSERT_EQ(me_size, vm_size, "checking memory entry size mismatch");

	T_LOG("---> Allocate for nofootprint");
	kr = mach_make_memory_entry_64(
		mach_task_self(),
		&me_size,
		0,
		permissions,
		&nofootprint_port,
		MACH_PORT_NULL);
	T_ASSERT_MACH_SUCCESS(kr, "make_memory_entry()");
	T_ASSERT_EQ(me_size, vm_size, "checking memory entry size mismatch");

	T_LOG("---> Allocate for secondary footprint");
	kr = mach_make_memory_entry_64(
		mach_task_self(),
		&me_size,
		0,
		permissions,
		&footprint_port2,
		MACH_PORT_NULL);
	T_ASSERT_MACH_SUCCESS(kr, "make_memory_entry()");
	T_ASSERT_EQ(me_size, vm_size, "checking memory entry size mismatch");

	T_LOG("---> Allocate for secondary nofootprint");
	kr = mach_make_memory_entry_64(
		mach_task_self(),
		&me_size,
		0,
		permissions,
		&nofootprint_port2,
		MACH_PORT_NULL);
	T_ASSERT_MACH_SUCCESS(kr, "make_memory_entry()");
	T_ASSERT_EQ(me_size, vm_size, "checking memory entry size mismatch");

	T_LOG("---> Allocate for file footprint");
	char const *tmp_dir = dt_tmpdir();
	char filepath[MAXPATHLEN];
	int fd = -1;
	char *file_addr = NULL;
	snprintf(filepath, sizeof(filepath), "%s/file.XXXXXX", tmp_dir);
	T_ASSERT_POSIX_SUCCESS(fd = mkstemp(filepath), NULL);
	T_ASSERT_POSIX_SUCCESS(unlink(filepath), NULL);
	T_ASSERT_POSIX_SUCCESS(ftruncate(fd, (off_t)vm_size), NULL);
	T_ASSERT_POSIX_SUCCESS(file_addr = mmap(NULL, (size_t)vm_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0), NULL);
	me_size = vm_size;
	T_ASSERT_MACH_SUCCESS(mach_make_memory_entry_64(
		    mach_task_self(),
		    &me_size,
		    (memory_object_offset_t)(uintptr_t)file_addr,
		    /* MAP_MEM_LEDGER_TAGGED | */ VM_PROT_READ,
		    &file_mem_entry,
		    MACH_PORT_NULL), NULL);
	T_ASSERT_EQ(me_size, vm_size, "checking memory entry size mismatch");
	T_ASSERT_POSIX_SUCCESS(mlock(file_addr, (size_t)vm_size), NULL);


	get_ledger_after();
	T_LOG("Change is not expected on neural ledgers");
	T_ASSERT_EQ(neural_nofootprint_before, neural_nofootprint_after,
	    "neural entry size changed %llu -> %llu",
	    neural_nofootprint_before, neural_nofootprint_after);
	T_ASSERT_EQ(neural_nofootprint_compressed_before, neural_nofootprint_compressed_after,
	    "neural entry size changed %llu -> %llu",
	    neural_nofootprint_compressed_before, neural_nofootprint_compressed_after);
	T_ASSERT_EQ(neural_footprint_before, neural_footprint_after,
	    "neural entry size changed %llu -> %llu",
	    neural_footprint_before, neural_footprint_after);
	T_ASSERT_EQ(neural_footprint_compressed_before, neural_footprint_compressed_after,
	    "neural entry size changed %llu -> %llu",
	    neural_footprint_compressed_before, neural_footprint_compressed_after);
	T_ASSERT_EQ(neural_total_before, neural_total_after,
	    "neural entry size changed %llu -> %llu",
	    neural_total_before, neural_total_after);


	T_ASSERT_MACH_SUCCESS(
		mach_vm_map(
			mach_task_self(),
			&footprint_vm_addr,
			dirty_size,
			0, /* mask */
			VM_FLAGS_ANYWHERE,
			footprint_port,
			0, /* offset */
			false, /* copy */
			VM_PROT_DEFAULT,
			VM_PROT_DEFAULT,
			VM_INHERIT_NONE),
		"mach_vm_map() for primary neural footprint"
		);

	T_ASSERT_MACH_SUCCESS(
		mach_vm_map(
			mach_task_self(),
			&nofootprint_vm_addr,
			dirty_size,
			0, /* mask */
			VM_FLAGS_ANYWHERE,
			nofootprint_port,
			0, /* offset */
			false, /* copy */
			VM_PROT_DEFAULT,
			VM_PROT_DEFAULT,
			VM_INHERIT_NONE),
		"mach_vm_map() for primary neural nofootprint"
		);

	T_ASSERT_MACH_SUCCESS(
		mach_vm_map(
			mach_task_self(),
			&footprint_vm_addr2,
			dirty_size,
			0, /* mask */
			VM_FLAGS_ANYWHERE,
			footprint_port2,
			0, /* offset */
			false, /* copy */
			VM_PROT_DEFAULT,
			VM_PROT_DEFAULT,
			VM_INHERIT_NONE),
		"mach_vm_map() for secondary neural footprint"
		);

	T_ASSERT_MACH_SUCCESS(
		mach_vm_map(
			mach_task_self(),
			&nofootprint_vm_addr2,
			dirty_size,
			0, /* mask */
			VM_FLAGS_ANYWHERE,
			nofootprint_port2,
			0, /* offset */
			false, /* copy */
			VM_PROT_DEFAULT,
			VM_PROT_DEFAULT,
			VM_INHERIT_NONE),
		"mach_vm_map() for secondary neural nofootprint"
		);

	T_LOG("Dirtying pages");
	memset((char *)(uintptr_t)footprint_vm_addr, 0xaa, (size_t)dirty_size);
	memset((char *)(uintptr_t)footprint_vm_addr2, 0xbb, (size_t)dirty_size);
	memset((char *)(uintptr_t)nofootprint_vm_addr, 0xcc, (size_t)dirty_size);
	memset((char *)(uintptr_t)nofootprint_vm_addr2, 0xdd, (size_t)dirty_size);

	T_LOG("Checking if compression works correctly with phys_footprint");
	check_phys_footprint_rusage();

	compress_pages(footprint_vm_addr, vm_size);

	check_phys_footprint_rusage_after();

	uncompress_pages(footprint_vm_addr, vm_size);

	get_ledger_before();

	T_LOG("---> Move primary footprint to neural");
	kr = mach_memory_entry_ownership(
		footprint_port, /* entry port */
		TASK_NULL,  /* owner remains unchanged */
		VM_LEDGER_TAG_NEURAL, /* ledger-tag */
		0); /* ledger flags */
	T_ASSERT_MACH_SUCCESS(kr,
	    "mach_memory_entry_ownership() primary neural footprint 0x%x (%s)",
	    kr, mach_error_string(kr));

	get_ledger_after();

	T_ASSERT_EQ(
		neural_footprint_before + vm_size,
		neural_footprint_after,
		"neural footprint increased  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		0ULL,
		neural_total_after,
		"neural total is zero  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);

	T_LOG("---> Move secondary footprint to neural");
	get_ledger_before();
	kr = mach_memory_entry_ownership(
		footprint_port2, /* entry port */
		TASK_NULL,  /* owner remains unchanged */
		VM_LEDGER_TAG_NEURAL, /* ledger-tag */
		0); /* ledger flags */
	T_ASSERT_MACH_SUCCESS(kr,
	    "mach_memory_entry_ownership() primary neural footprint 0x%x (%s)",
	    kr, mach_error_string(kr));
	get_ledger_after();

	T_ASSERT_EQ(
		neural_footprint_before + vm_size,
		neural_footprint_after,
		"neural footprint increased  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		0ULL,
		neural_total_after,
		"neural total is zero  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);

	T_LOG("---> Move primary nofootprint to neural");
	get_ledger_before();
	kr = mach_memory_entry_ownership(
		nofootprint_port, /* entry port */
		TASK_NULL,  /* owner remains unchanged */
		VM_LEDGER_TAG_NEURAL, /* ledger-tag */
		VM_LEDGER_FLAG_NO_FOOTPRINT); /* ledger flags */
	T_ASSERT_MACH_SUCCESS(kr,
	    "mach_memory_entry_ownership() primary neural footprint 0x%x (%s)",
	    kr, mach_error_string(kr));

	get_ledger_after();
	T_ASSERT_EQ(
		neural_nofootprint_before + vm_size,
		neural_nofootprint_after,
		"neural nofootprint increased  %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before + vm_size,
		neural_total_after,
		"neural total increased  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);

	T_LOG("---> Move secondary nofootprint to neural");
	get_ledger_before();
	kr = mach_memory_entry_ownership(
		nofootprint_port2, /* entry port */
		TASK_NULL,  /* owner remains unchanged */
		VM_LEDGER_TAG_NEURAL, /* ledger-tag */
		VM_LEDGER_FLAG_NO_FOOTPRINT); /* ledger flags */
	T_ASSERT_MACH_SUCCESS(kr,
	    "mach_memory_entry_ownership() primary neural footprint 0x%x (%s)",
	    kr, mach_error_string(kr));

	get_ledger_after();
	T_ASSERT_EQ(
		neural_nofootprint_before + vm_size,
		neural_nofootprint_after,
		"neural nofootprint increased  %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before + vm_size,
		neural_total_after,
		"neural total increased  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);

	neural_lifetime_max = neural_total_after;

	T_LOG("---? Check neural total lifetime max and max interval");
	ret = proc_pid_rusage(getpid(), RUSAGE_INFO_V6, (rusage_info_t *)&ru);
	T_QUIET;
	T_ASSERT_POSIX_SUCCESS(ret, "proc_pid_rusage");
	T_EXPECT_EQ(ru.ri_lifetime_max_neural_footprint,
	    neural_total_after,
	    "Neural max footprint is equal to 4 x vm_size (in pages): %llu = %llu",
	    ru.ri_lifetime_max_neural_footprint / vm_kernel_page_size,
	    4 * vm_size / vm_kernel_page_size);
	T_EXPECT_EQ(ru.ri_interval_max_neural_footprint,
	    neural_total_after,
	    "Neural max interval footprint is equal to current neural total: %llu = %llu",
	    ru.ri_interval_max_neural_footprint / vm_kernel_page_size,
	    neural_total_after / vm_kernel_page_size);

	T_LOG("---> compress primary footprint");
	get_ledger_before();
	compress_pages(footprint_vm_addr, vm_size);
	get_ledger_after();
	T_ASSERT_EQ(
		neural_footprint_before - vm_size,
		neural_footprint_after,
		"neural footprint decreased  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_footprint_compressed_before + vm_size,
		neural_footprint_compressed_after,
		"neural footprint compressed increased  %llu -> %llu pages",
		neural_footprint_compressed_before / vm_kernel_page_size,
		neural_footprint_compressed_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before,
		neural_total_after,
		"neural total did not change  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);


	T_LOG("---> compress primary nofootprint");
	get_ledger_before();
	compress_pages(nofootprint_vm_addr, vm_size);
	get_ledger_after();
	T_ASSERT_EQ(
		neural_nofootprint_before - vm_size,
		neural_nofootprint_after,
		"neural nofootprint decreased  %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_nofootprint_compressed_before + vm_size,
		neural_nofootprint_compressed_after,
		"neural nofootprint compressed increased  %llu -> %llu pages",
		neural_nofootprint_compressed_before / vm_kernel_page_size,
		neural_nofootprint_compressed_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before,
		neural_total_after,
		"neural total did not change  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);

	T_LOG("---> Decompress primary footprint");
	get_ledger_before();
	uncompress_pages(footprint_vm_addr, vm_size);
	get_ledger_after();
	T_ASSERT_EQ(
		neural_footprint_before + vm_size,
		neural_footprint_after,
		"neural footprint increased  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_footprint_compressed_before - vm_size,
		neural_footprint_compressed_after,
		"neural footprint compressed decreased  %llu -> %llu pages",
		neural_footprint_compressed_before / vm_kernel_page_size,
		neural_footprint_compressed_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before,
		neural_total_after,
		"neural total did not change  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);

	T_LOG("---> Make primary footprint volatile");
	get_ledger_before();
	make_volatile(footprint_vm_addr);
	get_ledger_after();
	T_ASSERT_EQ(
		neural_footprint_before - vm_size,
		neural_footprint_after,
		"neural footprint decreased  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_nofootprint_before + vm_size,
		neural_nofootprint_after,
		"neural nofootprint increased  %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before,
		neural_total_after,
		"neural total did not change  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);

	T_LOG("---> Make primary nofootprint (compressed) volatile");
	get_ledger_before();
	make_volatile(nofootprint_vm_addr);
	get_ledger_after();
	T_ASSERT_EQ(
		neural_nofootprint_before,
		neural_nofootprint_after,
		"neural nofootprint did not change %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_nofootprint_compressed_before,
		neural_nofootprint_compressed_after,
		"neural nofootprint_compressed did not change (volatile now) %llu -> %llu pages",
		neural_nofootprint_compressed_before / vm_kernel_page_size,
		neural_nofootprint_compressed_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_footprint_before,
		neural_footprint_after,
		"neural footprint did not change  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before - vm_size,
		neural_total_after,
		"neural total decreased  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);

	reset_max_interval(neural_total_after);

	T_LOG("---> Make primary footprint non-volatile");
	get_ledger_before();
	make_nonvolatile(footprint_vm_addr);
	get_ledger_after();
	T_ASSERT_EQ(
		neural_footprint_before + vm_size,
		neural_footprint_after,
		"neural footprint increased  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_nofootprint_before - vm_size,
		neural_nofootprint_after,
		"neural nofootprint decreased  %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before,
		neural_total_after,
		"neural total did not change  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);

	T_LOG("---> Make primary nofootprint (compressed) non-volatile");
	get_ledger_before();
	make_nonvolatile(nofootprint_vm_addr);
	get_ledger_after();
	T_ASSERT_EQ(
		neural_footprint_before,
		neural_footprint_after,
		"neural footprint did not change  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_nofootprint_before,
		neural_nofootprint_after,
		"neural nofootprint did not change  %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_nofootprint_compressed_before,
		neural_nofootprint_compressed_after,
		"neural nofootprint_compressed did not change (now non-volatile)  %llu -> %llu pages",
		neural_nofootprint_compressed_before / vm_kernel_page_size,
		neural_nofootprint_compressed_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before + vm_size,
		neural_total_after,
		"neural total increased  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);

	T_LOG("---? Check neural total max interval");
	ret = proc_pid_rusage(getpid(), RUSAGE_INFO_V6, (rusage_info_t *)&ru);
	T_QUIET;
	T_ASSERT_POSIX_SUCCESS(ret, "proc_pid_rusage");
	T_ASSERT_EQ(ru.ri_interval_max_neural_footprint,
	    neural_total_after,
	    "Neural max interval footprint is equal to total: %llu = %llu",
	    ru.ri_interval_max_neural_footprint / vm_kernel_page_size,
	    neural_total_after / vm_kernel_page_size);


	T_LOG("---> Take no-footprint ownership of the file");
	get_ledger_before();
	kr = mach_memory_entry_ownership(
		file_mem_entry, /* entry port */
		mach_task_self(),  /* claim ownership */
		VM_LEDGER_TAG_NEURAL, /* ledger-tag */
		VM_LEDGER_FLAG_NO_FOOTPRINT); /* ledger flags */
	T_ASSERT_MACH_SUCCESS(kr,
	    "mach_memory_entry_ownership() file neural no-footprint 0x%x (%s)",
	    kr, mach_error_string(kr));
	get_ledger_after();
	T_ASSERT_EQ(
		neural_nofootprint_before + vm_size,
		neural_nofootprint_after,
		"neural nofootprint increased  %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before + vm_size,
		neural_total_after,
		"neural total increase  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);
//	fprintf(stdout, "pausing...\n"); fflush(stdout); getchar();


	T_LOG("---> Take footprint ownership of the file");
	get_ledger_before();
	kr = mach_memory_entry_ownership(
		file_mem_entry, /* entry port */
		MACH_PORT_NULL,  /* owner remains unchanged */
		VM_LEDGER_TAG_NEURAL, /* ledger-tag */
		0); /* ledger flags */
	T_ASSERT_MACH_SUCCESS(kr,
	    "mach_memory_entry_ownership() file neural footprint 0x%x (%s)",
	    kr, mach_error_string(kr));
	get_ledger_after();
	T_ASSERT_EQ(
		neural_nofootprint_before - vm_size,
		neural_nofootprint_after,
		"neural nofootprint decreased  %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_footprint_before + vm_size,
		neural_footprint_after,
		"neural footprint increased  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before - vm_size,
		neural_total_after,
		"neural total decreased  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);
//	fprintf(stdout, "pausing...\n"); fflush(stdout); getchar();

	T_LOG("---> Unlock the file");
	get_ledger_before();
	T_ASSERT_POSIX_SUCCESS(munlock(file_addr, (size_t)vm_size), NULL);
	get_ledger_after();
	T_ASSERT_EQ(
		neural_nofootprint_before,
		neural_nofootprint_after,
		"neural nofootprint unchanged  %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_footprint_before - vm_size,
		neural_footprint_after,
		"neural footprint decreased  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before,
		neural_total_after,
		"neural total unchanged  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);
//	fprintf(stdout, "pausing...\n"); fflush(stdout); getchar();

	T_LOG("---> Relock the file");
	get_ledger_before();
	T_ASSERT_POSIX_SUCCESS(mlock(file_addr, (size_t)vm_size), NULL);
	get_ledger_after();
	T_ASSERT_EQ(
		neural_nofootprint_before,
		neural_nofootprint_after,
		"neural nofootprint unchanged  %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_footprint_before + vm_size,
		neural_footprint_after,
		"neural footprint increased  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		neural_total_before,
		neural_total_after,
		"neural total unchanged  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);
//	fprintf(stdout, "pausing...\n"); fflush(stdout); getchar();
	T_ASSERT_POSIX_SUCCESS(munlock(file_addr, (size_t)vm_size), NULL);
//	fprintf(stdout, "pausing...\n"); fflush(stdout); getchar();

	T_LOG("<--- Deallocate");
	get_ledger_before();
	/* deallocating memory while holding memory entry... */
	kr = mach_vm_deallocate(mach_task_self(), footprint_vm_addr, vm_size);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate() ");
	kr = mach_vm_deallocate(mach_task_self(), nofootprint_vm_addr, vm_size);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate() ");
	kr = mach_vm_deallocate(mach_task_self(), footprint_vm_addr2, vm_size);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate() ");
	kr = mach_vm_deallocate(mach_task_self(), nofootprint_vm_addr2, vm_size);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate() ");
	/* releasing the memory entry... */
	kr = mach_port_deallocate(mach_task_self(), footprint_port);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate() ");
	kr = mach_port_deallocate(mach_task_self(), footprint_port2);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate() ");
	kr = mach_port_deallocate(mach_task_self(), nofootprint_port);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate() ");
	kr = mach_port_deallocate(mach_task_self(), nofootprint_port2);
	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "vm_deallocate() ");
	get_ledger_after();

	T_ASSERT_EQ(
		0ULL,
		neural_footprint_after,
		"neural footprint zero  %llu -> %llu pages",
		neural_footprint_before / vm_kernel_page_size,
		neural_footprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		0ULL,
		neural_footprint_compressed_after,
		"neural footprint compressed zero  %llu -> %llu pages",
		neural_footprint_compressed_before / vm_kernel_page_size,
		neural_footprint_compressed_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		0ULL,
		neural_nofootprint_after,
		"neural nofootprint zero  %llu -> %llu pages",
		neural_nofootprint_before / vm_kernel_page_size,
		neural_nofootprint_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		0ULL,
		neural_nofootprint_compressed_after,
		"neural nofootprint compressed to zero  %llu -> %llu pages",
		neural_nofootprint_compressed_before / vm_kernel_page_size,
		neural_nofootprint_compressed_after / vm_kernel_page_size);
	T_ASSERT_EQ(
		0ULL,
		neural_total_after,
		"neural total zero  %llu -> %llu pages",
		neural_total_before / vm_kernel_page_size,
		neural_total_after / vm_kernel_page_size);
}