xref: /xnu-12377.1.9/tests/cpu_counters/kpc_tests.c (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
1*f6217f89SApple OSS Distributions // Copyright (c) 2018-2023 Apple Inc.  All rights reserved.
2*f6217f89SApple OSS Distributions 
3*f6217f89SApple OSS Distributions #include <darwintest.h>
4*f6217f89SApple OSS Distributions #include <ktrace/config.h>
5*f6217f89SApple OSS Distributions #include <ktrace/session.h>
6*f6217f89SApple OSS Distributions #include <inttypes.h>
7*f6217f89SApple OSS Distributions #include <libproc.h>
8*f6217f89SApple OSS Distributions #include <pthread.h>
9*f6217f89SApple OSS Distributions #include <stdint.h>
10*f6217f89SApple OSS Distributions #include <sys/resource.h>
11*f6217f89SApple OSS Distributions #include <sys/sysctl.h>
12*f6217f89SApple OSS Distributions 
13*f6217f89SApple OSS Distributions #include <kperf/kpc.h>
14*f6217f89SApple OSS Distributions #include <kperf/kperf.h>
15*f6217f89SApple OSS Distributions #include <kperfdata/kpep.h>
16*f6217f89SApple OSS Distributions 
17*f6217f89SApple OSS Distributions #include "ktrace_helpers.h"
18*f6217f89SApple OSS Distributions #include "kperf_helpers.h"
19*f6217f89SApple OSS Distributions #include "test_utils.h"
20*f6217f89SApple OSS Distributions 
21*f6217f89SApple OSS Distributions T_GLOBAL_META(
22*f6217f89SApple OSS Distributions 	T_META_NAMESPACE("xnu.cpu_counters"),
23*f6217f89SApple OSS Distributions 	T_META_RADAR_COMPONENT_NAME("xnu"),
24*f6217f89SApple OSS Distributions 	T_META_RADAR_COMPONENT_VERSION("cpu counters"),
25*f6217f89SApple OSS Distributions 	T_META_OWNER("mwidmann"),
26*f6217f89SApple OSS Distributions 	T_META_ASROOT(true),
27*f6217f89SApple OSS Distributions 	T_META_CHECK_LEAKS(false));
28*f6217f89SApple OSS Distributions 
29*f6217f89SApple OSS Distributions struct machine {
30*f6217f89SApple OSS Distributions 	unsigned int ncpus;
31*f6217f89SApple OSS Distributions 	unsigned int nfixed;
32*f6217f89SApple OSS Distributions 	unsigned int nconfig;
33*f6217f89SApple OSS Distributions 	uint64_t selector;
34*f6217f89SApple OSS Distributions };
35*f6217f89SApple OSS Distributions 
36*f6217f89SApple OSS Distributions #ifndef ABSV64
37*f6217f89SApple OSS Distributions #define ABSV64(n) ((((int64_t)(n)) < 0) ? -((int64_t)(n)) : ((int64_t)(n)))
38*f6217f89SApple OSS Distributions #endif
39*f6217f89SApple OSS Distributions 
40*f6217f89SApple OSS Distributions static void
skip_if_unsupported(void)41*f6217f89SApple OSS Distributions skip_if_unsupported(void)
42*f6217f89SApple OSS Distributions {
43*f6217f89SApple OSS Distributions 	int r;
44*f6217f89SApple OSS Distributions 	int supported = 0;
45*f6217f89SApple OSS Distributions 	size_t supported_size = sizeof(supported);
46*f6217f89SApple OSS Distributions 
47*f6217f89SApple OSS Distributions 	r = sysctlbyname("kern.monotonic.supported", &supported, &supported_size,
48*f6217f89SApple OSS Distributions 	    NULL, 0);
49*f6217f89SApple OSS Distributions 	if (r < 0) {
50*f6217f89SApple OSS Distributions 		T_WITH_ERRNO;
51*f6217f89SApple OSS Distributions 		T_SKIP("could not find \"kern.monotonic.supported\" sysctl");
52*f6217f89SApple OSS Distributions 	}
53*f6217f89SApple OSS Distributions 
54*f6217f89SApple OSS Distributions 	if (!supported) {
55*f6217f89SApple OSS Distributions 		T_SKIP("PMCs are not supported on this platform");
56*f6217f89SApple OSS Distributions 	}
57*f6217f89SApple OSS Distributions }
58*f6217f89SApple OSS Distributions 
59*f6217f89SApple OSS Distributions static struct rusage_info_v4 pre_ru = {};
60*f6217f89SApple OSS Distributions 
61*f6217f89SApple OSS Distributions static void
start_kpc(void)62*f6217f89SApple OSS Distributions start_kpc(void)
63*f6217f89SApple OSS Distributions {
64*f6217f89SApple OSS Distributions 	T_SETUPBEGIN;
65*f6217f89SApple OSS Distributions 
66*f6217f89SApple OSS Distributions 	kpc_classmask_t classes = KPC_CLASS_FIXED_MASK |
67*f6217f89SApple OSS Distributions 	    KPC_CLASS_CONFIGURABLE_MASK;
68*f6217f89SApple OSS Distributions 	int ret = kpc_set_counting(classes);
69*f6217f89SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "started counting");
70*f6217f89SApple OSS Distributions 
71*f6217f89SApple OSS Distributions 	ret = proc_pid_rusage(getpid(), RUSAGE_INFO_V4, (rusage_info_t *)&pre_ru);
72*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "got rusage information");
73*f6217f89SApple OSS Distributions 
74*f6217f89SApple OSS Distributions 	kpc_classmask_t classes_on = kpc_get_counting();
75*f6217f89SApple OSS Distributions 	T_QUIET;
76*f6217f89SApple OSS Distributions 	T_ASSERT_EQ(classes, classes_on, "classes counting is correct");
77*f6217f89SApple OSS Distributions 
78*f6217f89SApple OSS Distributions 	T_SETUPEND;
79*f6217f89SApple OSS Distributions }
80*f6217f89SApple OSS Distributions 
81*f6217f89SApple OSS Distributions static void kpc_reset_atend(void);
82*f6217f89SApple OSS Distributions 
83*f6217f89SApple OSS Distributions static void
_assert_kpep_ok(int kpep_err,const char * fmt,...)84*f6217f89SApple OSS Distributions _assert_kpep_ok(int kpep_err, const char *fmt, ...)
85*f6217f89SApple OSS Distributions {
86*f6217f89SApple OSS Distributions 	char msg[1024] = "";
87*f6217f89SApple OSS Distributions 	va_list args;
88*f6217f89SApple OSS Distributions 	va_start(args, fmt);
89*f6217f89SApple OSS Distributions 	vsnprintf(msg, sizeof(msg), fmt, args);
90*f6217f89SApple OSS Distributions 	va_end(args);
91*f6217f89SApple OSS Distributions 	T_QUIET;
92*f6217f89SApple OSS Distributions 	T_ASSERT_EQ(kpep_err, KPEP_ERR_NONE, "%s: %s", msg, kpep_strerror(kpep_err));
93*f6217f89SApple OSS Distributions }
94*f6217f89SApple OSS Distributions 
95*f6217f89SApple OSS Distributions static void
prepare_kpc(struct machine * mch,unsigned int n,const char * event_name,uint64_t period)96*f6217f89SApple OSS Distributions prepare_kpc(struct machine *mch, unsigned int n, const char *event_name,
97*f6217f89SApple OSS Distributions     uint64_t period)
98*f6217f89SApple OSS Distributions {
99*f6217f89SApple OSS Distributions 	T_SETUPBEGIN;
100*f6217f89SApple OSS Distributions 
101*f6217f89SApple OSS Distributions 	T_ATEND(kpc_reset_atend);
102*f6217f89SApple OSS Distributions 
103*f6217f89SApple OSS Distributions 	kpep_db_t db = NULL;
104*f6217f89SApple OSS Distributions 	int ret = kpep_db_create(NULL, &db);
105*f6217f89SApple OSS Distributions 	_assert_kpep_ok(ret, "get kpep database");
106*f6217f89SApple OSS Distributions 	kpep_config_t config = NULL;
107*f6217f89SApple OSS Distributions 	ret = kpep_config_create(db, &config);
108*f6217f89SApple OSS Distributions 	_assert_kpep_ok(ret, "creating event configuration");
109*f6217f89SApple OSS Distributions 	ret = kpep_config_force_counters(config);
110*f6217f89SApple OSS Distributions 	_assert_kpep_ok(ret, "forcing counters with configuration");
111*f6217f89SApple OSS Distributions 	kpep_event_t event = NULL;
112*f6217f89SApple OSS Distributions 	ret = kpep_db_event(db, event_name, &event);
113*f6217f89SApple OSS Distributions 	_assert_kpep_ok(ret, "finding event named %s", event_name);
114*f6217f89SApple OSS Distributions 
115*f6217f89SApple OSS Distributions 	size_t ncpus_sz = sizeof(mch->ncpus);
116*f6217f89SApple OSS Distributions 	ret = sysctlbyname("hw.logicalcpu_max", &mch->ncpus, &ncpus_sz,
117*f6217f89SApple OSS Distributions 	    NULL, 0);
118*f6217f89SApple OSS Distributions 	T_QUIET;
119*f6217f89SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname(hw.logicalcpu_max)");
120*f6217f89SApple OSS Distributions 	T_QUIET;
121*f6217f89SApple OSS Distributions 	T_ASSERT_GT(mch->ncpus, 0, "must have some number of CPUs");
122*f6217f89SApple OSS Distributions 
123*f6217f89SApple OSS Distributions 	ret = kpc_force_all_ctrs_set(1);
124*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kpc_force_all_ctrs_set(1)");
125*f6217f89SApple OSS Distributions 
126*f6217f89SApple OSS Distributions 	int forcing = 0;
127*f6217f89SApple OSS Distributions 	ret = kpc_force_all_ctrs_get(&forcing);
128*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kpc_force_all_ctrs_get");
129*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_EQ(forcing, 1, "counters must be forced");
130*f6217f89SApple OSS Distributions 
131*f6217f89SApple OSS Distributions 	mch->nfixed = kpc_get_counter_count(KPC_CLASS_FIXED_MASK);
132*f6217f89SApple OSS Distributions 	mch->nconfig = kpc_get_counter_count(KPC_CLASS_CONFIGURABLE_MASK);
133*f6217f89SApple OSS Distributions 
134*f6217f89SApple OSS Distributions 	T_LOG("machine: ncpus = %d, nfixed = %d, nconfig = %d", mch->ncpus,
135*f6217f89SApple OSS Distributions 	    mch->nfixed, mch->nconfig);
136*f6217f89SApple OSS Distributions 
137*f6217f89SApple OSS Distributions 	uint32_t nconfigs = kpc_get_config_count(KPC_CLASS_CONFIGURABLE_MASK);
138*f6217f89SApple OSS Distributions 	for (uint32_t i = 0; i < nconfigs; i++) {
139*f6217f89SApple OSS Distributions 		if (period != 0 && (n == 0 || i == 0)) {
140*f6217f89SApple OSS Distributions 			ret = kpep_config_add_event_trigger(config, &event, 0,
141*f6217f89SApple OSS Distributions 			    period + i * 1000, NULL);
142*f6217f89SApple OSS Distributions 		} else {
143*f6217f89SApple OSS Distributions 			ret = kpep_config_add_event(config, &event, 0, NULL);
144*f6217f89SApple OSS Distributions 		}
145*f6217f89SApple OSS Distributions 		if (ret == KPEP_ERR_CONFIG_CONFLICT) {
146*f6217f89SApple OSS Distributions 			T_LOG("configured %d counters with %s", i, event_name);
147*f6217f89SApple OSS Distributions 			break;
148*f6217f89SApple OSS Distributions 		}
149*f6217f89SApple OSS Distributions 		_assert_kpep_ok(ret, "adding %d event %s to configuration", i,
150*f6217f89SApple OSS Distributions 		    event_name);
151*f6217f89SApple OSS Distributions 	}
152*f6217f89SApple OSS Distributions 
153*f6217f89SApple OSS Distributions 	uint64_t *configs = calloc(nconfigs, sizeof(*configs));
154*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(configs, "allocated config words");
155*f6217f89SApple OSS Distributions 	ret = kpep_config_kpc(config, configs, nconfigs * sizeof(*configs));
156*f6217f89SApple OSS Distributions 	_assert_kpep_ok(ret, "get kpc configuration");
157*f6217f89SApple OSS Distributions 	for (uint32_t i = 0; i < nconfigs; i++) {
158*f6217f89SApple OSS Distributions 		if (configs[i] != 0) {
159*f6217f89SApple OSS Distributions 			mch->selector = configs[i];
160*f6217f89SApple OSS Distributions 			break;
161*f6217f89SApple OSS Distributions 		}
162*f6217f89SApple OSS Distributions 	}
163*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_NE(mch->selector, 0ULL, "found event selector to check");
164*f6217f89SApple OSS Distributions 	ret = kpc_set_config(KPC_CLASS_CONFIGURABLE_MASK, configs);
165*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kpc_set_config");
166*f6217f89SApple OSS Distributions 
167*f6217f89SApple OSS Distributions 	ret = kpep_config_kpc_periods(config, configs, nconfigs * sizeof(*configs));
168*f6217f89SApple OSS Distributions 	_assert_kpep_ok(ret, "get kpc periods");
169*f6217f89SApple OSS Distributions 	ret = kpc_set_period(KPC_CLASS_CONFIGURABLE_MASK, configs);
170*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kpc_set_period");
171*f6217f89SApple OSS Distributions 
172*f6217f89SApple OSS Distributions 	free(configs);
173*f6217f89SApple OSS Distributions 
174*f6217f89SApple OSS Distributions 	T_SETUPEND;
175*f6217f89SApple OSS Distributions }
176*f6217f89SApple OSS Distributions 
177*f6217f89SApple OSS Distributions static void
kpc_reset_atend(void)178*f6217f89SApple OSS Distributions kpc_reset_atend(void)
179*f6217f89SApple OSS Distributions {
180*f6217f89SApple OSS Distributions 	uint32_t nconfigs = kpc_get_config_count(KPC_CLASS_CONFIGURABLE_MASK);
181*f6217f89SApple OSS Distributions 	uint64_t *configs = calloc(nconfigs, sizeof(*configs));
182*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(configs, "allocated config words");
183*f6217f89SApple OSS Distributions 
184*f6217f89SApple OSS Distributions 	int ret = kpc_set_period(KPC_CLASS_CONFIGURABLE_MASK, configs);
185*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kpc_set_period");
186*f6217f89SApple OSS Distributions 	ret = kpc_set_config(KPC_CLASS_CONFIGURABLE_MASK, configs);
187*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kpc_set_config");
188*f6217f89SApple OSS Distributions 
189*f6217f89SApple OSS Distributions 	free(configs);
190*f6217f89SApple OSS Distributions }
191*f6217f89SApple OSS Distributions 
192*f6217f89SApple OSS Distributions static void *
spin(void * arg)193*f6217f89SApple OSS Distributions spin(void *arg)
194*f6217f89SApple OSS Distributions {
195*f6217f89SApple OSS Distributions 	while (*(volatile int *)arg == 0) {
196*f6217f89SApple OSS Distributions 		;
197*f6217f89SApple OSS Distributions 	}
198*f6217f89SApple OSS Distributions 
199*f6217f89SApple OSS Distributions 	return NULL;
200*f6217f89SApple OSS Distributions }
201*f6217f89SApple OSS Distributions 
202*f6217f89SApple OSS Distributions static pthread_t *
start_threads(const struct machine * mch,void * (* func)(void *),void * arg)203*f6217f89SApple OSS Distributions start_threads(const struct machine *mch, void *(*func)(void *), void *arg)
204*f6217f89SApple OSS Distributions {
205*f6217f89SApple OSS Distributions 	T_SETUPBEGIN;
206*f6217f89SApple OSS Distributions 
207*f6217f89SApple OSS Distributions 	pthread_t *threads = calloc((unsigned int)mch->ncpus,
208*f6217f89SApple OSS Distributions 	    sizeof(*threads));
209*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(threads, "allocated array of threads");
210*f6217f89SApple OSS Distributions 	for (unsigned int i = 0; i < mch->ncpus; i++) {
211*f6217f89SApple OSS Distributions 		int error = pthread_create(&threads[i], NULL, func, arg);
212*f6217f89SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_ZERO(error, "pthread_create");
213*f6217f89SApple OSS Distributions 	}
214*f6217f89SApple OSS Distributions 
215*f6217f89SApple OSS Distributions 	T_SETUPEND;
216*f6217f89SApple OSS Distributions 
217*f6217f89SApple OSS Distributions 	return threads;
218*f6217f89SApple OSS Distributions }
219*f6217f89SApple OSS Distributions 
220*f6217f89SApple OSS Distributions static void
end_threads(const struct machine * mch,pthread_t * threads)221*f6217f89SApple OSS Distributions end_threads(const struct machine *mch, pthread_t *threads)
222*f6217f89SApple OSS Distributions {
223*f6217f89SApple OSS Distributions 	for (unsigned int i = 0; i < mch->ncpus; i++) {
224*f6217f89SApple OSS Distributions 		int error = pthread_join(threads[i], NULL);
225*f6217f89SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_ZERO(error, "joined thread %d", i);
226*f6217f89SApple OSS Distributions 	}
227*f6217f89SApple OSS Distributions 	free(threads);
228*f6217f89SApple OSS Distributions }
229*f6217f89SApple OSS Distributions 
230*f6217f89SApple OSS Distributions struct tally {
231*f6217f89SApple OSS Distributions 	uint64_t firstvalue;
232*f6217f89SApple OSS Distributions 	uint64_t lastvalue;
233*f6217f89SApple OSS Distributions 	uint64_t nchecks;
234*f6217f89SApple OSS Distributions 	uint64_t nzero;
235*f6217f89SApple OSS Distributions 	uint64_t nstuck;
236*f6217f89SApple OSS Distributions 	uint64_t ndecrease;
237*f6217f89SApple OSS Distributions };
238*f6217f89SApple OSS Distributions 
239*f6217f89SApple OSS Distributions static void
check_counters(unsigned int ncpus,unsigned int nctrs,struct tally * tallies,uint64_t * counts)240*f6217f89SApple OSS Distributions check_counters(unsigned int ncpus, unsigned int nctrs, struct tally *tallies,
241*f6217f89SApple OSS Distributions 		uint64_t *counts)
242*f6217f89SApple OSS Distributions {
243*f6217f89SApple OSS Distributions 	for (unsigned int i = 0; i < ncpus; i++) {
244*f6217f89SApple OSS Distributions 		for (unsigned int j = 0; j < nctrs; j++) {
245*f6217f89SApple OSS Distributions 			unsigned int ctr = i * nctrs + j;
246*f6217f89SApple OSS Distributions 			struct tally *tly = &tallies[ctr];
247*f6217f89SApple OSS Distributions 			uint64_t count = counts[ctr];
248*f6217f89SApple OSS Distributions 
249*f6217f89SApple OSS Distributions 			if (counts[ctr] == 0) {
250*f6217f89SApple OSS Distributions 				tly->nzero++;
251*f6217f89SApple OSS Distributions 			}
252*f6217f89SApple OSS Distributions 			if (tly->lastvalue == count) {
253*f6217f89SApple OSS Distributions 				tly->nstuck++;
254*f6217f89SApple OSS Distributions 			}
255*f6217f89SApple OSS Distributions 			if (tly->lastvalue > count) {
256*f6217f89SApple OSS Distributions 				tly->ndecrease++;
257*f6217f89SApple OSS Distributions 			}
258*f6217f89SApple OSS Distributions 			tly->lastvalue = count;
259*f6217f89SApple OSS Distributions 			if (tly->nchecks == 0) {
260*f6217f89SApple OSS Distributions 				tly->firstvalue = count;
261*f6217f89SApple OSS Distributions 			}
262*f6217f89SApple OSS Distributions 			tly->nchecks++;
263*f6217f89SApple OSS Distributions 		}
264*f6217f89SApple OSS Distributions 	}
265*f6217f89SApple OSS Distributions }
266*f6217f89SApple OSS Distributions 
267*f6217f89SApple OSS Distributions static void
check_tally(unsigned int ncpus,unsigned int nctrs,struct tally * tallies)268*f6217f89SApple OSS Distributions check_tally(unsigned int ncpus, unsigned int nctrs, struct tally *tallies)
269*f6217f89SApple OSS Distributions {
270*f6217f89SApple OSS Distributions 	uint64_t nstuck = 0;
271*f6217f89SApple OSS Distributions 	uint64_t nchecks = 0;
272*f6217f89SApple OSS Distributions 	uint64_t nzero = 0;
273*f6217f89SApple OSS Distributions 	uint64_t ndecrease = 0;
274*f6217f89SApple OSS Distributions 
275*f6217f89SApple OSS Distributions 	for (unsigned int i = 0; i < ncpus; i++) {
276*f6217f89SApple OSS Distributions 		for (unsigned int j = 0; j < nctrs; j++) {
277*f6217f89SApple OSS Distributions 			unsigned int ctr = i * nctrs + j;
278*f6217f89SApple OSS Distributions 			struct tally *tly = &tallies[ctr];
279*f6217f89SApple OSS Distributions 
280*f6217f89SApple OSS Distributions 			T_LOG("CPU %2u PMC %u: nchecks = %llu, last value = %llx, "
281*f6217f89SApple OSS Distributions 				"delta = %llu, nstuck = %llu", i, j,
282*f6217f89SApple OSS Distributions 			    tly->nchecks, tly->lastvalue, tly->lastvalue - tly->firstvalue,
283*f6217f89SApple OSS Distributions 			    tly->nstuck);
284*f6217f89SApple OSS Distributions 
285*f6217f89SApple OSS Distributions 			nchecks += tly->nchecks;
286*f6217f89SApple OSS Distributions 			nstuck += tly->nstuck;
287*f6217f89SApple OSS Distributions 			nzero += tly->nzero;
288*f6217f89SApple OSS Distributions 			ndecrease += tly->ndecrease;
289*f6217f89SApple OSS Distributions 		}
290*f6217f89SApple OSS Distributions 	}
291*f6217f89SApple OSS Distributions 
292*f6217f89SApple OSS Distributions 	T_EXPECT_GT(nchecks, 0ULL, "checked 0x%" PRIx64 " counter values", nchecks);
293*f6217f89SApple OSS Distributions 	T_EXPECT_EQ(nzero, 0ULL, "found 0x%" PRIx64 " zero values", nzero);
294*f6217f89SApple OSS Distributions 	T_EXPECT_EQ(nstuck, 0ULL, "found 0x%" PRIx64 " stuck values", nstuck);
295*f6217f89SApple OSS Distributions 	T_EXPECT_EQ(ndecrease, 0ULL,
296*f6217f89SApple OSS Distributions 	    "found 0x%" PRIx64 " decreasing values", ndecrease);
297*f6217f89SApple OSS Distributions }
298*f6217f89SApple OSS Distributions 
299*f6217f89SApple OSS Distributions #define TESTDUR_NS (5 * NSEC_PER_SEC)
300*f6217f89SApple OSS Distributions 
301*f6217f89SApple OSS Distributions T_DECL(kpc_cpu_direct_configurable,
302*f6217f89SApple OSS Distributions     "test that configurable counters return monotonically increasing values",
303*f6217f89SApple OSS Distributions     XNU_T_META_SOC_SPECIFIC,
304*f6217f89SApple OSS Distributions     T_META_BOOTARGS_SET("enable_skstb=1"),
305*f6217f89SApple OSS Distributions     T_META_TAG_VM_NOT_ELIGIBLE,
306*f6217f89SApple OSS Distributions     T_META_ENABLED(false) /* rdar://134505531 */)
307*f6217f89SApple OSS Distributions {
308*f6217f89SApple OSS Distributions 	skip_if_unsupported();
309*f6217f89SApple OSS Distributions 
310*f6217f89SApple OSS Distributions 	struct machine mch = {};
311*f6217f89SApple OSS Distributions 	prepare_kpc(&mch, 0, "CORE_ACTIVE_CYCLE", 0);
312*f6217f89SApple OSS Distributions 
313*f6217f89SApple OSS Distributions 	int until = 0;
314*f6217f89SApple OSS Distributions 	pthread_t *threads = start_threads(&mch, spin, &until);
315*f6217f89SApple OSS Distributions 	start_kpc();
316*f6217f89SApple OSS Distributions 
317*f6217f89SApple OSS Distributions 	T_SETUPBEGIN;
318*f6217f89SApple OSS Distributions 
319*f6217f89SApple OSS Distributions 	uint64_t startns = clock_gettime_nsec_np(CLOCK_MONOTONIC);
320*f6217f89SApple OSS Distributions 	uint64_t *counts = kpc_counterbuf_alloc();
321*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(counts, "allocated space for counter values");
322*f6217f89SApple OSS Distributions 	memset(counts, 0, sizeof(*counts) * mch.ncpus * (mch.nfixed + mch.nconfig));
323*f6217f89SApple OSS Distributions 	struct tally *tly = calloc(mch.ncpus * mch.nconfig, sizeof(*tly));
324*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(tly, "allocated space for tallies");
325*f6217f89SApple OSS Distributions 
326*f6217f89SApple OSS Distributions 	T_SETUPEND;
327*f6217f89SApple OSS Distributions 
328*f6217f89SApple OSS Distributions 	int n = 0;
329*f6217f89SApple OSS Distributions 	while (clock_gettime_nsec_np(CLOCK_MONOTONIC) - startns < TESTDUR_NS) {
330*f6217f89SApple OSS Distributions 		int ret = kpc_get_cpu_counters(true,
331*f6217f89SApple OSS Distributions 		    KPC_CLASS_CONFIGURABLE_MASK, NULL, counts);
332*f6217f89SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kpc_get_cpu_counters");
333*f6217f89SApple OSS Distributions 
334*f6217f89SApple OSS Distributions 		check_counters(mch.ncpus, mch.nconfig, tly, counts);
335*f6217f89SApple OSS Distributions 
336*f6217f89SApple OSS Distributions 		usleep(10000);
337*f6217f89SApple OSS Distributions 		n++;
338*f6217f89SApple OSS Distributions 		if (n % 100 == 0) {
339*f6217f89SApple OSS Distributions 			T_LOG("checked 100 times");
340*f6217f89SApple OSS Distributions 		}
341*f6217f89SApple OSS Distributions 	}
342*f6217f89SApple OSS Distributions 
343*f6217f89SApple OSS Distributions 	check_tally(mch.ncpus, mch.nconfig, tly);
344*f6217f89SApple OSS Distributions 
345*f6217f89SApple OSS Distributions 	until = 1;
346*f6217f89SApple OSS Distributions 	end_threads(&mch, threads);
347*f6217f89SApple OSS Distributions }
348*f6217f89SApple OSS Distributions 
349*f6217f89SApple OSS Distributions T_DECL(kpc_thread_direct_instrs_cycles,
350*f6217f89SApple OSS Distributions     "test that fixed thread counters return monotonically increasing values",
351*f6217f89SApple OSS Distributions     XNU_T_META_SOC_SPECIFIC, T_META_TAG_VM_NOT_ELIGIBLE)
352*f6217f89SApple OSS Distributions {
353*f6217f89SApple OSS Distributions 	int err;
354*f6217f89SApple OSS Distributions 	uint32_t ctrs_cnt;
355*f6217f89SApple OSS Distributions 	uint64_t *ctrs_a;
356*f6217f89SApple OSS Distributions 	uint64_t *ctrs_b;
357*f6217f89SApple OSS Distributions 
358*f6217f89SApple OSS Distributions 	skip_if_unsupported();
359*f6217f89SApple OSS Distributions 
360*f6217f89SApple OSS Distributions 	T_SETUPBEGIN;
361*f6217f89SApple OSS Distributions 
362*f6217f89SApple OSS Distributions 	ctrs_cnt = kpc_get_counter_count(KPC_CLASS_FIXED_MASK);
363*f6217f89SApple OSS Distributions 	if (ctrs_cnt == 0) {
364*f6217f89SApple OSS Distributions 		T_SKIP("no fixed counters available");
365*f6217f89SApple OSS Distributions 	}
366*f6217f89SApple OSS Distributions 	T_LOG("device has %" PRIu32 " fixed counters", ctrs_cnt);
367*f6217f89SApple OSS Distributions 
368*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(kpc_force_all_ctrs_set(1), NULL);
369*f6217f89SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kpc_set_counting(KPC_CLASS_FIXED_MASK),
370*f6217f89SApple OSS Distributions 	    "kpc_set_counting");
371*f6217f89SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kpc_set_thread_counting(KPC_CLASS_FIXED_MASK),
372*f6217f89SApple OSS Distributions 	    "kpc_set_thread_counting");
373*f6217f89SApple OSS Distributions 
374*f6217f89SApple OSS Distributions 	T_SETUPEND;
375*f6217f89SApple OSS Distributions 
376*f6217f89SApple OSS Distributions 	ctrs_a = malloc(ctrs_cnt * sizeof(uint64_t));
377*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(ctrs_a, NULL);
378*f6217f89SApple OSS Distributions 
379*f6217f89SApple OSS Distributions 	err = kpc_get_thread_counters(0, ctrs_cnt, ctrs_a);
380*f6217f89SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(err, "kpc_get_thread_counters");
381*f6217f89SApple OSS Distributions 
382*f6217f89SApple OSS Distributions 	for (uint32_t i = 0; i < ctrs_cnt; i++) {
383*f6217f89SApple OSS Distributions 		T_LOG("checking counter %d with value %" PRIu64 " > 0", i, ctrs_a[i]);
384*f6217f89SApple OSS Distributions 		T_QUIET;
385*f6217f89SApple OSS Distributions 		T_EXPECT_GT(ctrs_a[i], UINT64_C(0), "counter %d is non-zero", i);
386*f6217f89SApple OSS Distributions 	}
387*f6217f89SApple OSS Distributions 
388*f6217f89SApple OSS Distributions 	ctrs_b = malloc(ctrs_cnt * sizeof(uint64_t));
389*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(ctrs_b, NULL);
390*f6217f89SApple OSS Distributions 
391*f6217f89SApple OSS Distributions 	err = kpc_get_thread_counters(0, ctrs_cnt, ctrs_b);
392*f6217f89SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(err, "kpc_get_thread_counters");
393*f6217f89SApple OSS Distributions 
394*f6217f89SApple OSS Distributions 	for (uint32_t i = 0; i < ctrs_cnt; i++) {
395*f6217f89SApple OSS Distributions 		T_LOG("checking counter %d with value %" PRIu64
396*f6217f89SApple OSS Distributions 		    " > previous value %" PRIu64, i, ctrs_b[i], ctrs_a[i]);
397*f6217f89SApple OSS Distributions 		T_QUIET;
398*f6217f89SApple OSS Distributions 		T_EXPECT_GT(ctrs_b[i], UINT64_C(0), "counter %d is non-zero", i);
399*f6217f89SApple OSS Distributions 		T_QUIET; T_EXPECT_LT(ctrs_a[i], ctrs_b[i],
400*f6217f89SApple OSS Distributions 		    "counter %d is increasing", i);
401*f6217f89SApple OSS Distributions 	}
402*f6217f89SApple OSS Distributions 
403*f6217f89SApple OSS Distributions 	free(ctrs_a);
404*f6217f89SApple OSS Distributions 	free(ctrs_b);
405*f6217f89SApple OSS Distributions }
406*f6217f89SApple OSS Distributions 
407*f6217f89SApple OSS Distributions #define PMI_TEST_DURATION_NS (15 * NSEC_PER_SEC)
408*f6217f89SApple OSS Distributions #define PERIODIC_CPU_COUNT_MS (250)
409*f6217f89SApple OSS Distributions #define NTIMESLICES (72)
410*f6217f89SApple OSS Distributions #define PMI_PERIOD (50ULL * 1000 * 1000)
411*f6217f89SApple OSS Distributions #define END_EVENT KDBG_EVENTID(0xfe, 0xfe, 0)
412*f6217f89SApple OSS Distributions 
413*f6217f89SApple OSS Distributions struct cpu {
414*f6217f89SApple OSS Distributions 	uint64_t prev_count, max_skid;
415*f6217f89SApple OSS Distributions 	unsigned int scheduled_outside_slice;
416*f6217f89SApple OSS Distributions 	unsigned int pmi_timeslices[NTIMESLICES];
417*f6217f89SApple OSS Distributions 	unsigned int scheduled_timeslices[NTIMESLICES];
418*f6217f89SApple OSS Distributions };
419*f6217f89SApple OSS Distributions 
420*f6217f89SApple OSS Distributions T_DECL(kpc_pmi_configurable,
421*f6217f89SApple OSS Distributions     "test that PMIs don't interfere with sampling counters in kperf",
422*f6217f89SApple OSS Distributions     XNU_T_META_SOC_SPECIFIC,
423*f6217f89SApple OSS Distributions     T_META_BOOTARGS_SET("enable_skstb=1"),
424*f6217f89SApple OSS Distributions     T_META_TAG_VM_NOT_ELIGIBLE,
425*f6217f89SApple OSS Distributions     T_META_ENABLED(false) /* rdar://134505531 */)
426*f6217f89SApple OSS Distributions {
427*f6217f89SApple OSS Distributions 	skip_if_unsupported();
428*f6217f89SApple OSS Distributions 
429*f6217f89SApple OSS Distributions 	start_controlling_ktrace();
430*f6217f89SApple OSS Distributions 	struct machine mch = {};
431*f6217f89SApple OSS Distributions 	prepare_kpc(&mch, 1, "CORE_ACTIVE_CYCLE", PMI_PERIOD);
432*f6217f89SApple OSS Distributions 
433*f6217f89SApple OSS Distributions 	T_SETUPBEGIN;
434*f6217f89SApple OSS Distributions 
435*f6217f89SApple OSS Distributions 	int32_t *actions = calloc(mch.nconfig, sizeof(*actions));
436*f6217f89SApple OSS Distributions 	actions[0] = 1;
437*f6217f89SApple OSS Distributions 	int ret = kpc_set_actionid(KPC_CLASS_CONFIGURABLE_MASK, actions);
438*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kpc_set_actionid");
439*f6217f89SApple OSS Distributions 	free(actions);
440*f6217f89SApple OSS Distributions 
441*f6217f89SApple OSS Distributions 	(void)kperf_action_count_set(1);
442*f6217f89SApple OSS Distributions 	ret = kperf_action_samplers_set(1,
443*f6217f89SApple OSS Distributions 	    KPERF_SAMPLER_TINFO | KPERF_SAMPLER_KSTACK);
444*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kperf_action_samplers_set");
445*f6217f89SApple OSS Distributions 
446*f6217f89SApple OSS Distributions 	ktrace_config_t ktconfig = ktrace_config_create_current();
447*f6217f89SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(ktconfig, "create current config");
448*f6217f89SApple OSS Distributions 	ret = ktrace_config_print_description(ktconfig, stdout);
449*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_ZERO(ret, "print config description");
450*f6217f89SApple OSS Distributions 
451*f6217f89SApple OSS Distributions 	struct cpu *cpus = calloc(mch.ncpus, sizeof(*cpus));
452*f6217f89SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(cpus, "allocate CPUs array");
453*f6217f89SApple OSS Distributions 
454*f6217f89SApple OSS Distributions 	__block unsigned int sample_count = 0;
455*f6217f89SApple OSS Distributions 	__block unsigned int pmi_count = 0;
456*f6217f89SApple OSS Distributions 	__block unsigned int callstack_count = 0;
457*f6217f89SApple OSS Distributions 	__block uint64_t first_ns = 0;
458*f6217f89SApple OSS Distributions 	__block uint64_t last_ns = 0;
459*f6217f89SApple OSS Distributions 
460*f6217f89SApple OSS Distributions 	ktrace_session_t sess = ktrace_session_create();
461*f6217f89SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(sess, "ktrace_session_create");
462*f6217f89SApple OSS Distributions 
463*f6217f89SApple OSS Distributions 	ktrace_events_single(sess, PERF_KPC_PMI, ^(struct trace_point *tp) {
464*f6217f89SApple OSS Distributions 		if (tp->debugid & DBG_FUNC_END) {
465*f6217f89SApple OSS Distributions 			return;
466*f6217f89SApple OSS Distributions 		}
467*f6217f89SApple OSS Distributions 
468*f6217f89SApple OSS Distributions 		uint64_t cur_ns = 0;
469*f6217f89SApple OSS Distributions 		int cret = ktrace_convert_timestamp_to_nanoseconds(sess,
470*f6217f89SApple OSS Distributions 		    tp->timestamp, &cur_ns);
471*f6217f89SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_ZERO(cret, "convert timestamp");
472*f6217f89SApple OSS Distributions 
473*f6217f89SApple OSS Distributions 		uint64_t desc = tp->arg1;
474*f6217f89SApple OSS Distributions 		uint64_t config = desc & UINT32_MAX;
475*f6217f89SApple OSS Distributions 		T_QUIET; T_EXPECT_EQ(config & UINT16_MAX,
476*f6217f89SApple OSS Distributions 				mch.selector & UINT16_MAX,
477*f6217f89SApple OSS Distributions 				"PMI argument matches configuration");
478*f6217f89SApple OSS Distributions 		__unused uint64_t counter = (desc >> 32) & UINT16_MAX;
479*f6217f89SApple OSS Distributions 		__unused uint64_t flags = desc >> 48;
480*f6217f89SApple OSS Distributions 
481*f6217f89SApple OSS Distributions 		uint64_t count = tp->arg2;
482*f6217f89SApple OSS Distributions 		if (first_ns == 0) {
483*f6217f89SApple OSS Distributions 			first_ns = cur_ns;
484*f6217f89SApple OSS Distributions 		}
485*f6217f89SApple OSS Distributions 		struct cpu *cpu = &cpus[tp->cpuid];
486*f6217f89SApple OSS Distributions 
487*f6217f89SApple OSS Distributions 		if (cpu->prev_count != 0) {
488*f6217f89SApple OSS Distributions 			uint64_t delta = count - cpu->prev_count;
489*f6217f89SApple OSS Distributions 			uint64_t skid = delta - PMI_PERIOD;
490*f6217f89SApple OSS Distributions 			if (skid > cpu->max_skid) {
491*f6217f89SApple OSS Distributions 				cpu->max_skid = skid;
492*f6217f89SApple OSS Distributions 			}
493*f6217f89SApple OSS Distributions 		}
494*f6217f89SApple OSS Distributions 		cpu->prev_count = count;
495*f6217f89SApple OSS Distributions 
496*f6217f89SApple OSS Distributions 		__unused uint64_t pc = tp->arg3;
497*f6217f89SApple OSS Distributions 
498*f6217f89SApple OSS Distributions 		double slice = (double)(cur_ns - first_ns) / PMI_TEST_DURATION_NS *
499*f6217f89SApple OSS Distributions 		    NTIMESLICES;
500*f6217f89SApple OSS Distributions 		if (slice < NTIMESLICES) {
501*f6217f89SApple OSS Distributions 			cpu->pmi_timeslices[(unsigned int)slice] += 1;
502*f6217f89SApple OSS Distributions 		}
503*f6217f89SApple OSS Distributions 
504*f6217f89SApple OSS Distributions 		pmi_count++;
505*f6217f89SApple OSS Distributions 	});
506*f6217f89SApple OSS Distributions 
507*f6217f89SApple OSS Distributions 	void (^sched_handler)(struct trace_point *tp) =
508*f6217f89SApple OSS Distributions 	    ^(struct trace_point *tp) {
509*f6217f89SApple OSS Distributions 		uint64_t cur_ns = 0;
510*f6217f89SApple OSS Distributions 		int cret = ktrace_convert_timestamp_to_nanoseconds(sess,
511*f6217f89SApple OSS Distributions 		    tp->timestamp, &cur_ns);
512*f6217f89SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_ZERO(cret, "convert timestamp");
513*f6217f89SApple OSS Distributions 		if (first_ns == 0) {
514*f6217f89SApple OSS Distributions 			first_ns = cur_ns;
515*f6217f89SApple OSS Distributions 		}
516*f6217f89SApple OSS Distributions 
517*f6217f89SApple OSS Distributions 		struct cpu *cpu = &cpus[tp->cpuid];
518*f6217f89SApple OSS Distributions 		double slice = (double)(cur_ns - first_ns) / PMI_TEST_DURATION_NS *
519*f6217f89SApple OSS Distributions 		    NTIMESLICES;
520*f6217f89SApple OSS Distributions 		if (slice < NTIMESLICES) {
521*f6217f89SApple OSS Distributions 			cpu->scheduled_timeslices[(unsigned int)slice] += 1;
522*f6217f89SApple OSS Distributions 		} else {
523*f6217f89SApple OSS Distributions 			cpu->scheduled_outside_slice += 1;
524*f6217f89SApple OSS Distributions 		}
525*f6217f89SApple OSS Distributions 	};
526*f6217f89SApple OSS Distributions 	ktrace_events_single(sess, MACH_SCHED, sched_handler);
527*f6217f89SApple OSS Distributions 	ktrace_events_single(sess, MACH_STACK_HANDOFF, sched_handler);
528*f6217f89SApple OSS Distributions 
529*f6217f89SApple OSS Distributions 	ktrace_events_single(sess, PERF_SAMPLE, ^(struct trace_point * tp) {
530*f6217f89SApple OSS Distributions 		if (tp->debugid & DBG_FUNC_START) {
531*f6217f89SApple OSS Distributions 			sample_count++;
532*f6217f89SApple OSS Distributions 		}
533*f6217f89SApple OSS Distributions 	});
534*f6217f89SApple OSS Distributions 	ktrace_events_single(sess, PERF_STK_KHDR,
535*f6217f89SApple OSS Distributions 	    ^(struct trace_point * __unused tp) {
536*f6217f89SApple OSS Distributions 		callstack_count++;
537*f6217f89SApple OSS Distributions 	});
538*f6217f89SApple OSS Distributions 
539*f6217f89SApple OSS Distributions 	ktrace_events_single(sess, END_EVENT, ^(struct trace_point *tp) {
540*f6217f89SApple OSS Distributions 		int cret = ktrace_convert_timestamp_to_nanoseconds(sess,
541*f6217f89SApple OSS Distributions 		    tp->timestamp, &last_ns);
542*f6217f89SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_ZERO(cret, "convert timestamp");
543*f6217f89SApple OSS Distributions 
544*f6217f89SApple OSS Distributions 		ktrace_end(sess, 1);
545*f6217f89SApple OSS Distributions 	});
546*f6217f89SApple OSS Distributions 
547*f6217f89SApple OSS Distributions 	uint64_t *counts = kpc_counterbuf_alloc();
548*f6217f89SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(counts,
549*f6217f89SApple OSS Distributions 			"allocated counter values array");
550*f6217f89SApple OSS Distributions 	memset(counts, 0, sizeof(*counts) * mch.ncpus * (mch.nfixed + mch.nconfig));
551*f6217f89SApple OSS Distributions 	struct tally *tly = calloc(mch.ncpus * (mch.nconfig + mch.nfixed),
552*f6217f89SApple OSS Distributions 			sizeof(*tly));
553*f6217f89SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(tly, "allocated tallies array");
554*f6217f89SApple OSS Distributions 
555*f6217f89SApple OSS Distributions 	dispatch_source_t cpu_count_timer = dispatch_source_create(
556*f6217f89SApple OSS Distributions 			DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
557*f6217f89SApple OSS Distributions     dispatch_source_set_timer(cpu_count_timer, dispatch_time(DISPATCH_TIME_NOW,
558*f6217f89SApple OSS Distributions         PERIODIC_CPU_COUNT_MS * NSEC_PER_MSEC),
559*f6217f89SApple OSS Distributions         PERIODIC_CPU_COUNT_MS * NSEC_PER_MSEC, 0);
560*f6217f89SApple OSS Distributions     dispatch_source_set_cancel_handler(cpu_count_timer, ^{
561*f6217f89SApple OSS Distributions         dispatch_release(cpu_count_timer);
562*f6217f89SApple OSS Distributions     });
563*f6217f89SApple OSS Distributions 
564*f6217f89SApple OSS Distributions     __block uint64_t first_check_ns = 0;
565*f6217f89SApple OSS Distributions     __block uint64_t last_check_ns = 0;
566*f6217f89SApple OSS Distributions 
567*f6217f89SApple OSS Distributions     dispatch_source_set_event_handler(cpu_count_timer, ^{
568*f6217f89SApple OSS Distributions 		int cret = kpc_get_cpu_counters(true,
569*f6217f89SApple OSS Distributions 		    KPC_CLASS_FIXED_MASK | KPC_CLASS_CONFIGURABLE_MASK, NULL, counts);
570*f6217f89SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_SUCCESS(cret, "kpc_get_cpu_counters");
571*f6217f89SApple OSS Distributions 
572*f6217f89SApple OSS Distributions 		if (!first_check_ns) {
573*f6217f89SApple OSS Distributions 			first_check_ns = clock_gettime_nsec_np(CLOCK_MONOTONIC);
574*f6217f89SApple OSS Distributions 		} else {
575*f6217f89SApple OSS Distributions 			last_check_ns = clock_gettime_nsec_np(CLOCK_MONOTONIC);
576*f6217f89SApple OSS Distributions 		}
577*f6217f89SApple OSS Distributions 		check_counters(mch.ncpus, mch.nfixed + mch.nconfig, tly, counts);
578*f6217f89SApple OSS Distributions 	});
579*f6217f89SApple OSS Distributions 	ktrace_events_class(sess, DBG_PERF, ^(struct trace_point * __unused tp) {});
580*f6217f89SApple OSS Distributions 
581*f6217f89SApple OSS Distributions 	int stop = 0;
582*f6217f89SApple OSS Distributions 	(void)start_threads(&mch, spin, &stop);
583*f6217f89SApple OSS Distributions 
584*f6217f89SApple OSS Distributions 	ktrace_set_completion_handler(sess, ^{
585*f6217f89SApple OSS Distributions 		dispatch_cancel(cpu_count_timer);
586*f6217f89SApple OSS Distributions 
587*f6217f89SApple OSS Distributions 		check_tally(mch.ncpus, mch.nfixed + mch.nconfig, tly);
588*f6217f89SApple OSS Distributions 
589*f6217f89SApple OSS Distributions 		struct rusage_info_v4 post_ru = {};
590*f6217f89SApple OSS Distributions 		int ruret = proc_pid_rusage(getpid(), RUSAGE_INFO_V4,
591*f6217f89SApple OSS Distributions 				(rusage_info_t *)&post_ru);
592*f6217f89SApple OSS Distributions 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ruret, "got rusage information");
593*f6217f89SApple OSS Distributions 		T_LOG("saw %llu cycles in process",
594*f6217f89SApple OSS Distributions 				post_ru.ri_cycles - pre_ru.ri_cycles);
595*f6217f89SApple OSS Distributions 		uint64_t total_cycles = 0;
596*f6217f89SApple OSS Distributions 
597*f6217f89SApple OSS Distributions 		T_LOG("saw pmis = %u, samples = %u, stacks = %u", pmi_count, sample_count,
598*f6217f89SApple OSS Distributions 		    callstack_count);
599*f6217f89SApple OSS Distributions 		// Allow some slop in case the trace is cut-off midway through a
600*f6217f89SApple OSS Distributions 		// sample.
601*f6217f89SApple OSS Distributions 		const unsigned int cutoff_leeway = 32;
602*f6217f89SApple OSS Distributions 		T_EXPECT_GE(sample_count + cutoff_leeway, pmi_count,
603*f6217f89SApple OSS Distributions 		    "saw as many samples as PMIs");
604*f6217f89SApple OSS Distributions 		T_EXPECT_GE(callstack_count + cutoff_leeway, pmi_count,
605*f6217f89SApple OSS Distributions 		    "saw as many stacks as PMIs");
606*f6217f89SApple OSS Distributions 
607*f6217f89SApple OSS Distributions 		unsigned int cpu_sample_count = 0;
608*f6217f89SApple OSS Distributions 		char sample_slices[NTIMESLICES + 1];
609*f6217f89SApple OSS Distributions 		sample_slices[NTIMESLICES] = '\0';
610*f6217f89SApple OSS Distributions 		for (unsigned int i = 0; i < mch.ncpus; i++) {
611*f6217f89SApple OSS Distributions 			memset(sample_slices, '-', sizeof(sample_slices) - 1);
612*f6217f89SApple OSS Distributions 
613*f6217f89SApple OSS Distributions 			struct cpu *cpu = &cpus[i];
614*f6217f89SApple OSS Distributions 			unsigned int pmi_slice_count = 0, no_sched_slice_count = 0,
615*f6217f89SApple OSS Distributions 					cpu_pmi_count = 0, last_contiguous = 0;
616*f6217f89SApple OSS Distributions 			bool seen_empty = false;
617*f6217f89SApple OSS Distributions 			for (unsigned int j = 0; j < NTIMESLICES; j++) {
618*f6217f89SApple OSS Distributions 				unsigned int slice_pmi_count = cpu->pmi_timeslices[j];
619*f6217f89SApple OSS Distributions 				unsigned int slice_sched_count = cpu->scheduled_timeslices[j];
620*f6217f89SApple OSS Distributions 				cpu_pmi_count += slice_pmi_count;
621*f6217f89SApple OSS Distributions 				if (slice_pmi_count > 0) {
622*f6217f89SApple OSS Distributions 					pmi_slice_count++;
623*f6217f89SApple OSS Distributions 					sample_slices[j] = '*';
624*f6217f89SApple OSS Distributions 				} else if (slice_sched_count == 0) {
625*f6217f89SApple OSS Distributions 					no_sched_slice_count++;
626*f6217f89SApple OSS Distributions 					sample_slices[j] = '.';
627*f6217f89SApple OSS Distributions 				} else {
628*f6217f89SApple OSS Distributions 					seen_empty = true;
629*f6217f89SApple OSS Distributions 				}
630*f6217f89SApple OSS Distributions 				if (!seen_empty) {
631*f6217f89SApple OSS Distributions 					last_contiguous = j;
632*f6217f89SApple OSS Distributions 				}
633*f6217f89SApple OSS Distributions 			}
634*f6217f89SApple OSS Distributions 			unsigned int ctr = i * (mch.nfixed + mch.nconfig) + mch.nfixed;
635*f6217f89SApple OSS Distributions 			uint64_t delta = tly[ctr].lastvalue - tly[ctr].firstvalue;
636*f6217f89SApple OSS Distributions 			T_LOG("%g GHz", (double)delta / (last_check_ns - first_check_ns));
637*f6217f89SApple OSS Distributions 			total_cycles += delta;
638*f6217f89SApple OSS Distributions 			uint64_t abs_max_skid = (uint64_t)ABSV64(cpu->max_skid);
639*f6217f89SApple OSS Distributions 			T_LOG("CPU %2u: %4up:%4un/%u, %6u/%llu, max skid = %llu (%.4f%%), "
640*f6217f89SApple OSS Distributions 					"last contiguous = %u, scheduled outside = %u", i,
641*f6217f89SApple OSS Distributions 					pmi_slice_count, no_sched_slice_count, NTIMESLICES,
642*f6217f89SApple OSS Distributions 					sample_count, delta / PMI_PERIOD, abs_max_skid,
643*f6217f89SApple OSS Distributions 					(double)abs_max_skid / PMI_PERIOD * 100, last_contiguous,
644*f6217f89SApple OSS Distributions 					cpu->scheduled_outside_slice);
645*f6217f89SApple OSS Distributions 			T_LOG("%s", sample_slices);
646*f6217f89SApple OSS Distributions 			if (cpu_pmi_count > 0) {
647*f6217f89SApple OSS Distributions 				cpu_sample_count++;
648*f6217f89SApple OSS Distributions 			}
649*f6217f89SApple OSS Distributions 			T_EXPECT_EQ(last_contiguous, NTIMESLICES - 1,
650*f6217f89SApple OSS Distributions 					"CPU %2u: saw samples in each time slice", i);
651*f6217f89SApple OSS Distributions 		}
652*f6217f89SApple OSS Distributions 		T_LOG("kpc reported %llu total cycles", total_cycles);
653*f6217f89SApple OSS Distributions 		T_LOG("saw %u sample events, across %u/%u cpus", sample_count,
654*f6217f89SApple OSS Distributions 				cpu_sample_count, mch.ncpus);
655*f6217f89SApple OSS Distributions 		T_EXPECT_EQ(cpu_sample_count, mch.ncpus,
656*f6217f89SApple OSS Distributions 				"should see PMIs on every CPU");
657*f6217f89SApple OSS Distributions 		T_END;
658*f6217f89SApple OSS Distributions 	});
659*f6217f89SApple OSS Distributions 
660*f6217f89SApple OSS Distributions 	int dbglvl = 3;
661*f6217f89SApple OSS Distributions 	ret = sysctlbyname("kperf.debug_level", NULL, NULL, &dbglvl,
662*f6217f89SApple OSS Distributions 	    sizeof(dbglvl));
663*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "set kperf debug level");
664*f6217f89SApple OSS Distributions 	ret = kperf_sample_set(1);
665*f6217f89SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "kperf_sample_set");
666*f6217f89SApple OSS Distributions 
667*f6217f89SApple OSS Distributions 	start_kpc();
668*f6217f89SApple OSS Distributions 
669*f6217f89SApple OSS Distributions 	int error = ktrace_start(sess, dispatch_get_main_queue());
670*f6217f89SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(error, "started tracing");
671*f6217f89SApple OSS Distributions 
672*f6217f89SApple OSS Distributions 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, PMI_TEST_DURATION_NS),
673*f6217f89SApple OSS Distributions 			dispatch_get_main_queue(), ^{
674*f6217f89SApple OSS Distributions 		T_LOG("ending tracing after timeout");
675*f6217f89SApple OSS Distributions 		kdebug_trace(END_EVENT, 0, 0, 0, 0);
676*f6217f89SApple OSS Distributions 	});
677*f6217f89SApple OSS Distributions 
678*f6217f89SApple OSS Distributions 	dispatch_activate(cpu_count_timer);
679*f6217f89SApple OSS Distributions 
680*f6217f89SApple OSS Distributions 	T_SETUPEND;
681*f6217f89SApple OSS Distributions 
682*f6217f89SApple OSS Distributions 	dispatch_main();
683*f6217f89SApple OSS Distributions }
684*f6217f89SApple OSS Distributions 
685*f6217f89SApple OSS Distributions #if defined(__arm64__)
686*f6217f89SApple OSS Distributions #define IS_ARM64 true
687*f6217f89SApple OSS Distributions #else // defined(__arm64__)
688*f6217f89SApple OSS Distributions #define IS_ARM64 false
689*f6217f89SApple OSS Distributions #endif // !defined(__arm64__)
690*f6217f89SApple OSS Distributions 
691*f6217f89SApple OSS Distributions T_DECL(kpc_pmu_config, "ensure PMU can be configured",
692*f6217f89SApple OSS Distributions     XNU_T_META_SOC_SPECIFIC,
693*f6217f89SApple OSS Distributions     T_META_ENABLED(IS_ARM64), T_META_TAG_VM_NOT_ELIGIBLE)
694*f6217f89SApple OSS Distributions {
695*f6217f89SApple OSS Distributions 	T_SETUPBEGIN;
696*f6217f89SApple OSS Distributions 	int ret = kpc_force_all_ctrs_set(1);
697*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret,
698*f6217f89SApple OSS Distributions 			"force all counters to allow raw PMU configuration");
699*f6217f89SApple OSS Distributions 	uint32_t nconfigs = kpc_get_config_count(KPC_CLASS_RAWPMU_MASK);
700*f6217f89SApple OSS Distributions 	T_LOG("found %u raw PMU configuration words", nconfigs);
701*f6217f89SApple OSS Distributions 	uint64_t *configs = calloc(nconfigs, sizeof(*configs));
702*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_NOTNULL(configs, "allocated config words");
703*f6217f89SApple OSS Distributions 	T_SETUPEND;
704*f6217f89SApple OSS Distributions 
705*f6217f89SApple OSS Distributions 	ret = kpc_set_config(KPC_CLASS_RAWPMU_MASK, configs);
706*f6217f89SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "should set PMU configuration");
707*f6217f89SApple OSS Distributions }
708*f6217f89SApple OSS Distributions 
709*f6217f89SApple OSS Distributions T_DECL(pmi_pc_capture, "ensure PC capture works for PMCs 5, 6, and 7",
710*f6217f89SApple OSS Distributions     XNU_T_META_SOC_SPECIFIC,
711*f6217f89SApple OSS Distributions     T_META_REQUIRES_SYSCTL_EQ("kpc.pc_capture_supported", 1), T_META_TAG_VM_NOT_ELIGIBLE)
712*f6217f89SApple OSS Distributions {
713*f6217f89SApple OSS Distributions 	start_controlling_ktrace();
714*f6217f89SApple OSS Distributions 	struct machine mch = {};
715*f6217f89SApple OSS Distributions 	prepare_kpc(&mch, 0, "INST_BRANCH_TAKEN", PMI_PERIOD);
716*f6217f89SApple OSS Distributions 
717*f6217f89SApple OSS Distributions 	T_SETUPBEGIN;
718*f6217f89SApple OSS Distributions 
719*f6217f89SApple OSS Distributions 	uint64_t *periods = calloc(mch.nconfig, sizeof(*periods));
720*f6217f89SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(periods, "allocate periods array");
721*f6217f89SApple OSS Distributions 	for (unsigned int i = 0; i < mch.nconfig; i++) {
722*f6217f89SApple OSS Distributions 		/*
723*f6217f89SApple OSS Distributions 		 * Offset the periods so the PMIs don't alias to the same PC capture.
724*f6217f89SApple OSS Distributions 		 * Since there's only one PC capture register, they will clobber each
725*f6217f89SApple OSS Distributions 		 * other.
726*f6217f89SApple OSS Distributions 		 */
727*f6217f89SApple OSS Distributions 		periods[i] = PMI_PERIOD / 1000 + (i * 1000);
728*f6217f89SApple OSS Distributions 	}
729*f6217f89SApple OSS Distributions 
730*f6217f89SApple OSS Distributions 	int ret = kpc_set_period(KPC_CLASS_CONFIGURABLE_MASK, periods);
731*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kpc_set_period");
732*f6217f89SApple OSS Distributions 	free(periods);
733*f6217f89SApple OSS Distributions 
734*f6217f89SApple OSS Distributions 	int32_t *actions = calloc(mch.nconfig, sizeof(*actions));
735*f6217f89SApple OSS Distributions 	for (unsigned int i = 0; i < mch.nconfig; i++) {
736*f6217f89SApple OSS Distributions 		actions[i] = 1;
737*f6217f89SApple OSS Distributions 	}
738*f6217f89SApple OSS Distributions 	ret = kpc_set_actionid(KPC_CLASS_CONFIGURABLE_MASK, actions);
739*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kpc_set_actionid");
740*f6217f89SApple OSS Distributions 	free(actions);
741*f6217f89SApple OSS Distributions 
742*f6217f89SApple OSS Distributions 	(void)kperf_action_count_set(1);
743*f6217f89SApple OSS Distributions 	ret = kperf_action_samplers_set(1, KPERF_SAMPLER_TINFO);
744*f6217f89SApple OSS Distributions 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kperf_action_samplers_set");
745*f6217f89SApple OSS Distributions 
746*f6217f89SApple OSS Distributions 	ktrace_session_t sess = ktrace_session_create();
747*f6217f89SApple OSS Distributions 	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(sess, "ktrace_session_create");
748*f6217f89SApple OSS Distributions 
749*f6217f89SApple OSS Distributions 	uint64_t pc_captured_arr[3] = {};
750*f6217f89SApple OSS Distributions 	uint64_t *pc_captured = pc_captured_arr;
751*f6217f89SApple OSS Distributions 	uint64_t pmi_event_arr[3] = {};
752*f6217f89SApple OSS Distributions 	uint64_t *pmi_event = pmi_event_arr;
753*f6217f89SApple OSS Distributions 	ktrace_events_single(sess, PERF_KPC_PMI, ^(struct trace_point *tp) {
754*f6217f89SApple OSS Distributions 		if (tp->debugid & DBG_FUNC_END) {
755*f6217f89SApple OSS Distributions 			return;
756*f6217f89SApple OSS Distributions 		}
757*f6217f89SApple OSS Distributions 
758*f6217f89SApple OSS Distributions 		uint64_t desc = tp->arg1;
759*f6217f89SApple OSS Distributions 
760*f6217f89SApple OSS Distributions #define KPC_DESC_COUNTER(DESC) (((DESC) >> 32) & 0xffff)
761*f6217f89SApple OSS Distributions #define KPC_DESC_CONFIG(DESC) ((DESC) & 0xffff)
762*f6217f89SApple OSS Distributions #define KPC_DESC_FLAGS(DESC) ((DESC) >> 48)
763*f6217f89SApple OSS Distributions #define KPC_FLAG_PC_CAPTURED (0x08)
764*f6217f89SApple OSS Distributions 
765*f6217f89SApple OSS Distributions 		uint64_t counter = KPC_DESC_COUNTER(desc);
766*f6217f89SApple OSS Distributions 		uint64_t flags = KPC_DESC_FLAGS(desc);
767*f6217f89SApple OSS Distributions 		if (counter >= 5 && counter <= 7) {
768*f6217f89SApple OSS Distributions 			pmi_event[counter - 5]++;
769*f6217f89SApple OSS Distributions 			if (flags & KPC_FLAG_PC_CAPTURED) {
770*f6217f89SApple OSS Distributions 				pc_captured[counter - 5]++;
771*f6217f89SApple OSS Distributions 			}
772*f6217f89SApple OSS Distributions 		}
773*f6217f89SApple OSS Distributions 		T_QUIET;
774*f6217f89SApple OSS Distributions 		T_ASSERT_EQ(KPC_DESC_CONFIG(desc), mch.selector,
775*f6217f89SApple OSS Distributions 		    "correct counter configuration");
776*f6217f89SApple OSS Distributions 	});
777*f6217f89SApple OSS Distributions 
778*f6217f89SApple OSS Distributions 	ktrace_events_single(sess, END_EVENT, ^(struct trace_point *tp __unused) {
779*f6217f89SApple OSS Distributions 		ktrace_config_t config = ktrace_config_create_current();
780*f6217f89SApple OSS Distributions 		ktrace_config_print_description(config, stdout);
781*f6217f89SApple OSS Distributions 		ktrace_config_destroy(config);
782*f6217f89SApple OSS Distributions 		T_LOG("saw ending event");
783*f6217f89SApple OSS Distributions 		ktrace_end(sess, 1);
784*f6217f89SApple OSS Distributions 	});
785*f6217f89SApple OSS Distributions 
786*f6217f89SApple OSS Distributions 	ktrace_set_completion_handler(sess, ^{
787*f6217f89SApple OSS Distributions 		ktrace_session_destroy(sess);
788*f6217f89SApple OSS Distributions 		for (unsigned int i = 0; i < 3; i++) {
789*f6217f89SApple OSS Distributions 			T_LOG("PMC%u: saw %llu/%llu (%g%%) PMIs with PC capture", i + 5,
790*f6217f89SApple OSS Distributions 			    pc_captured[i], pmi_event[i],
791*f6217f89SApple OSS Distributions 			    (double)pc_captured[i] / (double)pmi_event[i] * 100.0);
792*f6217f89SApple OSS Distributions 			T_EXPECT_GT(pc_captured[i], 0ULL, "saw PC capture for counter %u",
793*f6217f89SApple OSS Distributions 			    i + 5);
794*f6217f89SApple OSS Distributions 		}
795*f6217f89SApple OSS Distributions 		T_END;
796*f6217f89SApple OSS Distributions 	});
797*f6217f89SApple OSS Distributions 
798*f6217f89SApple OSS Distributions 	ret = kperf_sample_set(1);
799*f6217f89SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "kperf_sample_set");
800*f6217f89SApple OSS Distributions 
801*f6217f89SApple OSS Distributions 	start_kpc();
802*f6217f89SApple OSS Distributions 
803*f6217f89SApple OSS Distributions 	int error = ktrace_start(sess, dispatch_get_main_queue());
804*f6217f89SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(error, "started tracing");
805*f6217f89SApple OSS Distributions 
806*f6217f89SApple OSS Distributions 	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, PMI_TEST_DURATION_NS),
807*f6217f89SApple OSS Distributions 			dispatch_get_main_queue(), ^{
808*f6217f89SApple OSS Distributions 		T_LOG("ending tracing after timeout");
809*f6217f89SApple OSS Distributions 		kdebug_trace(END_EVENT, 0, 0, 0, 0);
810*f6217f89SApple OSS Distributions 	});
811*f6217f89SApple OSS Distributions 
812*f6217f89SApple OSS Distributions 	T_SETUPEND;
813*f6217f89SApple OSS Distributions 
814*f6217f89SApple OSS Distributions 	dispatch_main();
815*f6217f89SApple OSS Distributions }
816