Page MenuHomeFreeBSD

D57641.diff
No OneTemporary

D57641.diff

diff --git a/tools/regression/pmc/Makefile b/tools/regression/pmc/Makefile
--- a/tools/regression/pmc/Makefile
+++ b/tools/regression/pmc/Makefile
@@ -38,7 +38,7 @@
KTESTS= pmc_group_test pmc_mux_test pmc_mux_works_test
# Userland-only tests (no PMU access).
-UTESTS=
+UTESTS= test_libpmcstat_group
PMCSTAT?= /usr/sbin/pmcstat
@@ -57,8 +57,15 @@
pmc_mux_works_test: ${.CURDIR}/pmc_mux_works_test.c
${CC} ${CFLAGS} -o ${.TARGET} ${.ALLSRC} -lpmc
+# libpmcstat is INTERNALLIB. It has no .so file installed. Build the
+# parser TU directly into the test binary.
+test_libpmcstat_group: ${.CURDIR}/test_libpmcstat_group.c \
+ ${LIBPMCSTAT_SRC}/libpmcstat_group.c
+ ${CC} ${CFLAGS} ${LIBPMCSTAT_INC} -o ${.TARGET} ${.ALLSRC} -lpmc
+
# These are userland-only checks: parsers and pmcstat warning policy.
check: ${UTESTS}
+ ./test_libpmcstat_group
# This in-kernel check needs `kldload hwpmc` and root.
check-root: ${KTESTS}
diff --git a/tools/regression/pmc/test_libpmcstat_group.c b/tools/regression/pmc/test_libpmcstat_group.c
new file mode 100644
--- /dev/null
+++ b/tools/regression/pmc/test_libpmcstat_group.c
@@ -0,0 +1,246 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Unit test for pmcstat_parse_event_group().
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <errno.h>
+#include <pmc.h>
+#include <pmclog.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libpmcstat.h"
+
+static int n_pass;
+static int n_fail;
+
+#define CHECK(cond, fmt, ...) do { \
+ if (cond) { \
+ printf("PASS: " fmt "\n", ##__VA_ARGS__); \
+ n_pass++; \
+ } else { \
+ printf("FAIL: " fmt " (line %d)\n", \
+ ##__VA_ARGS__, __LINE__); \
+ n_fail++; \
+ } \
+} while (0)
+
+static void
+free_arr(char **a, size_t n)
+{
+
+ pmcstat_free_event_group(a, n);
+}
+
+/* Check that {a,b,c} parses as 3 elements and returns 0. */
+static void
+t_basic(void)
+{
+ char **out = NULL;
+ size_t n = 0;
+ int rv;
+
+ rv = pmcstat_parse_event_group("{a,b,c}", &out, &n);
+ CHECK(rv == 0, "basic: rv=%d, want 0", rv);
+ CHECK(n == 3, "basic: n=%zu, want 3", n);
+ if (rv == 0 && n == 3) {
+ CHECK(strcmp(out[0], "a") == 0, "basic[0]=%s", out[0]);
+ CHECK(strcmp(out[1], "b") == 0, "basic[1]=%s", out[1]);
+ CHECK(strcmp(out[2], "c") == 0, "basic[2]=%s", out[2]);
+ }
+ free_arr(out, n);
+}
+
+/* Check that a single element {ev} returns 1. */
+static void
+t_single_element(void)
+{
+ char **out = NULL;
+ size_t n = 0;
+ int rv;
+
+ rv = pmcstat_parse_event_group("{instructions}", &out, &n);
+ CHECK(rv == 1, "single: rv=%d, want 1", rv);
+ free_arr(out, n);
+}
+
+/* Check that input without braces returns 1. */
+static void
+t_not_a_group(void)
+{
+ char **out = NULL;
+ size_t n = 0;
+ int rv;
+
+ rv = pmcstat_parse_event_group("instructions", &out, &n);
+ CHECK(rv == 1, "not_a_group: rv=%d, want 1", rv);
+ CHECK(out == NULL, "not_a_group: out should be NULL");
+ free_arr(out, n);
+}
+
+/* Check that a missing closing brace returns -1 with EINVAL. */
+static void
+t_no_close_brace(void)
+{
+ char **out = NULL;
+ size_t n = 0;
+ int rv;
+
+ errno = 0;
+ rv = pmcstat_parse_event_group("{a,b,c", &out, &n);
+ CHECK(rv == -1, "no_close: rv=%d, want -1", rv);
+ CHECK(errno == EINVAL, "no_close: errno=%d, want EINVAL", errno);
+ free_arr(out, n);
+}
+
+/* Check that the parser trims whitespace around commas. */
+static void
+t_whitespace(void)
+{
+ char **out = NULL;
+ size_t n = 0;
+ int rv;
+
+ rv = pmcstat_parse_event_group("{ a , b , c }", &out, &n);
+ CHECK(rv == 0, "ws: rv=%d, want 0", rv);
+ CHECK(n == 3, "ws: n=%zu, want 3", n);
+ if (rv == 0 && n == 3) {
+ CHECK(strcmp(out[0], "a") == 0, "ws[0]=%s", out[0]);
+ CHECK(strcmp(out[1], "b") == 0, "ws[1]=%s", out[1]);
+ CHECK(strcmp(out[2], "c") == 0, "ws[2]=%s", out[2]);
+ }
+ free_arr(out, n);
+}
+
+/* Check that empty entries are discarded. */
+static void
+t_empty_entries(void)
+{
+ char **out = NULL;
+ size_t n = 0;
+ int rv;
+
+ rv = pmcstat_parse_event_group("{,a,,b,}", &out, &n);
+ CHECK(rv == 0, "empty: rv=%d, want 0", rv);
+ CHECK(n == 2, "empty: n=%zu, want 2", n);
+ if (rv == 0 && n == 2) {
+ CHECK(strcmp(out[0], "a") == 0, "empty[0]=%s", out[0]);
+ CHECK(strcmp(out[1], "b") == 0, "empty[1]=%s", out[1]);
+ }
+ free_arr(out, n);
+}
+
+/* Check that colon event qualifiers are kept. */
+static void
+t_colon_preserved(void)
+{
+ char **out = NULL;
+ size_t n = 0;
+ int rv;
+
+ rv = pmcstat_parse_event_group("{instructions:k,branches:u}",
+ &out, &n);
+ CHECK(rv == 0, "colon: rv=%d, want 0", rv);
+ CHECK(n == 2, "colon: n=%zu, want 2", n);
+ if (rv == 0 && n == 2) {
+ CHECK(strcmp(out[0], "instructions:k") == 0,
+ "colon[0]=%s", out[0]);
+ CHECK(strcmp(out[1], "branches:u") == 0,
+ "colon[1]=%s", out[1]);
+ }
+ free_arr(out, n);
+}
+
+/* Check that NULL arguments return -1 with EINVAL. */
+static void
+t_null_inputs(void)
+{
+ char **out = NULL;
+ size_t n = 0;
+ int rv;
+
+ errno = 0;
+ rv = pmcstat_parse_event_group(NULL, &out, &n);
+ CHECK(rv == -1, "null_spec: rv=%d, want -1", rv);
+ CHECK(errno == EINVAL, "null_spec: errno=%d, want EINVAL", errno);
+
+ errno = 0;
+ rv = pmcstat_parse_event_group("{a,b}", NULL, &n);
+ CHECK(rv == -1, "null_out: rv=%d, want -1", rv);
+ CHECK(errno == EINVAL, "null_out: errno=%d, want EINVAL", errno);
+
+ errno = 0;
+ rv = pmcstat_parse_event_group("{a,b}", &out, NULL);
+ CHECK(rv == -1, "null_n: rv=%d, want -1", rv);
+ CHECK(errno == EINVAL, "null_n: errno=%d, want EINVAL", errno);
+}
+
+/* Check that leading whitespace before the opening brace is accepted. */
+static void
+t_leading_ws(void)
+{
+ char **out = NULL;
+ size_t n = 0;
+ int rv;
+
+ rv = pmcstat_parse_event_group(" {a,b}", &out, &n);
+ CHECK(rv == 0, "lead_ws: rv=%d, want 0", rv);
+ CHECK(n == 2, "lead_ws: n=%zu, want 2", n);
+ free_arr(out, n);
+}
+
+/* Check that pmcstat_add_one_event appends the expected event descriptors. */
+static void
+t_add_one_event(void)
+{
+ struct pmcstat_args a;
+ struct pmcstat_ev *ev;
+ int seen;
+
+ memset(&a, 0, sizeof(a));
+ STAILQ_INIT(&a.pa_events);
+
+ pmcstat_add_one_event('p', "instructions", &a, /*gid*/ 7,
+ /*is_leader*/ 1);
+ pmcstat_add_one_event('p', "branches", &a, /*gid*/ 7,
+ /*is_leader*/ 0);
+
+ seen = 0;
+ STAILQ_FOREACH(ev, &a.pa_events, ev_next) {
+ CHECK(ev->ev_groupid == 7, "add: ev_groupid=%d", ev->ev_groupid);
+ CHECK(ev->ev_mode == PMC_MODE_TC, "add: ev_mode=%d",
+ (int)ev->ev_mode);
+ CHECK(ev->ev_pmcid == PMC_ID_INVALID, "add: ev_pmcid invalid");
+ seen++;
+ }
+ CHECK(seen == 2, "add: saw %d events, want 2", seen);
+ CHECK((a.pa_flags & FLAG_HAS_PROCESS_PMCS) != 0,
+ "add: FLAG_HAS_PROCESS_PMCS set");
+ CHECK((a.pa_flags & FLAG_HAS_COUNTING_PMCS) != 0,
+ "add: FLAG_HAS_COUNTING_PMCS set");
+}
+
+int
+main(void)
+{
+
+ t_basic();
+ t_single_element();
+ t_not_a_group();
+ t_no_close_brace();
+ t_whitespace();
+ t_empty_entries();
+ t_colon_preserved();
+ t_null_inputs();
+ t_leading_ws();
+ t_add_one_event();
+
+ printf("\nResults: %d pass, %d fail\n", n_pass, n_fail);
+ return (n_fail == 0 ? 0 : 1);
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Oct 19, 4:11 PM (9 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41055869
Default Alt Text
D57641.diff (7 KB)

Event Timeline