xref: /freebsd/tests/sys/pmc/pmc_wrap_test.c (revision 2cfd82f747c04f68f679824ba627460e87ab3848)
1*2cfd82f7SAlexander Leidinger /*-
2*2cfd82f7SAlexander Leidinger  * SPDX-License-Identifier: BSD-2-Clause
3*2cfd82f7SAlexander Leidinger  *
4*2cfd82f7SAlexander Leidinger  * Copyright (c) 2026 Alexander Leidinger <netchild@FreeBSD.org>
5*2cfd82f7SAlexander Leidinger  *
6*2cfd82f7SAlexander Leidinger  * Redistribution and use in source and binary forms, with or without
7*2cfd82f7SAlexander Leidinger  * modification, are permitted provided that the following conditions
8*2cfd82f7SAlexander Leidinger  * are met:
9*2cfd82f7SAlexander Leidinger  * 1. Redistributions of source code must retain the above copyright
10*2cfd82f7SAlexander Leidinger  *    notice, this list of conditions and the following disclaimer.
11*2cfd82f7SAlexander Leidinger  * 2. Redistributions in binary form must reproduce the above copyright
12*2cfd82f7SAlexander Leidinger  *    notice, this list of conditions and the following disclaimer in the
13*2cfd82f7SAlexander Leidinger  *    documentation and/or other advertising materials provided with the
14*2cfd82f7SAlexander Leidinger  *    distribution.
15*2cfd82f7SAlexander Leidinger  *
16*2cfd82f7SAlexander Leidinger  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*2cfd82f7SAlexander Leidinger  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*2cfd82f7SAlexander Leidinger  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*2cfd82f7SAlexander Leidinger  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*2cfd82f7SAlexander Leidinger  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*2cfd82f7SAlexander Leidinger  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*2cfd82f7SAlexander Leidinger  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*2cfd82f7SAlexander Leidinger  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*2cfd82f7SAlexander Leidinger  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*2cfd82f7SAlexander Leidinger  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*2cfd82f7SAlexander Leidinger  * SUCH DAMAGE.
27*2cfd82f7SAlexander Leidinger  */
28*2cfd82f7SAlexander Leidinger 
29*2cfd82f7SAlexander Leidinger /*
30*2cfd82f7SAlexander Leidinger  * Regression tests for process-mode counting PMCs whose accumulated
31*2cfd82f7SAlexander Leidinger  * count is close to, or beyond, the range of the underlying hardware
32*2cfd82f7SAlexander Leidinger  * counter.  Hardware counters are narrower than the 64-bit software
33*2cfd82f7SAlexander Leidinger  * counter (48 bits on modern x86), and the increment collected when a
34*2cfd82f7SAlexander Leidinger  * counter is read out (at context switch and at process exit) must be
35*2cfd82f7SAlexander Leidinger  * computed modulo the hardware width.
36*2cfd82f7SAlexander Leidinger  *
37*2cfd82f7SAlexander Leidinger  * Before the fix these tests panicked INVARIANTS kernels with
38*2cfd82f7SAlexander Leidinger  * "[pmc,...] negative increment" once the hardware counter wrapped,
39*2cfd82f7SAlexander Leidinger  * and silently corrupted the accumulated count on other kernels.
40*2cfd82f7SAlexander Leidinger  *
41*2cfd82f7SAlexander Leidinger  * The counter is attached to a short-lived child process which spins
42*2cfd82f7SAlexander Leidinger  * to advance it; the child exits before the PMC is released, so the
43*2cfd82f7SAlexander Leidinger  * accounting is torn down cleanly (the accumulation is collected on
44*2cfd82f7SAlexander Leidinger  * the exit path, which is one of the two sites the fix touches).
45*2cfd82f7SAlexander Leidinger  *
46*2cfd82f7SAlexander Leidinger  * The tests need a hardware counting event whose counter is narrower
47*2cfd82f7SAlexander Leidinger  * than 64 bits; they skip on systems without one (e.g. VMs without a
48*2cfd82f7SAlexander Leidinger  * vPMU, or with hwpmc(4) not loaded).
49*2cfd82f7SAlexander Leidinger  */
50*2cfd82f7SAlexander Leidinger 
51*2cfd82f7SAlexander Leidinger #include <sys/types.h>
52*2cfd82f7SAlexander Leidinger #include <sys/wait.h>
53*2cfd82f7SAlexander Leidinger 
54*2cfd82f7SAlexander Leidinger #include <errno.h>
55*2cfd82f7SAlexander Leidinger #include <pmc.h>
56*2cfd82f7SAlexander Leidinger #include <stdint.h>
57*2cfd82f7SAlexander Leidinger #include <stdio.h>
58*2cfd82f7SAlexander Leidinger #include <string.h>
59*2cfd82f7SAlexander Leidinger #include <unistd.h>
60*2cfd82f7SAlexander Leidinger 
61*2cfd82f7SAlexander Leidinger #include <atf-c.h>
62*2cfd82f7SAlexander Leidinger 
63*2cfd82f7SAlexander Leidinger /*
64*2cfd82f7SAlexander Leidinger  * High-rate events; at least one of these should be allocatable on
65*2cfd82f7SAlexander Leidinger  * any x86 or arm64 system with a vPMU.  All of them count while the
66*2cfd82f7SAlexander Leidinger  * child's spin loop runs.
67*2cfd82f7SAlexander Leidinger  */
68*2cfd82f7SAlexander Leidinger static const char *wrap_test_events[] = {
69*2cfd82f7SAlexander Leidinger 	"instructions",
70*2cfd82f7SAlexander Leidinger 	"cycles",
71*2cfd82f7SAlexander Leidinger 	"branches",
72*2cfd82f7SAlexander Leidinger 	"unhalted-core-cycles",
73*2cfd82f7SAlexander Leidinger 	"inst_retired.any",
74*2cfd82f7SAlexander Leidinger 	"cpu_clk_unhalted.thread",
75*2cfd82f7SAlexander Leidinger 	"ls_not_halted_cyc",
76*2cfd82f7SAlexander Leidinger 	NULL
77*2cfd82f7SAlexander Leidinger };
78*2cfd82f7SAlexander Leidinger 
79*2cfd82f7SAlexander Leidinger /* The child generates well more than this many events past the seed. */
80*2cfd82f7SAlexander Leidinger #define	WRAP_MARGIN	((uint64_t)1 << 20)
81*2cfd82f7SAlexander Leidinger 
82*2cfd82f7SAlexander Leidinger /* Spin iterations: comfortably more than WRAP_MARGIN events, still < 1s. */
83*2cfd82f7SAlexander Leidinger #define	SPIN_ITERS	((uint64_t)200 * 1000 * 1000)
84*2cfd82f7SAlexander Leidinger 
85*2cfd82f7SAlexander Leidinger /* Upper bound on events a single test run can plausibly generate. */
86*2cfd82f7SAlexander Leidinger #define	SANITY_BOUND	((uint64_t)1 << 40)
87*2cfd82f7SAlexander Leidinger 
88*2cfd82f7SAlexander Leidinger /*
89*2cfd82f7SAlexander Leidinger  * Child: wait for the parent to start the PMC (one byte on the pipe),
90*2cfd82f7SAlexander Leidinger  * spin to advance the counter across the end of its range, then exit.
91*2cfd82f7SAlexander Leidinger  */
92*2cfd82f7SAlexander Leidinger static void __attribute__((noinline))
child_spin(int startfd)93*2cfd82f7SAlexander Leidinger child_spin(int startfd)
94*2cfd82f7SAlexander Leidinger {
95*2cfd82f7SAlexander Leidinger 	volatile uint64_t sink = 0;
96*2cfd82f7SAlexander Leidinger 	uint64_t i;
97*2cfd82f7SAlexander Leidinger 	char c;
98*2cfd82f7SAlexander Leidinger 
99*2cfd82f7SAlexander Leidinger 	(void)read(startfd, &c, 1);
100*2cfd82f7SAlexander Leidinger 	for (i = 0; i < SPIN_ITERS; i++)
101*2cfd82f7SAlexander Leidinger 		sink += i;
102*2cfd82f7SAlexander Leidinger 	_exit((int)(sink & 0x7f));
103*2cfd82f7SAlexander Leidinger }
104*2cfd82f7SAlexander Leidinger 
105*2cfd82f7SAlexander Leidinger /*
106*2cfd82f7SAlexander Leidinger  * Allocate a process-mode counting PMC for the first available event
107*2cfd82f7SAlexander Leidinger  * backed by a counter narrower than 64 bits, attach it to a child,
108*2cfd82f7SAlexander Leidinger  * seed it with the given value, let the child spin past the end of
109*2cfd82f7SAlexander Leidinger  * the counter range, and check that the accumulated count stayed sane.
110*2cfd82f7SAlexander Leidinger  */
111*2cfd82f7SAlexander Leidinger static void
wrap_test(bool seed_beyond_width)112*2cfd82f7SAlexander Leidinger wrap_test(bool seed_beyond_width)
113*2cfd82f7SAlexander Leidinger {
114*2cfd82f7SAlexander Leidinger 	pmc_id_t pmcid;
115*2cfd82f7SAlexander Leidinger 	pmc_value_t final;
116*2cfd82f7SAlexander Leidinger 	uint64_t seed;
117*2cfd82f7SAlexander Leidinger 	uint32_t width;
118*2cfd82f7SAlexander Leidinger 	pid_t child;
119*2cfd82f7SAlexander Leidinger 	int i, pfd[2], status;
120*2cfd82f7SAlexander Leidinger 
121*2cfd82f7SAlexander Leidinger 	if (pmc_init() != 0)
122*2cfd82f7SAlexander Leidinger 		atf_tc_skip("hwpmc(4) is not available: %s", strerror(errno));
123*2cfd82f7SAlexander Leidinger 
124*2cfd82f7SAlexander Leidinger 	width = 0;
125*2cfd82f7SAlexander Leidinger 	pmcid = PMC_ID_INVALID;
126*2cfd82f7SAlexander Leidinger 	for (i = 0; wrap_test_events[i] != NULL; i++) {
127*2cfd82f7SAlexander Leidinger 		if (pmc_allocate(wrap_test_events[i], PMC_MODE_TC, 0,
128*2cfd82f7SAlexander Leidinger 		    PMC_CPU_ANY, &pmcid, 0) != 0)
129*2cfd82f7SAlexander Leidinger 			continue;
130*2cfd82f7SAlexander Leidinger 		ATF_REQUIRE(pmc_width(pmcid, &width) == 0);
131*2cfd82f7SAlexander Leidinger 		if (width >= 32 && width < 64)
132*2cfd82f7SAlexander Leidinger 			break;
133*2cfd82f7SAlexander Leidinger 		ATF_REQUIRE(pmc_release(pmcid) == 0);
134*2cfd82f7SAlexander Leidinger 		pmcid = PMC_ID_INVALID;
135*2cfd82f7SAlexander Leidinger 	}
136*2cfd82f7SAlexander Leidinger 	if (pmcid == PMC_ID_INVALID)
137*2cfd82f7SAlexander Leidinger 		atf_tc_skip("no allocatable counting event with a hardware "
138*2cfd82f7SAlexander Leidinger 		    "counter narrower than 64 bits");
139*2cfd82f7SAlexander Leidinger 
140*2cfd82f7SAlexander Leidinger 	if (seed_beyond_width) {
141*2cfd82f7SAlexander Leidinger 		/* Accumulated count that no longer fits the counter at all. */
142*2cfd82f7SAlexander Leidinger 		seed = ((uint64_t)1 << width) + 12345;
143*2cfd82f7SAlexander Leidinger 	} else {
144*2cfd82f7SAlexander Leidinger 		/* Just below the end of the counter range; the child crosses it. */
145*2cfd82f7SAlexander Leidinger 		seed = ((uint64_t)1 << width) - WRAP_MARGIN;
146*2cfd82f7SAlexander Leidinger 	}
147*2cfd82f7SAlexander Leidinger 
148*2cfd82f7SAlexander Leidinger 	ATF_REQUIRE(pipe(pfd) == 0);
149*2cfd82f7SAlexander Leidinger 	child = fork();
150*2cfd82f7SAlexander Leidinger 	ATF_REQUIRE(child >= 0);
151*2cfd82f7SAlexander Leidinger 	if (child == 0) {
152*2cfd82f7SAlexander Leidinger 		close(pfd[1]);
153*2cfd82f7SAlexander Leidinger 		child_spin(pfd[0]);
154*2cfd82f7SAlexander Leidinger 		/* NOTREACHED */
155*2cfd82f7SAlexander Leidinger 	}
156*2cfd82f7SAlexander Leidinger 	close(pfd[0]);
157*2cfd82f7SAlexander Leidinger 
158*2cfd82f7SAlexander Leidinger 	ATF_REQUIRE(pmc_attach(pmcid, child) == 0);
159*2cfd82f7SAlexander Leidinger 	ATF_REQUIRE(pmc_write(pmcid, seed) == 0);
160*2cfd82f7SAlexander Leidinger 	ATF_REQUIRE(pmc_start(pmcid) == 0);
161*2cfd82f7SAlexander Leidinger 
162*2cfd82f7SAlexander Leidinger 	/* Release the child, which spins and exits. */
163*2cfd82f7SAlexander Leidinger 	ATF_REQUIRE(write(pfd[1], "g", 1) == 1);
164*2cfd82f7SAlexander Leidinger 	close(pfd[1]);
165*2cfd82f7SAlexander Leidinger 	ATF_REQUIRE(waitpid(child, &status, 0) == child);
166*2cfd82f7SAlexander Leidinger 
167*2cfd82f7SAlexander Leidinger 	/*
168*2cfd82f7SAlexander Leidinger 	 * The child is gone: its final increment was collected on the
169*2cfd82f7SAlexander Leidinger 	 * exit path.  Read the accumulated 64-bit count and stop.
170*2cfd82f7SAlexander Leidinger 	 */
171*2cfd82f7SAlexander Leidinger 	ATF_REQUIRE(pmc_read(pmcid, &final) == 0);
172*2cfd82f7SAlexander Leidinger 	(void)pmc_stop(pmcid);
173*2cfd82f7SAlexander Leidinger 
174*2cfd82f7SAlexander Leidinger 	ATF_CHECK_MSG(final >= seed,
175*2cfd82f7SAlexander Leidinger 	    "accumulated count went backwards: seed 0x%jx, final 0x%jx "
176*2cfd82f7SAlexander Leidinger 	    "(width %u)", (uintmax_t)seed, (uintmax_t)final, width);
177*2cfd82f7SAlexander Leidinger 	ATF_CHECK_MSG(final - seed < SANITY_BOUND,
178*2cfd82f7SAlexander Leidinger 	    "accumulated count jumped implausibly: seed 0x%jx, final 0x%jx "
179*2cfd82f7SAlexander Leidinger 	    "(width %u)", (uintmax_t)seed, (uintmax_t)final, width);
180*2cfd82f7SAlexander Leidinger 
181*2cfd82f7SAlexander Leidinger 	ATF_REQUIRE(pmc_release(pmcid) == 0);
182*2cfd82f7SAlexander Leidinger }
183*2cfd82f7SAlexander Leidinger 
184*2cfd82f7SAlexander Leidinger ATF_TC(counting_pmc_wraps_hardware_counter);
ATF_TC_HEAD(counting_pmc_wraps_hardware_counter,tc)185*2cfd82f7SAlexander Leidinger ATF_TC_HEAD(counting_pmc_wraps_hardware_counter, tc)
186*2cfd82f7SAlexander Leidinger {
187*2cfd82f7SAlexander Leidinger 	atf_tc_set_md_var(tc, "descr",
188*2cfd82f7SAlexander Leidinger 	    "A process-mode counting PMC survives its hardware counter "
189*2cfd82f7SAlexander Leidinger 	    "wrapping around while the counted process runs");
190*2cfd82f7SAlexander Leidinger 	atf_tc_set_md_var(tc, "require.user", "root");
191*2cfd82f7SAlexander Leidinger }
ATF_TC_BODY(counting_pmc_wraps_hardware_counter,tc)192*2cfd82f7SAlexander Leidinger ATF_TC_BODY(counting_pmc_wraps_hardware_counter, tc)
193*2cfd82f7SAlexander Leidinger {
194*2cfd82f7SAlexander Leidinger 	wrap_test(false);
195*2cfd82f7SAlexander Leidinger }
196*2cfd82f7SAlexander Leidinger 
197*2cfd82f7SAlexander Leidinger ATF_TC(counting_pmc_beyond_hardware_width);
ATF_TC_HEAD(counting_pmc_beyond_hardware_width,tc)198*2cfd82f7SAlexander Leidinger ATF_TC_HEAD(counting_pmc_beyond_hardware_width, tc)
199*2cfd82f7SAlexander Leidinger {
200*2cfd82f7SAlexander Leidinger 	atf_tc_set_md_var(tc, "descr",
201*2cfd82f7SAlexander Leidinger 	    "A process-mode counting PMC keeps counting correctly once "
202*2cfd82f7SAlexander Leidinger 	    "its accumulated count exceeds the hardware counter range");
203*2cfd82f7SAlexander Leidinger 	atf_tc_set_md_var(tc, "require.user", "root");
204*2cfd82f7SAlexander Leidinger }
ATF_TC_BODY(counting_pmc_beyond_hardware_width,tc)205*2cfd82f7SAlexander Leidinger ATF_TC_BODY(counting_pmc_beyond_hardware_width, tc)
206*2cfd82f7SAlexander Leidinger {
207*2cfd82f7SAlexander Leidinger 	wrap_test(true);
208*2cfd82f7SAlexander Leidinger }
209*2cfd82f7SAlexander Leidinger 
ATF_TP_ADD_TCS(tp)210*2cfd82f7SAlexander Leidinger ATF_TP_ADD_TCS(tp)
211*2cfd82f7SAlexander Leidinger {
212*2cfd82f7SAlexander Leidinger 	ATF_TP_ADD_TC(tp, counting_pmc_wraps_hardware_counter);
213*2cfd82f7SAlexander Leidinger 	ATF_TP_ADD_TC(tp, counting_pmc_beyond_hardware_width);
214*2cfd82f7SAlexander Leidinger 
215*2cfd82f7SAlexander Leidinger 	return (atf_no_error());
216*2cfd82f7SAlexander Leidinger }
217