From 25a78dab35ecb4033399176934afeebb60b8e597 Mon Sep 17 00:00:00 2001 From: Ric Li Date: Fri, 27 Oct 2023 13:36:27 +0800 Subject: [PATCH] tools: add ebpf_tools (#548) The first tool is for tracing udp_send_skb calls. --------- Signed-off-by: Ric Li --- tools/ebpf/Makefile | 27 +++++++ tools/ebpf/README.md | 19 +++++ tools/ebpf/et.c | 169 ++++++++++++++++++++++++++++++++++++++++ tools/ebpf/et.h | 17 ++++ tools/ebpf/fentry.bpf.c | 64 +++++++++++++++ 5 files changed, 296 insertions(+) create mode 100644 tools/ebpf/Makefile create mode 100644 tools/ebpf/README.md create mode 100644 tools/ebpf/et.c create mode 100644 tools/ebpf/et.h create mode 100644 tools/ebpf/fentry.bpf.c diff --git a/tools/ebpf/Makefile b/tools/ebpf/Makefile new file mode 100644 index 000000000..bac2e3e34 --- /dev/null +++ b/tools/ebpf/Makefile @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright 2023 Intel Corporation + +.PHONY: all +all: et + +.PHONY: clean +clean: + rm -rf et *.o *.skel.h + +vmlinux.h: + bpftool btf dump file /sys/kernel/btf/vmlinux format c > $@ + +# Build BPF code, strip useless info +%.bpf.o: %.bpf.c vmlinux.h + clang -g -O2 -target bpf -c $(filter %.c,$^) -o $@ + llvm-strip -g $@ + +# Generate BPF skeletons +%.skel.h: %.bpf.o + bpftool gen skeleton $< > $@ + +# Get a list of all the .skel.h files +SKEL_FILES := $(patsubst %.bpf.c,%.skel.h,$(wildcard *.bpf.c)) + +et: et.c $(SKEL_FILES) + gcc -Wall -o $@ $(filter %.c,$^) -include $(SKEL_FILES) -l:libbpf.a -lelf -lz diff --git a/tools/ebpf/README.md b/tools/ebpf/README.md new file mode 100644 index 000000000..250a90dc7 --- /dev/null +++ b/tools/ebpf/README.md @@ -0,0 +1,19 @@ +# eBPF tools + +This directory contains tools for eBPF. + +## Build + +Dependencies: libbpf, bpftool, clang, llvm, gcc, libelf, zlib. + +```bash +make +``` + +## Run + +fentry: a simple program to trace udp_send_skb calls, requires kernel > 5.5. + +```bash +sudo ./et --prog fentry [--print] +``` diff --git a/tools/ebpf/et.c b/tools/ebpf/et.c new file mode 100644 index 000000000..827e863ec --- /dev/null +++ b/tools/ebpf/et.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Intel Corporation + */ + +#include "et.h" + +#include +#include +#include +#include +#include +#include +#include + +enum et_args_cmd { + ET_ARG_UNKNOWN = 0, + ET_ARG_PRINT_LIBBPF = 0x100, /* start from end of ascii */ + ET_ARG_PROG, + ET_ARG_HELP, +}; + +enum et_prog_type { + ET_PROG_UNKNOWN = 0, + ET_PROG_FENTRY, + ET_PROG_KPROBE, + ET_PROG_TRACEPOINT, + ET_PROG_XDP, +}; + +static const char* prog_type_str[] = { + [ET_PROG_FENTRY] = "fentry", + [ET_PROG_KPROBE] = "kprobe", + [ET_PROG_TRACEPOINT] = "tracepoint", + [ET_PROG_XDP] = "xdp", +}; + +struct et_ctx { + enum et_prog_type prog_type; +}; + +static volatile bool stop = false; + +static int libbpf_print_fn(enum libbpf_print_level level, const char* format, + va_list args) { + return vfprintf(stderr, format, args); +} + +static void et_sig_handler(int signo) { + printf("%s, signal %d\n", __func__, signo); + + switch (signo) { + case SIGINT: /* Interrupt from keyboard */ + stop = true; + break; + } + + return; +} + +static int udp_send_handler(void* ctx, void* data, size_t data_sz) { + const struct udp_send_event* e = data; + + printf("%s: pid %d, gso_size %u, bytes %u, duration_ns %llu\n", __func__, e->pid, + e->gso_size, e->udp_send_bytes, e->duration_ns); + return 0; +} + +static inline int et_fentry_loop() { + struct ring_buffer* rb = NULL; + struct fentry_bpf* skel; + int ret = 0; + + skel = fentry_bpf__open_and_load(); + if (!skel) { + printf("failed to open BPF skeleton\n"); + return -1; + } + + ret = fentry_bpf__attach(skel); + if (ret) { + printf("failed to attach BPF skeleton\n"); + goto cleanup; + } + + printf("fentry_bpf__attach() succeeded\n"); + + rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), udp_send_handler, NULL, NULL); + if (!rb) { + ret = -1; + fprintf(stderr, "failed to create ring buffer\n"); + goto cleanup; + } + + while (!stop) { + ret = ring_buffer__poll(rb, 100); + if (ret == -EINTR) { + ret = 0; + break; + } + if (ret < 0) { + printf("error polling perf buffer: %d\n", ret); + break; + } + } + +cleanup: + ring_buffer__free(rb); + fentry_bpf__destroy(skel); + return ret; +} + +static struct option et_args_options[] = {{"print", no_argument, 0, ET_ARG_PRINT_LIBBPF}, + {"prog", required_argument, 0, ET_ARG_PROG}, + {"help", no_argument, 0, ET_ARG_HELP}, + {0, 0, 0, 0}}; + +static void et_print_help() { + printf("\n"); + printf("##### Usage: #####\n\n"); + printf(" Params:\n"); + printf(" --help : print this help\n"); + printf(" --print : print libbpf output\n"); + printf(" --prog : attach to prog \n"); + printf("\n"); +} + +static int et_parse_args(struct et_ctx* ctx, int argc, char** argv) { + int cmd = -1, opt_idx = 0; + + while (1) { + cmd = getopt_long_only(argc, argv, "hv", et_args_options, &opt_idx); + if (cmd == -1) break; + + switch (cmd) { + case ET_ARG_PROG: + if (strcmp(optarg, "fentry") == 0) { + ctx->prog_type = ET_PROG_FENTRY; + } + break; + case ET_ARG_PRINT_LIBBPF: + libbpf_set_print(libbpf_print_fn); + break; + case ET_ARG_HELP: + default: + et_print_help(); + return -1; + } + }; + + return 0; +} + +int main(int argc, char** argv) { + struct et_ctx ctx = {0}; + et_parse_args(&ctx, argc, argv); + signal(SIGINT, et_sig_handler); + + printf("prog type is %s\n", prog_type_str[ctx.prog_type]); + switch (ctx.prog_type) { + case ET_PROG_FENTRY: + et_fentry_loop(); + break; + + default: + break; + } + + return 0; +} \ No newline at end of file diff --git a/tools/ebpf/et.h b/tools/ebpf/et.h new file mode 100644 index 000000000..091c6e7af --- /dev/null +++ b/tools/ebpf/et.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Intel Corporation + */ + +#ifndef __ET_H +#define __ET_H + +struct udp_send_event { + int pid; + int udp_send_cnt; + unsigned int gso_size; + unsigned long long duration_ns; + unsigned int udp_send_bytes; + int ret; +}; + +#endif /* __ET_H */ \ No newline at end of file diff --git a/tools/ebpf/fentry.bpf.c b/tools/ebpf/fentry.bpf.c new file mode 100644 index 000000000..2ea1db04c --- /dev/null +++ b/tools/ebpf/fentry.bpf.c @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + * Copyright(c) 2023 Intel Corporation + */ + +//clang-format off +#include "vmlinux.h" +//clang-format off +#include +#include + +#include "et.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 8192); + __type(key, u64); + __type(value, u64); +} start_time SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 256 * 1024); +} rb SEC(".maps"); + +SEC("fentry/udp_send_skb") +int BPF_PROG(udp_send_skb, struct sk_buff* skb, struct flowi4* fl4, + struct inet_cork* cork) { + u64 ts; + u64 skb_addr = (u64)skb; + + ts = bpf_ktime_get_ns(); + bpf_map_update_elem(&start_time, &skb_addr, &ts, BPF_ANY); + + return 0; +} + +SEC("fexit/udp_send_skb") +int BPF_PROG(udp_send_skb_exit, struct sk_buff* skb, struct flowi4* fl4, + struct inet_cork* cork, long ret) { + struct udp_send_event* e; + pid_t pid; + u64 *start_ts, duration_ns = 0; + u64 skb_addr = (u64)skb; + + pid = bpf_get_current_pid_tgid() >> 32; + start_ts = bpf_map_lookup_elem(&start_time, &skb_addr); + if (start_ts) duration_ns = bpf_ktime_get_ns() - *start_ts; + bpf_map_delete_elem(&start_time, &skb_addr); + + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); + if (!e) return 0; + + e->pid = pid; + e->gso_size = cork->gso_size; + e->duration_ns = duration_ns; + e->udp_send_bytes = skb->len; + e->ret = ret; + + bpf_ringbuf_submit(e, 0); + + return 0; +} \ No newline at end of file