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 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)
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,
137 SET_THREAD_BIND_BOOTARG,
138 T_META_ASROOT(true))
139 {
140 unsigned int level_count = perf_level_count();
141 struct thsc_time_energy_cpi *before = calloc(level_count, sizeof(*before));
142 struct thsc_time_energy_cpi *after = calloc(level_count, sizeof(*after));
143
144 run_on_all_perf_levels();
145
146 int err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, before,
147 level_count * sizeof(*before));
148 T_ASSERT_POSIX_ZERO(err,
149 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
150
151 run_on_all_perf_levels();
152
153 err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, after,
154 level_count * sizeof(*after));
155 T_ASSERT_POSIX_ZERO(err,
156 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
157
158 for (unsigned int i = 0; i < level_count; i++) {
159 _check_usage(&before[i], &after[i], perf_level_name(i));
160 }
161
162 free(before);
163 free(after);
164 }
165
166 static void
_expect_counts_on_perf_level(const char * name,struct thsc_time_energy_cpi * before,struct thsc_time_energy_cpi * after)167 _expect_counts_on_perf_level(const char *name,
168 struct thsc_time_energy_cpi *before,
169 struct thsc_time_energy_cpi *after)
170 {
171 unsigned int level_count = perf_level_count();
172 int err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, before,
173 level_count * sizeof(*before));
174 T_ASSERT_POSIX_ZERO(err,
175 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
176 (void)getppid();
177 err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, after,
178 level_count * sizeof(*after));
179 T_ASSERT_POSIX_ZERO(err,
180 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
181
182 unsigned int index = perf_level_index(name);
183 _check_usage(&before[index], &after[index], name);
184 }
185
186 static void
_expect_no_counts_on_perf_level(const char * name,struct thsc_time_energy_cpi * before,struct thsc_time_energy_cpi * after)187 _expect_no_counts_on_perf_level(const char *name,
188 struct thsc_time_energy_cpi *before,
189 struct thsc_time_energy_cpi *after)
190 {
191 unsigned int level_count = perf_level_count();
192 int err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, before,
193 level_count * sizeof(*before));
194 T_ASSERT_POSIX_ZERO(err,
195 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
196 (void)getppid();
197 err = thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, after,
198 level_count * sizeof(*after));
199 T_ASSERT_POSIX_ZERO(err,
200 "thread_selfcounts(THSC_TIME_ENERGY_CPI_PER_PERF_LEVEL, ...)");
201
202 unsigned int index = perf_level_index(name);
203 _check_no_usage(&before[index], &after[index], name);
204 }
205
206 T_DECL(thread_selfcounts_perf_level_correct,
207 "check that runtimes on each perf level match binding request",
208 REQUIRE_RECOUNT_PMCS,
209 REQUIRE_MULTIPLE_PERF_LEVELS,
210 SET_THREAD_BIND_BOOTARG,
211 T_META_ASROOT(true))
212 {
213 unsigned int level_count = perf_level_count();
214 for (unsigned int i = 0; i < level_count; i++) {
215 T_LOG("Level %d: %s", i, perf_level_name(i));
216 }
217
218 struct thsc_time_energy_cpi *before = calloc(level_count, sizeof(*before));
219 struct thsc_time_energy_cpi *after = calloc(level_count, sizeof(*after));
220
221 T_LOG("Binding to Efficiency cluster, should only see counts from E-cores");
222 T_SETUPBEGIN;
223 bind_to_cluster('E');
224 T_SETUPEND;
225 _expect_counts_on_perf_level("Efficiency", before, after);
226 _expect_no_counts_on_perf_level("Performance", before, after);
227
228 T_LOG("Binding to Performance cluster, should only see counts from P-cores");
229 T_SETUPBEGIN;
230 bind_to_cluster('P');
231 T_SETUPEND;
232 _expect_counts_on_perf_level("Performance", before, after);
233 _expect_no_counts_on_perf_level("Efficiency", before, after);
234
235 free(before);
236 free(after);
237 }
238
239 T_DECL(thread_selfcounts_cpi_perf,
240 "test the overhead of thread_selfcounts(2) THSC_CPI", T_META_TAG_PERF,
241 REQUIRE_RECOUNT_PMCS)
242 {
243 struct thsc_cpi counts[2];
244
245 T_SETUPBEGIN;
246 dt_stat_t instrs = dt_stat_create("thread_selfcounts_cpi_instrs",
247 "instructions");
248 dt_stat_t cycles = dt_stat_create("thread_selfcounts_cpi_cycles", "cycles");
249 T_SETUPEND;
250
251 while (!dt_stat_stable(instrs) || !dt_stat_stable(cycles)) {
252 int r1 = thread_selfcounts(THSC_CPI, &counts[0], sizeof(counts[0]));
253 int r2 = thread_selfcounts(THSC_CPI, &counts[1], sizeof(counts[1]));
254 T_QUIET; T_ASSERT_POSIX_ZERO(r1, "thread_selfcounts(THSC_CPI, ...)");
255 T_QUIET; T_ASSERT_POSIX_ZERO(r2, "thread_selfcounts(THSC_CPI, ...)");
256
257 dt_stat_add(instrs, counts[1].tcpi_instructions -
258 counts[0].tcpi_instructions);
259 dt_stat_add(cycles, counts[1].tcpi_cycles - counts[0].tcpi_cycles);
260 }
261
262 dt_stat_finalize(instrs);
263 dt_stat_finalize(cycles);
264 }
265