From: Tianyi Chen <diannaaav@gmail.com>
To: qmo@kernel.org, bpf@vger.kernel.org
Cc: andrii@kernel.org, eddyz87@gmail.com, ihor.solodrai@linux.dev,
linux-kselftest@vger.kernel.org
Subject: [PATCH bpf-next v3 1/2] bpftool: Read ring buffer maps with event_pipe
Date: Fri, 11 Sep 2026 10:51:13 +0800 [thread overview]
Message-ID: <20260911025114.190899-2-diannaaav@gmail.com> (raw)
In-Reply-To: <20260911025114.190899-1-diannaaav@gmail.com>
From: Tianyi Chen <hi@tychen.cc>
map event_pipe only accepts perf event arrays, leaving no built-in way
to inspect records produced through the BPF ring buffer API. Extend it
to consume queued and live BPF_MAP_TYPE_RINGBUF records in plain or
JSON output, retaining the existing perf event array path.
Ring buffers have a shared consumer position and no implicit CPU or
timestamp. Document that this command consumes records rather than
observing them passively, and reject perf-only CPU/index selectors.
A producer can keep ring_buffer__poll() busy after a stop signal, so
return -EINTR from the record callback when stopping. Keep stdio out of
the shared signal handler and preserve callback output errors.
Link: https://github.com/libbpf/bpftool/issues/54
Assisted-by: LLM
Signed-off-by: Tianyi Chen <hi@tychen.cc>
---
.../bpf/bpftool/Documentation/bpftool-map.rst | 14 ++-
tools/bpf/bpftool/bash-completion/bpftool | 4 +-
tools/bpf/bpftool/map_perf_ring.c | 87 ++++++++++++++-----
3 files changed, 81 insertions(+), 24 deletions(-)
diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst
index 5daf3de5c744..e4ed7e701c9e 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-map.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst
@@ -120,7 +120,8 @@ bpftool map pin *MAP* *FILE*
character ('.'), which is reserved for future extensions of *bpffs*.
bpftool map event_pipe *MAP* [cpu *N* index *M*]
- Read events from a **BPF_MAP_TYPE_PERF_EVENT_ARRAY** map.
+ Read events from a **BPF_MAP_TYPE_PERF_EVENT_ARRAY** or
+ **BPF_MAP_TYPE_RINGBUF** map.
Install perf rings into a perf event array map and dump output of any
**bpf_perf_event_output**\ () call in the kernel. By default read the
@@ -134,6 +135,17 @@ bpftool map event_pipe *MAP* [cpu *N* index *M*]
existing ring. Any other application will stop receiving events if it
installed its rings earlier.
+ For a ring buffer map, consume records submitted by BPF programs, including
+ records already queued before the command starts. **cpu** and **index**
+ are not supported. Each record is printed in full, including embedded zero
+ bytes. Plain output reports the record size followed by hexadecimal
+ bytes; JSON output contains **size** and **data** fields, with **data** an
+ array of byte values. Ring buffer records have no implicit CPU or timestamp.
+
+ Consuming a ring buffer advances its shared consumer position, so this
+ command must not run alongside another consumer of the same map.
+ **BPF_MAP_TYPE_USER_RINGBUF** maps are not supported.
+
bpftool map peek *MAP*
Peek next value in the queue or stack.
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index 75cbcb512eba..1750b488c9e3 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -878,11 +878,11 @@ _bpftool()
return 0
;;
id)
- _bpftool_get_map_ids_for_type perf_event_array
+ _bpftool_get_map_ids_for_type '"type": "\(perf_event_array\|ringbuf\)"'
return 0
;;
name)
- _bpftool_get_map_names_for_type perf_event_array
+ _bpftool_get_map_names_for_type '"type": "\(perf_event_array\|ringbuf\)"'
return 0
;;
cpu)
diff --git a/tools/bpf/bpftool/map_perf_ring.c b/tools/bpf/bpftool/map_perf_ring.c
index bcb767e2d673..7d555331f443 100644
--- a/tools/bpf/bpftool/map_perf_ring.c
+++ b/tools/bpf/bpftool/map_perf_ring.c
@@ -27,7 +27,7 @@
#define MMAP_PAGE_CNT 16
-static volatile bool stop;
+static volatile sig_atomic_t stop;
struct perf_event_sample {
struct perf_event_header header;
@@ -44,7 +44,6 @@ struct perf_event_lost {
static void int_exit(int signo)
{
- fprintf(stderr, "Stopping...\n");
stop = true;
}
@@ -107,6 +106,27 @@ print_bpf_output(void *private_data, int cpu, struct perf_event_header *event)
return LIBBPF_PERF_EVENT_CONT;
}
+static int print_ringbuf_output(void *ctx, void *data, size_t size)
+{
+ if (json_output) {
+ jsonw_start_object(json_wtr);
+ jsonw_uint_field(json_wtr, "size", size);
+ jsonw_name(json_wtr, "data");
+ print_data_json(data, size);
+ jsonw_end_object(json_wtr);
+ } else {
+ printf("== size: %zu =====\n", size);
+ fprint_hex(stdout, data, size, " ");
+ printf("\n");
+ }
+
+ if (fflush(stdout))
+ return errno ? -errno : -EIO;
+
+ /* A producer can keep poll() busy even after a signal arrives. */
+ return stop ? -EINTR : 0;
+}
+
int do_event_pipe(int argc, char **argv)
{
struct perf_event_attr perf_attr = {
@@ -123,18 +143,26 @@ int do_event_pipe(int argc, char **argv)
.cpu = -1,
.idx = -1,
};
- struct perf_buffer *pb;
+ struct perf_buffer *pb = NULL;
+ struct ring_buffer *rb = NULL;
__u32 map_info_len;
int err, map_fd;
+ stop = false;
map_info_len = sizeof(map_info);
map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len,
0);
if (map_fd < 0)
return -1;
- if (map_info.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
- p_err("map is not a perf event array");
+ if (map_info.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
+ map_info.type != BPF_MAP_TYPE_RINGBUF) {
+ p_err("map is not a perf event array or ring buffer");
+ goto err_close_map;
+ }
+
+ if (map_info.type == BPF_MAP_TYPE_RINGBUF && argc) {
+ p_err("ring buffer maps do not support cpu or index arguments");
goto err_close_map;
}
@@ -184,15 +212,24 @@ int do_event_pipe(int argc, char **argv)
ctx.idx = 0;
}
- opts.cpu_cnt = ctx.all_cpus ? 0 : 1;
- opts.cpus = &ctx.cpu;
- opts.map_keys = &ctx.idx;
- pb = perf_buffer__new_raw(map_fd, MMAP_PAGE_CNT, &perf_attr,
- print_bpf_output, &ctx, &opts);
- if (!pb) {
- p_err("failed to create perf buffer: %s (%d)",
- strerror(errno), errno);
- goto err_close_map;
+ if (map_info.type == BPF_MAP_TYPE_RINGBUF) {
+ rb = ring_buffer__new(map_fd, print_ringbuf_output, NULL, NULL);
+ if (!rb) {
+ p_err("failed to create ring buffer: %s (%d)",
+ strerror(errno), errno);
+ goto err_close_map;
+ }
+ } else {
+ opts.cpu_cnt = ctx.all_cpus ? 0 : 1;
+ opts.cpus = &ctx.cpu;
+ opts.map_keys = &ctx.idx;
+ pb = perf_buffer__new_raw(map_fd, MMAP_PAGE_CNT, &perf_attr,
+ print_bpf_output, &ctx, &opts);
+ if (!pb) {
+ p_err("failed to create perf buffer: %s (%d)",
+ strerror(errno), errno);
+ goto err_close_map;
+ }
}
signal(SIGINT, int_exit);
@@ -202,25 +239,33 @@ int do_event_pipe(int argc, char **argv)
if (json_output)
jsonw_start_array(json_wtr);
+ err = 0;
while (!stop) {
- err = perf_buffer__poll(pb, 200);
+ err = rb ? ring_buffer__poll(rb, 200) : perf_buffer__poll(pb, 200);
if (err < 0 && err != -EINTR) {
- p_err("perf buffer polling failed: %s (%d)",
- strerror(errno), errno);
- goto err_close_pb;
+ fprintf(stderr, "Error: %s buffer polling failed: %s (%d)\n",
+ rb ? "ring" : "perf", strerror(-err), -err);
+ break;
}
+ err = 0;
}
+ if (stop)
+ fprintf(stderr, "Stopping...\n");
if (json_output)
jsonw_end_array(json_wtr);
+ if (fflush(stdout)) {
+ fprintf(stderr, "Error: failed to write events: %s\n", strerror(errno));
+ err = -1;
+ }
+ ring_buffer__free(rb);
perf_buffer__free(pb);
+ /* Both buffer managers borrow map_fd. */
close(map_fd);
- return 0;
+ return err < 0 ? -1 : 0;
-err_close_pb:
- perf_buffer__free(pb);
err_close_map:
close(map_fd);
return -1;
--
2.55.0
next prev parent reply other threads:[~2026-09-11 2:51 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-11 2:51 [PATCH bpf-next v3 0/2] bpftool: Consume ring buffer maps with event_pipe Tianyi Chen
2026-09-11 2:51 ` Tianyi Chen [this message]
2026-09-11 2:51 ` [PATCH bpf-next v3 2/2] selftests/bpf: Cover bpftool ring buffer event_pipe Tianyi Chen
2026-09-11 3:57 ` bot+bpf-ci
[not found] ` <e7211adda619bf43e17dbaf59ffec76ca2442726a5e22485cd783ed9a5e9c655@mail.kernel.org>
2026-09-11 5:12 ` [PATCH bpf-next v3 0/2] bpftool: Consume ring buffer maps with event_pipe Tianyi Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260911025114.190899-2-diannaaav@gmail.com \
--to=diannaaav@gmail.com \
--cc=andrii@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=eddyz87@gmail.com \
--cc=ihor.solodrai@linux.dev \
--cc=linux-kselftest@vger.kernel.org \
--cc=qmo@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox