xref: /xnu-8796.101.5/tests/recount/thread_selfcounts_tests.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1 // Copyright (c) 2021 Apple Inc.  All rights reserved.
2 
3 #include <darwintest.h>
4 #include <stdlib.h>
5 #include <sys/resource_private.h>
6 #include <sys/sysctl.h>
7 
8 #include "test_utils.h"
9 #include "recount_test_utils.h"
10 
11 T_GLOBAL_META(
12 	T_META_RADAR_COMPONENT_NAME("xnu"),
13 	T_META_RADAR_COMPONENT_VERSION("RM"),
14     T_META_OWNER("mwidmann"),
15     T_META_CHECK_LEAKS(false));
16 
17 static void
_check_cpi(struct thsc_cpi * before,struct thsc_cpi * after,const char * name)18 _check_cpi(struct thsc_cpi *before, struct thsc_cpi *after, const char *name)
19 {
20 	T_QUIET;
21 	T_EXPECT_GT(before->tcpi_instructions, UINT64_C(0),
22 	    "%s: instructions non-zero", name);
23 	T_QUIET;
24 	T_EXPECT_GT(before->tcpi_cycles, UINT64_C(0), "%s: cycles non-zero",
25 	    name);
26 
27 	T_EXPECT_GT(after->tcpi_instructions, before->tcpi_instructions,
28 	    "%s: instructions monotonically-increasing", name);
29 	T_EXPECT_GT(after->tcpi_cycles, before->tcpi_cycles,
30 	    "%s: cycles monotonically-increasing", name);
31 }
32 
33 static struct thsc_cpi
_remove_time_from_cpi(struct thsc_time_cpi * time_cpi)34 _remove_time_from_cpi(struct thsc_time_cpi *time_cpi)
35 {
36 	return (struct thsc_cpi){
37 		.tcpi_instructions = time_cpi->ttci_instructions,
38 		.tcpi_cycles = time_cpi->ttci_cycles,
39 	};
40 }
41 
42 static void
_check_time_cpi(struct thsc_time_cpi * before,struct thsc_time_cpi * after,const char * name)43 _check_time_cpi(struct thsc_time_cpi *before, struct thsc_time_cpi *after,
44     const char *name)
45 {
46 	struct thsc_cpi before_cpi = _remove_time_from_cpi(before);
47 	struct thsc_cpi after_cpi = _remove_time_from_cpi(after);
48 	_check_cpi(&before_cpi, &after_cpi, name);
49 
50 	T_EXPECT_GT(after->ttci_user_time_mach, before->ttci_user_time_mach,
51 			"%s: user time monotonically-increasing", name);
52 
53 	if (has_user_system_times()) {
54 		T_EXPECT_GT(after->ttci_system_time_mach, before->ttci_system_time_mach,
55 				"%s: system time monotonically-increasing", name);
56 	}
57 }
58 
59 static struct thsc_time_cpi
_remove_energy_from_cpi(struct thsc_time_energy_cpi * energy_cpi)60 _remove_energy_from_cpi(struct thsc_time_energy_cpi *energy_cpi)
61 {
62 	return (struct thsc_time_cpi){
63 		.ttci_instructions = energy_cpi->ttec_instructions,
64 		.ttci_cycles = energy_cpi->ttec_cycles,
65 		.ttci_system_time_mach = energy_cpi->ttec_system_time_mach,
66 		.ttci_user_time_mach = energy_cpi->ttec_user_time_mach,
67 	};
68 }
69 
70 static void
_check_usage(struct thsc_time_energy_cpi * before,struct thsc_time_energy_cpi * after,const char * name)71 _check_usage(struct thsc_time_energy_cpi *before,
72     struct thsc_time_energy_cpi *after, const char *name)
73 {
74 	struct thsc_time_cpi before_time = _remove_energy_from_cpi(before);
75 	struct thsc_time_cpi after_time = _remove_energy_from_cpi(after);
76 	_check_time_cpi(&before_time, &after_time, name);
77 
78 	if (has_energy()) {
79 		T_EXPECT_GT(after->ttec_energy_nj, UINT64_C(0),
80 				"%s: energy monotonically-increasing", name);
81 	}
82 }
83 
84 T_DECL(thread_selfcounts_cpi_sanity, "check the current thread's CPI",
85     REQUIRE_RECOUNT_PMCS)
86 {
87 	int err;
88 	struct thsc_cpi counts[2] = { 0 };
89 
90 	err = thread_selfcounts(THSC_CPI, &counts[0], sizeof(counts[0]));
91 	T_ASSERT_POSIX_ZERO(err, "thread_selfcounts(THSC_CPI, ...)");
92 	err = thread_selfcounts(THSC_CPI, &counts[1], sizeof(counts[1]));
93 	T_ASSERT_POSIX_ZERO(err, "thread_selfcounts(THSC_CPI, ...)");
94 
95 	_check_cpi(&counts[0], &counts[1], "anywhere");
96 }
97 
98 T_DECL(thread_selfcounts_perf_level_sanity,
99     "check per-perf level time, energy, and CPI",
100     REQUIRE_RECOUNT_PMCS,
101     REQUIRE_MULTIPLE_PERF_LEVELS,
102     SET_THREAD_BIND_BOOTARG,
103     T_META_ASROOT(true))
104 {
105 	unsigned int level_count = perf_level_count();
106 	struct thsc_time_energy_cpi *before = calloc(level_count, sizeof(*before));
107 	struct thsc_time_energy_cpi *after = calloc(level_count, sizeof(*after));
108 
109 	run_on_all_perf_levels();
110 
111 	int err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, before,
112 			level_count * sizeof(*before));
113 	T_ASSERT_POSIX_ZERO(err,
114 			"thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
115 
116 	run_on_all_perf_levels();
117 
118 	err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, after,
119 			level_count * sizeof(*after));
120 	T_ASSERT_POSIX_ZERO(err,
121 			"thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
122 
123 	for (unsigned int i = 0; i < level_count; i++) {
124 		_check_usage(&before[i], &after[i], perf_level_name(i));
125 	}
126 
127 	free(before);
128 	free(after);
129 }
130 
131 T_DECL(thread_selfcounts_cpi_perf,
132     "test the overhead of thread_selfcounts(2) THSC_CPI", T_META_TAG_PERF,
133     REQUIRE_RECOUNT_PMCS)
134 {
135 	struct thsc_cpi counts[2];
136 
137 	T_SETUPBEGIN;
138 	dt_stat_t instrs = dt_stat_create("thread_selfcounts_cpi_instrs",
139 			"instructions");
140 	dt_stat_t cycles = dt_stat_create("thread_selfcounts_cpi_cycles", "cycles");
141 	T_SETUPEND;
142 
143 	while (!dt_stat_stable(instrs) || !dt_stat_stable(cycles)) {
144 		int r1 = thread_selfcounts(THSC_CPI, &counts[0], sizeof(counts[0]));
145 		int r2 = thread_selfcounts(THSC_CPI, &counts[1], sizeof(counts[1]));
146 		T_QUIET; T_ASSERT_POSIX_ZERO(r1, "thread_selfcounts(THSC_CPI, ...)");
147 		T_QUIET; T_ASSERT_POSIX_ZERO(r2, "thread_selfcounts(THSC_CPI, ...)");
148 
149 		dt_stat_add(instrs, counts[1].tcpi_instructions -
150 				counts[0].tcpi_instructions);
151 		dt_stat_add(cycles, counts[1].tcpi_cycles - counts[0].tcpi_cycles);
152 	}
153 
154 	dt_stat_finalize(instrs);
155 	dt_stat_finalize(cycles);
156 }
157