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, disabled due to rdar://111297938
312 SET_THREAD_BIND_BOOTARG, T_META_TAG_VM_NOT_ELIGIBLE)
313 {
314 struct rusage_info_v6 before = { 0 };
315 struct rusage_info_v6 after = { 0 };
316
317 // Until rdar://111297938, manually skip the test if there aren't multiple perf levels.
318 if (perf_level_count() < 2) {
319 T_SKIP("device is not eligible for checking perf levels because it is SMP");
320 }
321
322 _get_proc_pid_rusage(getpid(), &before);
323 run_on_all_perf_levels();
324 _get_proc_pid_rusage(getpid(), &after);
325
326 T_EXPECT_GE(after.ri_cycles, before.ri_cycles, "cycles increasing");
327 T_EXPECT_GE(after.ri_instructions, before.ri_instructions,
328 "instructions increasing");
329 T_EXPECT_GE(after.ri_user_time, before.ri_user_time,
330 "user_time increasing");
331 T_EXPECT_GE(after.ri_system_time, before.ri_system_time,
332 "system_time increasing");
333
334 T_EXPECT_GE(after.ri_pcycles, before.ri_pcycles, "cycles_p increasing");
335 T_EXPECT_GE(after.ri_pinstructions, before.ri_pinstructions,
336 "instructions_p increasing");
337 T_EXPECT_GE(after.ri_user_ptime, before.ri_user_ptime,
338 "user_time_p increasing");
339 T_EXPECT_GE(after.ri_system_ptime, before.ri_system_ptime,
340 "system_time_p increasing");
341
342 if (has_energy()) {
343 T_EXPECT_GE(after.ri_energy_nj, before.ri_energy_nj,
344 "energy_nj increasing");
345 T_EXPECT_GE(after.ri_penergy_nj, before.ri_penergy_nj,
346 "penergy_nj increasing");
347 }
348 }
349
350 T_DECL(proc_pid_rusage_secure_perf_levels,
351 "ensure proc_pid_rusage fills in per-perf level information",
352 REQUIRE_RECOUNT_PMCS,
353 REQUIRE_MULTIPLE_PERF_LEVELS,
354 REQUIRE_EXCLAVES,
355 SET_THREAD_BIND_BOOTARG,
356 T_META_TAG_VM_PREFERRED)
357 {
358 int status = 0;
359 size_t status_size = sizeof(status);
360 (void)sysctlbyname("kern.exclaves_status", &status, &status_size, NULL, 0);
361 if (status != 1) {
362 T_SKIP("exclaves must be supported");
363 }
364
365 struct rusage_info_v6 before = { 0 };
366 struct rusage_info_v6 after = { 0 };
367
368 _get_proc_pid_rusage(getpid(), &before);
369 run_in_exclaves_on_all_perf_levels();
370 _get_proc_pid_rusage(getpid(), &after);
371
372 T_EXPECT_GT(after.ri_secure_time_in_system, 0ULL,
373 "secure time after running in exclaves is non-zero");
374 T_EXPECT_GT(after.ri_secure_time_in_system, 0ULL,
375 "secure time on P-cores after running in exclaves is non-zero");
376
377 T_EXPECT_GT(after.ri_secure_time_in_system, before.ri_secure_time_in_system,
378 "secure time in system increasing");
379 T_EXPECT_GT(after.ri_secure_ptime_in_system,
380 before.ri_secure_ptime_in_system,
381 "secure time in system on P-cores increasing");
382
383 uint64_t system_time_delta = after.ri_system_time - before.ri_system_time;
384 uint64_t secure_time_delta = after.ri_secure_time_in_system -
385 before.ri_secure_time_in_system;
386 T_EXPECT_LE(secure_time_delta, system_time_delta,
387 "secure time is less than system time");
388 uint64_t system_ptime_delta = after.ri_system_ptime -
389 before.ri_system_ptime;
390 uint64_t secure_ptime_delta = after.ri_secure_ptime_in_system -
391 before.ri_secure_ptime_in_system;
392 T_EXPECT_LE(secure_ptime_delta, system_ptime_delta,
393 "secure time is less than system time on P-cores");
394 }
395
396 static void
_proc_pidthreadcounts_increasing(struct proc_threadcounts_data * before,struct proc_threadcounts_data * after,const char * perf_level)397 _proc_pidthreadcounts_increasing(struct proc_threadcounts_data *before,
398 struct proc_threadcounts_data *after, const char *perf_level)
399 {
400 const char *name = "PROC_PIDTHREADCOUNTS";
401 T_LOG("%s %s before: usr = %llu, sys = %llu, instrs = %llu, cycles = %llu, "
402 "energy = %llu", name, perf_level, before->ptcd_user_time_mach,
403 before->ptcd_system_time_mach, before->ptcd_instructions,
404 before->ptcd_cycles, before->ptcd_energy_nj);
405 T_LOG("%s %s after: usr = %llu, sys = %llu, instrs = %llu, cycles = %llu, "
406 "energy = %llu", name, perf_level, after->ptcd_user_time_mach,
407 after->ptcd_system_time_mach, after->ptcd_instructions,
408 after->ptcd_cycles, after->ptcd_energy_nj);
409
410 T_EXPECT_NE(before->ptcd_user_time_mach, 0ULL,
411 "%s user time should be non-zero", perf_level);
412 T_EXPECT_NE(before->ptcd_system_time_mach, 0ULL,
413 "%s system time should be non-zero", perf_level);
414 T_EXPECT_NE(before->ptcd_instructions, 0ULL,
415 "%s instructions should be non-zero", perf_level);
416 T_EXPECT_NE(before->ptcd_cycles, 0ULL,
417 "%s cycles should be non-zero", perf_level);
418
419 T_EXPECT_GT(after->ptcd_user_time_mach, before->ptcd_user_time_mach,
420 "%s user time should increase", perf_level);
421 T_EXPECT_GT(after->ptcd_system_time_mach, before->ptcd_system_time_mach,
422 "%s system time should increase", perf_level);
423 T_EXPECT_GT(after->ptcd_instructions, before->ptcd_instructions,
424 "%s instructions should increase", perf_level);
425 T_EXPECT_GT(after->ptcd_cycles, before->ptcd_cycles,
426 "%s cycles should increase", perf_level);
427
428 if (has_energy()) {
429 T_EXPECT_GT(after->ptcd_energy_nj, before->ptcd_energy_nj,
430 "%s energy should increase", perf_level);
431 }
432 }
433
434 static void
_threadcounts_to_rusage_info(struct proc_threadcounts_data * counts,struct rusage_info_v6 * info)435 _threadcounts_to_rusage_info(struct proc_threadcounts_data *counts,
436 struct rusage_info_v6 *info)
437 {
438 unsigned int level_count = perf_level_count();
439 for (unsigned int i = 0; i < level_count; i++) {
440 struct proc_threadcounts_data *count = &counts[i];
441 if (perf_level_name(i)[0] == 'P') {
442 info->ri_system_ptime += count->ptcd_system_time_mach;
443 info->ri_user_ptime += count->ptcd_user_time_mach;
444 info->ri_pinstructions += count->ptcd_instructions;
445 info->ri_pcycles += count->ptcd_cycles;
446 }
447 info->ri_system_time += count->ptcd_system_time_mach;
448 info->ri_user_time += count->ptcd_user_time_mach;
449 info->ri_instructions += count->ptcd_instructions;
450 info->ri_cycles += count->ptcd_cycles;
451 }
452 }
453
454 static void
_rusage_info_le(struct rusage_info_v6 * lhs,const char * lhs_name,struct rusage_info_v6 * rhs,const char * rhs_name)455 _rusage_info_le(struct rusage_info_v6 *lhs, const char *lhs_name,
456 struct rusage_info_v6 *rhs, const char *rhs_name)
457 {
458 T_EXPECT_LE(lhs->ri_user_time, rhs->ri_user_time,
459 "%s user time <= %s", lhs_name, rhs_name);
460 T_EXPECT_LE(lhs->ri_system_time, rhs->ri_system_time,
461 "%s system time <= %s", lhs_name, rhs_name);
462 T_EXPECT_LE(lhs->ri_instructions, rhs->ri_instructions,
463 "%s instructions <= %s", lhs_name, rhs_name);
464 T_EXPECT_LE(lhs->ri_cycles, rhs->ri_cycles,
465 "%s cycles <= %s", lhs_name, rhs_name);
466 T_EXPECT_LE(lhs->ri_energy_nj, rhs->ri_energy_nj,
467 "%s energy <= %s", lhs_name, rhs_name);
468
469 T_EXPECT_LE(lhs->ri_user_ptime, rhs->ri_user_ptime,
470 "%s P-core user time <= %s", lhs_name, rhs_name);
471 T_EXPECT_LE(lhs->ri_system_ptime, rhs->ri_system_ptime,
472 "%s P-core system time <= %s", lhs_name, rhs_name);
473 T_EXPECT_LE(lhs->ri_pinstructions, rhs->ri_pinstructions,
474 "%s P-core instructions <= %s", lhs_name, rhs_name);
475 T_EXPECT_LE(lhs->ri_pcycles, rhs->ri_pcycles,
476 "%s P-core cycles <= %s", lhs_name, rhs_name);
477 T_EXPECT_LE(lhs->ri_penergy_nj, rhs->ri_penergy_nj,
478 "%s energy <= %s", lhs_name, rhs_name);
479 }
480
481 struct thread_sequence {
482 dispatch_semaphore_t child_sema;
483 dispatch_semaphore_t parent_sema;
484 };
485
486 static void *
_thread_runs_on_perf_levels(void * vsequence)487 _thread_runs_on_perf_levels(void *vsequence)
488 {
489 struct thread_sequence *seq = vsequence;
490
491 run_on_all_perf_levels();
492 dispatch_semaphore_signal(seq->parent_sema);
493 dispatch_semaphore_wait(seq->child_sema, DISPATCH_TIME_FOREVER);
494
495 run_on_all_perf_levels();
496 dispatch_semaphore_signal(seq->parent_sema);
497 dispatch_semaphore_wait(seq->child_sema, DISPATCH_TIME_FOREVER);
498 return NULL;
499 }
500
501 T_DECL(proc_pidthreadcounts_sanity,
502 "check per-perf level time and CPI from proc_pidthreadcounts",
503 REQUIRE_RECOUNT_PMCS,
504 SET_THREAD_BIND_BOOTARG,
505 // Select the most comprehensive test to run on each SoC.
506 XNU_T_META_SOC_SPECIFIC,
507 T_META_ASROOT(true),
508 T_META_TAG_VM_NOT_ELIGIBLE)
509 {
510 T_SETUPBEGIN;
511
512 unsigned int level_count = perf_level_count();
513 T_LOG("found %u perf levels", level_count);
514 int counts_size = (int)sizeof(struct proc_threadcounts) +
515 (int)level_count * (int)sizeof(struct proc_threadcounts_data);
516 struct proc_threadcounts *before = malloc((unsigned int)counts_size);
517 T_QUIET; T_ASSERT_NOTNULL(before, "allocate before counts");
518 memset(before, 0, counts_size);
519 struct proc_threadcounts *after = malloc((unsigned int)counts_size);
520 T_QUIET; T_ASSERT_NOTNULL(before, "allocate after counts");
521 memset(after, 0, counts_size);
522 pthread_t target_thread = NULL;
523 uint64_t target_tid = 0;
524
525 struct thread_sequence seq = {
526 .parent_sema = dispatch_semaphore_create(0),
527 .child_sema = dispatch_semaphore_create(0),
528 };
529 int error = pthread_create(&target_thread, NULL,
530 _thread_runs_on_perf_levels, &seq);
531 T_QUIET; T_ASSERT_POSIX_ZERO(error, "pthread_create");
532 error = pthread_threadid_np(target_thread, &target_tid);
533 T_QUIET; T_ASSERT_POSIX_ZERO(error, "pthread_threadid_np");
534 T_LOG("created thread to run on all perf levels with ID %" PRIx64,
535 target_tid);
536
537 dispatch_semaphore_wait(seq.parent_sema, DISPATCH_TIME_FOREVER);
538
539 T_SETUPEND;
540
541 int size = proc_pidinfo(getpid(), PROC_PIDTHREADCOUNTS, target_tid, before,
542 counts_size);
543 T_WITH_ERRNO;
544 T_ASSERT_EQ(size, counts_size,
545 "proc_pidinfo(..., PROC_PIDTHREADCOUNTS, ...)");
546
547 dispatch_semaphore_signal(seq.child_sema);
548 dispatch_semaphore_wait(seq.parent_sema, DISPATCH_TIME_FOREVER);
549
550 size = proc_pidinfo(getpid(), PROC_PIDTHREADCOUNTS, target_tid, after,
551 counts_size);
552 T_WITH_ERRNO;
553 T_ASSERT_EQ(size, counts_size,
554 "proc_pidinfo(..., PROC_PIDTHREADCOUNTS, ...)");
555
556 struct rusage_info_v6 proc_usage = { 0 };
557 _get_proc_pid_rusage(getpid(), &proc_usage);
558
559
560 dispatch_semaphore_signal(seq.child_sema);
561
562 for (unsigned int i = 0; i < level_count; i++) {
563 _proc_pidthreadcounts_increasing(&before->ptc_counts[i],
564 &after->ptc_counts[i], perf_level_name(i));
565 }
566 struct rusage_info_v6 thread_usage = { 0 };
567 _threadcounts_to_rusage_info(after->ptc_counts, &thread_usage);
568 _rusage_info_le(&thread_usage, "thread", &proc_usage, "process");
569
570 (void)pthread_join(target_thread, NULL);
571 free(before);
572 free(after);
573 }
574
575 T_DECL(proc_pidthreadcounts_invalid_tid,
576 "check that proc_pidthreadcounts returns ESRCH on invalid thread",
577 REQUIRE_RECOUNT_PMCS,
578 T_META_ASROOT(true),
579 T_META_TAG_VM_PREFERRED)
580 {
581 T_SETUPBEGIN;
582 unsigned int level_count = perf_level_count();
583 int counts_size = (int)sizeof(struct proc_threadcounts) +
584 (int)level_count * (int)sizeof(struct proc_threadcounts_data);
585 struct proc_threadcounts *counts = malloc((unsigned int)counts_size);
586 T_QUIET; T_ASSERT_NOTNULL(counts, "allocate counts");
587 T_SETUPEND;
588
589 // proc_pidinfo has a unique return value protocol: it returns the size
590 // that was copied out and 0 if an error occurs, with errno set.
591 int size = proc_pidinfo(getpid(), PROC_PIDTHREADCOUNTS, UINT64_MAX, counts,
592 counts_size);
593 T_ASSERT_EQ(size, 0,
594 "proc_pidinfo(..., PROC_PIDTHREADCOUNTS, UINT64_MAX, ...) should "
595 "fail");
596 T_ASSERT_EQ(errno, ESRCH, "should fail with ESRCH");
597 }
598