diff options
author | Dave Marchevsky <davemarchevsky@fb.com> | 2022-03-17 01:29:42 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-17 01:29:42 -0400 |
commit | d1b1b61ceaee16e9d68e3c66e3ebddd55ba36d0f (patch) | |
tree | 8d67760cddd4e959b47e0521260659cdc5cba5b4 | |
parent | 4eff6ca4607e4c22fec403cc2b1a816f3e872c55 (diff) | |
parent | 36daa18c47ab1b7a0bc3a320d5ce88dbfa583ae7 (diff) | |
download | bcc-d1b1b61ceaee16e9d68e3c66e3ebddd55ba36d0f.tar.gz |
Merge pull request #3768 from chendotjs/tcpsynbl
libbpf-tools: convert BCC tcpsynbl to BPF CO-RE version
-rw-r--r-- | libbpf-tools/.gitignore | 1 | ||||
-rw-r--r-- | libbpf-tools/Makefile | 1 | ||||
-rw-r--r-- | libbpf-tools/tcpsynbl.bpf.c | 66 | ||||
-rw-r--r-- | libbpf-tools/tcpsynbl.c | 250 | ||||
-rw-r--r-- | libbpf-tools/tcpsynbl.h | 11 |
5 files changed, 329 insertions, 0 deletions
diff --git a/libbpf-tools/.gitignore b/libbpf-tools/.gitignore index aaafc139..ce95db71 100644 --- a/libbpf-tools/.gitignore +++ b/libbpf-tools/.gitignore @@ -44,6 +44,7 @@ /tcpconnect /tcpconnlat /tcprtt +/tcpsynbl /vfsstat /xfsdist /xfsslower diff --git a/libbpf-tools/Makefile b/libbpf-tools/Makefile index aba19e00..81a6faaa 100644 --- a/libbpf-tools/Makefile +++ b/libbpf-tools/Makefile @@ -56,6 +56,7 @@ APPS = \ tcpconnect \ tcpconnlat \ tcprtt \ + tcpsynbl \ vfsstat \ # diff --git a/libbpf-tools/tcpsynbl.bpf.c b/libbpf-tools/tcpsynbl.bpf.c new file mode 100644 index 00000000..c7d47faa --- /dev/null +++ b/libbpf-tools/tcpsynbl.bpf.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2021 Yaqi Chen +#include <vmlinux.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_core_read.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_endian.h> +#include "tcpsynbl.h" +#include "bits.bpf.h" +#include "maps.bpf.h" + +#define MAX_ENTRIES 65536 + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, MAX_ENTRIES); + __type(key, u64); + __type(value, struct hist); +} hists SEC(".maps"); + +static struct hist zero; + +static int do_entry(struct sock *sk) +{ + u64 max_backlog, backlog, slot; + struct hist *histp; + + max_backlog = BPF_CORE_READ(sk, sk_max_ack_backlog); + backlog = BPF_CORE_READ(sk, sk_ack_backlog); + histp = bpf_map_lookup_or_try_init(&hists, &max_backlog, &zero); + if (!histp) + return 0; + + slot = log2l(backlog); + if (slot >= MAX_SLOTS) + slot = MAX_SLOTS - 1; + __sync_fetch_and_add(&histp->slots[slot], 1); + return 0; +} + + +SEC("kprobe/tcp_v4_syn_recv_sock") +int BPF_KPROBE(tcp_v4_syn_recv_kprobe, struct sock *sk) +{ + return do_entry(sk); +} + +SEC("kprobe/tcp_v6_syn_recv_sock") +int BPF_KPROBE(tcp_v6_syn_recv_kprobe, struct sock *sk) +{ + return do_entry(sk); +} + +SEC("fentry/tcp_v4_syn_recv_sock") +int BPF_PROG(tcp_v4_syn_recv, struct sock *sk) +{ + return do_entry(sk); +} + +SEC("fentry/tcp_v6_syn_recv_sock") +int BPF_PROG(tcp_v6_syn_recv, struct sock *sk) +{ + return do_entry(sk); +} + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; diff --git a/libbpf-tools/tcpsynbl.c b/libbpf-tools/tcpsynbl.c new file mode 100644 index 00000000..f51580e7 --- /dev/null +++ b/libbpf-tools/tcpsynbl.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +// Copyright (c) 2021 Yaqi Chen +// +// Based on tcpsynbl(8) from BCC by Brendan Gregg. +// 19-Dec-2021 Yaqi Chen Created this. +#include <argp.h> +#include <stdio.h> +#include <signal.h> +#include <unistd.h> +#include <time.h> +#include <bpf/libbpf.h> +#include <bpf/bpf.h> +#include "tcpsynbl.h" +#include "tcpsynbl.skel.h" +#include "trace_helpers.h" + +static struct env { + bool ipv4; + bool ipv6; + time_t interval; + int times; + bool timestamp; + bool verbose; +} env = { + .interval = 99999999, + .times = 99999999, +}; + +static volatile sig_atomic_t exiting = 0; + +const char *argp_program_version = "tcpsynbl 0.1"; +const char *argp_program_bug_address = + "https://github.com/iovisor/bcc/tree/master/libbpf-tools"; +const char argp_program_doc[] = +"Summarize TCP SYN backlog as a histogram.\n" +"\n" +"USAGE: tcpsynbl [--help] [-T] [-4] [-6] [interval] [count]\n" +"\n" +"EXAMPLES:\n" +" tcpsynbl # summarize TCP SYN backlog as a histogram\n" +" tcpsynbl 1 10 # print 1 second summaries, 10 times\n" +" tcpsynbl -T 1 # 1s summaries with timestamps\n" +" tcpsynbl -4 # trace IPv4 family only\n" +" tcpsynbl -6 # trace IPv6 family only\n"; + + +static const struct argp_option opts[] = { + { "timestamp", 'T', NULL, 0, "Include timestamp on output" }, + { "ipv4", '4', NULL, 0, "Trace IPv4 family only" }, + { "ipv6", '6', NULL, 0, "Trace IPv6 family only" }, + { "verbose", 'v', NULL, 0, "Verbose debug output" }, + { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" }, + {}, +}; + + +static error_t parse_arg(int key, char *arg, struct argp_state *state) +{ + static int pos_args; + + switch (key) { + case 'h': + argp_state_help(state, stderr, ARGP_HELP_STD_HELP); + break; + case 'v': + env.verbose = true; + break; + case 'T': + env.timestamp = true; + break; + case '4': + env.ipv4 = true; + break; + case '6': + env.ipv6 = true; + break; + case ARGP_KEY_ARG: + errno = 0; + if (pos_args == 0) { + env.interval = strtol(arg, NULL, 10); + if (errno) { + fprintf(stderr, "invalid internal\n"); + argp_usage(state); + } + } else if (pos_args == 1) { + env.times = strtol(arg, NULL, 10); + if (errno) { + fprintf(stderr, "invalid times\n"); + argp_usage(state); + } + } else { + fprintf(stderr, + "unrecognized positional argument: %s\n", arg); + argp_usage(state); + } + pos_args++; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) +{ + if (level == LIBBPF_DEBUG && !env.verbose) + return 0; + return vfprintf(stderr, format, args); +} + +static void sig_handler(int sig) +{ + exiting = true; +} + +static void disable_all_progs(struct tcpsynbl_bpf *obj) +{ + bpf_program__set_autoload(obj->progs.tcp_v4_syn_recv_kprobe, false); + bpf_program__set_autoload(obj->progs.tcp_v6_syn_recv_kprobe, false); + bpf_program__set_autoload(obj->progs.tcp_v4_syn_recv, false); + bpf_program__set_autoload(obj->progs.tcp_v6_syn_recv, false); +} + +static void set_autoload_prog(struct tcpsynbl_bpf *obj, int version) +{ + if (version == 4) { + if (fentry_exists("tcp_v4_syn_recv_sock", NULL)) + bpf_program__set_autoload(obj->progs.tcp_v4_syn_recv, true); + else + bpf_program__set_autoload(obj->progs.tcp_v4_syn_recv_kprobe, true); + } + + if (version == 6){ + if (fentry_exists("tcp_v6_syn_recv_sock", NULL)) + bpf_program__set_autoload(obj->progs.tcp_v6_syn_recv, true); + else + bpf_program__set_autoload(obj->progs.tcp_v6_syn_recv_kprobe, true); + } +} + +static int print_log2_hists(int fd) +{ + __u64 lookup_key = -1, next_key; + struct hist hist; + int err; + + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + err = bpf_map_lookup_elem(fd, &next_key, &hist); + if (err < 0) { + fprintf(stderr, "failed to lookup hist: %d\n", err); + return -1; + } + printf("backlog_max = %lld\n", next_key); + print_log2_hist(hist.slots, MAX_SLOTS, "backlog"); + lookup_key = next_key; + } + + lookup_key = -1; + while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { + err = bpf_map_delete_elem(fd, &next_key); + if (err < 0) { + fprintf(stderr, "failed to cleanup hist : %d\n", err); + return -1; + } + lookup_key = next_key; + } + + return 0; +} + +int main(int argc, char **argv) +{ + static const struct argp argp = { + .options = opts, + .parser = parse_arg, + .doc = argp_program_doc + }; + + struct tcpsynbl_bpf *obj; + struct tm *tm; + char ts[32]; + time_t t; + int err, map_fd; + + err = argp_parse(&argp, argc, argv, 0, NULL, NULL); + if (err) + return err; + + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + libbpf_set_print(libbpf_print_fn); + + obj = tcpsynbl_bpf__open(); + if (!obj) { + fprintf(stderr, "failed to open BPF object\n"); + return 1; + } + + disable_all_progs(obj); + + if (env.ipv4) { + set_autoload_prog(obj, 4); + } else if (env.ipv6) { + set_autoload_prog(obj, 6); + } else { + set_autoload_prog(obj, 4); + set_autoload_prog(obj, 6); + } + + err = tcpsynbl_bpf__load(obj); + if (err) { + fprintf(stderr, "failed to load BPF object: %d\n", err); + goto cleanup; + } + + err = tcpsynbl_bpf__attach(obj); + if (err) { + fprintf(stderr, "failed to attach BPF programs\n"); + goto cleanup; + } + + map_fd= bpf_map__fd(obj->maps.hists); + + signal(SIGINT, sig_handler); + + printf("Tracing SYN backlog size. Ctrl-C to end.\n"); + + /* main: poll */ + while (1) { + sleep(env.interval); + printf("\n"); + + if (env.timestamp) { + time(&t); + tm = localtime(&t); + strftime(ts, sizeof(ts), "%H:%M:%S", tm); + printf("%-8s\n", ts); + } + + err = print_log2_hists(map_fd); + if (err) + break; + + if (exiting || --env.times == 0) + break; + } + +cleanup: + tcpsynbl_bpf__destroy(obj); + return err != 0; +} diff --git a/libbpf-tools/tcpsynbl.h b/libbpf-tools/tcpsynbl.h new file mode 100644 index 00000000..6c22abb2 --- /dev/null +++ b/libbpf-tools/tcpsynbl.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __TCPSYNBL_H +#define __TCPSYNBL_H + +#define MAX_SLOTS 32 + +struct hist { + __u32 slots[MAX_SLOTS]; +}; + +#endif /* __TCPSYNBL_H */ |