1 // Copyright (c) 2021-2023 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("cpu counters"),
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 void
_check_no_cpi(struct thsc_cpi * before,struct thsc_cpi * after,const char * name)34 _check_no_cpi(struct thsc_cpi *before, struct thsc_cpi *after, const char *name)
35 {
36 T_EXPECT_EQ(after->tcpi_instructions, before->tcpi_instructions,
37 "%s: instructions should not increase", name);
38 T_EXPECT_EQ(after->tcpi_cycles, before->tcpi_cycles,
39 "%s: cycles should not increase", name);
40 }
41
42 static struct thsc_cpi
_remove_time_from_cpi(struct thsc_time_cpi * time_cpi)43 _remove_time_from_cpi(struct thsc_time_cpi *time_cpi)
44 {
45 return (struct thsc_cpi){
46 .tcpi_instructions = time_cpi->ttci_instructions,
47 .tcpi_cycles = time_cpi->ttci_cycles,
48 };
49 }
50
51 static void
_check_time_cpi(struct thsc_time_cpi * before,struct thsc_time_cpi * after,const char * name)52 _check_time_cpi(struct thsc_time_cpi *before, struct thsc_time_cpi *after,
53 const char *name)
54 {
55 struct thsc_cpi before_cpi = _remove_time_from_cpi(before);
56 struct thsc_cpi after_cpi = _remove_time_from_cpi(after);
57 _check_cpi(&before_cpi, &after_cpi, name);
58
59 T_EXPECT_GT(after->ttci_user_time_mach, before->ttci_user_time_mach,
60 "%s: user time monotonically-increasing", name);
61
62 if (has_user_system_times()) {
63 T_EXPECT_GT(after->ttci_system_time_mach, before->ttci_system_time_mach,
64 "%s: system time monotonically-increasing", name);
65 }
66 }
67
68 static void
_check_no_time_cpi(struct thsc_time_cpi * before,struct thsc_time_cpi * after,const char * name)69 _check_no_time_cpi(struct thsc_time_cpi *before, struct thsc_time_cpi *after,
70 const char *name)
71 {
72 struct thsc_cpi before_cpi = _remove_time_from_cpi(before);
73 struct thsc_cpi after_cpi = _remove_time_from_cpi(after);
74 _check_no_cpi(&before_cpi, &after_cpi, name);
75
76 T_EXPECT_EQ(after->ttci_user_time_mach, before->ttci_user_time_mach,
77 "%s: user time should not change", name);
78
79 if (has_user_system_times()) {
80 T_EXPECT_EQ(after->ttci_system_time_mach, before->ttci_system_time_mach,
81 "%s: system time should not change", name);
82 }
83 }
84
85 static struct thsc_time_cpi
_remove_energy_from_cpi(struct thsc_time_energy_cpi * energy_cpi)86 _remove_energy_from_cpi(struct thsc_time_energy_cpi *energy_cpi)
87 {
88 return (struct thsc_time_cpi){
89 .ttci_instructions = energy_cpi->ttec_instructions,
90 .ttci_cycles = energy_cpi->ttec_cycles,
91 .ttci_system_time_mach = energy_cpi->ttec_system_time_mach,
92 .ttci_user_time_mach = energy_cpi->ttec_user_time_mach,
93 };
94 }
95
96 static void
_check_usage(struct thsc_time_energy_cpi * before,struct thsc_time_energy_cpi * after,const char * name)97 _check_usage(struct thsc_time_energy_cpi *before,
98 struct thsc_time_energy_cpi *after, const char *name)
99 {
100 struct thsc_time_cpi before_time = _remove_energy_from_cpi(before);
101 struct thsc_time_cpi after_time = _remove_energy_from_cpi(after);
102 _check_time_cpi(&before_time, &after_time, name);
103
104 if (has_energy()) {
105 T_EXPECT_GT(after->ttec_energy_nj, UINT64_C(0),
106 "%s: energy monotonically-increasing", name);
107 }
108 }
109
110 static void
_check_no_usage(struct thsc_time_energy_cpi * before,struct thsc_time_energy_cpi * after,const char * name)111 _check_no_usage(struct thsc_time_energy_cpi *before,
112 struct thsc_time_energy_cpi *after, const char *name)
113 {
114 struct thsc_time_cpi before_time = _remove_energy_from_cpi(before);
115 struct thsc_time_cpi after_time = _remove_energy_from_cpi(after);
116 _check_no_time_cpi(&before_time, &after_time, name);
117 }
118
119 T_DECL(thread_selfcounts_cpi_sanity, "check the current thread's CPI",
120 REQUIRE_RECOUNT_PMCS, T_META_TAG_VM_NOT_ELIGIBLE)
121 {
122 int err;
123 struct thsc_cpi counts[2] = { 0 };
124
125 err = thread_selfcounts(THSC_CPI, &counts[0], sizeof(counts[0]));
126 T_ASSERT_POSIX_ZERO(err, "thread_selfcounts(THSC_CPI, ...)");
127 err = thread_selfcounts(THSC_CPI, &counts[1], sizeof(counts[1]));
128 T_ASSERT_POSIX_ZERO(err, "thread_selfcounts(THSC_CPI, ...)");
129
130 _check_cpi(&counts[0], &counts[1], "anywhere");
131 }
132
133 T_DECL(thread_selfcounts_perf_level_sanity,
134 "check per-perf level time, energy, and CPI",
135 REQUIRE_RECOUNT_PMCS,
136 // REQUIRE_MULTIPLE_PERF_LEVELS, disabled due to rdar://111297938
137 SET_THREAD_BIND_BOOTARG,
138 T_META_ASROOT(true), T_META_TAG_VM_NOT_ELIGIBLE)
139 {
140 unsigned int level_count = perf_level_count();
141
142 // Until rdar://111297938, manually skip the test if there aren't multiple perf levels.
143 if (level_count < 2) {
144 T_SKIP("device is not eligible for checking perf levels because it is SMP");
145 }
146 struct thsc_time_energy_cpi *before = calloc(level_count, sizeof(*before));
147 struct thsc_time_energy_cpi *after = calloc(level_count, sizeof(*after));
148
149 run_on_all_perf_levels();
150
151 int err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, before,
152 level_count * sizeof(*before));
153 T_ASSERT_POSIX_ZERO(err,
154 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
155
156 run_on_all_perf_levels();
157
158 err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, after,
159 level_count * sizeof(*after));
160 T_ASSERT_POSIX_ZERO(err,
161 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
162
163 for (unsigned int i = 0; i < level_count; i++) {
164 _check_usage(&before[i], &after[i], perf_level_name(i));
165 }
166
167 free(before);
168 free(after);
169 }
170
171 static void
_expect_counts_on_perf_level(unsigned int perf_level_index,struct thsc_time_energy_cpi * before,struct thsc_time_energy_cpi * after)172 _expect_counts_on_perf_level(unsigned int perf_level_index,
173 struct thsc_time_energy_cpi *before,
174 struct thsc_time_energy_cpi *after)
175 {
176 unsigned int level_count = perf_level_count();
177 int err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, before,
178 level_count * sizeof(*before));
179 T_ASSERT_POSIX_ZERO(err,
180 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
181 (void)getppid();
182 err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, after,
183 level_count * sizeof(*after));
184 T_ASSERT_POSIX_ZERO(err,
185 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
186
187 char *name = perf_level_name(perf_level_index);
188 _check_usage(&before[perf_level_index], &after[perf_level_index], name);
189 }
190
191 static void
_expect_no_counts_on_perf_level(unsigned int perf_level_index,struct thsc_time_energy_cpi * before,struct thsc_time_energy_cpi * after)192 _expect_no_counts_on_perf_level(unsigned int perf_level_index,
193 struct thsc_time_energy_cpi *before,
194 struct thsc_time_energy_cpi *after)
195 {
196 unsigned int level_count = perf_level_count();
197 int err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, before,
198 level_count * sizeof(*before));
199 T_ASSERT_POSIX_ZERO(err,
200 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
201 (void)getppid();
202 err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, after,
203 level_count * sizeof(*after));
204 T_ASSERT_POSIX_ZERO(err,
205 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
206
207 char *name = perf_level_name(perf_level_index);
208 _check_no_usage(&before[perf_level_index], &after[perf_level_index], name);
209 }
210
211 T_DECL(thread_selfcounts_perf_level_correct,
212 "check that runtimes on each perf level match binding request",
213 REQUIRE_RECOUNT_PMCS,
214 // REQUIRE_MULTIPLE_PERF_LEVELS, disabled due to rdar://111297938
215 SET_THREAD_BIND_BOOTARG,
216 T_META_ASROOT(true), T_META_TAG_VM_NOT_ELIGIBLE)
217 {
218 unsigned int level_count = perf_level_count();
219
220 // Until rdar://111297938, manually skip the test if there aren't multiple perf levels.
221 if (level_count < 2) {
222 T_SKIP("device is not eligible for checking perf levels because it is SMP");
223 }
224 for (unsigned int i = 0; i < level_count; i++) {
225 T_LOG("Level %d: %s", i, perf_level_name(i));
226 }
227
228 struct thsc_time_energy_cpi *before = calloc(level_count, sizeof(*before));
229 struct thsc_time_energy_cpi *after = calloc(level_count, sizeof(*after));
230
231 T_LOG("Binding to Efficiency cluster, should only see counts from E-cores");
232 T_SETUPBEGIN;
233 bind_to_cluster('E');
234 T_SETUPEND;
235 _expect_counts_on_perf_level(1, before, after);
236 _expect_no_counts_on_perf_level(0, before, after);
237
238 T_LOG("Binding to Performance cluster, should only see counts from P-cores");
239 T_SETUPBEGIN;
240 bind_to_cluster('P');
241 T_SETUPEND;
242 _expect_counts_on_perf_level(0, before, after);
243 _expect_no_counts_on_perf_level(1, before, after);
244
245 free(before);
246 free(after);
247 }
248
249 T_DECL(thread_selfcounts_cpi_perf,
250 "test the overhead of thread_selfcounts(2) THSC_CPI", T_META_TAG_PERF,
251 REQUIRE_RECOUNT_PMCS, T_META_TAG_VM_NOT_ELIGIBLE)
252 {
253 struct thsc_cpi counts[2];
254
255 T_SETUPBEGIN;
256 dt_stat_t instrs = dt_stat_create("thread_selfcounts_cpi_instrs",
257 "instructions");
258 dt_stat_t cycles = dt_stat_create("thread_selfcounts_cpi_cycles", "cycles");
259 T_SETUPEND;
260
261 while (!dt_stat_stable(instrs) || !dt_stat_stable(cycles)) {
262 int r1 = thread_selfcounts(THSC_CPI, &counts[0], sizeof(counts[0]));
263 int r2 = thread_selfcounts(THSC_CPI, &counts[1], sizeof(counts[1]));
264 T_QUIET; T_ASSERT_POSIX_ZERO(r1, "thread_selfcounts(THSC_CPI, ...)");
265 T_QUIET; T_ASSERT_POSIX_ZERO(r2, "thread_selfcounts(THSC_CPI, ...)");
266
267 dt_stat_add(instrs, counts[1].tcpi_instructions -
268 counts[0].tcpi_instructions);
269 dt_stat_add(cycles, counts[1].tcpi_cycles - counts[0].tcpi_cycles);
270 }
271
272 dt_stat_finalize(instrs);
273 dt_stat_finalize(cycles);
274 }
275