Skip to content

Commit 30da46b

Browse files
qmonnetborkmann
authored andcommitted
tools: bpftool: add a command to dump the trace pipe
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>
1 parent 4188817 commit 30da46b

File tree

5 files changed

+176
-4
lines changed

5 files changed

+176
-4
lines changed

tools/bpf/bpftool/Documentation/bpftool-prog.rst

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ MAP COMMANDS
2626
| **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}]
2727
| **bpftool** **prog pin** *PROG* *FILE*
2828
| **bpftool** **prog { load | loadall }** *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
29-
| **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
30-
| **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
29+
| **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
30+
| **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
31+
| **bpftool** **prog tracelog**
3132
| **bpftool** **prog help**
3233
|
3334
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
@@ -117,6 +118,14 @@ DESCRIPTION
117118
parameter, with the exception of *flow_dissector* which is
118119
detached from the current networking name space.
119120

121+
**bpftool prog tracelog**
122+
Dump the trace pipe of the system to the console (stdout).
123+
Hit <Ctrl+C> to stop printing. BPF programs can write to this
124+
trace pipe at runtime with the **bpf_trace_printk()** helper.
125+
This should be used only for debugging purposes. For
126+
streaming data from BPF programs to user space, one can use
127+
perf events (see also **bpftool-map**\ (8)).
128+
120129
**bpftool prog help**
121130
Print short help message.
122131

tools/bpf/bpftool/bash-completion/bpftool

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,13 @@ _bpftool()
398398
;;
399399
esac
400400
;;
401+
tracelog)
402+
return 0
403+
;;
401404
*)
402405
[[ $prev == $object ]] && \
403406
COMPREPLY=( $( compgen -W 'dump help pin attach detach load \
404-
show list' -- "$cur" ) )
407+
show list tracelog' -- "$cur" ) )
405408
;;
406409
esac
407410
;;

tools/bpf/bpftool/main.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ int do_event_pipe(int argc, char **argv);
167167
int do_cgroup(int argc, char **arg);
168168
int do_perf(int argc, char **arg);
169169
int do_net(int argc, char **arg);
170+
int do_tracelog(int argc, char **arg);
170171

171172
int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what);
172173
int prog_parse_fd(int *argc, char ***argv);

tools/bpf/bpftool/prog.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,7 @@ static int do_help(int argc, char **argv)
11401140
" [pinmaps MAP_DIR]\n"
11411141
" %s %s attach PROG ATTACH_TYPE [MAP]\n"
11421142
" %s %s detach PROG ATTACH_TYPE [MAP]\n"
1143+
" %s %s tracelog\n"
11431144
" %s %s help\n"
11441145
"\n"
11451146
" " HELP_SPEC_MAP "\n"
@@ -1158,7 +1159,7 @@ static int do_help(int argc, char **argv)
11581159
"",
11591160
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
11601161
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
1161-
bin_name, argv[-2], bin_name, argv[-2]);
1162+
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
11621163

11631164
return 0;
11641165
}
@@ -1173,6 +1174,7 @@ static const struct cmd cmds[] = {
11731174
{ "loadall", do_loadall },
11741175
{ "attach", do_attach },
11751176
{ "detach", do_detach },
1177+
{ "tracelog", do_tracelog },
11761178
{ 0 }
11771179
};
11781180

tools/bpf/bpftool/tracelog.c

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2+
/* Copyright (c) 2015-2017 Daniel Borkmann */
3+
/* Copyright (c) 2018 Netronome Systems, Inc. */
4+
5+
#include <errno.h>
6+
#include <limits.h>
7+
#include <signal.h>
8+
#include <stdio.h>
9+
#include <string.h>
10+
#include <unistd.h>
11+
#include <linux/magic.h>
12+
#include <sys/fcntl.h>
13+
#include <sys/vfs.h>
14+
15+
#include "main.h"
16+
17+
#ifndef TRACEFS_MAGIC
18+
# define TRACEFS_MAGIC 0x74726163
19+
#endif
20+
21+
#define _textify(x) #x
22+
#define textify(x) _textify(x)
23+
24+
FILE *trace_pipe_fd;
25+
char *buff;
26+
27+
static int validate_tracefs_mnt(const char *mnt, unsigned long magic)
28+
{
29+
struct statfs st_fs;
30+
31+
if (statfs(mnt, &st_fs) < 0)
32+
return -ENOENT;
33+
if ((unsigned long)st_fs.f_type != magic)
34+
return -ENOENT;
35+
36+
return 0;
37+
}
38+
39+
static bool
40+
find_tracefs_mnt_single(unsigned long magic, char *mnt, const char *mntpt)
41+
{
42+
size_t src_len;
43+
44+
if (validate_tracefs_mnt(mntpt, magic))
45+
return false;
46+
47+
src_len = strlen(mntpt);
48+
if (src_len + 1 >= PATH_MAX) {
49+
p_err("tracefs mount point name too long");
50+
return false;
51+
}
52+
53+
strcpy(mnt, mntpt);
54+
return true;
55+
}
56+
57+
static bool find_tracefs_pipe(char *mnt)
58+
{
59+
static const char * const known_mnts[] = {
60+
"/sys/kernel/debug/tracing",
61+
"/sys/kernel/tracing",
62+
"/tracing",
63+
"/trace",
64+
};
65+
const char *pipe_name = "/trace_pipe";
66+
const char *fstype = "tracefs";
67+
char type[100], format[32];
68+
const char * const *ptr;
69+
bool found = false;
70+
FILE *fp;
71+
72+
for (ptr = known_mnts; ptr < known_mnts + ARRAY_SIZE(known_mnts); ptr++)
73+
if (find_tracefs_mnt_single(TRACEFS_MAGIC, mnt, *ptr))
74+
goto exit_found;
75+
76+
fp = fopen("/proc/mounts", "r");
77+
if (!fp)
78+
return false;
79+
80+
/* Allow room for NULL terminating byte and pipe file name */
81+
snprintf(format, sizeof(format), "%%*s %%%zds %%99s %%*s %%*d %%*d\\n",
82+
PATH_MAX - strlen(pipe_name) - 1);
83+
while (fscanf(fp, format, mnt, type) == 2)
84+
if (strcmp(type, fstype) == 0) {
85+
found = true;
86+
break;
87+
}
88+
fclose(fp);
89+
90+
/* The string from fscanf() might be truncated, check mnt is valid */
91+
if (!found || validate_tracefs_mnt(mnt, TRACEFS_MAGIC))
92+
return false;
93+
94+
exit_found:
95+
strcat(mnt, pipe_name);
96+
return true;
97+
}
98+
99+
static void exit_tracelog(int signum)
100+
{
101+
fclose(trace_pipe_fd);
102+
free(buff);
103+
104+
if (json_output) {
105+
jsonw_end_array(json_wtr);
106+
jsonw_destroy(&json_wtr);
107+
}
108+
109+
exit(0);
110+
}
111+
112+
int do_tracelog(int argc, char **argv)
113+
{
114+
const struct sigaction act = {
115+
.sa_handler = exit_tracelog
116+
};
117+
char trace_pipe[PATH_MAX];
118+
bool found_trace_pipe;
119+
size_t buff_len = 0;
120+
121+
if (json_output)
122+
jsonw_start_array(json_wtr);
123+
124+
found_trace_pipe = find_tracefs_pipe(trace_pipe);
125+
if (!found_trace_pipe) {
126+
p_err("could not find trace pipe, tracefs not mounted?");
127+
return -1;
128+
}
129+
130+
trace_pipe_fd = fopen(trace_pipe, "r");
131+
if (!trace_pipe_fd) {
132+
p_err("could not open trace pipe: %s", strerror(errno));
133+
return -1;
134+
}
135+
136+
sigaction(SIGHUP, &act, NULL);
137+
sigaction(SIGINT, &act, NULL);
138+
sigaction(SIGTERM, &act, NULL);
139+
while (1) {
140+
ssize_t ret;
141+
142+
ret = getline(&buff, &buff_len, trace_pipe_fd);
143+
if (ret <= 0) {
144+
p_err("failed to read content from trace pipe: %s",
145+
strerror(errno));
146+
break;
147+
}
148+
if (json_output)
149+
jsonw_string(json_wtr, buff);
150+
else
151+
printf("%s", buff);
152+
}
153+
154+
fclose(trace_pipe_fd);
155+
free(buff);
156+
return -1;
157+
}

0 commit comments

Comments
 (0)