]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bpftool: Add support for dumping streams
authorKumar Kartikeya Dwivedi <memxor@gmail.com>
Thu, 3 Jul 2025 20:48:17 +0000 (13:48 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 4 Jul 2025 02:30:07 +0000 (19:30 -0700)
Add support for printing the BPF stream contents of a program in
bpftool. The new bpftool prog tracelog command is extended to take
stdout and stderr arguments, and then the prog specification.

The bpf_prog_stream_read() API added in previous patch is simply reused
to grab data and then it is dumped to the respective file. The stdout
data is sent to stdout, and stderr is printed to stderr.

Cc: Quentin Monnet <qmo@kernel.org>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20250703204818.925464-12-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/bpf/bpftool/Documentation/bpftool-prog.rst
tools/bpf/bpftool/bash-completion/bpftool
tools/bpf/bpftool/prog.c

index da3152c16228fc6bd5865daa8e3354e76b9d76ea..f69fd92df8d89df098acbfe799474007f342a344 100644 (file)
@@ -35,6 +35,7 @@ PROG COMMANDS
 | **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
 | **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
 | **bpftool** **prog tracelog**
+| **bpftool** **prog tracelog** [ { **stdout** | **stderr**  } *PROG* ]
 | **bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
 | **bpftool** **prog profile** *PROG* [**duration** *DURATION*] *METRICs*
 | **bpftool** **prog help**
@@ -179,6 +180,12 @@ bpftool prog tracelog
     purposes. For streaming data from BPF programs to user space, one can use
     perf events (see also **bpftool-map**\ (8)).
 
+bpftool prog tracelog { stdout | stderr } *PROG*
+    Dump the BPF stream of the program. BPF programs can write to these streams
+    at runtime with the **bpf_stream_vprintk**\ () kfunc. The kernel may write
+    error messages to the standard error stream. This facility should be used
+    only for debugging purposes.
+
 bpftool prog run *PROG* data_in *FILE* [data_out *FILE* [data_size_out *L*]] [ctx_in *FILE* [ctx_out *FILE* [ctx_size_out *M*]]] [repeat *N*]
     Run BPF program *PROG* in the kernel testing infrastructure for BPF,
     meaning that the program works on the data and context provided by the
index 27512feb5c70ed1b88c3f07e37abed7295172efe..a759ba24471d67f5dd4aad4987dccde9a0f4e442 100644 (file)
@@ -518,7 +518,21 @@ _bpftool()
                     esac
                     ;;
                 tracelog)
-                    return 0
+                    case $prev in
+                        $command)
+                            COMPREPLY+=( $( compgen -W "stdout stderr" -- \
+                                "$cur" ) )
+                            return 0
+                            ;;
+                        stdout|stderr)
+                            COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \
+                                "$cur" ) )
+                            return 0
+                            ;;
+                        *)
+                            return 0
+                            ;;
+                    esac
                     ;;
                 profile)
                     case $cword in
index deeaa5c1ed7db0fbd2d1a86687ac7f4ac0467143..9722d841abc05deec3ab561d025abd58c920fb51 100644 (file)
@@ -1113,6 +1113,52 @@ static int do_detach(int argc, char **argv)
        return 0;
 }
 
+enum prog_tracelog_mode {
+       TRACE_STDOUT,
+       TRACE_STDERR,
+};
+
+static int
+prog_tracelog_stream(int prog_fd, enum prog_tracelog_mode mode)
+{
+       FILE *file = mode == TRACE_STDOUT ? stdout : stderr;
+       int stream_id = mode == TRACE_STDOUT ? 1 : 2;
+       char buf[512];
+       int ret;
+
+       ret = 0;
+       do {
+               ret = bpf_prog_stream_read(prog_fd, stream_id, buf, sizeof(buf), NULL);
+               if (ret > 0)
+                       fwrite(buf, sizeof(buf[0]), ret, file);
+       } while (ret > 0);
+
+       fflush(file);
+       return ret ? -1 : 0;
+}
+
+static int do_tracelog_any(int argc, char **argv)
+{
+       enum prog_tracelog_mode mode;
+       int fd;
+
+       if (argc == 0)
+               return do_tracelog(argc, argv);
+       if (!is_prefix(*argv, "stdout") && !is_prefix(*argv, "stderr"))
+               usage();
+       mode = is_prefix(*argv, "stdout") ? TRACE_STDOUT : TRACE_STDERR;
+       NEXT_ARG();
+
+       if (!REQ_ARGS(2))
+               return -1;
+
+       fd = prog_parse_fd(&argc, &argv);
+       if (fd < 0)
+               return -1;
+
+       return prog_tracelog_stream(fd, mode);
+}
+
 static int check_single_stdin(char *file_data_in, char *file_ctx_in)
 {
        if (file_data_in && file_ctx_in &&
@@ -2493,6 +2539,7 @@ static int do_help(int argc, char **argv)
                "                         [repeat N]\n"
                "       %1$s %2$s profile PROG [duration DURATION] METRICs\n"
                "       %1$s %2$s tracelog\n"
+               "       %1$s %2$s tracelog { stdout | stderr } PROG\n"
                "       %1$s %2$s help\n"
                "\n"
                "       " HELP_SPEC_MAP "\n"
@@ -2532,7 +2579,7 @@ static const struct cmd cmds[] = {
        { "loadall",    do_loadall },
        { "attach",     do_attach },
        { "detach",     do_detach },
-       { "tracelog",   do_tracelog },
+       { "tracelog",   do_tracelog_any },
        { "run",        do_run },
        { "profile",    do_profile },
        { 0 }