xref: /xnu-12377.61.12/tests/vm/memorystatus_priority_list.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
1 /*
2  * Copyright (c) 2024 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  *
28  */
29 
30 #include <sys/proc.h>
31 #include <sys/sysctl.h>
32 #include <sys/kern_memorystatus.h>
33 #include <mach-o/dyld.h>
34 #include <mach/mach_vm.h>
35 #include <mach/vm_page_size.h>
36 #include <mach/shared_region.h>
37 #include <mach/mach.h>
38 #include <os/reason_private.h>
39 #include <TargetConditionals.h>
40 #include <darwintest.h>
41 #include <darwintest_utils.h>
42 
43 T_GLOBAL_META(
44 	T_META_NAMESPACE("xnu.memorystatus"),
45 	T_META_RADAR_COMPONENT_NAME("xnu"),
46 	T_META_RADAR_COMPONENT_VERSION("VM"),
47 	T_META_ASROOT(true),
48 	T_META_TAG_VM_PREFERRED
49 	);
50 
51 #define SIZE_ESTIMATE_EXTRA_ENTRIES 5
52 #define MAX_TRIES 3
53 
54 #define _STR(x) #x
55 #define STR(x) _STR(x)
56 
57 memorystatus_priority_entry_v2_t*
get_priority_list(pid_t pid,int call,size_t entry_size,ssize_t * len)58 get_priority_list(pid_t pid, int call, size_t entry_size, ssize_t* len)
59 {
60 	int i;
61 	ssize_t size;
62 	memorystatus_priority_entry_v2_t *list = NULL;
63 
64 	for (i = 0; i < MAX_TRIES; i++) {
65 		T_LOG("Attempt %d", i + 1);
66 
67 		size = memorystatus_control(call, pid, 0, NULL, 0);
68 
69 		T_ASSERT_GT(size, 0l, "Priority list query size > 0");
70 		T_ASSERT_EQ(size % entry_size, 0ul, "Priority list size is multiple of struct size");
71 
72 		if (size <= 0) {
73 			return NULL;
74 		}
75 
76 		/* pad out the size just in case the list gets bigger */
77 		if (pid == 0) {
78 			size += SIZE_ESTIMATE_EXTRA_ENTRIES * entry_size;
79 		}
80 
81 		list = malloc(size);
82 		T_QUIET; T_ASSERT_NOTNULL(list, "malloc");
83 
84 		*len = memorystatus_control(call, pid, 0, list, size);
85 
86 		if (*len <= 0) {
87 			T_LOG("Failed, maybe the list grew? Trying again...");
88 			free(list);
89 			continue;
90 		}
91 
92 		T_ASSERT_GE(size, *len, "List fits in buffer");
93 		T_ASSERT_GT(size, 0l, "List size nonzero");
94 		return list;
95 	}
96 
97 	T_FAIL("Tried more than MAX_TRIES=" STR(MAX_TRIES) " times");
98 	return NULL;
99 }
100 
101 void
validate_entry(memorystatus_priority_entry_v2_t * entry,size_t entry_size,pid_t expect_pid)102 validate_entry(memorystatus_priority_entry_v2_t *entry, size_t entry_size, pid_t expect_pid)
103 {
104 	int i;
105 	if (expect_pid == -1) {
106 		T_QUIET; T_ASSERT_GE(entry->pid, 0, "PID valid");
107 		T_QUIET; T_ASSERT_NE(entry->pid, 0, "kernel_task not in list");
108 		T_QUIET; T_ASSERT_NE(entry->pid, 1, "launchd not in list");
109 	} else {
110 		T_QUIET; T_ASSERT_EQ(entry->pid, expect_pid, "PID correct");
111 	}
112 
113 	if (entry->pid > 1) {
114 		T_QUIET; T_ASSERT_GE(
115 			entry->priority,
116 			0,
117 			"Entry priority >= 0");
118 		T_QUIET; T_ASSERT_LE(
119 			entry->priority,
120 			JETSAM_PRIORITY_MAX,
121 			"Entry priority <= JETSAM_PRIORITY_MAX (" STR(JETSAM_PRIORITY_MAX) ")");
122 	} else {
123 		T_QUIET; T_ASSERT_EQ(
124 			entry->priority,
125 			JETSAM_PRIORITY_INTERNAL,
126 			"Entry priority == JETSAM_PRIORITY_INTERNAL (" STR(JETSAM_PRIORITY_INTERNAL) ")");
127 	}
128 
129 	if (entry_size == sizeof(memorystatus_priority_entry_v2_t)) {
130 		boolean_t found_nonzero = false;
131 		for (i = 0; i < sizeof(entry->_reserved) / sizeof(entry->_reserved[0]); i++) {
132 			if (entry->_reserved[i]) {
133 				found_nonzero = true;
134 				break;
135 			}
136 		}
137 		T_QUIET; T_ASSERT_FALSE(found_nonzero, "Entry reserved is empty");
138 	}
139 }
140 
141 T_DECL(jetsam_priority_list_v2_list, "Jetsam priority list v2 - list")
142 {
143 	int i;
144 	ssize_t len;
145 	memorystatus_priority_entry_v2_t *list = get_priority_list(
146 		0,
147 		MEMORYSTATUS_CMD_GET_PRIORITY_LIST_V2,
148 		sizeof(*list),
149 		&len);
150 
151 	T_ASSERT_NOTNULL(list, "Priority list not null");
152 	for (i = 0; i < len / sizeof(memorystatus_priority_entry_v2_t); i++) {
153 		validate_entry(&list[i], sizeof(*list), -1);
154 	}
155 	free(list);
156 	T_PASS("Entries valid");
157 }
158 
159 void
validate_single(pid_t pid)160 validate_single(pid_t pid)
161 {
162 	ssize_t len;
163 	memorystatus_priority_entry_v2_t *list = get_priority_list(
164 		pid,
165 		MEMORYSTATUS_CMD_GET_PRIORITY_LIST_V2,
166 		sizeof(*list),
167 		&len);
168 
169 	T_ASSERT_NOTNULL(list, "Getting self priority entry");
170 	T_ASSERT_EQ(len, sizeof(memorystatus_priority_entry_v2_t), "Single entry returned");
171 	validate_entry(list, sizeof(*list), pid);
172 	free(list);
173 }
174 
175 T_DECL(jetsam_priority_list_v2_single, "Jetsam priority list v2 - single")
176 {
177 	T_LOG("Getting entry for self...");
178 	validate_single(getpid());
179 
180 	T_LOG("Getting entry for launchd...");
181 	validate_single(1);
182 
183 	T_PASS("Entries valid");
184 }
185 
186 T_DECL(jetsam_priority_list_compat, "Jetsam priority list - v1 compat")
187 {
188 	int i;
189 	ssize_t len;
190 
191 	memorystatus_priority_entry_t *list = (memorystatus_priority_entry_t*) get_priority_list(
192 		0,
193 		MEMORYSTATUS_CMD_GET_PRIORITY_LIST,
194 		sizeof(*list),
195 		&len);
196 
197 	T_ASSERT_NOTNULL(list, "Priority list not null");
198 
199 	for (i = 0; i < len / sizeof(memorystatus_priority_entry_v2_t); i++) {
200 		validate_entry((memorystatus_priority_entry_v2_t*) (&list[i]), sizeof(*list), -1);
201 	}
202 
203 	free(list);
204 	T_PASS("Entries valid");
205 }
206