aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Marchevsky <davemarchevsky@fb.com>2022-03-17 01:29:42 -0400
committerGitHub <noreply@github.com>2022-03-17 01:29:42 -0400
commitd1b1b61ceaee16e9d68e3c66e3ebddd55ba36d0f (patch)
tree8d67760cddd4e959b47e0521260659cdc5cba5b4
parent4eff6ca4607e4c22fec403cc2b1a816f3e872c55 (diff)
parent36daa18c47ab1b7a0bc3a320d5ce88dbfa583ae7 (diff)
downloadbcc-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/.gitignore1
-rw-r--r--libbpf-tools/Makefile1
-rw-r--r--libbpf-tools/tcpsynbl.bpf.c66
-rw-r--r--libbpf-tools/tcpsynbl.c250
-rw-r--r--libbpf-tools/tcpsynbl.h11
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 */