xref: /xnu-10002.81.5/tests/recount/thread_selfcounts_tests.c (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
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