lkml.org 
[lkml]   [2020]   [Aug]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: [RFC PATCH bpf-next 2/4] bpf: make BTF show support generic, apply to seq files/bpf_trace_printk
On Wed, 12 Aug 2020, Alexei Starovoitov wrote:

> On Thu, Aug 06, 2020 at 03:42:23PM +0100, Alan Maguire wrote:
> >
> > The bpf_trace_printk tracepoint is augmented with a "trace_id"
> > field; it is used to allow tracepoint filtering as typed display
> > information can easily be interspersed with other tracing data,
> > making it hard to read. Specifying a trace_id will allow users
> > to selectively trace data, eliminating noise.
>
> Since trace_id is not seen in trace_pipe, how do you expect users
> to filter by it?

Sorry should have specified this. The approach is to use trace
instances and filtering such that we only see events associated
with a specific trace_id. There's no need for the trace event to
actually display the trace_id - it's still usable as a filter.
The steps involved are:

1. create a trace instance within which we can specify a fresh
set of trace event enablings, filters etc.

mkdir /sys/kernel/debug/tracing/instances/traceid100

2. enable the filter for the specific trace id

echo "trace_id == 100" >
/sys/kernel/debug/tracing/instances/traceid100/events/bpf_trace/bpf_trace_printk/filter

3. enable the trace event

echo 1 >
/sys/kernel/debug/tracing/instances/events/bpf_trace/bpf_trace_printk/enable

4. ensure the BPF program uses a trace_id 100 when calling bpf_trace_btf()

So the above can be done for multiple programs; output can then
be separated for different programs if trace_ids and filtering are
used together. The above trace instance only sees bpf_trace_btf()
events which specify trace_id 100.

I've attached a tweaked version of the patch 4 in the patchset that
ensures that a trace instance with filtering enabled as above sees
the bpf_trace_btf events, but _not_ bpf_trace_printk events (since
they have trace_id 0 by default).

To me the above provides a simple way to separate BPF program output
for simple BPF programs; ringbuf and perf events require a bit more
work in both BPF and userspace to support such coordination. What do
you think - does this approach seem worth using? If so we could also
consider extending it to bpf_trace_printk(), if we can find a way
to provide a trace_id there too.

> It also feels like workaround. May be let bpf prog print the whole
> struct in one go with multiple new lines and call
> trace_bpf_trace_printk(buf) once?

We can do that absolutely, but I'd be interested to get your take
on the filtering mechanism before taking that approach. I'll add
a description of the above mechanism to the cover letter and
patch to be clearer next time too.

>
> Also please add interface into bpf_seq_printf.
> BTF enabled struct prints is useful for iterators too
> and generalization you've done in this patch pretty much takes it there.
>

Sure, I'll try and tackle that next time.

> > +#define BTF_SHOW_COMPACT (1ULL << 0)
> > +#define BTF_SHOW_NONAME (1ULL << 1)
> > +#define BTF_SHOW_PTR_RAW (1ULL << 2)
> > +#define BTF_SHOW_ZERO (1ULL << 3)
> > +#define BTF_SHOW_NONEWLINE (1ULL << 32)
> > +#define BTF_SHOW_UNSAFE (1ULL << 33)
>
> I could have missed it earlier, but what is the motivation to leave the gap
> in bits? Just do bit 4 and 5 ?
>

Patch 3 uses the first 4 as flags to bpf_trace_btf();
the final two are not supported for the helper as flag values
so I wanted to leave some space for additional bpf_trace_btf() flags.
BTF_SHOW_NONEWLINE is always used for bpf_trace_btf(), since the
tracing adds a newline for us and we don't want to double up on newlines,
so it's ORed in as an implicit argument for the bpf_trace_btf() case.
BTF_SHOW_UNSAFE isn't allowed within BPF so it's not available as
a flag for the helper.

Thanks!

Alan

From 10bd268b2585084c8f35d1b6ab0c3df76203f5cc Mon Sep 17 00:00:00 2001
From: Alan Maguire <alan.maguire@oracle.com>
Date: Thu, 6 Aug 2020 14:21:10 +0200
Subject: [PATCH] selftests/bpf: add bpf_trace_btf helper tests

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@oracle.com>
---
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
\
 
 \ /
  Last update: 2020-08-14 15:09    [W:0.124 / U:0.180 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site