Skip to content

Commit 7b22e4e

Browse files
committed
Adding tracing03-xdp-debug-print example
In this lesson we will show how to print message from eBPF program into tracefs buffer. Signed-off-by: Jiri Olsa <jolsa@kernel.org>
1 parent 4e9bdcd commit 7b22e4e

4 files changed

Lines changed: 265 additions & 0 deletions

File tree

tracing03-xdp-debug-print/Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
2+
3+
XDP_TARGETS := xdp_prog_kern
4+
USER_TARGETS := trace_read
5+
6+
LLC ?= llc
7+
CLANG ?= clang
8+
CC := gcc
9+
10+
LIBBPF_DIR = ../libbpf/src/
11+
COMMON_DIR = ../common/
12+
13+
include $(COMMON_DIR)/common.mk
14+
COMMON_OBJS := $(COMMON_DIR)/common_params.o
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# -*- fill-column: 76; -*-
2+
#+TITLE: Tutorial: Tracing03 - debug print
3+
#+OPTIONS: ^:nil
4+
5+
In this lesson we will show how to print message from eBPF program
6+
into tracefs buffer.
7+
8+
* Table of Contents :TOC:
9+
- [[#bpf-trace-printk][eBPF trace printk helper]]
10+
- [[#tracefs-pipe-reader][The tracefs pipe reader]]
11+
- [[#assignments][Assignments]]
12+
- [[#assignment-1-setting-up-your-test-lab][Assignment 1: Setting up your test lab]]
13+
- [[#assignment-2-bpf-trace-printk][Assignment 2: Run debug code]]
14+
15+
16+
* eBPF trace printk helper
17+
18+
The bpf_trace_print helper function is very useful when debugging or
19+
when there's need for immediate feedback from the eBPF program.
20+
21+
It offers limited trace_printk capability and basically stores message
22+
into the tracefs buffer.
23+
24+
The bpf_trace_printk interface is:
25+
26+
#+begin_example sh
27+
/*
28+
* Only limited trace_printk() conversion specifiers allowed:
29+
* %d %i %u %x %ld %li %lu %lx %lld %lli %llu %llx %p %s
30+
*/
31+
BPF_CALL_5(bpf_trace_printk, char *, fmt, u32, fmt_size, u64, arg1,
32+
u64, arg2, u64, arg3)
33+
#+end_example
34+
35+
Because the above interface requires to put the size of the format
36+
string it's more convenient to use following macro, which allows
37+
alone string argument to be passed:
38+
39+
#+begin_example sh
40+
#define bpf_printk(fmt, ...) \
41+
({ \
42+
char ____fmt[] = fmt; \
43+
bpf_trace_printk(____fmt, sizeof(____fmt), \
44+
##__VA_ARGS__); \
45+
})
46+
47+
SEC("xdp")
48+
int xdp_prog_simple(struct xdp_md *ctx)
49+
{
50+
bpf_printk("...");
51+
return XDP_PASS;
52+
}
53+
#+end_example
54+
55+
* The tracefs pipe reader
56+
57+
To retrieve the message printed by bpf_trace_printk, you can either
58+
read tracefs buffer directly:
59+
60+
#+begin_example sh
61+
$ sudo cat /sys/kernel/debug/tracing/trace_pipe
62+
#+end_example
63+
64+
Or you can use standard C file-reading/parsing code to get the data:
65+
66+
#+begin_example sh
67+
stream = fopen(TRACEFS_PIPE, "r");
68+
69+
...
70+
71+
while ((nread = getline(&line, &len, stream)) != -1) {
72+
#+end_example
73+
74+
for more details please check on trace_read.c file.
75+
76+
* Assignments
77+
78+
** Assignment 1: Setting up your test lab
79+
80+
In this lesson we will use the setup of the previous lesson:
81+
Basic02 - loading a program by name [[https://github.com/xdp-project/xdp-tutorial/tree/master/basic02-prog-by-name#assignment-2-add-xdp_abort-program]]
82+
83+
Setup the environment:
84+
85+
#+begin_example sh
86+
$ sudo ../testenv/testenv.sh setup --name veth-basic02
87+
#+end_example
88+
89+
Load XDP program from xdp_prog_kern.o that will print
90+
ethertnet header on every incomming packet:
91+
92+
#+begin_example sh
93+
$ sudo ../basic02-prog-by-name/xdp_loader --dev veth-basic02 --force --progsec xdp
94+
#+end_example
95+
96+
and make some packets:
97+
98+
#+begin_example sh
99+
$ sudo ../testenv/testenv.sh enter --name veth-basic02
100+
# ping fc00:dead:cafe:1::1
101+
PING fc00:dead:cafe:1::1(fc00:dead:cafe:1::1) 56 data bytes
102+
#+end_example
103+
104+
- Assignment 2: Run debug code
105+
106+
#+begin_example sh
107+
bpf_printk("src: %llu, dst: %llu, proto: %u\n",
108+
ether_addr_to_u64(eth->h_source),
109+
ether_addr_to_u64(eth->h_dest),
110+
bpf_ntohs(eth->h_proto));
111+
#+end_example
112+
113+
You can monitor the message either via tracefs:
114+
115+
#+begin_example sh
116+
$ sudo cat /sys/kernel/debug/tracing/trace_pipe
117+
ping-28172 [001] ..s1 155229.100016: 0: src: 99726513069783, dst: 63819112930922, proto: 56710
118+
ping-28172 [001] ..s1 155230.124054: 0: src: 99726513069783, dst: 63819112930922, proto: 56710
119+
ping-28172 [001] ..s1 155231.148018: 0: src: 99726513069783, dst: 63819112930922, proto: 56710
120+
ping-28172 [001] ..s1 155232.172022: 0: src: 99726513069783, dst: 63819112930922, proto: 56710
121+
#+end_example
122+
123+
or with the trace_read application:
124+
125+
#+begin_example sh
126+
$ sudo ./trace_read
127+
src: 5a:b3:63:62:de:d7 dst: 3a:b:b:8e:5e:6a proto: 56710
128+
src: 5a:b3:63:62:de:d7 dst: 3a:b:b:8e:5e:6a proto: 56710
129+
src: 5a:b3:63:62:de:d7 dst: 3a:b:b:8e:5e:6a proto: 56710
130+
...
131+
#+end_example
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#define _GNU_SOURCE
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <string.h>
7+
#include "../common/common_defines.h"
8+
#include <netinet/ether.h>
9+
10+
#define TRACEFS_PIPE "/sys/kernel/debug/tracing/trace_pipe"
11+
12+
#ifndef PATH_MAX
13+
#define PATH_MAX 4096
14+
#endif
15+
16+
static void print_ether_addr(const char *type, char *str)
17+
{
18+
__u64 addr;
19+
20+
if (1 != sscanf(str, "%llu", &addr))
21+
return;
22+
23+
printf("%s: %s ", type, ether_ntoa((struct ether_addr *) &addr));
24+
}
25+
26+
int main(int argc, char **argv)
27+
{
28+
FILE *stream;
29+
char *line = NULL;
30+
size_t len = 0;
31+
ssize_t nread;
32+
33+
stream = fopen(TRACEFS_PIPE, "r");
34+
if (stream == NULL) {
35+
perror("fopen");
36+
exit(EXIT_FAILURE);
37+
}
38+
39+
40+
while ((nread = getline(&line, &len, stream)) != -1) {
41+
char *tok, *saveptr;
42+
unsigned int proto;
43+
44+
tok = strtok_r(line, " ", &saveptr);
45+
46+
while (tok) {
47+
if (!strncmp(tok, "src:", 4)) {
48+
tok = strtok_r(NULL, " ", &saveptr);
49+
print_ether_addr("src", tok);
50+
}
51+
52+
if (!strncmp(tok, "dst:", 4)) {
53+
tok = strtok_r(NULL, " ", &saveptr);
54+
print_ether_addr("dst", tok);
55+
}
56+
57+
if (!strncmp(tok, "proto:", 5)) {
58+
tok = strtok_r(NULL, " ", &saveptr);
59+
if (1 == sscanf(tok, "%u", &proto))
60+
printf("proto: %u", proto);
61+
}
62+
tok = strtok_r(NULL, " ", &saveptr);
63+
}
64+
65+
printf("\n");
66+
}
67+
68+
free(line);
69+
fclose(stream);
70+
return EXIT_OK;
71+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#include <linux/bpf.h>
3+
#include <linux/if_ether.h>
4+
#include <linux/if_packet.h>
5+
#include <linux/if_vlan.h>
6+
#include <linux/ip.h>
7+
#include <linux/in.h>
8+
#include <stdbool.h>
9+
#include "bpf_helpers.h"
10+
#include "bpf_endian.h"
11+
12+
#define bpf_printk(fmt, ...) \
13+
({ \
14+
char ____fmt[] = fmt; \
15+
bpf_trace_printk(____fmt, sizeof(____fmt), \
16+
##__VA_ARGS__); \
17+
})
18+
19+
/* to u64 in host order */
20+
static inline __u64 ether_addr_to_u64(const __u8 *addr)
21+
{
22+
__u64 u = 0;
23+
int i;
24+
25+
for (i = ETH_ALEN; i >= 0; i--)
26+
u = u << 8 | addr[i];
27+
return u;
28+
}
29+
30+
SEC("xdp")
31+
int xdp_prog_simple(struct xdp_md *ctx)
32+
{
33+
void *data = (void *)(long)ctx->data;
34+
void *data_end = (void *)(long)ctx->data_end;
35+
struct ethhdr *eth = data;
36+
__u64 offset = sizeof(*eth);
37+
38+
if ((void *)eth + offset > data_end)
39+
return 0;
40+
41+
bpf_printk("src: %llu, dst: %llu, proto: %u\n",
42+
ether_addr_to_u64(eth->h_source),
43+
ether_addr_to_u64(eth->h_dest),
44+
bpf_ntohs(eth->h_proto));
45+
46+
return XDP_PASS;
47+
}
48+
49+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)