/* * Copyright (c) 2024 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ * */ #include #include #include #include #include #include #include #include #include #include #include #include T_GLOBAL_META( T_META_NAMESPACE("xnu.memorystatus"), T_META_RADAR_COMPONENT_NAME("xnu"), T_META_RADAR_COMPONENT_VERSION("VM"), T_META_ASROOT(true), T_META_TAG_VM_PREFERRED ); #define SIZE_ESTIMATE_EXTRA_ENTRIES 5 #define MAX_TRIES 3 #define _STR(x) #x #define STR(x) _STR(x) memorystatus_priority_entry_v2_t* get_priority_list(pid_t pid, int call, size_t entry_size, ssize_t* len) { int i; ssize_t size; memorystatus_priority_entry_v2_t *list = NULL; for (i = 0; i < MAX_TRIES; i++) { T_LOG("Attempt %d", i + 1); size = memorystatus_control(call, pid, 0, NULL, 0); T_ASSERT_GT(size, 0l, "Priority list query size > 0"); T_ASSERT_EQ(size % entry_size, 0ul, "Priority list size is multiple of struct size"); if (size <= 0) { return NULL; } /* pad out the size just in case the list gets bigger */ if (pid == 0) { size += SIZE_ESTIMATE_EXTRA_ENTRIES * entry_size; } list = malloc(size); T_QUIET; T_ASSERT_NOTNULL(list, "malloc"); *len = memorystatus_control(call, pid, 0, list, size); if (*len <= 0) { T_LOG("Failed, maybe the list grew? Trying again..."); free(list); continue; } T_ASSERT_GE(size, *len, "List fits in buffer"); T_ASSERT_GT(size, 0l, "List size nonzero"); return list; } T_FAIL("Tried more than MAX_TRIES=" STR(MAX_TRIES) " times"); return NULL; } void validate_entry(memorystatus_priority_entry_v2_t *entry, size_t entry_size, pid_t expect_pid) { int i; if (expect_pid == -1) { T_QUIET; T_ASSERT_GE(entry->pid, 0, "PID valid"); T_QUIET; T_ASSERT_NE(entry->pid, 0, "kernel_task not in list"); T_QUIET; T_ASSERT_NE(entry->pid, 1, "launchd not in list"); } else { T_QUIET; T_ASSERT_EQ(entry->pid, expect_pid, "PID correct"); } if (entry->pid > 1) { T_QUIET; T_ASSERT_GE( entry->priority, 0, "Entry priority >= 0"); T_QUIET; T_ASSERT_LE( entry->priority, JETSAM_PRIORITY_MAX, "Entry priority <= JETSAM_PRIORITY_MAX (" STR(JETSAM_PRIORITY_MAX) ")"); } else { T_QUIET; T_ASSERT_EQ( entry->priority, JETSAM_PRIORITY_INTERNAL, "Entry priority == JETSAM_PRIORITY_INTERNAL (" STR(JETSAM_PRIORITY_INTERNAL) ")"); } if (entry_size == sizeof(memorystatus_priority_entry_v2_t)) { boolean_t found_nonzero = false; for (i = 0; i < sizeof(entry->_reserved) / sizeof(entry->_reserved[0]); i++) { if (entry->_reserved[i]) { found_nonzero = true; break; } } T_QUIET; T_ASSERT_FALSE(found_nonzero, "Entry reserved is empty"); } } T_DECL(jetsam_priority_list_v2_list, "Jetsam priority list v2 - list") { int i; ssize_t len; memorystatus_priority_entry_v2_t *list = get_priority_list( 0, MEMORYSTATUS_CMD_GET_PRIORITY_LIST_V2, sizeof(*list), &len); T_ASSERT_NOTNULL(list, "Priority list not null"); for (i = 0; i < len / sizeof(memorystatus_priority_entry_v2_t); i++) { validate_entry(&list[i], sizeof(*list), -1); } free(list); T_PASS("Entries valid"); } void validate_single(pid_t pid) { ssize_t len; memorystatus_priority_entry_v2_t *list = get_priority_list( pid, MEMORYSTATUS_CMD_GET_PRIORITY_LIST_V2, sizeof(*list), &len); T_ASSERT_NOTNULL(list, "Getting self priority entry"); T_ASSERT_EQ(len, sizeof(memorystatus_priority_entry_v2_t), "Single entry returned"); validate_entry(list, sizeof(*list), pid); free(list); } T_DECL(jetsam_priority_list_v2_single, "Jetsam priority list v2 - single") { T_LOG("Getting entry for self..."); validate_single(getpid()); T_LOG("Getting entry for launchd..."); validate_single(1); T_PASS("Entries valid"); } T_DECL(jetsam_priority_list_compat, "Jetsam priority list - v1 compat") { int i; ssize_t len; memorystatus_priority_entry_t *list = (memorystatus_priority_entry_t*) get_priority_list( 0, MEMORYSTATUS_CMD_GET_PRIORITY_LIST, sizeof(*list), &len); T_ASSERT_NOTNULL(list, "Priority list not null"); for (i = 0; i < len / sizeof(memorystatus_priority_entry_v2_t); i++) { validate_entry((memorystatus_priority_entry_v2_t*) (&list[i]), sizeof(*list), -1); } free(list); T_PASS("Entries valid"); }