[PATCH] selftests/bpf: add bpf_trace_btf helper tests

From: Alan Maguire
Date: Thu Aug 06 2020 - 08:21:10 EST


Basic tests verifying various flag combinations for bpf_trace_btf()
using a tp_btf program to trace skb data. Also verify that we
can create a trace instance to filter trace data, using the
trace_id value passed to bpf_trace/bpf_trace_printk events.

trace_id is specifiable for bpf_trace_btf() so the test ensures
the trace instance sees the filtered events only.

Signed-off-by: Alan Maguire <alan.maguire@xxxxxxxxxx>
---
tools/testing/selftests/bpf/prog_tests/trace_btf.c | 150 +++++++++++++++++++++
.../selftests/bpf/progs/netif_receive_skb.c | 48 +++++++
2 files changed, 198 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/trace_btf.c
create mode 100644 tools/testing/selftests/bpf/progs/netif_receive_skb.c

diff --git a/tools/testing/selftests/bpf/prog_tests/trace_btf.c b/tools/testing/selftests/bpf/prog_tests/trace_btf.c
new file mode 100644
index 0000000..64a920f
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/trace_btf.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+
+#include "netif_receive_skb.skel.h"
+
+#define TRACEFS "/sys/kernel/debug/tracing/"
+#define TRACEBUF TRACEFS "trace_pipe"
+#define TRACEID "100"
+#define TRACEINST TRACEFS "instances/traceid" TRACEID "/"
+#define TRACEINSTBUF TRACEINST "trace_pipe"
+#define TRACEINSTEVENT TRACEINST "events/bpf_trace/bpf_trace_printk/"
+#define BTFMSG "(struct sk_buff)"
+#define PRINTKMSG "testing,testing"
+#define MAXITER 1000
+
+static bool findmsg(FILE *fp, const char *msg)
+{
+ bool found = false;
+ char *buf = NULL;
+ size_t buflen;
+ int iter = 0;
+
+ /* verify our search string is in the trace buffer */
+ while (++iter < MAXITER &&
+ (getline(&buf, &buflen, fp) >= 0 || errno == EAGAIN)) {
+ found = strstr(buf, msg) != NULL;
+ if (found)
+ break;
+ }
+ free(buf);
+
+ return found;
+}
+
+/* Demonstrate that bpf_trace_btf succeeds with non-zero return values,
+ * and that filtering by trace instance works. bpf_trace_btf() is called
+ * with trace_id of 100, so we create a trace instance that enables
+ * bpf_trace_printk() events and filters on trace_id. The trace instance
+ * pipe should therefore only see the trace_id == 100 events, i.e. the
+ * bpf_trace_btf() events. The bpf program also uses bpf_trace_printk() -
+ * we ensure the global trace_pipe sees those but the instance does not.
+ */
+void test_trace_btf(void)
+{
+ struct netif_receive_skb *skel;
+ struct netif_receive_skb__bss *bss;
+ FILE *fp = NULL, *fpinst = NULL;
+ int err, duration = 0;
+
+ skel = netif_receive_skb__open();
+ if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
+ return;
+
+ err = netif_receive_skb__load(skel);
+ if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err))
+ goto cleanup;
+
+ bss = skel->bss;
+
+ err = netif_receive_skb__attach(skel);
+ if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
+ goto cleanup;
+
+ /* Enable trace instance which filters on trace id associated with
+ * bpf_trace_btf().
+ */
+ if (CHECK(system("mkdir " TRACEINST), "could not create trace instance",
+ "error creating %s", TRACEINST))
+ goto cleanup;
+
+ if (CHECK(system("echo \"trace_id == " TRACEID "\" > " TRACEINSTEVENT "filter"),
+ "could not specify trace id filter for %s", TRACEID))
+ goto cleanup;
+ if (CHECK(system("echo 1 > " TRACEINSTEVENT "enable"), "could not enable trace event",
+ "error enabling %s", TRACEINSTEVENT))
+ goto cleanup;
+
+ fp = fopen(TRACEBUF, "r");
+ if (CHECK(fp == NULL, "could not open trace buffer",
+ "error %d opening %s", errno, TRACEBUF))
+ goto cleanup;
+
+ /* We do not want to wait forever if this test fails... */
+ fcntl(fileno(fp), F_SETFL, O_NONBLOCK);
+
+ fpinst = fopen(TRACEINSTBUF, "r");
+ if (CHECK(fpinst == NULL, "could not open instance trace buffer",
+ "error %d opening %s", errno, TRACEINSTBUF))
+ goto cleanup;
+
+ /* We do not want to wait forever if this test fails... */
+ fcntl(fileno(fpinst), F_SETFL, O_NONBLOCK);
+
+ /* generate receive event */
+ system("ping -c 1 127.0.0.1 > /dev/null");
+
+ /*
+ * Make sure netif_receive_skb program was triggered
+ * and it set expected return values from bpf_trace_printk()s
+ * and all tests ran.
+ */
+ if (CHECK(bss->ret <= 0,
+ "bpf_trace_btf: got return value",
+ "ret <= 0 %ld test %d\n", bss->ret, bss->num_subtests))
+ goto cleanup;
+
+ if (CHECK(bss->num_subtests != bss->ran_subtests, "check all subtests ran",
+ "only ran %d of %d tests\n", bss->num_subtests,
+ bss->ran_subtests))
+ goto cleanup;
+
+ /* All messages should be in global trace_pipe */
+ if (CHECK(!findmsg(fp, BTFMSG), "global trace pipe should have BTF",
+ "btf data not in trace pipe"))
+ goto cleanup;
+
+
+ if (CHECK(!findmsg(fp, PRINTKMSG),
+ "global trace pipe should have printk",
+ "trace_printk data not in global trace pipe"))
+ goto cleanup;
+
+
+ if (CHECK(!findmsg(fpinst, BTFMSG),
+ "instance trace pipe should have BTF",
+ "btf data not in instance trace pipe"))
+ goto cleanup;
+
+
+ /* we filtered on trace id, so bpf_trace_printk() message should not
+ * be in our trace instance log.
+ */
+ CHECK(findmsg(fpinst, PRINTKMSG),
+ "instance trace pipe should not have printk",
+ "trace_printk data should not be in instance trace pipe");
+
+cleanup:
+ if (fpinst)
+ fclose(fpinst);
+ if (fp)
+ fclose(fp);
+
+ system("echo 0 > " TRACEINSTEVENT "enable");
+ system("rmdir " TRACEINST);
+ netif_receive_skb__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/netif_receive_skb.c b/tools/testing/selftests/bpf/progs/netif_receive_skb.c
new file mode 100644
index 0000000..a14f79b
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/netif_receive_skb.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2020, Oracle and/or its affiliates. */
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+long ret = 0;
+int num_subtests = 0;
+int ran_subtests = 0;
+
+/* Note a trace id of 100 is used, allowing filtering. */
+#define CHECK_TRACE(_p, flags) \
+ do { \
+ ++num_subtests; \
+ if (ret >= 0) { \
+ ++ran_subtests; \
+ ret = bpf_trace_btf(_p, sizeof(*(_p)), 100, flags); \
+ } \
+ } while (0)
+
+/* TRACE_EVENT(netif_receive_skb,
+ * TP_PROTO(struct sk_buff *skb),
+ */
+SEC("tp_btf/netif_receive_skb")
+int BPF_PROG(trace_netif_receive_skb, struct sk_buff *skb)
+{
+ static const char printkmsg[] = "testing,testing, ran %d so far...";
+ static const char skb_type[] = "struct sk_buff";
+ static struct btf_ptr p = { };
+
+ p.ptr = skb;
+ p.type = skb_type;
+
+ /* This message will go to the global tracing log */
+ bpf_trace_printk(printkmsg, sizeof(printkmsg), ran_subtests);
+
+ CHECK_TRACE(&p, 0);
+ CHECK_TRACE(&p, BTF_TRACE_F_COMPACT);
+ CHECK_TRACE(&p, BTF_TRACE_F_NONAME);
+ CHECK_TRACE(&p, BTF_TRACE_F_PTR_RAW);
+ CHECK_TRACE(&p, BTF_TRACE_F_ZERO);
+ CHECK_TRACE(&p, BTF_TRACE_F_COMPACT | BTF_TRACE_F_NONAME |
+ BTF_TRACE_F_PTR_RAW | BTF_TRACE_F_ZERO);
+
+ return 0;
+}
--
1.8.3.1