Page MenuHomeFreeBSD

D59371.id189835.diff
No OneTemporary

D59371.id189835.diff

diff --git a/tests/sys/pmc/Makefile b/tests/sys/pmc/Makefile
--- a/tests/sys/pmc/Makefile
+++ b/tests/sys/pmc/Makefile
@@ -4,6 +4,9 @@
ATF_TESTS_C+= pmc_credexec_test
ATF_TESTS_C+= pmc_detach_test
ATF_TESTS_C+= pmc_exec_test
+ATF_TESTS_C+= pmc_group_gate_test
+ATF_TESTS_C+= pmc_group_test
+ATF_TESTS_C+= pmc_groupread_test
.if ${MACHINE_ARCH} == "amd64"
ATF_TESTS_C+= pmc_exterr_test
.endif
diff --git a/tests/sys/pmc/pmc_group_gate_test.c b/tests/sys/pmc/pmc_group_gate_test.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/pmc/pmc_group_gate_test.c
@@ -0,0 +1,204 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ */
+
+/*
+ * The v1.4 input gates.
+ * Allocation accepts the inputs the revision makes legal:
+ * PMC_F_DESCENDANTS, and system-wide sampling on a deferred PMC.
+ * Commit rejects the two ways of misusing PMC_F_DESCENDANTS.
+ *
+ * The split matters for feature probing.
+ * Allocation-time EOPNOTSUPP is the standard signal for "this module
+ * does not have the feature".
+ * So misuse of a feature that IS present must instead return EINVAL
+ * from commit. A tool that saw EOPNOTSUPP here would wrongly report
+ * a working module as one without grouping.
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <errno.h>
+#include <pmc.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define TEST_EVENT "instructions"
+#define TEST_CPU 0
+#define SAMPLE_COUNT 65536
+
+static bool
+is_amd(void)
+{
+ char vendor[64];
+ size_t len;
+
+ len = sizeof(vendor);
+ if (sysctlbyname("kern.hwpmc.cpuid", vendor, &len, NULL, 0) != 0)
+ return (false);
+ return (strstr(vendor, "AuthenticAMD") != NULL ||
+ strstr(vendor, "HygonGenuine") != NULL);
+}
+
+static void
+require_hwpmc(void)
+{
+
+ if (pmc_init() != 0)
+ atf_tc_skip("hwpmc(4) is not available: %s", strerror(errno));
+ if (!is_amd())
+ atf_tc_skip("PMC grouping is supported only on AMD CPUs");
+}
+
+static void
+require_root(void)
+{
+
+ if (geteuid() != 0)
+ atf_tc_skip("system-mode PMCs require root");
+}
+
+/*
+ * Commit a two-member group. The leader and non-leader carry the
+ * given flags. Return the commit result: 0, or -1 with errno set.
+ * Release everything before returning.
+ */
+static int
+commit_with_flags(enum pmc_mode mode, int cpu, uint32_t leader_flags,
+ uint32_t member_flags, uint64_t count)
+{
+ uint32_t gid;
+ pmc_id_t leader, member;
+ int error, rv;
+
+ leader = member = PMC_ID_INVALID;
+ ATF_REQUIRE_MSG(pmc_allocate_group(TEST_EVENT, mode, leader_flags, cpu,
+ &leader, count) == 0,
+ "leader pmc_allocate_group failed: errno %d (%s)", errno,
+ strerror(errno));
+ ATF_REQUIRE_MSG(pmc_allocate_group(TEST_EVENT, mode, member_flags, cpu,
+ &member, count) == 0,
+ "member pmc_allocate_group failed: errno %d (%s)", errno,
+ strerror(errno));
+ ATF_REQUIRE_MSG(pmc_group_create(&gid) == 0,
+ "pmc_group_create failed: errno %d (%s)", errno, strerror(errno));
+ ATF_REQUIRE_MSG(pmc_group_add(gid, leader, 1) == 0,
+ "leader pmc_group_add failed: errno %d (%s)", errno,
+ strerror(errno));
+ ATF_REQUIRE_MSG(pmc_group_add(gid, member, 0) == 0,
+ "member pmc_group_add failed: errno %d (%s)", errno,
+ strerror(errno));
+ rv = pmc_group_commit(gid);
+
+ error = errno;
+ if (leader != PMC_ID_INVALID)
+ (void)pmc_release(leader);
+ if (member != PMC_ID_INVALID)
+ (void)pmc_release(member);
+ errno = error;
+ return (rv);
+}
+
+/* Allocate accepts PMC_F_DESCENDANTS. The leader designation comes later. */
+ATF_TC_WITHOUT_HEAD(alloc_descendants_accepted);
+ATF_TC_BODY(alloc_descendants_accepted, tc)
+{
+ pmc_id_t id;
+
+ require_hwpmc();
+ ATF_REQUIRE_MSG(pmc_allocate_group(TEST_EVENT, PMC_MODE_TC,
+ PMC_F_DESCENDANTS, PMC_CPU_ANY, &id, 0) == 0,
+ "deferred alloc with PMC_F_DESCENDANTS failed: %s",
+ strerror(errno));
+ ATF_CHECK_EQ(pmc_release(id), 0);
+}
+
+/* Allocate accepts a deferred system-sampling allocation. */
+ATF_TC_WITHOUT_HEAD(alloc_system_sampling_accepted);
+ATF_TC_BODY(alloc_system_sampling_accepted, tc)
+{
+ pmc_id_t id;
+
+ require_hwpmc();
+ require_root();
+ ATF_REQUIRE_MSG(pmc_allocate_group(TEST_EVENT, PMC_MODE_SS, 0,
+ TEST_CPU, &id, SAMPLE_COUNT) == 0,
+ "deferred PMC_MODE_SS alloc failed: %s", strerror(errno));
+ ATF_CHECK_EQ(pmc_release(id), 0);
+}
+
+/* A grouped sampling allocation may carry a nonzero count. Counting may not. */
+ATF_TC_WITHOUT_HEAD(alloc_count_gate_is_counting_only);
+ATF_TC_BODY(alloc_count_gate_is_counting_only, tc)
+{
+ pmc_id_t id;
+
+ require_hwpmc();
+ ATF_REQUIRE_MSG(pmc_allocate_group(TEST_EVENT, PMC_MODE_TS, 0,
+ PMC_CPU_ANY, &id, SAMPLE_COUNT) == 0,
+ "grouped sampling alloc with nonzero count failed: %s",
+ strerror(errno));
+ ATF_CHECK_EQ(pmc_release(id), 0);
+
+ errno = 0;
+ ATF_CHECK_EQ(pmc_allocate_group(TEST_EVENT, PMC_MODE_TC, 0,
+ PMC_CPU_ANY, &id, SAMPLE_COUNT), -1);
+ ATF_CHECK_EQ(errno, EINVAL);
+}
+
+/* The flag is leader-only. On a non-leader, commit fails with EINVAL. */
+ATF_TC_WITHOUT_HEAD(commit_descendants_on_nonleader);
+ATF_TC_BODY(commit_descendants_on_nonleader, tc)
+{
+
+ require_hwpmc();
+ errno = 0;
+ ATF_CHECK_EQ(commit_with_flags(PMC_MODE_TC, PMC_CPU_ANY, 0,
+ PMC_F_DESCENDANTS, 0), -1);
+ ATF_CHECK_EQ(errno, EINVAL);
+}
+
+/* A CPU-bound group cannot follow a fork. Commit fails with EINVAL. */
+ATF_TC_WITHOUT_HEAD(commit_descendants_on_system_leader);
+ATF_TC_BODY(commit_descendants_on_system_leader, tc)
+{
+
+ require_hwpmc();
+ require_root();
+ errno = 0;
+ ATF_CHECK_EQ(commit_with_flags(PMC_MODE_SC, TEST_CPU,
+ PMC_F_DESCENDANTS, 0, 0), -1);
+ ATF_CHECK_EQ(errno, EINVAL);
+}
+
+/* The same group without the flag commits. So the flag causes the EINVAL above. */
+ATF_TC_WITHOUT_HEAD(commit_descendants_on_virtual_leader);
+ATF_TC_BODY(commit_descendants_on_virtual_leader, tc)
+{
+
+ require_hwpmc();
+ ATF_CHECK_MSG(commit_with_flags(PMC_MODE_TC, PMC_CPU_ANY,
+ PMC_F_DESCENDANTS, 0, 0) == 0,
+ "virtual group with PMC_F_DESCENDANTS on the leader failed to "
+ "commit: %s", strerror(errno));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, alloc_descendants_accepted);
+ ATF_TP_ADD_TC(tp, alloc_system_sampling_accepted);
+ ATF_TP_ADD_TC(tp, alloc_count_gate_is_counting_only);
+ ATF_TP_ADD_TC(tp, commit_descendants_on_nonleader);
+ ATF_TP_ADD_TC(tp, commit_descendants_on_system_leader);
+ ATF_TP_ADD_TC(tp, commit_descendants_on_virtual_leader);
+
+ return (atf_no_error());
+}
diff --git a/tests/sys/pmc/pmc_group_test.c b/tests/sys/pmc/pmc_group_test.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/pmc/pmc_group_test.c
@@ -0,0 +1,258 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <pmc.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static bool
+is_amd(void)
+{
+ char vendor[64];
+ size_t len;
+
+ len = sizeof(vendor);
+ if (sysctlbyname("kern.hwpmc.cpuid", vendor, &len, NULL, 0) != 0)
+ return (false);
+ return (strstr(vendor, "AuthenticAMD") != NULL ||
+ strstr(vendor, "HygonGenuine") != NULL);
+}
+
+static void
+require_hwpmc(void)
+{
+
+ if (pmc_init() != 0)
+ atf_tc_skip("hwpmc(4) is not available: %s", strerror(errno));
+ if (!is_amd())
+ atf_tc_skip("PMC grouping is supported only on AMD CPUs");
+}
+
+static void __attribute__((noinline))
+spin(void)
+{
+ volatile uint64_t sink;
+ uint64_t i;
+
+ sink = 0;
+ for (i = 0; i < 10 * 1000 * 1000; i++)
+ sink += i;
+}
+
+static pmc_id_t
+allocate_grouped(void)
+{
+ pmc_id_t id;
+
+ ATF_REQUIRE_MSG(pmc_allocate_group("instructions", PMC_MODE_TC, 0,
+ PMC_CPU_ANY, &id, 0) == 0, "pmc_allocate_group failed: %s",
+ strerror(errno));
+ return (id);
+}
+
+ATF_TC_WITHOUT_HEAD(committed_release);
+ATF_TC_BODY(committed_release, tc)
+{
+ pmc_id_t fresh, leader, sibling;
+ pmc_value_t value;
+ uint32_t groupid;
+
+ require_hwpmc();
+ leader = allocate_grouped();
+ sibling = allocate_grouped();
+ ATF_REQUIRE_MSG(pmc_group_create(&groupid) == 0,
+ "pmc_group_create failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_group_add(groupid, leader, 1) == 0,
+ "leader add failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_group_add(groupid, sibling, 0) == 0,
+ "sibling add failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_group_commit(groupid) == 0,
+ "pmc_group_commit failed: %s", strerror(errno));
+
+ errno = 0;
+ ATF_CHECK_EQ(pmc_detach(leader, getpid()), -1);
+ ATF_CHECK_EQ(errno, EBUSY);
+ errno = 0;
+ ATF_CHECK_EQ(pmc_detach(sibling, getpid()), -1);
+ ATF_CHECK_EQ(errno, EBUSY);
+ ATF_REQUIRE_MSG(pmc_attach(leader, getpid()) == 0,
+ "leader attach failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_start(leader) == 0,
+ "leader start failed: %s", strerror(errno));
+ spin();
+
+ ATF_REQUIRE_MSG(pmc_release(leader) == 0,
+ "leader release failed: %s", strerror(errno));
+ errno = 0;
+ ATF_CHECK_EQ(pmc_release(sibling), -1);
+ ATF_CHECK_EQ(errno, EINVAL);
+ errno = 0;
+ ATF_CHECK_EQ(pmc_read(sibling, &value), -1);
+ ATF_CHECK_EQ(errno, EINVAL);
+
+ fresh = allocate_grouped();
+ ATF_CHECK(fresh != sibling);
+ errno = 0;
+ ATF_CHECK_EQ(pmc_group_add(groupid, fresh, 1), -1);
+ ATF_CHECK_EQ(errno, EINVAL);
+ ATF_REQUIRE(pmc_release(fresh) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(committed_nonleader_errno_contract);
+ATF_TC_BODY(committed_nonleader_errno_contract, tc)
+{
+ pmc_id_t leader, sibling;
+ uint32_t groupid;
+ int error;
+
+ require_hwpmc();
+ leader = allocate_grouped();
+ sibling = allocate_grouped();
+ ATF_REQUIRE_MSG(pmc_group_create(&groupid) == 0,
+ "pmc_group_create failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_group_add(groupid, leader, 1) == 0,
+ "leader add failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_group_add(groupid, sibling, 0) == 0,
+ "sibling add failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_group_commit(groupid) == 0,
+ "pmc_group_commit failed: %s", strerror(errno));
+
+ errno = 0;
+ error = pmc_attach(sibling, getpid());
+ ATF_CHECK_MSG(error == -1 && errno == ENOTTY,
+ "non-leader pmc_attach returned %d, errno %d (%s), expected "
+ "-1/ENOTTY", error, errno, strerror(errno));
+ errno = 0;
+ error = pmc_start(sibling);
+ ATF_CHECK_MSG(error == -1 && errno == ENOTTY,
+ "non-leader pmc_start returned %d, errno %d (%s), expected "
+ "-1/ENOTTY", error, errno, strerror(errno));
+ errno = 0;
+ error = pmc_stop(sibling);
+ ATF_CHECK_MSG(error == -1 && errno == ENOTTY,
+ "non-leader pmc_stop returned %d, errno %d (%s), expected "
+ "-1/ENOTTY", error, errno, strerror(errno));
+ errno = 0;
+ error = pmc_release(sibling);
+ ATF_CHECK_MSG(error == -1 && errno == ENOTTY,
+ "non-leader pmc_release returned %d, errno %d (%s), expected "
+ "-1/ENOTTY", error, errno, strerror(errno));
+
+ ATF_REQUIRE_MSG(pmc_release(leader) == 0,
+ "leader cleanup release failed: %s", strerror(errno));
+}
+
+ATF_TC_WITHOUT_HEAD(uncommitted_release);
+ATF_TC_BODY(uncommitted_release, tc)
+{
+ pmc_id_t leader, new_leader, probe, sibling;
+ uint32_t groupid, released_groupid;
+
+ require_hwpmc();
+ leader = allocate_grouped();
+ sibling = allocate_grouped();
+ ATF_REQUIRE(pmc_group_create(&groupid) == 0);
+ ATF_REQUIRE(pmc_group_add(groupid, leader, 1) == 0);
+ ATF_REQUIRE(pmc_group_add(groupid, sibling, 0) == 0);
+
+ ATF_REQUIRE_MSG(pmc_release(leader) == 0,
+ "uncommitted leader release failed: %s", strerror(errno));
+ errno = 0;
+ ATF_CHECK_EQ(pmc_group_commit(groupid), -1);
+ ATF_CHECK_EQ(errno, EINVAL);
+
+ new_leader = allocate_grouped();
+ ATF_REQUIRE_MSG(pmc_group_add(groupid, new_leader, 1) == 0,
+ "replacement leader add failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_group_commit(groupid) == 0,
+ "replacement leader commit failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_release(new_leader) == 0,
+ "replacement leader release failed: %s", strerror(errno));
+
+ probe = allocate_grouped();
+ ATF_REQUIRE(pmc_group_create(&released_groupid) == 0);
+ ATF_REQUIRE(pmc_group_add(released_groupid, probe, 0) == 0);
+ ATF_REQUIRE_MSG(pmc_release(probe) == 0,
+ "last uncommitted member release failed: %s", strerror(errno));
+ probe = allocate_grouped();
+ errno = 0;
+ ATF_CHECK_EQ(pmc_group_add(released_groupid, probe, 1), -1);
+ ATF_CHECK_EQ(errno, EINVAL);
+ ATF_REQUIRE(pmc_release(probe) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(owner_exit);
+ATF_TC_BODY(owner_exit, tc)
+{
+ pmc_id_t id;
+ uint32_t groupid;
+ pid_t pid;
+ int status;
+
+ require_hwpmc();
+ pid = fork();
+ ATF_REQUIRE(pid >= 0);
+ if (pid == 0) {
+ if (pmc_allocate_group("instructions", PMC_MODE_TC, 0,
+ PMC_CPU_ANY, &id, 0) != 0 ||
+ pmc_group_create(&groupid) != 0 ||
+ pmc_group_add(groupid, id, 1) != 0 ||
+ pmc_group_commit(groupid) != 0 || pmc_attach(id, 0) != 0 ||
+ pmc_start(id) != 0)
+ _exit(1);
+ spin();
+ _exit(0);
+ }
+ ATF_REQUIRE(waitpid(pid, &status, 0) == pid);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ id = allocate_grouped();
+ ATF_REQUIRE(pmc_group_create(&groupid) == 0);
+ ATF_REQUIRE(pmc_group_add(groupid, id, 1) == 0);
+ ATF_REQUIRE_MSG(pmc_group_commit(groupid) == 0,
+ "post-exit group commit failed: %s", strerror(errno));
+ ATF_REQUIRE(pmc_release(id) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(uncommitted_nonleader_release);
+ATF_TC_BODY(uncommitted_nonleader_release, tc)
+{
+ pmc_id_t leader, sibling;
+ uint32_t groupid;
+
+ require_hwpmc();
+ leader = allocate_grouped();
+ sibling = allocate_grouped();
+ ATF_REQUIRE(pmc_group_create(&groupid) == 0);
+ ATF_REQUIRE(pmc_group_add(groupid, leader, 1) == 0);
+ ATF_REQUIRE(pmc_group_add(groupid, sibling, 0) == 0);
+ ATF_REQUIRE_MSG(pmc_release(sibling) == 0,
+ "uncommitted sibling release failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_group_commit(groupid) == 0,
+ "one-member commit failed: %s", strerror(errno));
+ ATF_REQUIRE(pmc_release(leader) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, committed_release);
+ ATF_TP_ADD_TC(tp, committed_nonleader_errno_contract);
+ ATF_TP_ADD_TC(tp, owner_exit);
+ ATF_TP_ADD_TC(tp, uncommitted_release);
+ ATF_TP_ADD_TC(tp, uncommitted_nonleader_release);
+ return (atf_no_error());
+}
diff --git a/tests/sys/pmc/pmc_groupread_test.c b/tests/sys/pmc/pmc_groupread_test.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/pmc/pmc_groupread_test.c
@@ -0,0 +1,460 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <errno.h>
+#include <pmc.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define SPIN_ITERS (50ULL * 1000 * 1000)
+
+struct test_group {
+ uint32_t tg_groupid;
+ pmc_id_t tg_ids[2];
+ u_int tg_nmembers;
+ u_int tg_leader;
+ bool tg_started;
+};
+
+static bool
+is_amd(void)
+{
+ char vendor[64];
+ size_t len;
+
+ len = sizeof(vendor);
+ if (sysctlbyname("kern.hwpmc.cpuid", vendor, &len, NULL, 0) != 0)
+ return (false);
+ return (strstr(vendor, "AuthenticAMD") != NULL ||
+ strstr(vendor, "HygonGenuine") != NULL);
+}
+
+static void
+cleanup_group(struct test_group *group)
+{
+
+ if (group->tg_started)
+ (void)pmc_stop(group->tg_ids[group->tg_leader]);
+ (void)pmc_release(group->tg_ids[group->tg_leader]);
+}
+
+static int
+setup_group(struct test_group *group, u_int nmembers, u_int leader)
+{
+ u_int i;
+ int error;
+
+ memset(group, 0, sizeof(*group));
+ group->tg_nmembers = nmembers;
+ group->tg_leader = leader;
+ for (i = 0; i < nitems(group->tg_ids); i++)
+ group->tg_ids[i] = PMC_ID_INVALID;
+ for (i = 0; i < nmembers; i++) {
+ if (pmc_allocate_group("instructions", PMC_MODE_TC, 0,
+ PMC_CPU_ANY, &group->tg_ids[i], 0) != 0)
+ goto fail;
+ }
+ if (pmc_group_create(&group->tg_groupid) != 0)
+ goto fail;
+ for (i = 0; i < nmembers; i++) {
+ if (pmc_group_add(group->tg_groupid, group->tg_ids[i],
+ i == leader) != 0)
+ goto fail;
+ }
+ errno = 0;
+ if (pmc_group_add(group->tg_groupid, group->tg_ids[leader], 1) == 0 ||
+ errno != EINVAL) {
+ errno = EPROTO;
+ goto fail;
+ }
+ if (pmc_group_commit(group->tg_groupid) != 0)
+ goto fail;
+ return (0);
+
+fail:
+ error = errno;
+ for (i = 0; i < nmembers; i++) {
+ if (group->tg_ids[i] != PMC_ID_INVALID)
+ (void)pmc_release(group->tg_ids[i]);
+ }
+ errno = error;
+ return (-1);
+}
+
+static void
+require_hwpmc(void)
+{
+
+ if (pmc_init() != 0)
+ atf_tc_skip("hwpmc(4) is not available: %s", strerror(errno));
+ if (!is_amd())
+ atf_tc_skip("PMC grouping is supported only on AMD CPUs");
+}
+
+static void __attribute__((noinline))
+spin(void)
+{
+ volatile uint64_t sink;
+ uint64_t i;
+
+ sink = 0;
+ for (i = 0; i < SPIN_ITERS; i++)
+ sink += i;
+}
+
+ATF_TC_WITHOUT_HEAD(capacity_and_order);
+ATF_TC_BODY(capacity_and_order, tc)
+{
+ struct pmc_group_member members[2], members_before[2];
+ struct pmc_group_times times, times_before;
+ struct test_group group;
+ pmc_value_t value;
+ uint64_t enabled, running;
+ uint32_t n;
+ int rv;
+
+ require_hwpmc();
+ ATF_REQUIRE_MSG(setup_group(&group, 2, 1) == 0,
+ "setup_group failed: %s", strerror(errno));
+
+ /* A size query returns the member count and, if asked, the times. */
+ memset(&times, 0xa5, sizeof(times));
+ memcpy(&times_before, &times, sizeof(times));
+ n = 0;
+ ATF_CHECK_EQ(pmc_group_read(group.tg_ids[group.tg_leader], &n, NULL,
+ &times), 0);
+ ATF_CHECK_EQ(n, 2);
+ ATF_CHECK(memcmp(&times, &times_before, sizeof(times)) != 0);
+ ATF_CHECK(times.pgt_running <= times.pgt_enabled);
+
+ /* A size query without times must succeed. */
+ n = 0;
+ ATF_CHECK_EQ(pmc_group_read(group.tg_ids[group.tg_leader], &n, NULL,
+ NULL), 0);
+ ATF_CHECK_EQ(n, 2);
+
+ memset(members, 0xa5, sizeof(members));
+ memcpy(members_before, members, sizeof(members));
+ memset(&times, 0xa5, sizeof(times));
+ memcpy(&times_before, &times, sizeof(times));
+ n = 1;
+ errno = 0;
+ rv = pmc_group_read(group.tg_ids[group.tg_leader], &n, members,
+ &times);
+ ATF_CHECK_EQ(rv, -1);
+ ATF_CHECK_EQ(errno, E2BIG);
+ ATF_CHECK_EQ(n, 2);
+ ATF_CHECK(memcmp(members, members_before, sizeof(members)) == 0);
+ ATF_CHECK(memcmp(&times, &times_before, sizeof(times)) == 0);
+
+ memset(members, 0, sizeof(members));
+ memset(&times, 0, sizeof(times));
+ n = nitems(members);
+ ATF_CHECK_EQ(pmc_group_read(group.tg_ids[group.tg_leader], &n,
+ members, &times), 0);
+ ATF_CHECK_EQ(n, 2);
+ ATF_CHECK_EQ(members[0].pm_pmcid, group.tg_ids[group.tg_leader]);
+ ATF_CHECK_EQ(members[1].pm_pmcid, group.tg_ids[0]);
+ ATF_CHECK_EQ(members[0].pm_value, 0);
+ ATF_CHECK_EQ(members[1].pm_value, 0);
+ ATF_CHECK_EQ(times.pgt_enabled, 0);
+ ATF_CHECK_EQ(times.pgt_running, 0);
+ ATF_CHECK_EQ(times.pgt_enabled_wall, 0);
+ ATF_CHECK_EQ(times.pgt_wall, 0);
+ ATF_CHECK_EQ(times.pgt_flags, PMC_GROUP_F_TIME_THREAD_NS);
+
+ n = PMC_GROUP_MAX_MEMBERS + 1;
+ errno = 0;
+ ATF_CHECK_EQ(pmc_group_read(group.tg_ids[group.tg_leader], &n,
+ members, &times), -1);
+ ATF_CHECK_EQ(errno, EINVAL);
+ ATF_CHECK_EQ(n, PMC_GROUP_MAX_MEMBERS + 1);
+
+ memset(members, 0xa5, sizeof(members));
+ memcpy(members_before, members, sizeof(members));
+ memset(&times, 0xa5, sizeof(times));
+ memcpy(&times_before, &times, sizeof(times));
+ n = nitems(members);
+ errno = 0;
+ ATF_CHECK_EQ(pmc_group_read(group.tg_ids[0], &n, members, &times), -1);
+ ATF_CHECK_EQ(errno, ENOTTY);
+ ATF_CHECK_EQ(n, nitems(members));
+ ATF_CHECK(memcmp(members, members_before, sizeof(members)) == 0);
+ ATF_CHECK(memcmp(&times, &times_before, sizeof(times)) == 0);
+
+ value = 11;
+ enabled = 22;
+ running = 33;
+ errno = 0;
+ ATF_CHECK_EQ(pmc_read_pair(group.tg_ids[0], &value, &enabled,
+ &running), -1);
+ ATF_CHECK_EQ(errno, EOPNOTSUPP);
+ ATF_CHECK_EQ(value, 11);
+ ATF_CHECK_EQ(enabled, 22);
+ ATF_CHECK_EQ(running, 33);
+
+ cleanup_group(&group);
+}
+
+ATF_TC_WITHOUT_HEAD(live_snapshot);
+ATF_TC_BODY(live_snapshot, tc)
+{
+ struct pmc_group_member first, second, third;
+ struct pmc_group_times first_times, second_times, third_times;
+ struct test_group group;
+ pmc_value_t pair_value;
+ uint64_t pair_enabled, pair_running;
+ uint32_t n;
+
+ require_hwpmc();
+ ATF_REQUIRE_MSG(setup_group(&group, 1, 0) == 0,
+ "setup_group failed: %s", strerror(errno));
+ if (pmc_attach(group.tg_ids[0], getpid()) != 0) {
+ ATF_CHECK_MSG(false, "pmc_attach failed: %s", strerror(errno));
+ cleanup_group(&group);
+ return;
+ }
+ if (pmc_start(group.tg_ids[0]) != 0) {
+ ATF_CHECK_MSG(false, "pmc_start failed: %s", strerror(errno));
+ cleanup_group(&group);
+ return;
+ }
+ group.tg_started = true;
+
+ spin();
+ n = 1;
+ if (pmc_group_read(group.tg_ids[0], &n, &first, &first_times) != 0) {
+ ATF_CHECK_MSG(false, "first group read failed: %s",
+ strerror(errno));
+ cleanup_group(&group);
+ return;
+ }
+ n = 1;
+ if (pmc_group_read(group.tg_ids[0], &n, &second,
+ &second_times) != 0) {
+ ATF_CHECK_MSG(false, "second group read failed: %s",
+ strerror(errno));
+ cleanup_group(&group);
+ return;
+ }
+ spin();
+ n = 1;
+ if (pmc_group_read(group.tg_ids[0], &n, &third, &third_times) != 0) {
+ ATF_CHECK_MSG(false, "third group read failed: %s",
+ strerror(errno));
+ cleanup_group(&group);
+ return;
+ }
+
+ ATF_CHECK_EQ(first.pm_pmcid, group.tg_ids[0]);
+ ATF_CHECK(first.pm_value > 0);
+ ATF_CHECK(second.pm_value >= first.pm_value);
+ ATF_CHECK(third.pm_value > second.pm_value);
+ ATF_CHECK(second.pm_value - first.pm_value < first.pm_value);
+ ATF_CHECK(first_times.pgt_running <= first_times.pgt_enabled);
+ ATF_CHECK(second_times.pgt_running <= second_times.pgt_enabled);
+ ATF_CHECK(third_times.pgt_running <= third_times.pgt_enabled);
+ ATF_CHECK(first_times.pgt_enabled_wall > 0);
+ ATF_CHECK(first_times.pgt_wall > 0);
+ ATF_CHECK(second_times.pgt_enabled >= first_times.pgt_enabled);
+ ATF_CHECK(second_times.pgt_running >= first_times.pgt_running);
+ ATF_CHECK(second_times.pgt_enabled_wall >=
+ first_times.pgt_enabled_wall);
+ ATF_CHECK(second_times.pgt_wall >= first_times.pgt_wall);
+ ATF_CHECK(third_times.pgt_enabled > second_times.pgt_enabled);
+ ATF_CHECK(third_times.pgt_running > second_times.pgt_running);
+ ATF_CHECK(third_times.pgt_enabled_wall >
+ second_times.pgt_enabled_wall);
+ ATF_CHECK(third_times.pgt_wall > second_times.pgt_wall);
+ ATF_CHECK_EQ(third_times.pgt_flags, PMC_GROUP_F_TIME_THREAD_NS);
+
+ pair_value = 0;
+ pair_enabled = 0;
+ pair_running = 0;
+ ATF_CHECK_EQ(pmc_read_pair(group.tg_ids[0], &pair_value,
+ &pair_enabled, &pair_running), 0);
+ ATF_CHECK(pair_value >= third.pm_value);
+ ATF_CHECK(pair_enabled >= third_times.pgt_enabled);
+ ATF_CHECK(pair_running >= third_times.pgt_running);
+ ATF_CHECK(pair_running <= pair_enabled);
+
+ cleanup_group(&group);
+}
+
+ATF_TC_WITHOUT_HEAD(sampling_member);
+ATF_TC_BODY(sampling_member, tc)
+{
+ struct pmc_group_member members[2];
+ struct pmc_group_times times;
+ struct test_group group;
+ uint32_t n;
+
+ require_hwpmc();
+ memset(&group, 0, sizeof(group));
+ group.tg_nmembers = 2;
+ group.tg_leader = 0;
+ group.tg_ids[0] = group.tg_ids[1] = PMC_ID_INVALID;
+ ATF_REQUIRE_MSG(pmc_allocate_group("instructions", PMC_MODE_TC, 0,
+ PMC_CPU_ANY, &group.tg_ids[0], 0) == 0,
+ "counting allocation failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_allocate_group("instructions", PMC_MODE_TS, 0,
+ PMC_CPU_ANY, &group.tg_ids[1], 1000) == 0,
+ "sampling allocation failed: %s", strerror(errno));
+ ATF_REQUIRE(pmc_group_create(&group.tg_groupid) == 0);
+ ATF_REQUIRE(pmc_group_add(group.tg_groupid, group.tg_ids[0], 1) == 0);
+ ATF_REQUIRE(pmc_group_add(group.tg_groupid, group.tg_ids[1], 0) == 0);
+ ATF_REQUIRE_MSG(pmc_group_commit(group.tg_groupid) == 0,
+ "mixed group commit failed: %s", strerror(errno));
+
+ memset(members, 0, sizeof(members));
+ memset(&times, 0, sizeof(times));
+ n = nitems(members);
+ ATF_REQUIRE_MSG(pmc_group_read(group.tg_ids[0], &n, members,
+ &times) == 0, "mixed group read failed: %s", strerror(errno));
+ ATF_CHECK_EQ(n, 2);
+ ATF_CHECK_EQ(members[0].pm_pmcid, group.tg_ids[0]);
+ ATF_CHECK_EQ(members[0].pm_mflags, 0);
+ ATF_CHECK_EQ(members[1].pm_pmcid, group.tg_ids[1]);
+ ATF_CHECK_EQ(members[1].pm_mflags, PMC_GROUP_MEMBER_F_SAMPLES);
+ ATF_CHECK_EQ(members[1].pm_value, 0);
+ cleanup_group(&group);
+}
+
+ATF_TC_WITHOUT_HEAD(sampling_live);
+ATF_TC_BODY(sampling_live, tc)
+{
+ struct pmc_group_member first, second;
+ struct pmc_group_times first_times, second_times;
+ struct test_group group;
+ pmc_value_t pair_value;
+ FILE *logfile;
+ uint64_t pair_enabled, pair_running;
+ uint32_t n;
+
+ require_hwpmc();
+ logfile = tmpfile();
+ ATF_REQUIRE(logfile != NULL);
+ ATF_REQUIRE_MSG(pmc_configure_logfile(fileno(logfile)) == 0,
+ "logfile configuration failed: %s", strerror(errno));
+ memset(&group, 0, sizeof(group));
+ group.tg_nmembers = 1;
+ group.tg_leader = 0;
+ group.tg_ids[0] = group.tg_ids[1] = PMC_ID_INVALID;
+ ATF_REQUIRE_MSG(pmc_allocate_group("instructions", PMC_MODE_TS, 0,
+ PMC_CPU_ANY, &group.tg_ids[0], 1000) == 0,
+ "sampling allocation failed: %s", strerror(errno));
+ ATF_REQUIRE(pmc_group_create(&group.tg_groupid) == 0);
+ ATF_REQUIRE(pmc_group_add(group.tg_groupid, group.tg_ids[0], 1) == 0);
+ ATF_REQUIRE_MSG(pmc_group_commit(group.tg_groupid) == 0,
+ "sampling group commit failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_attach(group.tg_ids[0], getpid()) == 0,
+ "sampling attach failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(pmc_start(group.tg_ids[0]) == 0,
+ "sampling start failed: %s", strerror(errno));
+ group.tg_started = true;
+
+ spin();
+ n = 1;
+ ATF_REQUIRE_MSG(pmc_group_read(group.tg_ids[0], &n, &first,
+ &first_times) == 0, "live sampling read failed: %s",
+ strerror(errno));
+ ATF_CHECK_EQ(first.pm_mflags, PMC_GROUP_MEMBER_F_SAMPLES);
+ ATF_CHECK(first.pm_value > 0);
+ ATF_CHECK(first_times.pgt_running <= first_times.pgt_enabled);
+ ATF_REQUIRE(pmc_stop(group.tg_ids[0]) == 0);
+ group.tg_started = false;
+ n = 1;
+ ATF_REQUIRE(pmc_group_read(group.tg_ids[0], &n, &second,
+ &second_times) == 0);
+ ATF_CHECK(second.pm_value >= first.pm_value);
+ ATF_CHECK(second_times.pgt_enabled >= first_times.pgt_enabled);
+ ATF_CHECK(second_times.pgt_running >= first_times.pgt_running);
+
+ pair_value = pair_enabled = pair_running = 0;
+ ATF_REQUIRE(pmc_read_pair(group.tg_ids[0], &pair_value,
+ &pair_enabled, &pair_running) == 0);
+ ATF_CHECK(pair_value >= second.pm_value);
+ ATF_CHECK(pair_running <= pair_enabled);
+ cleanup_group(&group);
+ ATF_REQUIRE(pmc_configure_logfile(-1) == 0);
+ ATF_REQUIRE(fclose(logfile) == 0);
+}
+
+/* Check that a size query returns group times. */
+ATF_TC_WITHOUT_HEAD(times_without_members);
+ATF_TC_BODY(times_without_members, tc)
+{
+ struct pmc_group_member member;
+ struct pmc_group_times query_times, member_times;
+ struct test_group group;
+ uint32_t n;
+
+ require_hwpmc();
+ ATF_REQUIRE_MSG(setup_group(&group, 1, 0) == 0,
+ "setup_group failed: %s", strerror(errno));
+ if (pmc_attach(group.tg_ids[0], getpid()) != 0) {
+ ATF_CHECK_MSG(false, "pmc_attach failed: %s", strerror(errno));
+ cleanup_group(&group);
+ return;
+ }
+ if (pmc_start(group.tg_ids[0]) != 0) {
+ ATF_CHECK_MSG(false, "pmc_start failed: %s", strerror(errno));
+ cleanup_group(&group);
+ return;
+ }
+ group.tg_started = true;
+
+ spin();
+ memset(&query_times, 0, sizeof(query_times));
+ n = 0;
+ if (pmc_group_read(group.tg_ids[0], &n, NULL, &query_times) != 0) {
+ ATF_CHECK_MSG(false, "size query failed: %s", strerror(errno));
+ cleanup_group(&group);
+ return;
+ }
+ ATF_CHECK_EQ(n, group.tg_nmembers);
+ ATF_CHECK(query_times.pgt_enabled > 0);
+ ATF_CHECK(query_times.pgt_running > 0);
+ ATF_CHECK(query_times.pgt_enabled_wall > 0);
+ ATF_CHECK(query_times.pgt_wall > 0);
+ ATF_CHECK(query_times.pgt_running <= query_times.pgt_enabled);
+ ATF_CHECK_EQ(query_times.pgt_flags, PMC_GROUP_F_TIME_THREAD_NS);
+
+ /* Check the times with a member read. */
+ memset(&member_times, 0, sizeof(member_times));
+ n = 1;
+ if (pmc_group_read(group.tg_ids[0], &n, &member, &member_times) != 0) {
+ ATF_CHECK_MSG(false, "member read failed: %s",
+ strerror(errno));
+ cleanup_group(&group);
+ return;
+ }
+ ATF_CHECK(member_times.pgt_enabled >= query_times.pgt_enabled);
+ ATF_CHECK(member_times.pgt_running >= query_times.pgt_running);
+ ATF_CHECK_EQ(member_times.pgt_flags, query_times.pgt_flags);
+
+ cleanup_group(&group);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, capacity_and_order);
+ ATF_TP_ADD_TC(tp, live_snapshot);
+ ATF_TP_ADD_TC(tp, sampling_member);
+ ATF_TP_ADD_TC(tp, sampling_live);
+ ATF_TP_ADD_TC(tp, times_without_members);
+ return (atf_no_error());
+}

File Metadata

Mime Type
text/plain
Expires
Tue, Oct 20, 5:05 AM (7 h, 29 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41092305
Default Alt Text
D59371.id189835.diff (28 KB)

Event Timeline