Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F160656086
D57641.id179986.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
D57641.id179986.diff
View Options
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,257 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ *
+ * Userland unit test for pmcstat_parse_event_group().
+ *
+ * Build: cc -o test_libpmcstat_group test_libpmcstat_group.c -lpmcstat
+ * Run: ./test_libpmcstat_group (no root, no kernel module needed)
+ *
+ * The test exercises the brace-list parser only; no PMU is touched.
+ * Exit status:
+ * 0 - all assertions pass
+ * 1 - one or more cases failed
+ */
+
+#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);
+}
+
+/* {a,b,c} -> 3 elements, returns 0 (real group). */
+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);
+}
+
+/* {ev} (single element) -> rv==1 (degenerate, caller handles as non-group). */
+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);
+}
+
+/* No leading '{' -> rv==1 (not a brace list). */
+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);
+}
+
+/* No closing '}' -> rv==-1 (parse error), errno=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);
+}
+
+/* {a, b , c} with whitespace around commas -> 3 trimmed elements. */
+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);
+}
+
+/* Empty entries collapse: {,a,,b,} -> only "a" and "b". */
+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);
+}
+
+/* Colon qualifier inside event spec must be preserved untouched. */
+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);
+}
+
+/* NULL inputs must return -1 with errno EINVAL, not crash. */
+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);
+}
+
+/* A leading-space spec like " {a,b}" should still parse as a group. */
+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);
+}
+
+/* pmcstat_add_one_event: verify it appends to args.pa_events with the
+ * expected ev_groupid / ev_is_leader / ev_mode for option='p'. */
+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
Sat, Jun 27, 1:06 PM (14 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34068310
Default Alt Text
D57641.id179986.diff (6 KB)
Attached To
Mode
D57641: tools/regression/pmc: libpmcstat group-parser unit test
Attached
Detach File
Event Timeline
Log In to Comment