Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F176406798
D57641.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
D57641.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D57641: tools/regression/pmc: libpmcstat group-parser unit test
Attached
Detach File
Event Timeline
Log In to Comment