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