tools: bpftool: add a command to dump the trace pipe
authorQuentin Monnet <quentin.monnet@netronome.com>
Wed, 5 Dec 2018 10:28:24 +0000 (10:28 +0000)
committerDaniel Borkmann <daniel@iogearbox.net>
Wed, 5 Dec 2018 15:41:52 +0000 (16:41 +0100)
BPF programs can use the bpf_trace_printk() helper to print debug
information into the trace pipe. Add a subcommand
"bpftool prog tracelog" to simply dump this pipe to the console.

This is for a good part copied from iproute2, where the feature is
available with "tc exec bpf dbg". Changes include dumping pipe content
to stdout instead of stderr and adding JSON support (content is dumped
as an array of strings, one per line read from the pipe). This version
is dual-licensed, with Daniel's permission.

Cc: Daniel Borkmann <daniel@iogearbox.net>
Suggested-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
tools/bpf/bpftool/Documentation/bpftool-prog.rst
tools/bpf/bpftool/bash-completion/bpftool
tools/bpf/bpftool/main.h
tools/bpf/bpftool/prog.c
tools/bpf/bpftool/tracelog.c [new file with mode: 0644]

index ab36e920e552165e08b2e09eb75e4b7eb362dd6c..5524b6dccd856468668a6234a903ac0bb07a318f 100644 (file)
@@ -26,8 +26,9 @@ MAP COMMANDS
 |      **bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes**}]
 |      **bpftool** **prog pin** *PROG* *FILE*
 |      **bpftool** **prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
-|       **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
-|       **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
+|      **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
+|      **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
+|      **bpftool** **prog tracelog**
 |      **bpftool** **prog help**
 |
 |      *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
@@ -117,6 +118,14 @@ DESCRIPTION
                  parameter, with the exception of *flow_dissector* which is
                  detached from the current networking name space.
 
+       **bpftool prog tracelog**
+                 Dump the trace pipe of the system to the console (stdout).
+                 Hit <Ctrl+C> to stop printing. BPF programs can write to this
+                 trace pipe at runtime with the **bpf_trace_printk()** helper.
+                 This should be used only for debugging purposes. For
+                 streaming data from BPF programs to user space, one can use
+                 perf events (see also **bpftool-map**\ (8)).
+
        **bpftool prog help**
                  Print short help message.
 
index 9a60080f085f1977d99761ee999cae29a5b6aadb..44c189ba072a3bf3e1cfee218ac6fc89c0200d05 100644 (file)
@@ -398,10 +398,13 @@ _bpftool()
                             ;;
                     esac
                     ;;
+                tracelog)
+                    return 0
+                    ;;
                 *)
                     [[ $prev == $object ]] && \
                         COMPREPLY=( $( compgen -W 'dump help pin attach detach load \
-                            show list' -- "$cur" ) )
+                            show list tracelog' -- "$cur" ) )
                     ;;
             esac
             ;;
index 2761981669c8284062ef7c7b429c143d0a9ae3d6..0be0dd8f467ff5c053bc26cdd85c7e3600bbb147 100644 (file)
@@ -167,6 +167,7 @@ int do_event_pipe(int argc, char **argv);
 int do_cgroup(int argc, char **arg);
 int do_perf(int argc, char **arg);
 int do_net(int argc, char **arg);
+int do_tracelog(int argc, char **arg);
 
 int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what);
 int prog_parse_fd(int *argc, char ***argv);
index 56db61c5a91fea59a4e2c18a013ac06c5023e405..54c8dbf05c9cb385788ade0f26b158627a09489f 100644 (file)
@@ -1140,6 +1140,7 @@ static int do_help(int argc, char **argv)
                "                         [pinmaps MAP_DIR]\n"
                "       %s %s attach PROG ATTACH_TYPE [MAP]\n"
                "       %s %s detach PROG ATTACH_TYPE [MAP]\n"
+               "       %s %s tracelog\n"
                "       %s %s help\n"
                "\n"
                "       " HELP_SPEC_MAP "\n"
@@ -1158,7 +1159,7 @@ static int do_help(int argc, char **argv)
                "",
                bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
                bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
-               bin_name, argv[-2], bin_name, argv[-2]);
+               bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
 
        return 0;
 }
@@ -1173,6 +1174,7 @@ static const struct cmd cmds[] = {
        { "loadall",    do_loadall },
        { "attach",     do_attach },
        { "detach",     do_detach },
+       { "tracelog",   do_tracelog },
        { 0 }
 };
 
diff --git a/tools/bpf/bpftool/tracelog.c b/tools/bpf/bpftool/tracelog.c
new file mode 100644 (file)
index 0000000..1fa8e51
--- /dev/null
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (c) 2015-2017 Daniel Borkmann */
+/* Copyright (c) 2018 Netronome Systems, Inc. */
+
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/magic.h>
+#include <sys/fcntl.h>
+#include <sys/vfs.h>
+
+#include "main.h"
+
+#ifndef TRACEFS_MAGIC
+# define TRACEFS_MAGIC 0x74726163
+#endif
+
+#define _textify(x)    #x
+#define textify(x)     _textify(x)
+
+FILE *trace_pipe_fd;
+char *buff;
+
+static int validate_tracefs_mnt(const char *mnt, unsigned long magic)
+{
+       struct statfs st_fs;
+
+       if (statfs(mnt, &st_fs) < 0)
+               return -ENOENT;
+       if ((unsigned long)st_fs.f_type != magic)
+               return -ENOENT;
+
+       return 0;
+}
+
+static bool
+find_tracefs_mnt_single(unsigned long magic, char *mnt, const char *mntpt)
+{
+       size_t src_len;
+
+       if (validate_tracefs_mnt(mntpt, magic))
+               return false;
+
+       src_len = strlen(mntpt);
+       if (src_len + 1 >= PATH_MAX) {
+               p_err("tracefs mount point name too long");
+               return false;
+       }
+
+       strcpy(mnt, mntpt);
+       return true;
+}
+
+static bool find_tracefs_pipe(char *mnt)
+{
+       static const char * const known_mnts[] = {
+               "/sys/kernel/debug/tracing",
+               "/sys/kernel/tracing",
+               "/tracing",
+               "/trace",
+       };
+       const char *pipe_name = "/trace_pipe";
+       const char *fstype = "tracefs";
+       char type[100], format[32];
+       const char * const *ptr;
+       bool found = false;
+       FILE *fp;
+
+       for (ptr = known_mnts; ptr < known_mnts + ARRAY_SIZE(known_mnts); ptr++)
+               if (find_tracefs_mnt_single(TRACEFS_MAGIC, mnt, *ptr))
+                       goto exit_found;
+
+       fp = fopen("/proc/mounts", "r");
+       if (!fp)
+               return false;
+
+       /* Allow room for NULL terminating byte and pipe file name */
+       snprintf(format, sizeof(format), "%%*s %%%zds %%99s %%*s %%*d %%*d\\n",
+                PATH_MAX - strlen(pipe_name) - 1);
+       while (fscanf(fp, format, mnt, type) == 2)
+               if (strcmp(type, fstype) == 0) {
+                       found = true;
+                       break;
+               }
+       fclose(fp);
+
+       /* The string from fscanf() might be truncated, check mnt is valid */
+       if (!found || validate_tracefs_mnt(mnt, TRACEFS_MAGIC))
+               return false;
+
+exit_found:
+       strcat(mnt, pipe_name);
+       return true;
+}
+
+static void exit_tracelog(int signum)
+{
+       fclose(trace_pipe_fd);
+       free(buff);
+
+       if (json_output) {
+               jsonw_end_array(json_wtr);
+               jsonw_destroy(&json_wtr);
+       }
+
+       exit(0);
+}
+
+int do_tracelog(int argc, char **argv)
+{
+       const struct sigaction act = {
+               .sa_handler = exit_tracelog
+       };
+       char trace_pipe[PATH_MAX];
+       bool found_trace_pipe;
+       size_t buff_len = 0;
+
+       if (json_output)
+               jsonw_start_array(json_wtr);
+
+       found_trace_pipe = find_tracefs_pipe(trace_pipe);
+       if (!found_trace_pipe) {
+               p_err("could not find trace pipe, tracefs not mounted?");
+               return -1;
+       }
+
+       trace_pipe_fd = fopen(trace_pipe, "r");
+       if (!trace_pipe_fd) {
+               p_err("could not open trace pipe: %s", strerror(errno));
+               return -1;
+       }
+
+       sigaction(SIGHUP, &act, NULL);
+       sigaction(SIGINT, &act, NULL);
+       sigaction(SIGTERM, &act, NULL);
+       while (1) {
+               ssize_t ret;
+
+               ret = getline(&buff, &buff_len, trace_pipe_fd);
+               if (ret <= 0) {
+                       p_err("failed to read content from trace pipe: %s",
+                             strerror(errno));
+                       break;
+               }
+               if (json_output)
+                       jsonw_string(json_wtr, buff);
+               else
+                       printf("%s", buff);
+       }
+
+       fclose(trace_pipe_fd);
+       free(buff);
+       return -1;
+}