1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2026 Alexander Leidinger <netchild@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other advertising materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /** 30 * @file 31 * Tests for hwpmc(4)'s process-attachment lifecycle: what happens as a PMC 32 * is attached, inherited, and torn down, especially when the teardown does 33 * not happen in the tidy order the common path assumes. 34 * 35 * The orderings driven here are a target that exits before it is 36 * detached, an owner that exits before its target (which reaches the 37 * REMOVE side of pmc_find_process_descriptor()), 38 * a release of a still-running attached PMC, row exhaustion released out 39 * of order, and PMC_F_DESCENDANTS, where the teardown has to account for a 40 * target the owner never attached by hand. 41 * 42 * All PMCs are SOFT-class, so nothing here needs a hardware counter. 43 */ 44 45 #include <sys/param.h> 46 #include <sys/wait.h> 47 48 #include <errno.h> 49 #include <pmc.h> 50 #include <signal.h> 51 #include <stdint.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 57 #include <atf-c.h> 58 59 static void 60 require_hwpmc(void) 61 { 62 63 if (pmc_init() != 0) 64 atf_tc_skip("hwpmc(4) is not available"); 65 } 66 67 /** 68 * @internal 69 * Allocate the first SOFT event this mode and flag set accept. Which event 70 * it is does not matter; that one exists is what the case needs. 71 */ 72 static pmc_id_t 73 allocate_soft_pmc_flags(enum pmc_mode mode, uint32_t flags) 74 { 75 const char **names; 76 char spec[128]; 77 pmc_id_t id; 78 int nnames, i; 79 80 if (pmc_event_names_of_class(PMC_CLASS_SOFT, &names, &nnames) != 0) 81 return (PMC_ID_INVALID); 82 for (i = 0; i < nnames; i++) { 83 (void)snprintf(spec, sizeof(spec), "SOFT-%s", names[i]); 84 if (pmc_allocate(spec, mode, flags, PMC_CPU_ANY, &id, 0) == 0) 85 return (id); 86 } 87 return (PMC_ID_INVALID); 88 } 89 90 static pmc_id_t 91 require_soft_pmc(enum pmc_mode mode) 92 { 93 pmc_id_t id; 94 95 if ((id = allocate_soft_pmc_flags(mode, 0)) == PMC_ID_INVALID) 96 atf_tc_skip("no SOFT-class PMC is allocatable"); 97 return (id); 98 } 99 100 /** 101 * @internal 102 * A child that does nothing until killed, for use as an attach target. 103 */ 104 static pid_t 105 spawn_idle_child(void) 106 { 107 pid_t pid; 108 109 pid = fork(); 110 if (pid == 0) { 111 for (;;) 112 (void)pause(); 113 } 114 return (pid); 115 } 116 117 static void 118 reap(pid_t pid) 119 { 120 int status; 121 122 (void)kill(pid, SIGKILL); 123 (void)waitpid(pid, &status, 0); 124 } 125 126 /** 127 * @internal 128 * The bookkeeping around attach and detach. 129 */ 130 ATF_TC_WITHOUT_HEAD(attach_detach_bookkeeping); 131 ATF_TC_BODY(attach_detach_bookkeeping, tc) 132 { 133 pmc_id_t id; 134 pid_t other; 135 136 require_hwpmc(); 137 id = require_soft_pmc(PMC_MODE_TC); 138 other = spawn_idle_child(); 139 ATF_REQUIRE(other > 0); 140 141 ATF_REQUIRE_MSG(pmc_attach(id, getpid()) == 0, "pmc_attach: %s", 142 strerror(errno)); 143 144 /* A second attach of the same target must be refused, not doubled. */ 145 errno = 0; 146 ATF_CHECK_MSG(pmc_attach(id, getpid()) != 0, 147 "a second attach of the same target succeeded"); 148 149 /* Detaching a pid that was never attached must be refused. */ 150 errno = 0; 151 ATF_CHECK_MSG(pmc_detach(id, other) != 0, 152 "detached a target that was never attached"); 153 154 /* One detach undoes the one attach. */ 155 ATF_CHECK_MSG(pmc_detach(id, getpid()) == 0, "pmc_detach: %s", 156 strerror(errno)); 157 158 /* A second detach must now be refused. */ 159 errno = 0; 160 ATF_CHECK_MSG(pmc_detach(id, getpid()) != 0, 161 "a second detach of the same target succeeded"); 162 163 ATF_CHECK_MSG(pmc_release(id) == 0, "pmc_release: %s", 164 strerror(errno)); 165 reap(other); 166 } 167 168 /** 169 * @internal 170 * Detach a target that has already exited and been reaped. The 171 * kernel's own eventhandler removes an exiting process's descriptor, so by 172 * the time we detach there is nothing there; it must say so cleanly, and 173 * the PMC must still release. 174 */ 175 ATF_TC_WITHOUT_HEAD(detach_after_target_reaped); 176 ATF_TC_BODY(detach_after_target_reaped, tc) 177 { 178 pmc_id_t id; 179 pid_t target; 180 181 require_hwpmc(); 182 id = require_soft_pmc(PMC_MODE_TC); 183 target = spawn_idle_child(); 184 ATF_REQUIRE(target > 0); 185 186 ATF_REQUIRE_MSG(pmc_attach(id, target) == 0, "pmc_attach: %s", 187 strerror(errno)); 188 189 /* Kill and fully reap the target before detaching. */ 190 reap(target); 191 192 errno = 0; 193 ATF_CHECK_MSG(pmc_detach(id, target) != 0, 194 "detached a target that had exited and been reaped"); 195 196 ATF_CHECK_MSG(pmc_release(id) == 0, "pmc_release: %s", 197 strerror(errno)); 198 } 199 200 /** 201 * @internal 202 * Release a PMC that is still attached and still running, with no 203 * prior detach or stop: the release path has to reclaim the reference 204 * the running PMC still holds rather than wedge. 205 */ 206 ATF_TC_WITHOUT_HEAD(release_running_attached); 207 ATF_TC_BODY(release_running_attached, tc) 208 { 209 pmc_id_t id; 210 211 require_hwpmc(); 212 id = require_soft_pmc(PMC_MODE_TC); 213 214 ATF_REQUIRE_MSG(pmc_attach(id, getpid()) == 0, "pmc_attach: %s", 215 strerror(errno)); 216 ATF_REQUIRE_MSG(pmc_start(id) == 0, "pmc_start: %s", strerror(errno)); 217 218 /* Run a little, so the PMC is actually loaded on a CPU. */ 219 { 220 volatile unsigned long s __unused = 0; 221 int i; 222 223 for (i = 0; i < 5000000; i++) 224 s += i; 225 } 226 (void)usleep(100000); 227 228 /* Straight to release: no stop, no detach. */ 229 ATF_CHECK_MSG(pmc_release(id) == 0, 230 "release of a running, attached PMC failed: %s", strerror(errno)); 231 232 /* The driver is still usable afterwards. */ 233 { 234 pmc_id_t again = allocate_soft_pmc_flags(PMC_MODE_TC, 0); 235 236 ATF_CHECK_MSG(again != PMC_ID_INVALID, 237 "hwpmc is unusable after the release"); 238 if (again != PMC_ID_INVALID) 239 (void)pmc_release(again); 240 } 241 } 242 243 /** 244 * @internal 245 * The owner exits before the target. A child process is the owner: 246 * it allocates a PMC and attaches it to a sibling that outlives it, then 247 * exits without releasing. The kernel tears the owner's PMCs down through 248 * the REMOVE side of pmc_find_process_descriptor() - the other of its two 249 * unlink mechanisms, which no test exercised. The parent then confirms 250 * the driver is still usable and the target still alive. 251 */ 252 ATF_TC_WITHOUT_HEAD(owner_exit_before_target); 253 ATF_TC_BODY(owner_exit_before_target, tc) 254 { 255 pmc_id_t id; 256 pid_t target, owner; 257 int status; 258 259 require_hwpmc(); 260 261 target = spawn_idle_child(); 262 ATF_REQUIRE(target > 0); 263 264 owner = fork(); 265 ATF_REQUIRE(owner >= 0); 266 if (owner == 0) { 267 /* Owner: attach to the sibling target, then just exit. */ 268 if (pmc_init() != 0) 269 _exit(10); 270 id = allocate_soft_pmc_flags(PMC_MODE_TC, 0); 271 if (id == PMC_ID_INVALID) 272 _exit(11); 273 if (pmc_attach(id, target) != 0) 274 _exit(12); 275 if (pmc_start(id) != 0) 276 _exit(13); 277 /* Exit with the PMC still allocated, attached and running. */ 278 _exit(0); 279 } 280 281 ATF_REQUIRE(waitpid(owner, &status, 0) == owner); 282 ATF_REQUIRE_MSG(WIFEXITED(status), 283 "the owner did not exit normally (it may have panicked the " 284 "kernel; status 0x%x)", status); 285 ATF_REQUIRE_MSG(WEXITSTATUS(status) == 0, 286 "the owner's setup failed (exit %d): 10=init 11=alloc 12=attach " 287 "13=start", WEXITSTATUS(status)); 288 289 /* 290 * The owner is gone. Its descriptor teardown must have run without 291 * wedging: prove the driver still works and the target is still here. 292 */ 293 id = allocate_soft_pmc_flags(PMC_MODE_TC, 0); 294 ATF_CHECK_MSG(id != PMC_ID_INVALID, 295 "hwpmc is unusable after the owner exited with a live target"); 296 if (id != PMC_ID_INVALID) { 297 ATF_CHECK(pmc_attach(id, getpid()) == 0); 298 ATF_CHECK(pmc_detach(id, getpid()) == 0); 299 ATF_CHECK(pmc_release(id) == 0); 300 } 301 ATF_CHECK_MSG(kill(target, 0) == 0, 302 "the target did not survive the owner's exit"); 303 reap(target); 304 } 305 306 /** 307 * @internal 308 * Allocate SOFT PMCs until the rows are exhausted, then release them 309 * in an order other than allocation order. Exercises the row free-list 310 * against out-of-order frees. 311 */ 312 ATF_TC_WITHOUT_HEAD(exhaust_then_release_reverse); 313 ATF_TC_BODY(exhaust_then_release_reverse, tc) 314 { 315 pmc_id_t ids[64]; 316 int n, i; 317 318 require_hwpmc(); 319 320 n = 0; 321 while (n < (int)nitems(ids)) { 322 pmc_id_t id = allocate_soft_pmc_flags(PMC_MODE_TC, 0); 323 324 if (id == PMC_ID_INVALID) 325 break; 326 ids[n++] = id; 327 } 328 ATF_REQUIRE_MSG(n > 0, "not one SOFT PMC could be allocated"); 329 330 /* At least one allocation should have failed at exhaustion. */ 331 ATF_CHECK_MSG(n < (int)nitems(ids), 332 "allocation never hit a limit in %zu tries", nitems(ids)); 333 334 /* Release in reverse order; every one must succeed. */ 335 for (i = n - 1; i >= 0; i--) 336 ATF_CHECK_MSG(pmc_release(ids[i]) == 0, 337 "reverse release of id 0x%08x (slot %d) failed: %s", 338 ids[i], i, strerror(errno)); 339 } 340 341 /** 342 * @internal 343 * A PMC_F_DESCENDANTS counter, whose child inherits the attachment. 344 * The owner attaches to itself with descendants set, forks a child that 345 * does a little work and exits, reaps it, and releases. The teardown has 346 * to account for the child the owner never attached to by hand. 347 */ 348 ATF_TC_WITHOUT_HEAD(descendants_inherit_and_release); 349 ATF_TC_BODY(descendants_inherit_and_release, tc) 350 { 351 pmc_id_t id; 352 pid_t child; 353 int status; 354 355 require_hwpmc(); 356 id = allocate_soft_pmc_flags(PMC_MODE_TC, PMC_F_DESCENDANTS); 357 if (id == PMC_ID_INVALID) 358 atf_tc_skip("no SOFT PMC allocatable with PMC_F_DESCENDANTS"); 359 360 ATF_REQUIRE_MSG(pmc_attach(id, getpid()) == 0, "pmc_attach: %s", 361 strerror(errno)); 362 ATF_REQUIRE_MSG(pmc_start(id) == 0, "pmc_start: %s", strerror(errno)); 363 364 child = fork(); 365 ATF_REQUIRE(child >= 0); 366 if (child == 0) { 367 volatile unsigned long s __unused = 0; 368 int i; 369 370 for (i = 0; i < 2000000; i++) 371 s += i; 372 _exit(0); 373 } 374 ATF_REQUIRE(waitpid(child, &status, 0) == child); 375 ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0, 376 "the descendant did not exit cleanly (status 0x%x)", status); 377 378 ATF_CHECK(pmc_stop(id) == 0); 379 ATF_CHECK_MSG(pmc_release(id) == 0, 380 "release of a descendants PMC failed: %s", strerror(errno)); 381 } 382 383 /** 384 * @internal 385 * The fork-storm variant: descendants set, then many short-lived children, 386 * so several inherit-and-exit teardowns overlap, then release. 387 */ 388 ATF_TC_WITHOUT_HEAD(descendants_fork_storm); 389 ATF_TC_BODY(descendants_fork_storm, tc) 390 { 391 pmc_id_t id; 392 int i, status; 393 394 require_hwpmc(); 395 id = allocate_soft_pmc_flags(PMC_MODE_TC, PMC_F_DESCENDANTS); 396 if (id == PMC_ID_INVALID) 397 atf_tc_skip("no SOFT PMC allocatable with PMC_F_DESCENDANTS"); 398 399 ATF_REQUIRE_MSG(pmc_attach(id, getpid()) == 0, "pmc_attach: %s", 400 strerror(errno)); 401 ATF_REQUIRE_MSG(pmc_start(id) == 0, "pmc_start: %s", strerror(errno)); 402 403 for (i = 0; i < 32; i++) { 404 pid_t c = fork(); 405 406 if (c == 0) { 407 volatile unsigned long s __unused = 0; 408 int j; 409 410 for (j = 0; j < 200000; j++) 411 s += j; 412 _exit(0); 413 } 414 if (c > 0) 415 (void)waitpid(c, &status, 0); 416 } 417 418 ATF_CHECK(pmc_stop(id) == 0); 419 ATF_CHECK_MSG(pmc_release(id) == 0, 420 "release after a descendants fork storm failed: %s", 421 strerror(errno)); 422 } 423 424 ATF_TP_ADD_TCS(tp) 425 { 426 427 ATF_TP_ADD_TC(tp, attach_detach_bookkeeping); 428 ATF_TP_ADD_TC(tp, detach_after_target_reaped); 429 ATF_TP_ADD_TC(tp, release_running_attached); 430 ATF_TP_ADD_TC(tp, owner_exit_before_target); 431 ATF_TP_ADD_TC(tp, exhaust_then_release_reverse); 432 ATF_TP_ADD_TC(tp, descendants_inherit_and_release); 433 ATF_TP_ADD_TC(tp, descendants_fork_storm); 434 435 return (atf_no_error()); 436 } 437