This commit is contained in:
yunwei37
2023-05-06 18:10:51 +00:00
parent bcc37b85d4
commit f56e271d19
12 changed files with 864 additions and 187 deletions

View File

@@ -285,7 +285,7 @@ int BPF_PROG(irq_handler_exit, int irq, struct irqaction *action)
char LICENSE[] SEC("license") = "GPL";
</code></pre>
<p>这是一个 BPFBerkeley Packet Filter程序。BPF 程序是小型程序,可以直接在 Linux 内核中运行,用于过滤和操纵网络流量。这个特定的程序似乎旨在收集内核中中断处理程序的统计信息。它定义了一些地图(可以在 BPF 程序和内核的其他部分之间共享的数据结构和两个函数handle_entry 和 handle_exit。当内核进入和退出中断处理程序时分别执行这些函数。handle_entry 函数用于跟踪中断处理程序被执行的次数,而 handle_exit 则用于测量中断处理程序中花费的时间。</p>
<p>这是一个 BPFBerkeley Packet Filter程序。BPF 程序是小型程序,可以直接在 Linux 内核中运行,用于过滤和操纵网络流量。这个特定的程序似乎旨在收集内核中中断处理程序的统计信息。它定义了一些 maps (可以在 BPF 程序和内核的其他部分之间共享的数据结构和两个函数handle_entry 和 handle_exit。当内核进入和退出中断处理程序时分别执行这些函数。handle_entry 函数用于跟踪中断处理程序被执行的次数,而 handle_exit 则用于测量中断处理程序中花费的时间。</p>
<h2 id="运行代码"><a class="header" href="#运行代码">运行代码</a></h2>
<p>eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <a href="https://github.com/eunomia-bpf/eunomia-bpf">https://github.com/eunomia-bpf/eunomia-bpf</a> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。</p>
<p>要编译这个程序,请使用 ecc 工具:</p>

141
11-bootstrap/Makefile Normal file
View File

@@ -0,0 +1,141 @@
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
OUTPUT := .output
CLANG ?= clang
LIBBPF_SRC := $(abspath ../../libbpf/src)
BPFTOOL_SRC := $(abspath ../../bpftool/src)
LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a)
BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool)
BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool
LIBBLAZESYM_SRC := $(abspath ../../blazesym/)
LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a)
LIBBLAZESYM_HEADER := $(abspath $(OUTPUT)/blazesym.h)
ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \
| sed 's/arm.*/arm/' \
| sed 's/aarch64/arm64/' \
| sed 's/ppc64le/powerpc/' \
| sed 's/mips.*/mips/' \
| sed 's/riscv64/riscv/' \
| sed 's/loongarch64/loongarch/')
VMLINUX := ../../vmlinux/$(ARCH)/vmlinux.h
# Use our own libbpf API headers and Linux UAPI headers distributed with
# libbpf to avoid dependency on system-wide headers, which could be missing or
# outdated
INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX))
CFLAGS := -g -Wall
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)
APPS = bootstrap # minimal minimal_legacy uprobe kprobe fentry usdt sockfilter tc ksyscall
CARGO ?= $(shell which cargo)
ifeq ($(strip $(CARGO)),)
BZS_APPS :=
else
BZS_APPS := # profile
APPS += $(BZS_APPS)
# Required by libblazesym
ALL_LDFLAGS += -lrt -ldl -lpthread -lm
endif
# Get Clang's default includes on this system. We'll explicitly add these dirs
# to the includes list when compiling with `-target bpf` because otherwise some
# architecture-specific dirs will be "missing" on some architectures/distros -
# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h,
# sys/cdefs.h etc. might be missing.
#
# Use '-idirafter': Don't interfere with include mechanics except where the
# build would have failed anyways.
CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - </dev/null 2>&1 \
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')
ifeq ($(V),1)
Q =
msg =
else
Q = @
msg = @printf ' %-8s %s%s\n' \
"$(1)" \
"$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \
"$(if $(3), $(3))";
MAKEFLAGS += --no-print-directory
endif
define allow-override
$(if $(or $(findstring environment,$(origin $(1))),\
$(findstring command line,$(origin $(1)))),,\
$(eval $(1) = $(2)))
endef
$(call allow-override,CC,$(CROSS_COMPILE)cc)
$(call allow-override,LD,$(CROSS_COMPILE)ld)
.PHONY: all
all: $(APPS)
.PHONY: clean
clean:
$(call msg,CLEAN)
$(Q)rm -rf $(OUTPUT) $(APPS)
$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT):
$(call msg,MKDIR,$@)
$(Q)mkdir -p $@
# Build libbpf
$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf
$(call msg,LIB,$@)
$(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \
OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \
INCLUDEDIR= LIBDIR= UAPIDIR= \
install
# Build bpftool
$(BPFTOOL): | $(BPFTOOL_OUTPUT)
$(call msg,BPFTOOL,$@)
$(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap
$(LIBBLAZESYM_SRC)/target/release/libblazesym.a::
$(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --features=cheader,dont-generate-test-files --release
$(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB, $@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@
$(LIBBLAZESYM_HEADER): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB,$@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/blazesym.h $@
# Build BPF code
$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL)
$(call msg,BPF,$@)
$(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \
$(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \
-c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
$(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
# Generate BPF skeletons
$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL)
$(call msg,GEN-SKEL,$@)
$(Q)$(BPFTOOL) gen skeleton $< > $@
# Build user-space code
$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h
$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT)
$(call msg,CC,$@)
$(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@
$(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_HEADER)
$(BZS_APPS): $(LIBBLAZESYM_OBJ)
# Build application binary
$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT)
$(call msg,BINARY,$@)
$(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@
# delete failed targets
.DELETE_ON_ERROR:
# keep intermediate (.skel.h, .bpf.o, etc) targets
.SECONDARY:

View File

@@ -1,112 +1,112 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2020 Facebook */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include "bootstrap.h"
char LICENSE[] SEC("license") = "Dual BSD/GPL";
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 8192);
__type(key, pid_t);
__type(value, u64);
} exec_start SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} rb SEC(".maps");
const volatile unsigned long long min_duration_ns = 0;
SEC("tp/sched/sched_process_exec")
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
{
struct task_struct *task;
unsigned fname_off;
struct event *e;
pid_t pid;
u64 ts;
/* remember time exec() was executed for this PID */
pid = bpf_get_current_pid_tgid() >> 32;
ts = bpf_ktime_get_ns();
bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY);
/* don't emit exec events when minimum duration is specified */
if (min_duration_ns)
return 0;
/* reserve sample from BPF ringbuf */
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
if (!e)
return 0;
/* fill out the sample with data */
task = (struct task_struct *)bpf_get_current_task();
e->exit_event = false;
e->pid = pid;
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
bpf_get_current_comm(&e->comm, sizeof(e->comm));
fname_off = ctx->__data_loc_filename & 0xFFFF;
bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + fname_off);
/* successfully submit it to user-space for post-processing */
bpf_ringbuf_submit(e, 0);
return 0;
}
SEC("tp/sched/sched_process_exit")
int handle_exit(struct trace_event_raw_sched_process_template* ctx)
{
struct task_struct *task;
struct event *e;
pid_t pid, tid;
u64 id, ts, *start_ts, duration_ns = 0;
/* get PID and TID of exiting thread/process */
id = bpf_get_current_pid_tgid();
pid = id >> 32;
tid = (u32)id;
/* ignore thread exits */
if (pid != tid)
return 0;
/* if we recorded start of the process, calculate lifetime duration */
start_ts = bpf_map_lookup_elem(&exec_start, &pid);
if (start_ts)
duration_ns = bpf_ktime_get_ns() - *start_ts;
else if (min_duration_ns)
return 0;
bpf_map_delete_elem(&exec_start, &pid);
/* if process didn't live long enough, return early */
if (min_duration_ns && duration_ns < min_duration_ns)
return 0;
/* reserve sample from BPF ringbuf */
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
if (!e)
return 0;
/* fill out the sample with data */
task = (struct task_struct *)bpf_get_current_task();
e->exit_event = true;
e->duration_ns = duration_ns;
e->pid = pid;
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;
bpf_get_current_comm(&e->comm, sizeof(e->comm));
/* send data to user-space for post-processing */
bpf_ringbuf_submit(e, 0);
return 0;
}
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2020 Facebook */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include "bootstrap.h"
char LICENSE[] SEC("license") = "Dual BSD/GPL";
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 8192);
__type(key, pid_t);
__type(value, u64);
} exec_start SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} rb SEC(".maps");
const volatile unsigned long long min_duration_ns = 0;
SEC("tp/sched/sched_process_exec")
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
{
struct task_struct *task;
unsigned fname_off;
struct event *e;
pid_t pid;
u64 ts;
/* remember time exec() was executed for this PID */
pid = bpf_get_current_pid_tgid() >> 32;
ts = bpf_ktime_get_ns();
bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY);
/* don't emit exec events when minimum duration is specified */
if (min_duration_ns)
return 0;
/* reserve sample from BPF ringbuf */
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
if (!e)
return 0;
/* fill out the sample with data */
task = (struct task_struct *)bpf_get_current_task();
e->exit_event = false;
e->pid = pid;
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
bpf_get_current_comm(&e->comm, sizeof(e->comm));
fname_off = ctx->__data_loc_filename & 0xFFFF;
bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + fname_off);
/* successfully submit it to user-space for post-processing */
bpf_ringbuf_submit(e, 0);
return 0;
}
SEC("tp/sched/sched_process_exit")
int handle_exit(struct trace_event_raw_sched_process_template* ctx)
{
struct task_struct *task;
struct event *e;
pid_t pid, tid;
u64 id, ts, *start_ts, duration_ns = 0;
/* get PID and TID of exiting thread/process */
id = bpf_get_current_pid_tgid();
pid = id >> 32;
tid = (u32)id;
/* ignore thread exits */
if (pid != tid)
return 0;
/* if we recorded start of the process, calculate lifetime duration */
start_ts = bpf_map_lookup_elem(&exec_start, &pid);
if (start_ts)
duration_ns = bpf_ktime_get_ns() - *start_ts;
else if (min_duration_ns)
return 0;
bpf_map_delete_elem(&exec_start, &pid);
/* if process didn't live long enough, return early */
if (min_duration_ns && duration_ns < min_duration_ns)
return 0;
/* reserve sample from BPF ringbuf */
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
if (!e)
return 0;
/* fill out the sample with data */
task = (struct task_struct *)bpf_get_current_task();
e->exit_event = true;
e->duration_ns = duration_ns;
e->pid = pid;
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;
bpf_get_current_comm(&e->comm, sizeof(e->comm));
/* send data to user-space for post-processing */
bpf_ringbuf_submit(e, 0);
return 0;
}

173
11-bootstrap/bootstrap.c Normal file
View File

@@ -0,0 +1,173 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2020 Facebook */
#include <argp.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include "bootstrap.h"
#include "bootstrap.skel.h"
static struct env {
bool verbose;
long min_duration_ms;
} env;
const char *argp_program_version = "bootstrap 0.0";
const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
const char argp_program_doc[] =
"BPF bootstrap demo application.\n"
"\n"
"It traces process start and exits and shows associated \n"
"information (filename, process duration, PID and PPID, etc).\n"
"\n"
"USAGE: ./bootstrap [-d <min-duration-ms>] [-v]\n";
static const struct argp_option opts[] = {
{ "verbose", 'v', NULL, 0, "Verbose debug output" },
{ "duration", 'd', "DURATION-MS", 0, "Minimum process duration (ms) to report" },
{},
};
static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
switch (key) {
case 'v':
env.verbose = true;
break;
case 'd':
errno = 0;
env.min_duration_ms = strtol(arg, NULL, 10);
if (errno || env.min_duration_ms <= 0) {
fprintf(stderr, "Invalid duration: %s\n", arg);
argp_usage(state);
}
break;
case ARGP_KEY_ARG:
argp_usage(state);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static const struct argp argp = {
.options = opts,
.parser = parse_arg,
.doc = argp_program_doc,
};
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 volatile bool exiting = false;
static void sig_handler(int sig)
{
exiting = true;
}
static int handle_event(void *ctx, void *data, size_t data_sz)
{
const struct event *e = data;
struct tm *tm;
char ts[32];
time_t t;
time(&t);
tm = localtime(&t);
strftime(ts, sizeof(ts), "%H:%M:%S", tm);
if (e->exit_event) {
printf("%-8s %-5s %-16s %-7d %-7d [%u]",
ts, "EXIT", e->comm, e->pid, e->ppid, e->exit_code);
if (e->duration_ns)
printf(" (%llums)", e->duration_ns / 1000000);
printf("\n");
} else {
printf("%-8s %-5s %-16s %-7d %-7d %s\n",
ts, "EXEC", e->comm, e->pid, e->ppid, e->filename);
}
return 0;
}
int main(int argc, char **argv)
{
struct ring_buffer *rb = NULL;
struct bootstrap_bpf *skel;
int err;
/* Parse command line arguments */
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
if (err)
return err;
/* Set up libbpf errors and debug info callback */
libbpf_set_print(libbpf_print_fn);
/* Cleaner handling of Ctrl-C */
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
/* Load and verify BPF application */
skel = bootstrap_bpf__open();
if (!skel) {
fprintf(stderr, "Failed to open and load BPF skeleton\n");
return 1;
}
/* Parameterize BPF code with minimum duration parameter */
skel->rodata->min_duration_ns = env.min_duration_ms * 1000000ULL;
/* Load & verify BPF programs */
err = bootstrap_bpf__load(skel);
if (err) {
fprintf(stderr, "Failed to load and verify BPF skeleton\n");
goto cleanup;
}
/* Attach tracepoints */
err = bootstrap_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to attach BPF skeleton\n");
goto cleanup;
}
/* Set up ring buffer polling */
rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL);
if (!rb) {
err = -1;
fprintf(stderr, "Failed to create ring buffer\n");
goto cleanup;
}
/* Process events */
printf("%-8s %-5s %-16s %-7s %-7s %s\n",
"TIME", "EVENT", "COMM", "PID", "PPID", "FILENAME/EXIT CODE");
while (!exiting) {
err = ring_buffer__poll(rb, 100 /* timeout, ms */);
/* Ctrl-C will cause -EINTR */
if (err == -EINTR) {
err = 0;
break;
}
if (err < 0) {
printf("Error polling perf buffer: %d\n", err);
break;
}
}
cleanup:
/* Clean up */
ring_buffer__free(rb);
bootstrap_bpf__destroy(skel);
return err < 0 ? -err : 0;
}

View File

@@ -1,19 +1,19 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
/* Copyright (c) 2020 Facebook */
#ifndef __BOOTSTRAP_H
#define __BOOTSTRAP_H
#define TASK_COMM_LEN 16
#define MAX_FILENAME_LEN 127
struct event {
int pid;
int ppid;
unsigned exit_code;
unsigned long long duration_ns;
char comm[TASK_COMM_LEN];
char filename[MAX_FILENAME_LEN];
bool exit_event;
};
#endif /* __BOOTSTRAP_H */
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
/* Copyright (c) 2020 Facebook */
#ifndef __BOOTSTRAP_H
#define __BOOTSTRAP_H
#define TASK_COMM_LEN 16
#define MAX_FILENAME_LEN 127
struct event {
int pid;
int ppid;
unsigned exit_code;
unsigned long long duration_ns;
char comm[TASK_COMM_LEN];
char filename[MAX_FILENAME_LEN];
bool exit_event;
};
#endif /* __BOOTSTRAP_H */

View File

@@ -276,6 +276,15 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
<p>程序定义了一个名为handle_exec的SECstatic evaluator of code函数它被附加到跟踪进程执行的BPF程序上。该函数记录为该PID执行exec的时间并在指定了最小持续时间时不发出exec事件。如果未指定最小持续时间则会从BPF ringbuf保留样本并使用数据填充样本然后将其提交给用户空间进行后处理。</p>
<p>程序还定义了一个名为handle_exit的SEC函数它被附加到跟踪进程退出的BPF程序上。该函数会在确定PID和TID后计算进程的生命周期然后根据min_duration_ns的值决定是否发出退出事件。如果进程的生命周期足够长则会从BPF ringbuf保留样本并使用数据填充样本然后将其提交给用户空间进行后处理。</p>
<p>最后主函数调用bpf_ringbuf_poll来轮询BPF ringbuf并在接收到新的事件时处理该事件。这个函数会持续运行直到全局标志exiting被设置为true此时它会清理资源并退出。</p>
<h2 id="install-dependencies"><a class="header" href="#install-dependencies">Install Dependencies</a></h2>
<p>You will need <code>clang</code>, <code>libelf</code> and <code>zlib</code> to build the examples, package names may vary across distros.</p>
<p>On Ubuntu/Debian, you need:</p>
<pre><code class="language-shell">$ apt install clang libelf1 libelf-dev zlib1g-dev
</code></pre>
<p>On CentOS/Fedora, you need:</p>
<pre><code class="language-shell">$ dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel
</code></pre>
<h2 id="编译运行"><a class="header" href="#编译运行">编译运行</a></h2>
<p>编译运行上述代码:</p>
<pre><code class="language-console">$ ecc bootstrap.bpf.c bootstrap.h
Compiling bpf object...
@@ -284,7 +293,7 @@ $ sudo ecli run package.json
Runing eBPF program...
</code></pre>
<h2 id="总结"><a class="header" href="#总结">总结</a></h2>
<p>这是一个使用BPF的C程序用于跟踪进程的启动和退出事件并显示有关这些事件的信息。它通过使用argp API来解析命令行参数并使用BPF地图存储进程的信息包括进程的PID和执行文件的文件名。程序还使用了SEC函数来附加BPF程序以监视进程的执行和退出事件。最后程序在终端中打印出启动和退出的进程信息。</p>
<p>这是一个使用BPF的C程序用于跟踪进程的启动和退出事件并显示有关这些事件的信息。它通过使用argp API来解析命令行参数并使用BPF maps 存储进程的信息包括进程的PID和执行文件的文件名。程序还使用了SEC函数来附加BPF程序以监视进程的执行和退出事件。最后程序在终端中打印出启动和退出的进程信息。</p>
<p>编译这个程序可以使用 ecc 工具,运行时可以使用 ecli 命令。更多的例子和详细的开发指南,请参考 eunomia-bpf 的官方文档https://github.com/eunomia-bpf/eunomia-bpf</p>
</main>

141
12-profile/Makefile Normal file
View File

@@ -0,0 +1,141 @@
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
OUTPUT := .output
CLANG ?= clang
LIBBPF_SRC := $(abspath ../../libbpf/src)
BPFTOOL_SRC := $(abspath ../../bpftool/src)
LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a)
BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool)
BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool
LIBBLAZESYM_SRC := $(abspath ../../blazesym/)
LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a)
LIBBLAZESYM_HEADER := $(abspath $(OUTPUT)/blazesym.h)
ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \
| sed 's/arm.*/arm/' \
| sed 's/aarch64/arm64/' \
| sed 's/ppc64le/powerpc/' \
| sed 's/mips.*/mips/' \
| sed 's/riscv64/riscv/' \
| sed 's/loongarch64/loongarch/')
VMLINUX := ../../vmlinux/$(ARCH)/vmlinux.h
# Use our own libbpf API headers and Linux UAPI headers distributed with
# libbpf to avoid dependency on system-wide headers, which could be missing or
# outdated
INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX))
CFLAGS := -g -Wall
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)
APPS = # minimal minimal_legacy bootstrap uprobe kprobe fentry usdt sockfilter tc ksyscall
CARGO ?= $(shell which cargo)
ifeq ($(strip $(CARGO)),)
BZS_APPS :=
else
BZS_APPS := profile
APPS += $(BZS_APPS)
# Required by libblazesym
ALL_LDFLAGS += -lrt -ldl -lpthread -lm
endif
# Get Clang's default includes on this system. We'll explicitly add these dirs
# to the includes list when compiling with `-target bpf` because otherwise some
# architecture-specific dirs will be "missing" on some architectures/distros -
# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h,
# sys/cdefs.h etc. might be missing.
#
# Use '-idirafter': Don't interfere with include mechanics except where the
# build would have failed anyways.
CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - </dev/null 2>&1 \
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')
ifeq ($(V),1)
Q =
msg =
else
Q = @
msg = @printf ' %-8s %s%s\n' \
"$(1)" \
"$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \
"$(if $(3), $(3))";
MAKEFLAGS += --no-print-directory
endif
define allow-override
$(if $(or $(findstring environment,$(origin $(1))),\
$(findstring command line,$(origin $(1)))),,\
$(eval $(1) = $(2)))
endef
$(call allow-override,CC,$(CROSS_COMPILE)cc)
$(call allow-override,LD,$(CROSS_COMPILE)ld)
.PHONY: all
all: $(APPS)
.PHONY: clean
clean:
$(call msg,CLEAN)
$(Q)rm -rf $(OUTPUT) $(APPS)
$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT):
$(call msg,MKDIR,$@)
$(Q)mkdir -p $@
# Build libbpf
$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf
$(call msg,LIB,$@)
$(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \
OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \
INCLUDEDIR= LIBDIR= UAPIDIR= \
install
# Build bpftool
$(BPFTOOL): | $(BPFTOOL_OUTPUT)
$(call msg,BPFTOOL,$@)
$(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap
$(LIBBLAZESYM_SRC)/target/release/libblazesym.a::
$(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --features=cheader,dont-generate-test-files --release
$(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB, $@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@
$(LIBBLAZESYM_HEADER): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB,$@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/blazesym.h $@
# Build BPF code
$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL)
$(call msg,BPF,$@)
$(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \
$(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \
-c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
$(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
# Generate BPF skeletons
$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL)
$(call msg,GEN-SKEL,$@)
$(Q)$(BPFTOOL) gen skeleton $< > $@
# Build user-space code
$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h
$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT)
$(call msg,CC,$@)
$(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@
$(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_HEADER)
$(BZS_APPS): $(LIBBLAZESYM_OBJ)
# Build application binary
$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT)
$(call msg,BINARY,$@)
$(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@
# delete failed targets
.DELETE_ON_ERROR:
# keep intermediate (.skel.h, .bpf.o, etc) targets
.SECONDARY:

244
12-profile/profile.c Normal file
View File

@@ -0,0 +1,244 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2022 Facebook */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
#include <linux/perf_event.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "profile.skel.h"
#include "profile.h"
#include "blazesym.h"
/*
* This function is from libbpf, but it is not a public API and can only be
* used for demonstration. We can use this here because we statically link
* against the libbpf built from submodule during build.
*/
extern int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz);
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
int ret;
ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
return ret;
}
static struct blazesym *symbolizer;
static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid)
{
const struct blazesym_result *result;
const struct blazesym_csym *sym;
sym_src_cfg src;
int i, j;
if (pid) {
src.src_type = SRC_T_PROCESS;
src.params.process.pid = pid;
} else {
src.src_type = SRC_T_KERNEL;
src.params.kernel.kallsyms = NULL;
src.params.kernel.kernel_image = NULL;
}
result = blazesym_symbolize(symbolizer, &src, 1, (const uint64_t *)stack, stack_sz);
for (i = 0; i < stack_sz; i++) {
if (!result || result->size <= i || !result->entries[i].size) {
printf(" %d [<%016llx>]\n", i, stack[i]);
continue;
}
if (result->entries[i].size == 1) {
sym = &result->entries[i].syms[0];
if (sym->path && sym->path[0]) {
printf(" %d [<%016llx>] %s+0x%llx %s:%ld\n",
i, stack[i], sym->symbol,
stack[i] - sym->start_address,
sym->path, sym->line_no);
} else {
printf(" %d [<%016llx>] %s+0x%llx\n",
i, stack[i], sym->symbol,
stack[i] - sym->start_address);
}
continue;
}
printf(" %d [<%016llx>]\n", i, stack[i]);
for (j = 0; j < result->entries[i].size; j++) {
sym = &result->entries[i].syms[j];
if (sym->path && sym->path[0]) {
printf(" %s+0x%llx %s:%ld\n",
sym->symbol, stack[i] - sym->start_address,
sym->path, sym->line_no);
} else {
printf(" %s+0x%llx\n", sym->symbol,
stack[i] - sym->start_address);
}
}
}
blazesym_result_free(result);
}
/* Receive events from the ring buffer. */
static int event_handler(void *_ctx, void *data, size_t size)
{
struct stacktrace_event *event = data;
if (event->kstack_sz <= 0 && event->ustack_sz <= 0)
return 1;
printf("COMM: %s (pid=%d) @ CPU %d\n", event->comm, event->pid, event->cpu_id);
if (event->kstack_sz > 0) {
printf("Kernel:\n");
show_stack_trace(event->kstack, event->kstack_sz / sizeof(__u64), 0);
} else {
printf("No Kernel Stack\n");
}
if (event->ustack_sz > 0) {
printf("Userspace:\n");
show_stack_trace(event->ustack, event->ustack_sz / sizeof(__u64), event->pid);
} else {
printf("No Userspace Stack\n");
}
printf("\n");
return 0;
}
static void show_help(const char *progname)
{
printf("Usage: %s [-f <frequency>] [-h]\n", progname);
}
int main(int argc, char * const argv[])
{
const char *online_cpus_file = "/sys/devices/system/cpu/online";
int freq = 1, pid = -1, cpu;
struct profile_bpf *skel = NULL;
struct perf_event_attr attr;
struct bpf_link **links = NULL;
struct ring_buffer *ring_buf = NULL;
int num_cpus, num_online_cpus;
int *pefds = NULL, pefd;
int argp, i, err = 0;
bool *online_mask = NULL;
while ((argp = getopt(argc, argv, "hf:")) != -1) {
switch (argp) {
case 'f':
freq = atoi(optarg);
if (freq < 1)
freq = 1;
break;
case 'h':
default:
show_help(argv[0]);
return 1;
}
}
err = parse_cpu_mask_file(online_cpus_file, &online_mask, &num_online_cpus);
if (err) {
fprintf(stderr, "Fail to get online CPU numbers: %d\n", err);
goto cleanup;
}
num_cpus = libbpf_num_possible_cpus();
if (num_cpus <= 0) {
fprintf(stderr, "Fail to get the number of processors\n");
err = -1;
goto cleanup;
}
skel = profile_bpf__open_and_load();
if (!skel) {
fprintf(stderr, "Fail to open and load BPF skeleton\n");
err = -1;
goto cleanup;
}
symbolizer = blazesym_new();
if (!symbolizer) {
fprintf(stderr, "Fail to create a symbolizer\n");
err = -1;
goto cleanup;
}
/* Prepare ring buffer to receive events from the BPF program. */
ring_buf = ring_buffer__new(bpf_map__fd(skel->maps.events), event_handler, NULL, NULL);
if (!ring_buf) {
err = -1;
goto cleanup;
}
pefds = malloc(num_cpus * sizeof(int));
for (i = 0; i < num_cpus; i++) {
pefds[i] = -1;
}
links = calloc(num_cpus, sizeof(struct bpf_link *));
memset(&attr, 0, sizeof(attr));
attr.type = PERF_TYPE_HARDWARE;
attr.size = sizeof(attr);
attr.config = PERF_COUNT_HW_CPU_CYCLES;
attr.sample_freq = freq;
attr.freq = 1;
for (cpu = 0; cpu < num_cpus; cpu++) {
/* skip offline/not present CPUs */
if (cpu >= num_online_cpus || !online_mask[cpu])
continue;
/* Set up performance monitoring on a CPU/Core */
pefd = perf_event_open(&attr, pid, cpu, -1, PERF_FLAG_FD_CLOEXEC);
if (pefd < 0) {
fprintf(stderr, "Fail to set up performance monitor on a CPU/Core\n");
err = -1;
goto cleanup;
}
pefds[cpu] = pefd;
/* Attach a BPF program on a CPU */
links[cpu] = bpf_program__attach_perf_event(skel->progs.profile, pefd);
if (!links[cpu]) {
err = -1;
goto cleanup;
}
}
/* Wait and receive stack traces */
while (ring_buffer__poll(ring_buf, -1) >= 0) {
}
cleanup:
if (links) {
for (cpu = 0; cpu < num_cpus; cpu++)
bpf_link__destroy(links[cpu]);
free(links);
}
if (pefds) {
for (i = 0; i < num_cpus; i++) {
if (pefds[i] >= 0)
close(pefds[i]);
}
free(pefds);
}
ring_buffer__free(ring_buf);
profile_bpf__destroy(skel);
blazesym_free(symbolizer);
free(online_mask);
return -err;
}

View File

@@ -264,28 +264,8 @@ cleanup:
return 0;
}
</code></pre>
<h3 id="eunomia-测试-demo"><a class="header" href="#eunomia-测试-demo">Eunomia 测试 demo</a></h3>
<p>使用命令行进行追踪:</p>
<pre><code class="language-bash">$ sudo build/bin/Release/eunomia run tcpconnlat
[sudo] password for yunwei:
[2022-08-07 02:13:39.601] [info] eunomia run in cmd...
[2022-08-07 02:13:40.534] [info] press 'Ctrl C' key to exit...
PID COMM IP SRC DEST PORT LAT(ms) CONATINER/OS
3477 openresty 4 172.19.0.7 172.19.0.5 2379 0.05 docker-apisix_apisix_1
3483 openresty 4 172.19.0.7 172.19.0.5 2379 0.08 docker-apisix_apisix_1
3477 openresty 4 172.19.0.7 172.19.0.5 2379 0.04 docker-apisix_apisix_1
3478 openresty 4 172.19.0.7 172.19.0.5 2379 0.05 docker-apisix_apisix_1
3478 openresty 4 172.19.0.7 172.19.0.5 2379 0.03 docker-apisix_apisix_1
3478 openresty 4 172.19.0.7 172.19.0.5 2379 0.03 docker-apisix_apisix_1
</code></pre>
<p>还可以使用 eunomia 作为 prometheus exporter在运行上述命令之后打开 prometheus 自带的可视化面板:</p>
<p>使用下述查询命令即可看到延时的统计图表:</p>
<pre><code class="language-plain"> rate(eunomia_observed_tcpconnlat_v4_histogram_sum[5m])
/
rate(eunomia_observed_tcpconnlat_v4_histogram_count[5m])
</code></pre>
<p>结果:</p>
<p><img src="tcpconnlat_p.png" alt="result" /></p>
<h3 id="编译运行"><a class="header" href="#编译运行">编译运行</a></h3>
<p>TODO</p>
<h3 id="总结"><a class="header" href="#总结">总结</a></h3>
<p>通过上面的实验我们可以看到tcpconnlat 工具的实现原理是基于内核的TCP连接的跟踪并且可以跟踪到 tcp 连接的延迟时间除了命令行使用方式之外还可以将其和容器、k8s 等元信息综合起来,通过 <code>prometheus</code><code>grafana</code> 等工具进行网络性能分析。</p>
<blockquote>

View File

@@ -1486,7 +1486,7 @@ int BPF_PROG(irq_handler_exit, int irq, struct irqaction *action)
char LICENSE[] SEC(&quot;license&quot;) = &quot;GPL&quot;;
</code></pre>
<p>这是一个 BPFBerkeley Packet Filter程序。BPF 程序是小型程序,可以直接在 Linux 内核中运行,用于过滤和操纵网络流量。这个特定的程序似乎旨在收集内核中中断处理程序的统计信息。它定义了一些地图(可以在 BPF 程序和内核的其他部分之间共享的数据结构和两个函数handle_entry 和 handle_exit。当内核进入和退出中断处理程序时分别执行这些函数。handle_entry 函数用于跟踪中断处理程序被执行的次数,而 handle_exit 则用于测量中断处理程序中花费的时间。</p>
<p>这是一个 BPFBerkeley Packet Filter程序。BPF 程序是小型程序,可以直接在 Linux 内核中运行,用于过滤和操纵网络流量。这个特定的程序似乎旨在收集内核中中断处理程序的统计信息。它定义了一些 maps (可以在 BPF 程序和内核的其他部分之间共享的数据结构和两个函数handle_entry 和 handle_exit。当内核进入和退出中断处理程序时分别执行这些函数。handle_entry 函数用于跟踪中断处理程序被执行的次数,而 handle_exit 则用于测量中断处理程序中花费的时间。</p>
<h2 id="运行代码"><a class="header" href="#运行代码">运行代码</a></h2>
<p>eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <a href="https://github.com/eunomia-bpf/eunomia-bpf">https://github.com/eunomia-bpf/eunomia-bpf</a> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。</p>
<p>要编译这个程序,请使用 ecc 工具:</p>
@@ -1632,6 +1632,15 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
<p>程序定义了一个名为handle_exec的SECstatic evaluator of code函数它被附加到跟踪进程执行的BPF程序上。该函数记录为该PID执行exec的时间并在指定了最小持续时间时不发出exec事件。如果未指定最小持续时间则会从BPF ringbuf保留样本并使用数据填充样本然后将其提交给用户空间进行后处理。</p>
<p>程序还定义了一个名为handle_exit的SEC函数它被附加到跟踪进程退出的BPF程序上。该函数会在确定PID和TID后计算进程的生命周期然后根据min_duration_ns的值决定是否发出退出事件。如果进程的生命周期足够长则会从BPF ringbuf保留样本并使用数据填充样本然后将其提交给用户空间进行后处理。</p>
<p>最后主函数调用bpf_ringbuf_poll来轮询BPF ringbuf并在接收到新的事件时处理该事件。这个函数会持续运行直到全局标志exiting被设置为true此时它会清理资源并退出。</p>
<h2 id="install-dependencies"><a class="header" href="#install-dependencies">Install Dependencies</a></h2>
<p>You will need <code>clang</code>, <code>libelf</code> and <code>zlib</code> to build the examples, package names may vary across distros.</p>
<p>On Ubuntu/Debian, you need:</p>
<pre><code class="language-shell">$ apt install clang libelf1 libelf-dev zlib1g-dev
</code></pre>
<p>On CentOS/Fedora, you need:</p>
<pre><code class="language-shell">$ dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel
</code></pre>
<h2 id="编译运行-1"><a class="header" href="#编译运行-1">编译运行</a></h2>
<p>编译运行上述代码:</p>
<pre><code class="language-console">$ ecc bootstrap.bpf.c bootstrap.h
Compiling bpf object...
@@ -1640,7 +1649,7 @@ $ sudo ecli run package.json
Runing eBPF program...
</code></pre>
<h2 id="总结-10"><a class="header" href="#总结-10">总结</a></h2>
<p>这是一个使用BPF的C程序用于跟踪进程的启动和退出事件并显示有关这些事件的信息。它通过使用argp API来解析命令行参数并使用BPF地图存储进程的信息包括进程的PID和执行文件的文件名。程序还使用了SEC函数来附加BPF程序以监视进程的执行和退出事件。最后程序在终端中打印出启动和退出的进程信息。</p>
<p>这是一个使用BPF的C程序用于跟踪进程的启动和退出事件并显示有关这些事件的信息。它通过使用argp API来解析命令行参数并使用BPF maps 存储进程的信息包括进程的PID和执行文件的文件名。程序还使用了SEC函数来附加BPF程序以监视进程的执行和退出事件。最后程序在终端中打印出启动和退出的进程信息。</p>
<p>编译这个程序可以使用 ecc 工具,运行时可以使用 ecli 命令。更多的例子和详细的开发指南,请参考 eunomia-bpf 的官方文档https://github.com/eunomia-bpf/eunomia-bpf</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="ebpf入门实践教程使用-libbpf-bootstrap-开发程序统计-tcp-连接延时"><a class="header" href="#ebpf入门实践教程使用-libbpf-bootstrap-开发程序统计-tcp-连接延时">eBPF入门实践教程使用 libbpf-bootstrap 开发程序统计 TCP 连接延时</a></h1>
<h2 id="背景"><a class="header" href="#背景">背景</a></h2>
@@ -1761,7 +1770,7 @@ cleanup:
return 0;
}
</code></pre>
<h2 id="编译运行-1"><a class="header" href="#编译运行-1">编译运行</a></h2>
<h2 id="编译运行-2"><a class="header" href="#编译运行-2">编译运行</a></h2>
<ul>
<li><code>git clone https://github.com/libbpf/libbpf-bootstrap libbpf-bootstrap-cloned</code></li>
<li><a href="13-tcpconnlat/libbpf-bootstrap">libbpf-bootstrap</a>目录下的文件复制到 <code>libbpf-bootstrap-cloned/examples/c</code></li>
@@ -1900,28 +1909,8 @@ cleanup:
return 0;
}
</code></pre>
<h3 id="eunomia-测试-demo"><a class="header" href="#eunomia-测试-demo">Eunomia 测试 demo</a></h3>
<p>使用命令行进行追踪:</p>
<pre><code class="language-bash">$ sudo build/bin/Release/eunomia run tcpconnlat
[sudo] password for yunwei:
[2022-08-07 02:13:39.601] [info] eunomia run in cmd...
[2022-08-07 02:13:40.534] [info] press 'Ctrl C' key to exit...
PID COMM IP SRC DEST PORT LAT(ms) CONATINER/OS
3477 openresty 4 172.19.0.7 172.19.0.5 2379 0.05 docker-apisix_apisix_1
3483 openresty 4 172.19.0.7 172.19.0.5 2379 0.08 docker-apisix_apisix_1
3477 openresty 4 172.19.0.7 172.19.0.5 2379 0.04 docker-apisix_apisix_1
3478 openresty 4 172.19.0.7 172.19.0.5 2379 0.05 docker-apisix_apisix_1
3478 openresty 4 172.19.0.7 172.19.0.5 2379 0.03 docker-apisix_apisix_1
3478 openresty 4 172.19.0.7 172.19.0.5 2379 0.03 docker-apisix_apisix_1
</code></pre>
<p>还可以使用 eunomia 作为 prometheus exporter在运行上述命令之后打开 prometheus 自带的可视化面板:</p>
<p>使用下述查询命令即可看到延时的统计图表:</p>
<pre><code class="language-plain"> rate(eunomia_observed_tcpconnlat_v4_histogram_sum[5m])
/
rate(eunomia_observed_tcpconnlat_v4_histogram_count[5m])
</code></pre>
<p>结果:</p>
<p><img src="13-tcpconnlat/tcpconnlat_p.png" alt="result" /></p>
<h3 id="编译运行-3"><a class="header" href="#编译运行-3">编译运行</a></h3>
<p>TODO</p>
<h3 id="总结-12"><a class="header" href="#总结-12">总结</a></h3>
<p>通过上面的实验我们可以看到tcpconnlat 工具的实现原理是基于内核的TCP连接的跟踪并且可以跟踪到 tcp 连接的延迟时间除了命令行使用方式之外还可以将其和容器、k8s 等元信息综合起来,通过 <code>prometheus</code><code>grafana</code> 等工具进行网络性能分析。</p>
<blockquote>
@@ -2041,7 +2030,7 @@ static void handle_lost_events(void* ctx, int cpu, __u64 lost_cnt) {
}
</code></pre>
<p>收到事件后所调用对应的处理函数并进行输出打印。</p>
<h2 id="编译运行-2"><a class="header" href="#编译运行-2">编译运行</a></h2>
<h2 id="编译运行-4"><a class="header" href="#编译运行-4">编译运行</a></h2>
<ul>
<li><code>git clone https://github.com/libbpf/libbpf-bootstrap libbpf-bootstrap-cloned</code></li>
<li><a href="14-tcpstates/libbpf-bootstrap">libbpf-bootstrap</a>目录下的文件复制到 <code>libbpf-bootstrap-cloned/examples/c</code></li>
@@ -2175,7 +2164,7 @@ int BPF_PROG(tcp_rcv, struct sock *sk)
<p>如果用户设置了&quot;-show-ext&quot;选项,则还会累加直方图的总延迟(latency)和计数(cnt)。</p>
</li>
</ol>
<h2 id="编译运行-3"><a class="header" href="#编译运行-3">编译运行</a></h2>
<h2 id="编译运行-5"><a class="header" href="#编译运行-5">编译运行</a></h2>
<p>eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <a href="https://github.com/eunomia-bpf/eunomia-bpf">https://github.com/eunomia-bpf/eunomia-bpf</a> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。</p>
<p>Compile:</p>
<pre><code class="language-shell">docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
@@ -2459,7 +2448,7 @@ int BPF_KPROBE(free_enter, void *address)
<p>gen_free_enter函数接收一个地址参数该函数首先使用allocs map查找该地址对应的内存分配信息。如果未找到则表示该地址没有被分配该函数返回0。如果找到了对应的内存分配信息则使用bpf_map_delete_elem从allocs map中删除该信息。</p>
<p>接下来调用update_statistics_del函数用于更新内存分配的统计信息它接收堆栈ID和内存块大小作为参数。首先在combined_allocs map中查找堆栈ID对应的内存分配统计信息。如果没有找到则输出一条日志表示查找失败并且函数直接返回。如果找到了对应的内存分配统计信息则使用原子操作从内存分配统计信息中减去该内存块大小和1表示减少了1个内存块。这是因为堆栈ID对应的内存块数量减少了1而堆栈ID对应的内存块总大小也减少了该内存块的大小。</p>
<p>最后定义了一个bpf程序BPF_KPROBE(free_enter, void *address)会在进程调用free函数时执行。它会接收参数address表示正在释放的内存块的地址并调用gen_free_enter函数来处理该内存块的释放。</p>
<h2 id="编译运行-4"><a class="header" href="#编译运行-4">编译运行</a></h2>
<h2 id="编译运行-6"><a class="header" href="#编译运行-6">编译运行</a></h2>
<pre><code class="language-console">$ git clone https://github.com/iovisor/bcc.git --recurse-submodules
$ cd libbpf-tools/
$ make memleak
@@ -2583,7 +2572,7 @@ int BPF_PROG(restrict_connect, struct socket *sock, struct sockaddr *address, in
<li>若请求地址为 1.1.1.1 则拒绝连接,否则允许连接;</li>
</ul>
<p>在程序运行期间,所有通过 socket 的连接操作都会被输出到 <code>/sys/kernel/debug/tracing/trace_pipe</code></p>
<h2 id="编译运行-5"><a class="header" href="#编译运行-5">编译运行</a></h2>
<h2 id="编译运行-7"><a class="header" href="#编译运行-7">编译运行</a></h2>
<p>通过容器编译:</p>
<pre><code class="language-console">docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
</code></pre>
@@ -2665,7 +2654,7 @@ char __license[] SEC(&quot;license&quot;) = &quot;GPL&quot;;
</code></pre>
<p>这些注释告诉 TC 将 eBPF 程序附加到网络接口的 ingress 附加点,并指定了 handle 和 priority 选项的值。</p>
<p>总之,这段代码实现了一个简单的 eBPF 程序,用于捕获数据包并打印出它们的信息。</p>
<h2 id="编译运行-6"><a class="header" href="#编译运行-6">编译运行</a></h2>
<h2 id="编译运行-8"><a class="header" href="#编译运行-8">编译运行</a></h2>
<pre><code class="language-console">docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
</code></pre>
<p>or compile with <code>ecc</code>:</p>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long