xref: /xnu-12377.61.12/tests/recount/recount_tests.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
1 // Copyright 2021-2023 (c) Apple Inc.  All rights reserved.
2 
3 #include <darwintest.h>
4 #include <darwintest_utils.h>
5 #include <inttypes.h>
6 #include <libproc.h>
7 #include <mach/mach.h>
8 #include <mach/task_info.h>
9 #include <mach/thread_info.h>
10 #include <stdint.h>
11 #include <sys/resource.h>
12 #include <unistd.h>
13 
14 #include "test_utils.h"
15 #include "recount_test_utils.h"
16 
17 T_GLOBAL_META(
18 	T_META_RADAR_COMPONENT_NAME("xnu"),
19 	T_META_RADAR_COMPONENT_VERSION("cpu counters"),
20     T_META_OWNER("mwidmann"),
21     T_META_CHECK_LEAKS(false));
22 
23 static void
proc_pidtaskinfo_increasing(pid_t pid,struct proc_taskinfo * last,const char * desc)24 proc_pidtaskinfo_increasing(pid_t pid, struct proc_taskinfo *last,
25     const char *desc)
26 {
27 	struct proc_taskinfo info = { 0 };
28 	T_SETUPBEGIN;
29 	int ret = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &info, sizeof(info));
30 	T_QUIET;
31 	T_ASSERT_POSIX_SUCCESS(ret, "proc_pidinfo(..., PROC_PIDTASKINFO, ...)");
32 	T_SETUPEND;
33 
34 	const char *name = "PROC_PIDTASKINFO";
35 	T_LOG("%s: usr = %llu, sys = %llu, th_usr = %llu, th_sys = %llu, "
36 			"term_usr = %llu, term_sys = %llu", name, info.pti_total_user,
37 			info.pti_total_system, info.pti_threads_user,
38 			info.pti_threads_system,
39 			info.pti_total_user - info.pti_threads_user,
40 			info.pti_total_system - info.pti_threads_system);
41 	T_EXPECT_GE(info.pti_total_user, last->pti_total_user,
42 			"%s user time should increase %s", name, desc);
43 	T_EXPECT_GE(info.pti_total_system, last->pti_total_system,
44 			"%s system time should increase %s", name, desc);
45 	*last = info;
46 }
47 
48 static void *
spin_thread(void * arg)49 spin_thread(void *arg)
50 {
51 	volatile int *spin = arg;
52 	while (*spin);
53 	return NULL;
54 }
55 
56 static void *
sleep_thread(void * arg)57 sleep_thread(void *arg)
58 {
59 	volatile int *keep_going = arg;
60 	while (*keep_going) {
61 		usleep(100000);
62 	}
63 	return NULL;
64 }
65 
66 enum usage_style {
67 	USAGE_SPIN,
68 	USAGE_SLEEP,
69 };
70 
71 struct usage_thread {
72 	enum usage_style ut_style;
73 	const char *ut_name;
74 	uintptr_t ut_arg;
75 	pthread_t ut_thread;
76 };
77 
78 static void
thread_start(struct usage_thread * th,const char * name,enum usage_style style)79 thread_start(struct usage_thread *th, const char *name, enum usage_style style)
80 {
81 	th->ut_style = style;
82 	th->ut_name = name;
83 	th->ut_arg = 1;
84 	T_SETUPBEGIN;
85 	int error = pthread_create(&th->ut_thread, NULL,
86 			style == USAGE_SPIN ? spin_thread : sleep_thread, &th->ut_arg);
87 	T_QUIET; T_ASSERT_POSIX_ZERO(error, "pthread_create");
88 	T_LOG("created %s thread to %s", name,
89 			style == USAGE_SPIN ? "spin" : "sleep");
90 	T_SETUPEND;
91 }
92 
93 static void
thread_end(struct usage_thread * th)94 thread_end(struct usage_thread *th)
95 {
96 	th->ut_arg = 0;
97 	T_SETUPBEGIN;
98 	int error = pthread_join(th->ut_thread, NULL);
99 	T_QUIET; T_ASSERT_POSIX_ZERO(error, "pthread_join");
100 	T_LOG("terminated %s thread", th->ut_name);
101 	T_SETUPEND;
102 }
103 
104 T_DECL(proc_pidtaskinfo_sanity, "ensure proc_pidtaskinfo CPU times are sane", T_META_TAG_VM_NOT_ELIGIBLE)
105 {
106 	struct proc_taskinfo prev = { 0 };
107 	struct usage_thread first = { 0 };
108 	struct usage_thread second = { 0 };
109 
110 	proc_pidtaskinfo_increasing(getpid(), &prev, "initially");
111 	thread_start(&first, "first", USAGE_SPIN);
112 	proc_pidtaskinfo_increasing(getpid(), &prev,
113 			"after first thread has been created");
114 	thread_start(&second, "second", USAGE_SPIN);
115 	proc_pidtaskinfo_increasing(getpid(), &prev,
116 			"after second thread has been created");
117 	// Sleep for ~10 quanta.
118 	usleep(100 * 1000);
119 	thread_end(&first);
120 	proc_pidtaskinfo_increasing(getpid(), &prev,
121 			"after first thread has terminated");
122 	thread_end(&second);
123 	proc_pidtaskinfo_increasing(getpid(), &prev,
124 			"after all threads have terminated");
125 }
126 
127 struct usr_sys_times {
128 	uint64_t usr_time;
129 	uint64_t sys_time;
130 };
131 
132 static void
_assert_increasing(struct usr_sys_times * before,struct usr_sys_times * after,const char * name,const char * desc)133 _assert_increasing(struct usr_sys_times *before, struct usr_sys_times *after,
134     const char *name, const char *desc)
135 {
136 	T_EXPECT_GE(after->usr_time, before->usr_time,
137 			"%s user time should increase %s", name, desc);
138 	T_EXPECT_GE(after->sys_time, before->sys_time,
139 			"%s system time should increase %s", name, desc);
140 }
141 
142 static void
test_usr_sys_time_sanity(struct usr_sys_times (* fn)(pid_t),const char * name)143 test_usr_sys_time_sanity(struct usr_sys_times (*fn)(pid_t), const char *name)
144 {
145 	struct usr_sys_times init = fn(getpid());
146 	struct usage_thread first = { 0 };
147 	thread_start(&first, "first", USAGE_SLEEP);
148 
149 	struct usr_sys_times thread_active = fn(getpid());
150 	_assert_increasing(&init, &thread_active, name,
151 			"after first thread has been created");
152 
153 	struct usage_thread second = { 0 };
154 	thread_start(&second, "second", USAGE_SLEEP);
155 
156 	struct usr_sys_times thread_top_active = fn(getpid());
157 	_assert_increasing(&thread_active, &thread_top_active, name,
158 			"after second thread has been created");
159 
160 	thread_end(&first);
161 
162 	struct usr_sys_times thread_top_gone = fn(getpid());
163 	_assert_increasing(&thread_top_active, &thread_top_gone, name,
164 			"after first thread has terminated");
165 
166 	thread_end(&second);
167 
168 	struct usr_sys_times thread_gone = fn(getpid());
169 	_assert_increasing(&thread_top_gone, &thread_gone, name,
170 			"after all threads have terminated");
171 }
172 
173 static void
_get_proc_pid_rusage(pid_t pid,struct rusage_info_v6 * info)174 _get_proc_pid_rusage(pid_t pid, struct rusage_info_v6 *info)
175 {
176 	T_SETUPBEGIN;
177 	int ret = proc_pid_rusage(pid, RUSAGE_INFO_V6, (rusage_info_t *)info);
178 	T_QUIET;
179 	T_ASSERT_POSIX_SUCCESS(ret, "proc_pid_rusage");
180 	T_SETUPEND;
181 }
182 
183 static struct usr_sys_times
proc_pid_rusage_times(pid_t pid)184 proc_pid_rusage_times(pid_t pid)
185 {
186 	struct rusage_info_v6 info = { 0 };
187 	_get_proc_pid_rusage(pid, &info);
188 	return (struct usr_sys_times){
189 		.usr_time = info.ri_user_time,
190 		.sys_time = info.ri_system_time,
191 	};
192 }
193 
194 T_DECL(proc_pid_rusage_sanity, "ensure proc_pidtaskinfo CPU times are sane", T_META_TAG_VM_NOT_ELIGIBLE)
195 {
196 	test_usr_sys_time_sanity(proc_pid_rusage_times, "proc_pid_rusage");
197 }
198 
199 static struct usr_sys_times
task_basic_info_times(pid_t __unused pid)200 task_basic_info_times(pid_t __unused pid)
201 {
202 	struct task_basic_info_64 info = { 0 };
203 	mach_msg_type_number_t info_count = TASK_BASIC_INFO_64_COUNT;
204 
205 	T_SETUPBEGIN;
206 	kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO_64,
207 			(task_info_t)&info, &info_count);
208 	T_QUIET;
209 	T_ASSERT_MACH_SUCCESS(kr, "task_info(... TASK_BASIC_INFO_64 ...)");
210 	T_SETUPEND;
211 
212 	return (struct usr_sys_times){
213 		.usr_time = ns_from_time_value(info.user_time),
214 		.sys_time = ns_from_time_value(info.system_time),
215 	};
216 }
217 
218 T_DECL(task_basic_info_sanity, "ensure TASK_BASIC_INFO CPU times are sane", T_META_TAG_VM_PREFERRED)
219 {
220 	test_usr_sys_time_sanity(task_basic_info_times, "TASK_BASIC_INFO");
221 }
222 
223 static struct usr_sys_times
task_power_info_times(pid_t __unused pid)224 task_power_info_times(pid_t __unused pid)
225 {
226 	struct task_power_info info = { 0 };
227 	mach_msg_type_number_t info_count = TASK_POWER_INFO_COUNT;
228 	kern_return_t kr = task_info(mach_task_self(), TASK_POWER_INFO,
229 			(task_info_t)&info, &info_count);
230 
231 	T_SETUPBEGIN;
232 	T_QUIET;
233 	T_ASSERT_MACH_SUCCESS(kr, "task_info(... TASK_POWER_INFO ...)");
234 	T_SETUPEND;
235 
236 	return (struct usr_sys_times){
237 		.usr_time = ns_from_mach(info.total_user),
238 		.sys_time = ns_from_mach(info.total_system),
239 	};
240 }
241 
242 T_DECL(task_power_info_sanity, "ensure TASK_POWER_INFO CPU times are sane", T_META_TAG_VM_NOT_ELIGIBLE)
243 {
244 	test_usr_sys_time_sanity(task_power_info_times, "TASK_POWER_INFO");
245 }
246 
247 static struct usr_sys_times
task_absolutetime_info_times(pid_t __unused pid)248 task_absolutetime_info_times(pid_t __unused pid)
249 {
250 	task_absolutetime_info_data_t info = { 0 };
251 	mach_msg_type_number_t info_count = TASK_ABSOLUTETIME_INFO_COUNT;
252 	kern_return_t kr = task_info(mach_task_self(), TASK_ABSOLUTETIME_INFO,
253 			(task_info_t)&info, &info_count);
254 
255 	T_SETUPBEGIN;
256 	T_QUIET;
257 	T_ASSERT_MACH_SUCCESS(kr, "task_info(... TASK_ABSOLUTETIME_INFO ...)");
258 	T_SETUPEND;
259 
260 	return (struct usr_sys_times){
261 		.usr_time = ns_from_mach(info.total_user),
262 		.sys_time = ns_from_mach(info.total_system),
263 	};
264 }
265 
266 T_DECL(task_absolutetime_info_sanity,
267 		"ensure TASK_ABSOLUTETIME_INFO CPU times are sane", T_META_TAG_VM_PREFERRED)
268 {
269 	test_usr_sys_time_sanity(task_absolutetime_info_times,
270 			"TASK_ABSOLUTETIME_INFO");
271 }
272 
273 static struct usr_sys_times
getrusage_times(pid_t __unused pid)274 getrusage_times(pid_t __unused pid)
275 {
276 	struct rusage usage = { 0 };
277 	int ret = getrusage(RUSAGE_SELF, &usage);
278 
279 	T_SETUPBEGIN;
280 	T_QUIET;
281 	T_ASSERT_POSIX_SUCCESS(ret, "getrusage(RUSAGE_SELF ...)");
282 	T_SETUPEND;
283 
284 	return (struct usr_sys_times){
285 		.usr_time = ns_from_timeval(usage.ru_utime),
286 		.sys_time = ns_from_timeval(usage.ru_stime),
287 	};
288 }
289 
290 T_DECL(getrusage_sanity, "ensure getrusage CPU times are sane", T_META_TAG_VM_NOT_PREFERRED)
291 {
292 	test_usr_sys_time_sanity(getrusage_times, "getrusage");
293 }
294 
295 T_DECL(thread_selfusage_sanity, "ensure thread_selfusage times are sane", T_META_TAG_VM_PREFERRED)
296 {
297 	uint64_t before = __thread_selfusage();
298 	uint64_t after = __thread_selfusage();
299 	T_ASSERT_GT(after, before, "thread_selfusage is increasing");
300 	before = __thread_selfusage();
301 	for (int i = 0; i < 5; i++) {
302 		usleep(1000);
303 	}
304 	after = __thread_selfusage();
305 	T_ASSERT_GT(after, before, "thread_selfusage increases after sleeping");
306 }
307 
308 T_DECL(proc_pid_rusage_perf_levels,
309 		"ensure proc_pid_rusage fills in per-perf level information",
310 		REQUIRE_RECOUNT_PMCS,
311 		REQUIRE_MULTIPLE_PERF_LEVELS,
312 		SET_THREAD_BIND_BOOTARG, T_META_TAG_VM_NOT_ELIGIBLE)
313 {
314 	T_QUIET; T_ASSERT_GT(perf_level_count(), 1, "Platform should be AMP");
315 
316 	struct rusage_info_v6 before = { 0 };
317 	struct rusage_info_v6 after = { 0 };
318 
319 	_get_proc_pid_rusage(getpid(), &before);
320 	run_on_all_perf_levels();
321 	_get_proc_pid_rusage(getpid(), &after);
322 
323 	T_EXPECT_GE(after.ri_cycles, before.ri_cycles, "cycles increasing");
324 	T_EXPECT_GE(after.ri_instructions, before.ri_instructions,
325 			"instructions increasing");
326 	T_EXPECT_GE(after.ri_user_time, before.ri_user_time,
327 			"user_time increasing");
328 	T_EXPECT_GE(after.ri_system_time, before.ri_system_time,
329 			"system_time increasing");
330 
331 	T_EXPECT_GE(after.ri_pcycles, before.ri_pcycles, "cycles_p increasing");
332 	T_EXPECT_GE(after.ri_pinstructions, before.ri_pinstructions,
333 			"instructions_p increasing");
334 	T_EXPECT_GE(after.ri_user_ptime, before.ri_user_ptime,
335 			"user_time_p increasing");
336 	T_EXPECT_GE(after.ri_system_ptime, before.ri_system_ptime,
337 			"system_time_p increasing");
338 
339 	if (has_energy()) {
340 		T_EXPECT_GE(after.ri_energy_nj, before.ri_energy_nj,
341 				"energy_nj increasing");
342 		T_EXPECT_GE(after.ri_penergy_nj, before.ri_penergy_nj,
343 				"penergy_nj increasing");
344 	}
345 }
346 
347 T_DECL(proc_pid_rusage_secure_perf_levels,
348 		"ensure proc_pid_rusage fills in per-perf level information",
349 		REQUIRE_RECOUNT_PMCS,
350 		REQUIRE_MULTIPLE_PERF_LEVELS,
351 		REQUIRE_EXCLAVES,
352 		SET_THREAD_BIND_BOOTARG,
353 		T_META_TAG_VM_PREFERRED)
354 {
355 	int status = 0;
356 	size_t status_size = sizeof(status);
357 	(void)sysctlbyname("kern.exclaves_status", &status, &status_size, NULL, 0);
358 	if (status != 1) {
359 		T_SKIP("exclaves must be supported");
360 	}
361 
362 	struct rusage_info_v6 before = { 0 };
363 	struct rusage_info_v6 after = { 0 };
364 
365 	_get_proc_pid_rusage(getpid(), &before);
366 	run_in_exclaves_on_all_perf_levels();
367 	_get_proc_pid_rusage(getpid(), &after);
368 
369 	T_EXPECT_GT(after.ri_secure_time_in_system, 0ULL,
370 			"secure time after running in exclaves is non-zero");
371 	T_EXPECT_GT(after.ri_secure_time_in_system, 0ULL,
372 			"secure time on P-cores after running in exclaves is non-zero");
373 
374 	T_EXPECT_GT(after.ri_secure_time_in_system, before.ri_secure_time_in_system,
375 			"secure time in system increasing");
376 	T_EXPECT_GT(after.ri_secure_ptime_in_system,
377 			before.ri_secure_ptime_in_system,
378 			"secure time in system on P-cores increasing");
379 
380 	uint64_t system_time_delta = after.ri_system_time - before.ri_system_time;
381 	uint64_t secure_time_delta = after.ri_secure_time_in_system -
382 			before.ri_secure_time_in_system;
383 	T_EXPECT_LE(secure_time_delta, system_time_delta,
384 			"secure time is less than system time");
385 	uint64_t system_ptime_delta = after.ri_system_ptime -
386 			before.ri_system_ptime;
387 	uint64_t secure_ptime_delta = after.ri_secure_ptime_in_system -
388 			before.ri_secure_ptime_in_system;
389 	T_EXPECT_LE(secure_ptime_delta, system_ptime_delta,
390 			"secure time is less than system time on P-cores");
391 }
392 
393 static void
_proc_pidthreadcounts_increasing(struct proc_threadcounts_data * before,struct proc_threadcounts_data * after,const char * perf_level)394 _proc_pidthreadcounts_increasing(struct proc_threadcounts_data *before,
395 		struct proc_threadcounts_data *after, const char *perf_level)
396 {
397 	const char *name = "PROC_PIDTHREADCOUNTS";
398 	T_LOG("%s %s before: usr = %llu, sys = %llu, instrs = %llu, cycles = %llu, "
399 			"energy = %llu", name, perf_level,  before->ptcd_user_time_mach,
400 			before->ptcd_system_time_mach, before->ptcd_instructions,
401 			before->ptcd_cycles, before->ptcd_energy_nj);
402 	T_LOG("%s %s after: usr = %llu, sys = %llu, instrs = %llu, cycles = %llu, "
403 			"energy = %llu", name, perf_level, after->ptcd_user_time_mach,
404 			after->ptcd_system_time_mach, after->ptcd_instructions,
405 			after->ptcd_cycles, after->ptcd_energy_nj);
406 
407 	T_EXPECT_NE(before->ptcd_user_time_mach, 0ULL,
408 			"%s user time should be non-zero", perf_level);
409 	T_EXPECT_NE(before->ptcd_system_time_mach, 0ULL,
410 			"%s system time should be non-zero", perf_level);
411 	T_EXPECT_NE(before->ptcd_instructions, 0ULL,
412 			"%s instructions should be non-zero", perf_level);
413 	T_EXPECT_NE(before->ptcd_cycles, 0ULL,
414 			"%s cycles should be non-zero", perf_level);
415 
416 	T_EXPECT_GT(after->ptcd_user_time_mach, before->ptcd_user_time_mach,
417 			"%s user time should increase", perf_level);
418 	T_EXPECT_GT(after->ptcd_system_time_mach, before->ptcd_system_time_mach,
419 			"%s system time should increase", perf_level);
420 	T_EXPECT_GT(after->ptcd_instructions, before->ptcd_instructions,
421 			"%s instructions should increase", perf_level);
422 	T_EXPECT_GT(after->ptcd_cycles, before->ptcd_cycles,
423 			"%s cycles should increase", perf_level);
424 
425 	if (has_energy()) {
426 		T_EXPECT_GT(after->ptcd_energy_nj, before->ptcd_energy_nj,
427 				"%s energy should increase", perf_level);
428 	}
429 }
430 
431 static void
_threadcounts_to_rusage_info(struct proc_threadcounts_data * counts,struct rusage_info_v6 * info)432 _threadcounts_to_rusage_info(struct proc_threadcounts_data *counts,
433 		struct rusage_info_v6 *info)
434 {
435 	unsigned int level_count = perf_level_count();
436 	for (unsigned int i = 0; i < level_count; i++) {
437 		struct proc_threadcounts_data *count = &counts[i];
438 		if (perf_level_name(i)[0] == 'P') {
439 			info->ri_system_ptime += count->ptcd_system_time_mach;
440 			info->ri_user_ptime += count->ptcd_user_time_mach;
441 			info->ri_pinstructions += count->ptcd_instructions;
442 			info->ri_pcycles += count->ptcd_cycles;
443 		}
444 		info->ri_system_time += count->ptcd_system_time_mach;
445 		info->ri_user_time += count->ptcd_user_time_mach;
446 		info->ri_instructions += count->ptcd_instructions;
447 		info->ri_cycles += count->ptcd_cycles;
448 	}
449 }
450 
451 static void
_rusage_info_le(struct rusage_info_v6 * lhs,const char * lhs_name,struct rusage_info_v6 * rhs,const char * rhs_name)452 _rusage_info_le(struct rusage_info_v6 *lhs, const char *lhs_name,
453 		struct rusage_info_v6 *rhs, const char *rhs_name)
454 {
455 	T_EXPECT_LE(lhs->ri_user_time, rhs->ri_user_time,
456 			"%s user time <= %s", lhs_name, rhs_name);
457 	T_EXPECT_LE(lhs->ri_system_time, rhs->ri_system_time,
458 			"%s system time <= %s", lhs_name, rhs_name);
459 	T_EXPECT_LE(lhs->ri_instructions, rhs->ri_instructions,
460 			"%s instructions <= %s", lhs_name, rhs_name);
461 	T_EXPECT_LE(lhs->ri_cycles, rhs->ri_cycles,
462 			"%s cycles <= %s", lhs_name, rhs_name);
463 	T_EXPECT_LE(lhs->ri_energy_nj, rhs->ri_energy_nj,
464 			"%s energy <= %s", lhs_name, rhs_name);
465 
466 	T_EXPECT_LE(lhs->ri_user_ptime, rhs->ri_user_ptime,
467 			"%s P-core user time <= %s", lhs_name, rhs_name);
468 	T_EXPECT_LE(lhs->ri_system_ptime, rhs->ri_system_ptime,
469 			"%s P-core system time <= %s", lhs_name, rhs_name);
470 	T_EXPECT_LE(lhs->ri_pinstructions, rhs->ri_pinstructions,
471 			"%s P-core instructions <= %s", lhs_name, rhs_name);
472 	T_EXPECT_LE(lhs->ri_pcycles, rhs->ri_pcycles,
473 			"%s P-core cycles <= %s", lhs_name, rhs_name);
474 	T_EXPECT_LE(lhs->ri_penergy_nj, rhs->ri_penergy_nj,
475 			"%s energy <= %s", lhs_name, rhs_name);
476 }
477 
478 struct thread_sequence {
479 	dispatch_semaphore_t child_sema;
480 	dispatch_semaphore_t parent_sema;
481 };
482 
483 static void *
_thread_runs_on_perf_levels(void * vsequence)484 _thread_runs_on_perf_levels(void *vsequence)
485 {
486 	struct thread_sequence *seq = vsequence;
487 
488 	run_on_all_perf_levels();
489 	dispatch_semaphore_signal(seq->parent_sema);
490 	dispatch_semaphore_wait(seq->child_sema, DISPATCH_TIME_FOREVER);
491 
492 	run_on_all_perf_levels();
493 	dispatch_semaphore_signal(seq->parent_sema);
494 	dispatch_semaphore_wait(seq->child_sema, DISPATCH_TIME_FOREVER);
495 	return NULL;
496 }
497 
498 T_DECL(proc_pidthreadcounts_sanity,
499 		"check per-perf level time and CPI from proc_pidthreadcounts",
500 		REQUIRE_RECOUNT_PMCS,
501 		SET_THREAD_BIND_BOOTARG,
502 		// Select the most comprehensive test to run on each SoC.
503 		XNU_T_META_SOC_SPECIFIC,
504 		T_META_ASROOT(true),
505 		T_META_TAG_VM_NOT_ELIGIBLE)
506 {
507 	T_SETUPBEGIN;
508 
509 	unsigned int level_count = perf_level_count();
510 	T_LOG("found %u perf levels", level_count);
511 	int counts_size = (int)sizeof(struct proc_threadcounts) +
512 			(int)level_count * (int)sizeof(struct proc_threadcounts_data);
513 	struct proc_threadcounts *before = malloc((unsigned int)counts_size);
514 	T_QUIET; T_ASSERT_NOTNULL(before, "allocate before counts");
515 	memset(before, 0, counts_size);
516 	struct proc_threadcounts *after = malloc((unsigned int)counts_size);
517 	T_QUIET; T_ASSERT_NOTNULL(before, "allocate after counts");
518 	memset(after, 0, counts_size);
519 	pthread_t target_thread = NULL;
520 	uint64_t target_tid = 0;
521 
522 	struct thread_sequence seq = {
523 		.parent_sema = dispatch_semaphore_create(0),
524 		.child_sema = dispatch_semaphore_create(0),
525 	};
526 	int error = pthread_create(&target_thread, NULL,
527 			_thread_runs_on_perf_levels, &seq);
528 	T_QUIET; T_ASSERT_POSIX_ZERO(error, "pthread_create");
529 	error = pthread_threadid_np(target_thread, &target_tid);
530 	T_QUIET; T_ASSERT_POSIX_ZERO(error, "pthread_threadid_np");
531 	T_LOG("created thread to run on all perf levels with ID %" PRIx64,
532 			target_tid);
533 
534 	dispatch_semaphore_wait(seq.parent_sema, DISPATCH_TIME_FOREVER);
535 
536 	T_SETUPEND;
537 
538 	int size = proc_pidinfo(getpid(), PROC_PIDTHREADCOUNTS, target_tid, before,
539 			counts_size);
540 	T_WITH_ERRNO;
541 	T_ASSERT_EQ(size, counts_size,
542 			"proc_pidinfo(..., PROC_PIDTHREADCOUNTS, ...)");
543 
544 	dispatch_semaphore_signal(seq.child_sema);
545 	dispatch_semaphore_wait(seq.parent_sema, DISPATCH_TIME_FOREVER);
546 
547 	size = proc_pidinfo(getpid(), PROC_PIDTHREADCOUNTS, target_tid, after,
548 			counts_size);
549 	T_WITH_ERRNO;
550 	T_ASSERT_EQ(size, counts_size,
551 			"proc_pidinfo(..., PROC_PIDTHREADCOUNTS, ...)");
552 
553 	struct rusage_info_v6 proc_usage = { 0 };
554 	_get_proc_pid_rusage(getpid(), &proc_usage);
555 
556 
557 	dispatch_semaphore_signal(seq.child_sema);
558 
559 	for (unsigned int i = 0; i < level_count; i++) {
560 		_proc_pidthreadcounts_increasing(&before->ptc_counts[i],
561 				&after->ptc_counts[i], perf_level_name(i));
562 	}
563 	struct rusage_info_v6 thread_usage = { 0 };
564 	_threadcounts_to_rusage_info(after->ptc_counts, &thread_usage);
565 	_rusage_info_le(&thread_usage, "thread", &proc_usage, "process");
566 
567 	(void)pthread_join(target_thread, NULL);
568 	free(before);
569 	free(after);
570 }
571 
572 T_DECL(proc_pidthreadcounts_invalid_tid,
573 		"check that proc_pidthreadcounts returns ESRCH on invalid thread",
574 		REQUIRE_RECOUNT_PMCS,
575 		T_META_ASROOT(true),
576 		T_META_TAG_VM_PREFERRED)
577 {
578 	T_SETUPBEGIN;
579 	unsigned int level_count = perf_level_count();
580 	int counts_size = (int)sizeof(struct proc_threadcounts) +
581 			(int)level_count * (int)sizeof(struct proc_threadcounts_data);
582 	struct proc_threadcounts *counts = malloc((unsigned int)counts_size);
583 	T_QUIET; T_ASSERT_NOTNULL(counts, "allocate counts");
584 	T_SETUPEND;
585 
586 	// proc_pidinfo has a unique return value protocol: it returns the size
587 	// that was copied out and 0 if an error occurs, with errno set.
588 	int size = proc_pidinfo(getpid(), PROC_PIDTHREADCOUNTS, UINT64_MAX, counts,
589 			counts_size);
590 	T_ASSERT_EQ(size, 0,
591 			"proc_pidinfo(..., PROC_PIDTHREADCOUNTS, UINT64_MAX, ...) should "
592 			"fail");
593 	T_ASSERT_EQ(errno, ESRCH, "should fail with ESRCH");
594 }
595 
596 // Shared state for the getrusage_thread_terminate_increasing test.
597 
598 static struct {
599 	pthread_mutex_t lock;
600 	pthread_cond_t wait_for_thread;
601 	pthread_cond_t wait_for_test;
602 } _getrusage_thread_state = {
603 	.lock = PTHREAD_MUTEX_INITIALIZER,
604 	.wait_for_thread = PTHREAD_COND_INITIALIZER,
605 	.wait_for_test = PTHREAD_COND_INITIALIZER,
606 };
607 
608 
609 static void *
_thread_spin_and_exit(void * __unused arg)610 _thread_spin_and_exit(void * __unused arg)
611 {
612 	pthread_mutex_lock(&_getrusage_thread_state.lock);
613 
614 	volatile int counter = 0;
615 	while (counter++ < 100000) {}
616 
617 	pthread_cond_signal(&_getrusage_thread_state.wait_for_thread);
618 	pthread_cond_wait(&_getrusage_thread_state.wait_for_test,
619 			&_getrusage_thread_state.lock);
620 	pthread_mutex_unlock(&_getrusage_thread_state.lock);
621 	return NULL;
622 }
623 
624 static uint64_t
_rusage_to_time_us(struct rusage * usage)625 _rusage_to_time_us(struct rusage *usage)
626 {
627 	return usage->ru_utime.tv_sec * USEC_PER_SEC + usage->ru_utime.tv_usec;
628 }
629 
630 T_DECL(getrusage_thread_terminate_increasing,
631 		"check that getrusage(2) is monotonically increasing, even with threads terminating",
632 		T_META_TAG_VM_PREFERRED)
633 {
634 	const uint64_t test_duration_secs = 2;
635 	uint64_t now_ns = clock_gettime_nsec_np(CLOCK_MONOTONIC);
636 	uint64_t end_ns = now_ns + test_duration_secs * NSEC_PER_SEC;
637 
638 	while (clock_gettime_nsec_np(CLOCK_MONOTONIC) < end_ns) {
639 		pthread_t thread;
640 		struct rusage usage;
641 		uint64_t old_usage_us, new_usage_us;
642 
643 		// Start the thread running and doing work.
644 		pthread_mutex_lock(&_getrusage_thread_state.lock);
645 		pthread_create(&thread, NULL, _thread_spin_and_exit, NULL);
646 		pthread_cond_wait(&_getrusage_thread_state.wait_for_thread,
647 				&_getrusage_thread_state.lock);
648 		pthread_mutex_unlock(&_getrusage_thread_state.lock);
649 
650 		// Gather the current process user and system time accumulation.
651 		T_QUIET; T_ASSERT_POSIX_SUCCESS(getrusage(RUSAGE_SELF, &usage), NULL);
652 		old_usage_us = _rusage_to_time_us(&usage);
653 
654 		// Let the thread terminate.
655 		pthread_cond_signal(&_getrusage_thread_state.wait_for_test);
656 		pthread_mutex_unlock(&_getrusage_thread_state.lock);
657 		pthread_join(thread, NULL);
658 
659 		// Gather the times again, which might have gone backwards if the
660 		// thread's time was temporarily lost due to a race condition in
661 		// getrusage(2).
662 		T_QUIET; T_ASSERT_POSIX_SUCCESS(getrusage(RUSAGE_SELF, &usage), NULL);
663 
664 		new_usage_us = _rusage_to_time_us(&usage);
665 		T_QUIET;
666 		T_ASSERT_GE(new_usage_us, old_usage_us,
667 			"getrusage(2) times were not monotonically increasing");
668 	}
669 
670 	T_PASS("checked getrusage(2) times for %llu second%s while threads terminated",
671 			test_duration_secs, test_duration_secs == 1 ? "" : "s");
672 }
673