xref: /xnu-11417.121.6/tests/recount/recount_test_utils.c (revision a1e26a70f38d1d7daa7b49b258e2f8538ad81650) !
1 // Copyright (c) 2021-2022 Apple Inc.  All rights reserved.
2 
3 #include <darwintest.h>
4 #include <darwintest_utils.h>
5 #include <dispatch/dispatch.h>
6 #include <mach/semaphore.h>
7 #include <mach/mach.h>
8 #include <mach/task.h>
9 #include <mach-o/dyld.h>
10 #include <stdbool.h>
11 #include <stdlib.h>
12 #include <sys/sysctl.h>
13 #include <unistd.h>
14 
15 #include "recount_test_utils.h"
16 
17 static bool may_fail_status = false;
18 
19 void
set_expects_may_fail(bool may_fail)20 set_expects_may_fail(bool may_fail)
21 {
22 	may_fail_status = may_fail;
23 }
24 
25 bool
expects_may_fail(void)26 expects_may_fail(void)
27 {
28 	return may_fail_status;
29 }
30 
31 bool
has_user_system_times(void)32 has_user_system_times(void)
33 {
34 	static dispatch_once_t user_system_once;
35 	static bool precise_times = false;
36 	dispatch_once(&user_system_once, ^{
37 		int precise_times_int = 0;
38 		size_t precise_times_size = sizeof(precise_times_int);
39 		T_SETUPBEGIN;
40 		T_QUIET;
41 		T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.precise_user_kernel_time",
42 				&precise_times_int, &precise_times_size, NULL, 0),
43 				"sysctl kern.precise_user_kernel_time");
44 		T_SETUPEND;
45 		precise_times = precise_times_int != 0;
46 	});
47 	return precise_times;
48 }
49 
50 bool
has_cpi(void)51 has_cpi(void)
52 {
53 	static dispatch_once_t cpi_once;
54 	static int cpi = 0;
55 	dispatch_once(&cpi_once, ^{
56 		size_t cpi_size = sizeof(cpi);
57 		T_SETUPBEGIN;
58 		int ret = sysctlbyname("kern.monotonic.supported", &cpi, &cpi_size,
59 				NULL, 0);
60 		// ENOENT also means that CPI is unavailable.
61 		if (ret != 0 && errno != ENOENT) {
62 			T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.monotonic.supported");
63 		}
64 		T_SETUPEND;
65 	});
66 	return cpi != 0;
67 }
68 
69 bool
has_energy(void)70 has_energy(void)
71 {
72 	static dispatch_once_t energy_once;
73 	static int energy = false;
74 	dispatch_once(&energy_once, ^{
75 		size_t energy_size = sizeof(energy);
76 		T_SETUPBEGIN;
77 		T_QUIET;
78 		T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.pervasive_energy",
79 				&energy, &energy_size, NULL, 0),
80 				"sysctl kern.pervasive_energy");
81 		T_SETUPEND;
82 	});
83 	return energy != 0;
84 }
85 
86 unsigned int
perf_level_count(void)87 perf_level_count(void)
88 {
89 	static dispatch_once_t count_once;
90 	static unsigned int count = 0;
91 	dispatch_once(&count_once, ^{
92 		T_SETUPBEGIN;
93 		T_QUIET;
94 		T_ASSERT_POSIX_SUCCESS(sysctlbyname("hw.nperflevels", &count,
95 				&(size_t){ sizeof(count) }, NULL, 0),
96 				"sysctl hw.nperflevels");
97 		T_SETUPEND;
98 	});
99 	return count;
100 }
101 
102 static const char **
_perf_level_names(void)103 _perf_level_names(void)
104 {
105 	static char names[2][32] = { 0 };
106 	static dispatch_once_t names_once;
107 	dispatch_once(&names_once, ^{
108 		T_SETUPBEGIN;
109 		unsigned int count = perf_level_count();
110 		for (unsigned int i = 0; i < count; i++) {
111 			char sysctl_name[64] = { 0 };
112 			snprintf(sysctl_name, sizeof(sysctl_name), "hw.perflevel%d.name",
113 					i);
114 			T_QUIET;
115 			T_ASSERT_POSIX_SUCCESS(sysctlbyname(sysctl_name, &names[i],
116 					&(size_t){ sizeof(names[i]) }, NULL, 0),
117 					"sysctl %s", sysctl_name);
118 		}
119 		T_SETUPEND;
120 	});
121 	static const char *ret_names[] = {
122 		(char *)&names[0],
123 		(char *)&names[1],
124 	};
125 	return ret_names;
126 }
127 
128 const char *
perf_level_name(unsigned int perf_level)129 perf_level_name(unsigned int perf_level)
130 {
131 	return _perf_level_names()[perf_level];
132 }
133 
134 unsigned int
perf_level_index(const char * name)135 perf_level_index(const char *name)
136 {
137 	unsigned int count = perf_level_count();
138 	const char **names = _perf_level_names();
139 	for (unsigned int i = 0; i < count; i++) {
140 		if (strcmp(name, names[i]) == 0) {
141 			return i;
142 		}
143 	}
144 	T_ASSERT_FAIL("cannot find perf level named %s", name);
145 }
146 
147 void
bind_to_cluster(char type)148 bind_to_cluster(char type)
149 {
150 	int ret = sysctlbyname("kern.sched_thread_bind_cluster_type", NULL, NULL,
151 			&type, sizeof(type));
152 	T_QUIET;
153 	T_ASSERT_POSIX_SUCCESS(ret, "sysctl kern.sched_thread_bind_cluster_type");
154 	// Do a little spin to tighten the odds that we get CPU time on the bound
155 	// cluster--needed for devices running the AMP scheduler.
156 	volatile int count = 0;
157 	while (count++ < 100000);
158 }
159 
160 char *
sched_policy_name(void)161 sched_policy_name(void)
162 {
163 	static char policy_name[64] = { 0 };
164 	static dispatch_once_t policy_name_once;
165 	dispatch_once(&policy_name_once, ^{
166 		T_SETUPBEGIN;
167 		T_QUIET;
168 		T_ASSERT_POSIX_SUCCESS(sysctlbyname("kern.sched", &policy_name,
169 				&(size_t){ sizeof(policy_name) }, NULL, 0), "kern.sched");
170 		T_SETUPEND;
171 	});
172 	return policy_name;
173 }
174 
175 static void
_unbind_from_cluster(void)176 _unbind_from_cluster(void)
177 {
178 	// Best effort.
179 	(void)sysctlbyname("kern.sched_thread_bind_cluster_type", NULL, NULL, NULL,
180 			0);
181 }
182 
183 void
run_on_all_perf_levels(void)184 run_on_all_perf_levels(void)
185 {
186 	if (perf_level_count() == 1) {
187 		return;
188 	}
189 
190 	T_SETUPBEGIN;
191 	bind_to_cluster('P');
192 	bind_to_cluster('E');
193 	// Return to the kernel to synchronize timings with the scheduler.
194 	(void)getppid();
195 	_unbind_from_cluster();
196 	T_SETUPEND;
197 }
198 
199 static void
_run_on_exclaves(void)200 _run_on_exclaves(void)
201 {
202 	int64_t output = 0;
203 	size_t output_size = sizeof(output);
204 	int64_t input = 0;
205 	int ret = sysctlbyname("debug.test.exclaves_hello_exclave_test", &output,
206 			&output_size, &input, sizeof(input));
207 	T_QUIET;
208 	T_ASSERT_POSIX_SUCCESS(ret, "systcl debug.test.exclaves_hello_exclave_test");
209 }
210 
211 void
run_in_exclaves_on_all_perf_levels(void)212 run_in_exclaves_on_all_perf_levels(void)
213 {
214 	if (perf_level_count() == 1) {
215 		_run_on_exclaves();
216 	}
217 
218 	T_SETUPBEGIN;
219 	bind_to_cluster('P');
220 	_run_on_exclaves();
221 	bind_to_cluster('E');
222 	_run_on_exclaves();
223 	_unbind_from_cluster();
224 	T_SETUPEND;
225 }
226 
227 uint64_t
ns_from_mach(uint64_t mach_time)228 ns_from_mach(uint64_t mach_time)
229 {
230 	mach_timebase_info_data_t tbi = { 0 };
231 	mach_timebase_info(&tbi);
232 	return mach_time * tbi.numer / tbi.denom;
233 }
234 
235 uint64_t
ns_from_timeval(struct timeval tv)236 ns_from_timeval(struct timeval tv)
237 {
238 	return (uint64_t)tv.tv_sec * NSEC_PER_SEC + (uint64_t)tv.tv_usec * 1000;
239 }
240 
241 struct timeval
timeval_from_ns(uint64_t ns)242 timeval_from_ns(uint64_t ns)
243 {
244 	return (struct timeval){
245 		.tv_sec = ns / NSEC_PER_SEC,
246 		.tv_usec = (ns % NSEC_PER_SEC) / 1000,
247 	};
248 }
249 
250 uint64_t
ns_from_time_value(struct time_value tv)251 ns_from_time_value(struct time_value tv)
252 {
253 	return (uint64_t)tv.seconds * NSEC_PER_SEC +
254 			(uint64_t)tv.microseconds * 1000;
255 }
256 
257 struct time_value
time_value_from_ns(uint64_t ns)258 time_value_from_ns(uint64_t ns)
259 {
260 	return (struct time_value){
261 		.seconds = (integer_t)(ns / NSEC_PER_SEC),
262 		.microseconds = (ns % NSEC_PER_SEC) / 1000,
263 	};
264 }
265 
266 static void *
spin_role(void * arg)267 spin_role(void *arg)
268 {
269 	volatile uintptr_t *keep_spinning = arg;
270 	while (*keep_spinning) {
271 		;
272 	}
273 	return NULL;
274 }
275 
276 struct wait_start {
277 	semaphore_t ws_wait;
278 	semaphore_t ws_start;
279 };
280 
281 static void *
wait_role(void * arg)282 wait_role(void *arg)
283 {
284 	struct wait_start *ws = arg;
285 	semaphore_wait_signal(ws->ws_wait, ws->ws_start);
286 	return NULL;
287 }
288 
289 struct scene *
scene_start(unsigned int n,role_t * roles)290 scene_start(unsigned int n, role_t *roles)
291 {
292 	if (n == 0) {
293 		return NULL;
294 	}
295 
296 	T_SETUPBEGIN;
297 
298 	size_t scene_size = sizeof(struct scene) + (n + 1) * sizeof(struct actor);
299 	struct scene *scene = malloc(scene_size);
300 	T_QUIET; T_WITH_ERRNO;
301 	T_ASSERT_NOTNULL(scene, "scene = malloc(%zu)", scene_size);
302 
303 	bzero(scene, scene_size);
304 	unsigned int role_i = 0;
305 	unsigned int wait_count = 0;
306 	for (unsigned int i = 0; i < n; i++) {
307 		role_t role = roles[role_i];
308 		if (role == ROLE_NONE) {
309 			role_i = 0;
310 			role = roles[role_i];
311 		}
312 		if (role == ROLE_WAIT) {
313 			wait_count++;
314 		}
315 		scene->scn_actors[i].act_role = role;
316 		role_i++;
317 	}
318 
319 	struct wait_start ws = { 0 };
320 	kern_return_t kr = semaphore_create(mach_task_self(), &ws.ws_wait,
321 			SYNC_POLICY_FIFO, 0);
322 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "semaphore_create(... wait ...)");
323 	kr = semaphore_create(mach_task_self(), &ws.ws_start,
324 			SYNC_POLICY_FIFO, 0);
325 	T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "semaphore_create(... start ...)");
326 
327 	for (unsigned int i = 0; i < n; i++) {
328 		struct actor *act = &scene->scn_actors[i];
329 		void *(*action)(void *) = NULL;
330 		void *sync = NULL;
331 		switch (act->act_role) {
332 		case ROLE_SPIN:
333 			sync = &scene->scn_spin_sync;
334 			action = spin_role;
335 			break;
336 		case ROLE_WAIT:
337 			sync = &ws;
338 			action = wait_role;
339 			break;
340 		default:
341 			T_ASSERT_FAIL("unexpected role: %d", act->act_role);
342 		}
343 		int error = pthread_create(&act->act_thread, NULL, action, sync);
344 		T_QUIET; T_ASSERT_POSIX_ZERO(error, "pthread_create");
345 	}
346 
347 	T_SETUPEND;
348 	for (unsigned int i = 0; i < wait_count; i++) {
349 		semaphore_wait(ws.ws_start);
350 	}
351 	semaphore_destroy(mach_task_self(), ws.ws_start);
352 	scene->scn_wait_sync = (void *)(uintptr_t)ws.ws_wait;
353 	return scene;
354 }
355 
356 void
scene_end(struct scene * scene)357 scene_end(struct scene *scene)
358 {
359 	if (!scene) {
360 		return;
361 	}
362 
363 	scene->scn_spin_sync = 0;
364 	semaphore_signal_all((semaphore_t)scene->scn_wait_sync);
365 	semaphore_destroy(mach_task_self(), (semaphore_t)scene->scn_wait_sync);
366 	struct actor *act = scene->scn_actors;
367 	while (act->act_role != ROLE_NONE) {
368 		int error = pthread_join(act->act_thread, NULL);
369 		T_QUIET; T_ASSERT_POSIX_ZERO(error, "pthread_join");
370 		act++;
371 	}
372 	free(scene);
373 }
374 
375 pid_t
launch_helper(char * name)376 launch_helper(char *name)
377 {
378 	char bin_path[MAXPATHLEN];
379 	uint32_t path_size = sizeof(bin_path);
380 
381 	T_SETUPBEGIN;
382 	int ret = _NSGetExecutablePath(bin_path, &path_size);
383 	T_QUIET;
384 	T_ASSERT_EQ(ret, 0, "_NSGetExecutablePath()");
385 	pid_t pid = 0;
386 	ret = dt_launch_tool(&pid, (char *[]){ bin_path, name, NULL}, false, NULL,
387 	    NULL);
388 	T_QUIET;
389 	T_ASSERT_POSIX_SUCCESS(ret, "dt_launch_tool(... %s, %s ...)", bin_path,
390 	    name);
391 	T_SETUPEND;
392 
393 	return pid;
394 }
395