本文章是前端时间读代码时的随手记录,没有做系统整理,估计也不会填坑了,大家随便看看就好。
用户态代码:rust+c,rust使用FFI(Foreign Function Interface)调用c,过程中使用 libc crate,它包含了 C 标准库中的类型别名和函数定义,编译时会静态连接libc。
build.rs
main()
|-- set_build_info() 这里还会检查git仓的信息
set_build_libtrace() 这里会执行src/ebpf下的make
set_linkage() 打印出链接信息
src/ebpf
|-- kernel 内核态ebpf程序
|-- user 用户态程序c实现
|-- mod.rs rust FFI对user封装的mod
src/ebpf/Makefile
定义了函数compile_socket_trace_elf:构建socket_trace.elf,使用bintobuffer把字节码转成buffer放到一个.c文件(bintobuffer是这个项目自带的一个工具)
定义了函数compile_perf_profiler_elf:构建perf_profiler.elf,使用bintobuffer把字节码转成buffer放到一个.c文件
build:先编译内核态生成.elf,然后再编译用户态生成.a(.a会被rust静态链接)
src/ebpf/kernel/Makefile
将socket_trace和perf_profiler编译成.elf(elf文件收敛到两个,其他.c被include到这两个文件里)
将编译后的文件反编译成.objdump
剥离掉对象文件中的调试信息
src/main.rs
main()
|-- src/trident.rs
Trident::start()
-> Self::run()
-> Components::new()
-> AgentComponents::new()
|-- src/ebpf_dispatcher/ebpf_dispatcher.rs
EbpfCollector::new()
-> Self::ebpf_init()
|-- src/ebpf/mod.rs
bpf_tracer_init()
rpc
PROGTP(io_event)(void *ctx)
= SEC("prog/tp/io_event") int bpf_prog_tp__io_event(void *ctx)
{
获取tgid,pid
查询active_read_args_map
如果被标记跟踪:
{
trace_io_event_common(ctx, data_args, T_INGRESS, id)
{
从trace_conf_map获取一下配置
从trace_map中获取trace_info
从io_event_buffer(map)中获取内存块读取buffer内容
从data_buf(map)中获取存放socket_buffer的内存块v_buff(这个v_buff可能存多个__socket_data)
将socket_buffer放到v_buff后面
bpf_get_current_comm(用当前进程名填充socket_buffer的comm)
设置尾调上下文socket_buffer->data
触发尾调progs_jmp_tp_map(具体尾调函数看用户态代码)
}
删除跟踪
return // 读写跟踪只能开启一个
}
查询active_write_args_map
如果被标记跟踪:
{
trace_io_event_common(ctx, data_args, T_EGRESS, id)
删除跟踪
return // 读写跟踪只能开启一个
}
}
ebpf_init
|-- src/ebpf/mod.rs
running_socket_tracer
|-- src/ebpf/user/socket.c
running_socket_tracer
-> process_events_handle_main
-> process_probes_act
|-- src/ebpf/user/trace.c
tracer_hooks_attach/tracer_hooks_dettach
-> tracer_hooks_process
-> probe_attach
-> exec_attach_kprobe/exec_attach_uprobe
|-- src/ebpf/user/probe.c
program__attach_kprobe/program__attach_uprobe
-> program__attach_probe
-> bpf_attach_kprobe/bpf_attach_uprobe(bcc)
running_socket_tracer
bpf_bin_buffer指向ebpf字节码
buffer_sz字节码长度
|-- src/ebpf/user/trace.c
tracer_bpf_load
|-- src/ebpf/user/load.c
ebpf_open_buffer
|-- src/ebpf/user/elf.c
elf_info_collect
--> set_obj__version
--> set_obj__license
--> ebpf_obj__maps_collect
--> ebpf_btf_collect
--> ebpf_btf_ext_collect
ebpf_obj_load
--> bcc_create_map(bcc)
|-- src/ebpf/user/btf_vmlinux.c
ebpf_obj__load_vmlinux_btf
--> load_obj__progs
struct bpf_insn {
__u8 code; // 操作码
__u8 dst_reg:4; // 目标寄存器
__u8 src_reg:4; // 源寄存器
__s16 off; // 偏移量
__s32 imm; // 立即操作数
};
socket 句柄是通过 hook 系统调用获取 (得到socket fd),socket相关信息是通过 get_socket_from_fd() 得到 socket address,相关 socket 信息(比如 元组信息,tcpseq number 等)是通过 socket 内核结构读取的.
syscalls::sys_enter_write
TPPROG(sys_enter_write)
先把系统调用写到map里
TPPROG(sys_exit_write)
从map里读出来处理
--> process_syscall_data
--> process_data
|-- src/ebpf/kernel/include/task_struct_utils.h
get_socket_from_fd 在这里判断是不是socket
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。