bpf的了解以及bpftrace的简单练习

news2024/11/24 0:59:29

最近接触到bpf的概念,简单做一些练习,做以下整理,学习笔记。

0:总结

使用ebpf可以实现的功能打开新世界,可以不改变源码的情况下,实现内存,网络,对应接口等各种期望内容的监控。

可以使用bpf直接进行开发,使用clang编译成ebpf字节序后加载进行使用。

使用bcc进行开发实现功能,多是python/lua,对应的demo可以参考githuab.

使用bpftool工具集可以进行加载,查看,卸载等一系列的功能,没有过多研究。

使用bpftrace一行指令已经可以方便实现很多的功能,比如监控文件打开,监控内存申请,监控函数调用次数,在内存,网络,sql调用,以及接口相关都有很多很方便的功能,进行简单的使用测试。

1:了解什么是ebpf,bpf,bcc, bpftool,bpftrace

直接问ai,简单又一些认知。

eBPF 是底层技术,所有这些工具(BPF, BCC, bpftool, BPFtrace)都基于 eBPF 工作。
BPF 是 eBPF 的前身,如今被 eBPF 所取代。
BCC 提供了一套方便的 API 和工具,帮助开发者快速编写和运行 eBPF 程序。
bpftool 用于管理和调试 eBPF 程序,可以查看运行的 eBPF 程序及其相关资源。
BPFtrace 提供了一个高级脚本语言,用于快速编写和运行简单的 eBPF 程序,主要用于追踪和分析。
总之,这些工具和框架都围绕 eBPF 工作,帮助开发者更方便地利用 eBPF 的强大功能。

eBPF(Extended Berkeley Packet Filter)及其相关工具(BPF, BCC, bpftool, BPFtrace)都是用于动态追踪、性能分析和网络调试的技术和工具,尤其是在 Linux 环境中。以下是对它们的介绍以及它们之间的关系:

1. eBPF (Extended Berkeley Packet Filter)
eBPF 是 Linux 内核中的一个扩展,它允许用户在内核中运行自定义的字节码程序,而无需修改或重启内核。
最初用于网络数据包过滤,但现在 eBPF 的应用范围已经扩展到了性能监控、安全分析、网络流量控制等多个领域。
eBPF 程序在内核中运行时是安全的,因为它们在执行前会经过内核的验证器验证,以确保不会损坏系统。
2. BPF
BPF 是 Berkeley Packet Filter 的缩写,是 eBPF 的前身。
最早设计用于网络数据包过滤,但功能较为有限,eBPF 则是其增强版。
BPF 程序可以通过系统调用加载到内核中执行,但随着时间的推移,eBPF 逐渐取代了传统 BPF。
3. BCC (BPF Compiler Collection)
BCC 是一个工具集,用于简化 eBPF 程序的编写和运行。
它提供了高级语言(如 Python 和 Lua)的 API,以便用户能够方便地编写 eBPF 程序。
BCC 中包含了许多现成的 eBPF 工具,用于追踪系统事件、网络流量、文件系统等。
使用 BCC 时,用户不需要直接编写 eBPF 字节码,而是通过高级语言和宏来生成和管理 eBPF 程序。
4. bpftool
bpftool 是 eBPF 的管理工具,主要用于查看、加载、卸载和调试 eBPF 程序。
它允许用户列出内核中加载的 eBPF 程序、地图(maps)和统计信息。
bpftool 提供了对内核 eBPF 子系统的细粒度控制和信息查看功能,通常用于开发和调试 eBPF 程序。
5. BPFtrace
BPFtrace 是一个简化的 eBPF 追踪工具,专注于动态追踪和性能分析。
它的语法受 DTrace 启发,更为简洁,用户可以通过简单的脚本编写复杂的追踪逻辑。
相比 BCC,BPFtrace 更加适合快速编写和运行短小的 eBPF 程序,适合于即时性能分析和诊断。

2:使用bpf进行测试,用户在终端上使用ls等指令时,测试demo能进行监控并展示。

使用22.04进行测试时,和已有demo不适配,高版本直接用bcc,bpftrace等进行直接开发吧。

这里用18.04进行环境安装以及demo测试。

2.1 在18.04上安装bpf

不支持直接安装,没有libbpf-dev

ubuntu@ubuntu:~$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 18.04.6 LTS
Release:	18.04
Codename:	bionic
ubuntu@ubuntu:~$ sudo apt-get install build-essential
#可以看到 不支持这个库的直接安装
ubuntu@ubuntu:~$ sudo apt install libbpf-dev
E: Unable to locate package libbpf-dev

使用源码安装的方式

#使用源码安装的方式  去linux内核官网找对应系统的源码下载也可以  ()
#方案一  下载linux内核源码,转到对应的版本分治     这个在网络不稳定的情况下,太耗时,分支太多,太大了。
git clone https://github.com/torvalds/linux.git  
cd linux
git checkout v$(uname -r | cut -d'-' -f1)

#方案二  发现一个很容易获取系统对应内核源码的方式 这种方式可能获取到的版本比较旧 与当前内核版本有差异 (可以下载指定内核的)
sudo apt-get install linux-source
cd /usr/src
tar xjf linux-source-$(uname -r).tar.bz2
cd linux-source-$(uname -r)

实际操作如下:

ubuntu@ubuntu:~$ cd /usr/src/
ubuntu@ubuntu:/usr/src$ ls
linux-headers-4.15.0-213  linux-headers-4.15.0-213-generic

ubuntu@ubuntu:/usr/src$ sudo apt-cache search linux-source
ubuntu@ubuntu:/usr/src$ sudo apt-get install linux-source
ubuntu@ubuntu:/usr/src$ ls
linux-headers-4.15.0-213  linux-headers-4.15.0-213-generic  linux-source-4.15.0  linux-source-4.15.0.tar.bz2
ubuntu@ubuntu:/usr/src$ sudo tar xf linux-source-4.15.0.tar.bz2

#遇到问题 处理问题  这里直到最后编译成功  连bpftool也编译成功了。
ubuntu@ubuntu:/usr/src/linux-source-4.15.0/tools/bpf$ sudo apt-get install binutils-dev
ubuntu@ubuntu:/usr/src/linux-source-4.15.0/tools/bpf$ sudo apt-get install libreadline-dev
ubuntu@ubuntu:/usr/src/linux-source-4.15.0/tools/bpf$ sudo apt-get install bison
ubuntu@ubuntu:/usr/src/linux-source-4.15.0/tools/bpf$ sudo apt-get install binutils elfutils

root@ubuntu:/usr/src/linux-source-4.15.0/tools/lib/bpf# make

Auto-detecting system features:
...                        libelf: [ on  ]
...                           bpf: [ on  ]

Warning: Kernel ABI header at 'tools/include/uapi/linux/bpf.h' differs from latest version at 'include/uapi/linux/bpf.h'
  CC       libbpf.o
  CC       bpf.o
  LD       libbpf-in.o
  LINK     libbpf.a
  LINK     libbpf.so
root@ubuntu:/usr/src/linux-source-4.15.0/tools/lib/bpf# make install
Warning: Kernel ABI header at 'tools/include/uapi/linux/bpf.h' differs from latest version at 'include/uapi/linux/bpf.h'
  INSTALL  libbpf.a
  INSTALL  libbpf.so
 #看起来默认安装在 /usr/local/lib64
root@ubuntu:/# find -name libbpf.a
./usr/local/lib64/libbpf.a
./usr/src/linux-source-4.15.0/tools/lib/bpf/libbpf.a

2.2 进行测试,首先要实现ebpf程序。(目标:使用clang把该程序编译成ebpf字节码。)

eBPF 程序通常是内核态的代码,使用 clang 编译为特定格式的字节码(BPF 字节码),然后加载到内核中。

//bpf_program.c
#include <linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))

static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = (void *)BPF_FUNC_trace_printk;

SEC("tracepoint/syscalls/sys_enter_execve")
int bpf_prog(void *ctx) {
  char msg[] = "Hello, BPF World!";
  bpf_trace_printk(msg, sizeof(msg));
return 0;
}

char _license[] SEC("license") = "GPL";

使用clang把该程序编译成ebpf字节码:

ubuntu@ubuntu:~/bpf_test/bpf$ sudo apt-get install clang
ubuntu@ubuntu:~/bpf_test/bpf$ clang -O2 -target bpf -c bpf_program.c -I /usr/include/x86_64-linux-gnu/ -o bpf_program.o

2.3 编写用户空间程序,加载上面的ebpf程序,并执行。

这里的内核版本提供了bpf_load.h,使用这个接口进行使用。(高版本已经不提供了,实现可能有差异,不研究了)

#include "bpf_load.h"
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

#define DEBUGFS "/sys/kernel/debug/tracing/"

void myread_trace_pipe(void)
{
	int trace_fd;

    	trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0);
    	if (trace_fd < 0)
        	return;

    	while (1) {
            	static char buf[4096];
            	ssize_t sz;

            	sz = read(trace_fd, buf, sizeof(buf) - 1);
            	if (sz > 0) {
                	buf[sz] = 0;
                	puts(buf);
            	}
    	}
}

int main(int argc, char **argv) {
  if (load_bpf_file("bpf_program.o") != 0) {
    printf("The kernel didn't load the BPF program\\n");
    return -1;
  }

  myread_trace_pipe();

 return 0;
}

2.4 借助对应makefile进行编译

注意:这里使用了内核下的 bpf_load.c

CLANG = clang

EXECABLE = monitor-exec

#这里之际使用了内核提供给的bpf_load.c 借助libbpf
BPFCODE = bpf_program
BPFTOOLS = /usr/src/linux-source-4.15.0/samples/bpf
BPFLOADER = $(BPFTOOLS)/bpf_load.c
BPFLIB = /usr/src/linux-source-4.15.0/tools/lib/bpf/libbpf.c 


#/usr/include/linux/types.h:5:10: fatal error: 'asm/types.h' file not found  这个与我ubuntu有关吧
CCINCLUDE += -I/usr/include/x86_64-linux-gnu/

CCINCLUDE += -I/usr/src/linux-source-4.15.0/tools/testing/selftests/bpf


LOADINCLUDE += -I/usr/src/linux-source-4.15.0/samples/bpf
LOADINCLUDE += -I/usr/src/linux-source-4.15.0/tools/lib
LOADINCLUDE += -I/usr/src/linux-source-4.15.0/tools/perf
LOADINCLUDE += -I/usr/src/linux-source-4.15.0/tools/include
LIBRARY_PATH = -L/usr/local/lib64
BPFSO = -lbpf

CFLAGS += $(shell grep -q "define HAVE_ATTR_TEST 1" /usr/src/linux-source-4.15.0/tools/perf/perf-sys.h && echo "-DHAVE_ATTR_TEST=0")

.PHONY: clean $(CLANG) bpfload build

clean:
	rm -f *.o *.so $(EXECABLE)

build: ${BPFCODE.c} ${BPFLOADER}
	$(CLANG) -O2 -target bpf -c $(BPFCODE:=.c) $(CCINCLUDE) -o ${BPFCODE:=.o}

bpfload: build
	clang $(CFLAGS) -o $(EXECABLE) -lelf $(LOADINCLUDE) $(LIBRARY_PATH) $(BPFSO) $(BPFLIB) $(BPFLOADER)  loader.c

$(EXECABLE): bpfload

.DEFAULT_GOAL := $(EXECABLE)

2.4 运行测试(运行ls时触发到监控打印Hello, BPF World)

ubuntu@ubuntu:~/bpf_test/bpf$ make
clang -O2 -target bpf -c bpf_program.c -I/usr/include/x86_64-linux-gnu/ -I/usr/src/linux-source-4.15.0/tools/testing/selftests/bpf -o bpf_program.o
clang  -o monitor-exec -lelf -I/usr/src/linux-source-4.15.0/samples/bpf -I/usr/src/linux-source-4.15.0/tools/lib -I/usr/src/linux-source-4.15.0/tools/perf -I/usr/src/linux-source-4.15.0/tools/include -L/usr/local/lib64 -lbpf /usr/src/linux-source-4.15.0/tools/lib/bpf/libbpf.c  /usr/src/linux-source-4.15.0/samples/bpf/bpf_load.c  loader.c

#默认安装在/usr/local/lib64
ubuntu@ubuntu:~/bpf_test/bpf$ sudo ./monitor-exec 
./monitor-exec: error while loading shared libraries: libbpf.so: cannot open shared object file: No such file or directory
ubuntu@ubuntu:~/bpf_test/bpf$ ldconfig /usr/local/lib64
/sbin/ldconfig.real: Can't create temporary cache file /etc/ld.so.cache~: Permission denied
ubuntu@ubuntu:~/bpf_test/bpf$ sudo ldconfig /usr/local/lib64
ubuntu@ubuntu:~/bpf_test/bpf$ sudo ./monitor-exec 
           <...>-11425 [000] ....  4464.404847: 0x00000001: Hello, BPF World!
           <...>-11426 [000] ....  4466.860991: 0x00000001: Hello, BPF World!
            bash-11427 [001] ....  4470.069820: 0x00000001: Hello, BPF World!
            
#这里执行后,在另外的终端上 执行ls 就会触发上面的逻辑,其他的逻辑可以扩展。

3:bcc和bpftool

基于libbpf进行调用外,可以使用bcc调用python/lua脚本进行实现。 安装后,bcc其实有一些可以直接使用的功能。可以参考开源库bcc下对应的example。

bpftool是一个工具,可以用于查看、加载、卸载、和调试 eBPF 程序的功能。(最好从github上源码入手) ubuntu@ubuntu:/$ sudo apt install -y linux-tools-$(uname -r)

ubuntu@ubuntu:~$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 22.04.2 LTS
Release:	22.04
Codename:	jammy

ubuntu@ubuntu:~$ sudo apt-get install build-essential 

#可以使用源码进行安装  git clone https://github.com/libbpf/libbpf.git   
#可以在内核源码中直接安装吧?
ubuntu@ubuntu:/usr/lib$ sudo apt install libbpf-dev
ubuntu@ubuntu:~$ cd /usr/lib
ubuntu@ubuntu:/usr/lib$ sudo find /usr/lib -name libbpf.so
/usr/lib/x86_64-linux-gnu/libbpf.so
ubuntu@ubuntu:/usr/lib$ ls -ls /usr/include/bpf/
total 288
 20 -rw-r--r-- 1 root root  17992 Dec  1  2022 bpf_core_read.h
  4 -rw-r--r-- 1 root root   3750 Dec  1  2022 bpf_endian.h
 12 -rw-r--r-- 1 root root  10066 Dec  1  2022 bpf.h
148 -rw-r--r-- 1 root root 149473 Dec  1  2022 bpf_helper_defs.h
  8 -rw-r--r-- 1 root root   7679 Dec  1  2022 bpf_helpers.h
 20 -rw-r--r-- 1 root root  19194 Dec  1  2022 bpf_tracing.h
 16 -rw-r--r-- 1 root root  12856 Dec  1  2022 btf.h
  4 -rw-r--r-- 1 root root   1421 Dec  1  2022 libbpf_common.h
 36 -rw-r--r-- 1 root root  33279 Dec  1  2022 libbpf.h
  4 -rw-r--r-- 1 root root   1525 Dec  1  2022 libbpf_legacy.h
  4 -rw-r--r-- 1 root root   2894 Dec  1  2022 skel_internal.h
 12 -rw-r--r-- 1 root root   8608 Dec  1  2022 xsk.h
 
 #终于搞清楚了 ,内核一些低版本,提供了使用bpf_load.h对ebpf的一些封装,可以使用,高一些的版本,是没有这个文件的,可以直接用ebpf来实现。
#bpf_load.h 是内核中的一个头文件,常见于 samples/bpf 和 tools/testing/selftests/bpf 目录中,提供了简化加载 eBPF 程序并管理 eBPF maps 的 API。#这使得用户空间程序可以更轻松地与 eBPF 程序交互并加载到内核中

#本来想基于22.04试试bpf的安装后运行,发现和18.04有点差异,18.04有已有demo,可正常运行,但22.04相关高版本内核不提供bpf_load.h,主要了解逻辑,不关注了。


sudo apt install build-essential \
        libelf-dev \
        libz-dev \
        libcap-dev \
        binutils-dev \
        pkg-config
 #终于知道这几个库用在哪里用了  make报错依次解决  手动编译流程~   git clone https://github.com/libbpf/bpftool.git 下载?        
root@ubuntu:/usr/src/linux-source-5.15.0/tools/bpf/bpftool# sudo apt-get install binutils-dev
root@ubuntu:/usr/src/linux-source-5.15.0/tools/bpf/bpftool# sudo apt-get install libcap-dev
root@ubuntu:/usr/src/linux-source-5.15.0/tools/bpf/bpftool# sudo apt-get install llvm
#直接在刚才下载linux源码的位置处编译bpftool
root@ubuntu:/usr/src/linux-source-5.15.0/tools/bpf/bpftool# make

Auto-detecting system features:
...                        libbfd: [ on  ]
...        disassembler-four-args: [ on  ]
...                          zlib: [ on  ]
...                        libcap: [ on  ]
...               clang-bpf-co-re: [ on  ]
。。。
root@ubuntu:/usr/src/linux-source-5.15.0/tools/bpf/bpftool# ./bpftool --version
./bpftool v5.15.163
features: libbfd, skeletons

#上面bpf的测试在22.04 编译不过啊   高版本已经不提供用bpf_load.h 了 

4:bpftrace的安装练习

bpftrace 一行指令已经实现很多功能,可以参考github进行学习。

bpftrace 可以说实现监控网络,内存等各种功能,根据业务可以思考。

思考探测内核函数,用户可执行程序中接口,so接口,静态库接口。

在这里插入图片描述

4.1 安装bpftrace (ubuntu 22.04)

#基于前面已经安装过一系列的组件   可以参考网络或者源码安装 这里初入门了解
root@ubuntu:/usr/src/linux-source-5.15.0/tools/bpf/bpftool# apt install bpftrace
root@ubuntu:/usr/src/linux-source-5.15.0/tools/bpf/bpftool# bpftrace --version
bpftrace v0.14.0

4.2 简单了解bpftrace 相关语法。

4.2.1 结构语法及参数介绍

在这里插入图片描述

在这里插入图片描述

4.2.2 常用内置变量
内置变量
bpftrace 脚本常用变量如下:
    uid:用户 id。
    tid:线程 id
    pid:进程 id。
    cpu:cpu id。
    cgroup:cgroup id.
    probe:当前的 trace 点。
    comm:进程名字。
    nsecs:纳秒级别的时间戳。
    kstack:内核栈描述
    curtask:当前进程的 task_struct 地址。
    args:获取该 kprobe 或者 tracepoint 的参数列表
    arg0:获取该 kprobe 的第一个变量,tracepoint 不可用
    arg1:获取该 kprobe 的第二个变量,tracepoint 不可用
    arg2:获取该 kprobe 的第三个变量,tracepoint 不可用
    retval: kretprobe 中获取函数返回值
    args->ret: kretprobe 中获取函数返回值

自定义变量
    以'$'标志起来定义与引用变量,例如:$idx = 0;
Map 变量
    Map 变量是用于内核向用户空间传递数据的一种存储结构,定义方式是以'@'符号作为标志
    @path[tid] = nsecs;
    @path[pid, $fd] = nsecs;
	Bpftrace 默认在结束时会打印从内核接收到的 map 变量
内置函数
    exit():退出 bpftrace 程序
    str(char *):转换一个指针到 string 类型
    system(format[, arguments ...]):运行一个 shell 命令
    join(char *str[]):打印一个字符串列表并在每个前面加上空格,比如可以用 来输出 args->argv
    ksym(addr):用于转换一个地址到内核 symbol
    kaddr(char *name):通过 symbol 转换为内核地址
    print(@m [, top [, div]]):可选择参数打印 map 中的 top n 个数据,数据可选择除以一个 div 值
    buf(void *p [, int length])
    strncmp(char *s1, char *s2, int length)
    sizeof(expression)
    ustack([limit])
    ksym(void *p)
    kstack([limit])
    usym(void *p)
    kaddr(char *name)
    uaddr(char *name)
    ntop([int af,]int|char[4:16] addr)
    reg(char *name)
    cgroupid(char *path)


bpftrace 内置了 map 对象操作函数,用于传递数据给 map 变量。
    count():用于计算次数
    sum(int n):用于累加计算
    avg(int n):用于计算平均值
    min(int n):用于计算最小值
    max(int n):用于计算最大值
    hist(int n):数据分布直方图(范围为 2 的幂次增长)
    lhist(int n):数据线性直方图
    delete(@m[key]):删除 map 中的对应的 key 数据
    clear(@m):删除 map 中的所有数据
    zero(@m):map 中的所有值设置为 0

4.3 简单练习bpf的相关一行指令

#统计调用read的次数
root@ubuntu:/home/ubuntu# bpftrace -e 't:syscalls:sys_enter_read {@[probe]=count(); }'
Attaching 1 probe...
^C

@[tracepoint:syscalls:sys_enter_read]: 94

#统计write调用的次数
root@ubuntu:/home/ubuntu#  bpftrace -e 't:syscalls:sys_enter_write {@[probe]=count();}'
Attaching 1 probe...
^C

@[tracepoint:syscalls:sys_enter_write]: 22

#统计调用read 按参数 输出 数据分布直方图展示   ssize_t read(int fd, void *buf, size_t count);
root@ubuntu:/home/ubuntu# bpftrace -e 't:syscalls:sys_enter_read {@=hist(args->count);}'
Attaching 1 probe...
^C

@: 
[1K, 2K)              29 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[2K, 4K)               0 |                                                    |
[4K, 8K)               0 |                                                    |
[8K, 16K)              0 |                                                    |
[16K, 32K)             4 |@@@@@@@                                             |
[32K, 64K)             0 |                                                    |
[64K, 128K)            0 |                                                    |
[128K, 256K)           0 |                                                    |
[256K, 512K)           4 |@@@@@@@                                             |

 #追踪openat 系统调用  打开文件的入口   统计进程名和打开的文件 
root@ubuntu:/home/ubuntu# bpftrace -e 't:syscalls:sys_enter_openat { printf("%s–> %s\n", comm,str(args->filename)); }'
Attaching 1 probe...
irqbalance–> /proc/interrupts
irqbalance–> /proc/stat
vmtoolsd–> /proc/meminfo
vmtoolsd–> /proc/vmstat
vmtoolsd–> /proc/stat
vmtoolsd–> /proc/zoneinfo
vmtoolsd–> /proc/uptime
vmtoolsd–> /proc/diskstats

#需要查找文件时触发  追踪内核函数 lookup_fast 的调用次数
root@ubuntu:/home/ubuntu#  bpftrace -e 'kprobe:lookup_fast { @[comm, pid] = count(); }'
Attaching 1 probe...
^C

@[systemd-network, 847]: 3
@[systemd, 1]: 6
@[irqbalance, 879]: 8
@[cron, 871]: 15
@[vmtoolsd, 762]: 24
@[snapd, 885]: 148

#统计阻塞 io 事件   表示捕获所有与块设备相关的 tracepoint 事件 并记录每个 tracepoint 被触发的次数
root@ubuntu:/home/ubuntu#  bpftrace -e 't:block:* { @[probe] = count(); }'
Attaching 18 probes...
^C

@[tracepoint:block:block_rq_complete]: 17
@[tracepoint:block:block_rq_issue]: 17

#统计 阻塞 io 操作数据大小     block_rq_issue用于捕获块设备的 I/O 请求被发出的事件
root@ubuntu:/home/ubuntu#  bpftrace -e 't:block:block_rq_issue { @bytes = hist(args->bytes); }'
Attaching 1 probe...
^C

@bytes: 
[8, 16)                4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|

#追踪块设备的 I/O 请求完成事件 block_rq_complete,并打印出那些包含错误的请求的相关信息
root@ubuntu:/home/ubuntu#  bpftrace -e 't:block:block_rq_complete /args->error/ {printf("dev %d type %s error %d\n", args->dev, args->rwbs, args->error); }'
Attaching 1 probe...
^C
root@ubuntu:/home/ubuntu#  bpftrace -e 't:block:block_rq_complete {printf("dev %d type %s error %d\n", args->dev, args->rwbs, args->error); }'
Attaching 1 probe...
dev 0 type N error 0
dev 0 type N error 0
dev 0 type N error 0

#启动的进程名与命令行参数
root@ubuntu:/home/ubuntu# bpftrace -e 'tracepoint:syscalls:sys_enter_execve { join(args->argv); }'
Attaching 1 probe...
ls --color=auto
ls --color=auto
ls --color=auto
ls --color=auto
cat 1

#追踪所有系统调用的进入点 统计次数
root@ubuntu:/home/ubuntu# bpftrace -e 'tracepoint:raw_syscalls:sys_enter{ @[pid, comm] = count(); }'
Attaching 1 probe...
^C

@[882, rsyslogd]: 3
@[871, cron]: 6
@[889, pool-udisksd]: 6
@[889, udisksd]: 12
@[887, systemd-logind]: 15
@[749, systemd-timesyn]: 15
@[849, systemd-resolve]: 15
@[847, systemd-network]: 20
@[1316, sshd]: 24
@[1627, bash]: 33
@[1, systemd]: 46
@[762, HangDetector]: 52
@[879, irqbalance]: 70
@[1593, sshd]: 74
@[1594, bash]: 76
@[1627, ls]: 121
@[885, snapd]: 181
@[569, multipathd]: 324
@[1620, bpftrace]: 525
@[762, vmtoolsd]: 3969
 
#追踪进程上下文切换事件,并统计每个上下文切换期间的内核堆栈信息的出现次数 
root@ubuntu:/home/ubuntu# bpftrace -e 'tracepoint:sched:sched_switch { @[kstack] = count(); }'
Attaching 1 probe...
^C

@[
    __schedule+855
    __schedule+855
    schedule+105
    schedule_timeout+135
    msleep+45
    usb_port_suspend+908
    usb_generic_driver_suspend+67
    usb_suspend_both+506
    usb_runtime_suspend+47
    __rpm_callback+74
    rpm_callback+103
    rpm_suspend+356
    __pm_runtime_suspend+73
    usb_runtime_idle+58
    rpm_idle+195
    pm_runtime_work+141
    process_one_work+552
    worker_thread+83
    kthread+295
    ret_from_fork+31
]: 1

bpftrace -e 'u:/lib/x86_64-linux-gnu/libpthread2.31.so:pthread_create { printf("%s by %s (%d)\n", probe, comm, pid ); }'
sudo find -name libpthread*
ldd ./usr/lib32/libpthread.so.0
ldd ./usr/lib/x86_64-linux-gnu/libpthread.so.0
nm -D ./usr/lib/x86_64-linux-gnu/libpthread.so.0
ldd ./usr/lib/x86_64-linux-gnu/libpthread.so.0
nm -D lib/x86_64-linux-gnu/libc.so.6|grep thread_create

root@ubuntu:/# ldconfig -p | grep libpthread
	libpthread.so.0 (libc6,x86-64, OS ABI: Linux 3.2.0) => /lib/x86_64-linux-gnu/libpthread.so.0
	libpthread.so.0 (libc6, OS ABI: Linux 3.2.0) => /lib32/libpthread.so.0
	
#进程创建 pthread_create	探测  (写了个测试代码调用pthread_create)
root@ubuntu:/# bpftrace -e 'u:/lib/x86_64-linux-gnu/libc.so.6:pthread_create { printf("%s by %s (%d)\n", probe, comm, pid ); }'
Attaching 1 probe...
uprobe:/lib/x86_64-linux-gnu/libc.so.6:pthread_create by pthread (17916)
uprobe:/lib/x86_64-linux-gnu/libc.so.6:pthread_create by pthread (17918)
uprobe:/lib/x86_64-linux-gnu/libc.so.6:pthread_create by pthread (17920)

#内核堆栈跟踪的内存分配情况 统计内存分配总字节数
 root@ubuntu:/# bpftrace -e 't:kmem:kmem_cache_alloc { @bytes[kstack] = sum(args->bytes_alloc); }'
Attaching 1 probe...
^C^C

@bytes[
    kmem_cache_alloc+623
    kmem_cache_alloc+623
    __sigqueue_alloc+123
    __send_signal+614
    send_signal+233
    do_send_sig_info+96
    __kill_pgrp_info+133
    kill_pgrp+53
    isig+150
    n_tty_receive_signal_char+27
    n_tty_receive_char_special+1086
    n_tty_receive_buf_standard+326
    __receive_buf+507
    n_tty_receive_buf_common+139
    n_tty_receive_buf2+20
    tty_ldisc_receive_buf+31
    tty_port_default_receive_buf+66
    flush_to_ldisc+170
    process_one_work+552
    worker_thread+83
    kthread+295
    ret_from_fork+31
]: 80

#统计malloc的调用次数  
root@ubuntu:/#  bpftrace -e 'u:/lib/x86_64-linux-gnu/libc.so.6:malloc {@[ustack, comm] = sum(arg0); }'
Attaching 1 probe...
^C

@[
    __libc_malloc+0
, bpftrace]: 4
@[
    __libc_malloc+0
    0x55e8989dbcc0
    0x55e8989d7400
    0x8148535554415541
, bpftrace]: 8
@[
    __libc_malloc+0
, bpftrace]: 8
@[
    __libc_malloc+0
, bpftrace]: 24
@[
    __libc_malloc+0
    0x5618bcac89f0
    0x20
, sshd]: 448

#统计进程发生缺页中断  写了个mmap 分配内存并访问 触发缺页中断
root@ubuntu:/# bpftrace -e 't:exceptions:page_fault_user { @[ustack, comm] = count(); }'
Attaching 1 probe...
^C

ERROR: failed to look up stack id -17 (pid -1): -1
@[
    0x7fc0b22a57e1
, bash]: 1
@[
    0x7f5cafa99610
    0x69622f3d4c4c4548
, mmap]: 1

4.4 使用脚本进行练习

4.4.1 探测打开文件 输出对应文件名
#脚本进行测试  首先测试 探测打开文件的名称或者路径 
root@ubuntu:/# bpftrace -e '
kprobe:vfs_open {
    $path = (struct path *)arg0;
    $dentry = $path->dentry;
    printf("open path: %s\n", str($dentry->d_name.name));
}'
#写入文件
ubuntu@ubuntu:~/test$ cat fs.bt 
#include <linux/path.h>
#include <linux/dcache.h>

kprobe:vfs_open {
	// printf("open path :%s \n",str((struct path*)arg0)->dentry->d_name.name); //不能直接打印 要提取到用户空间
    $path = (struct path *)arg0;
    $dentry = $path->dentry;
    printf("open path: %s\n", str($dentry->d_name.name));
}

root@ubuntu:/home/ubuntu/test# bpftrace fs.bt 
Attaching 1 probe...
open path: interrupts
open path: stat
open path: smp_affinity
open path: smp_affinity
4.4.2 探测tcp连接的网络五元组。
//探测五元组   除了accept  还可以其他相关 比如tcp的状态,比如connect recv send按需求处理

#include <linux/socket.h>
#include <net/sock.h>
#include <linux/types.h>

// BEGIN 
// { 
// 	printf("hello\n"); 
// }

//梳理 kprobe和kretprobe区别 
kprobe:inet_csk_accept
{
	printf("inet_csk_accept\n"); 
}

kretprobe:inet_csk_accept
{
	$sk = (struct sock*)retval;
	$inet_family = $sk->__sk_common.skc_family;

	$daddr = ntop(0);
	$saddr = ntop(0);

	if ($inet_family == AF_INET) {

		$daddr = ntop($sk->__sk_common.skc_daddr);
		$saddr = ntop($sk->__sk_common.skc_rcv_saddr);		

	}
	
	$sport = $sk->__sk_common.skc_num;
	$dport = $sk->__sk_common.skc_dport; //大小端转换 不提供类型 自处理

	$dport_num = ((($dport <<8) &0xFF00 )| (($dport>>8)&0xFF)) &0xFFFF;
	printf(" tcp_accept: %-16s:%d %d--> %-16s:%d\n", $daddr, $dport, $dport_num, $saddr, $sport);
}

使用网络助手进行连接 测试结果:

测试结果: 写了一个tcp的服务端程序  用网络助手进行连接,测试可以打印: 注意大小端转换。
root@ubuntu:/home/ubuntu/test# bpftrace accept.bt 
Attaching 2 probes...
inet_csk_accept
 tcp_accept: 192.168.40.1    :1782 62982--> 192.168.40.137  :6666
inet_csk_accept
 tcp_accept: 192.168.40.1    :2038 62983--> 192.168.40.137  :6666
inet_csk_accept
 tcp_accept: 192.168.40.1    :6902 63002--> 192.168.40.137  :6666

4.4.3 探测mysql执行的语句 以及对应的耗时。

mysql.bt

uprobe:/usr/sbin/mysqld:*dispatch_command*
{
	@start = nsecs;
	@sql = str(*arg1);
}

uretprobe:/usr/sbin/mysqld:*dispatch_command*
/@start/
{
	$dur = nsecs - @start;
	printf("%u %s \n", $dur, @sql);
}

mysql执行结果: 可以看到对应的mysql执行语句 以及对应的耗时

root@ubuntu:/home/ubuntu/test# bpftrace mysql.bt 
WARNING: Addrspace is not set
Attaching 2 probes...
1175223 show databases 
12474975 CREATE DATABASE `mytest2` 
12438296 CREATE DATABASE mytest3 
541816 CREATE DATABASE 'mytest3'    #这种正常的单引号 创建会失败


#mysql创建后,第一次登录mysql可以直接登录,要给root手动设置密码。    set password for root@localhost = password('123456');  

#这次用的这个密码
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;

参考,手头已有文档以及网络:

eBPF 入门开发实践教程一:Hello World,基本框架和开发流程 - eunomia

如何在 Ubuntu 上配置 eBPF 开发环境 | YaoYao’s Blog

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2143027.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记忆化搜索(算法篇)

算法篇之记忆化搜索 记忆化搜索 概念&#xff1a; 记忆化搜索就是深度优先搜索的一种优化策略&#xff0c;记忆化搜索深度优先搜索形式动态规划思想由于dfs本质是暴力搜索&#xff0c;没有很好地处理重叠子问题&#xff0c;因此很低效记忆化算法在求解地时候还是按照自顶向下…

固态硬盘数据怎么恢复?用这4个软件你就知道了。

其实想要恢复固态硬盘里面的数据&#xff0c;方法有很多种。我们可以从回收站恢复&#xff0c;用备份恢复&#xff0c;或者是找专业人员帮助恢复。如果这些方法都不行的话&#xff0c;可以试试使用专业的数据恢复软件。我知道的数据恢复软件就有好几款&#xff0c;可以分享给大…

java编程行业特点

Java编程行业是一个庞大且充满活力的领域&#xff0c;它涵盖了从桌面应用到企业级应用、从Web开发到移动应用、从大数据处理到云计算服务的广泛范围。Java作为一种跨平台、面向对象的编程语言&#xff0c;自1995年发布以来&#xff0c;一直受到全球开发者的青睐&#xff0c;并在…

我的5周年创作纪念日,不忘初心,方得始终。

一、机缘 五年前&#xff0c;我怀着对知识的渴望和分享的热情&#xff0c;踏入了 CSDN 这个充满活力的技术交流平台&#xff0c;开启了我的创作之旅。那时的我&#xff0c;刚刚在技术的海洋中启航&#xff0c;渴望找到一个地方记录自己的成长足迹&#xff0c;与更多的人分享自…

【模板的特殊继承关系】用参数化的方式表达成员函数的虚拟性

一、使用混入技术实现的简单范例 成员函数的虚拟性可以通过特殊的继承来表达&#xff0c;我们先看看一个简单的范例&#xff0c;通过混入技术来实现&#xff1a; //基类 template<typename... T> class Base:public T... { public:void myfunc(){std::cout << &q…

禹神3小时快速上手typescript

一、TypeScript简介 TypeScript 由微软开发&#xff0c;是基于 JavaScript 的⼀个扩展语⾔。TypeScript 包含了 JavaScript 的所有内容&#xff0c;即&#xff1a; TypeScript 是 JavaScrip t 的超集。TypeScript 增加了&#xff1a;静态类型检查、接⼝、 泛型等很多现代开发特…

6--SpringBootWeb案例(详解)

目录 环境搭建 部门管理 查询部门 接口文档 代码 删除部门 接口文档 代码 新增部门 接口文档 代码 已有前端&#xff0c;根据接口文档完成后端功能的开发 成品如下&#xff1a; 环境搭建 1. 准备数据库表 (dept 、 emp) -- 部门管理 create table dept( id int un…

云计算第四阶段------CLOUD Day4---Day6

Cloud DAY4 项目架构图&#xff1a; 环境准备&#xff1a; 主机名称IP地址配置logstash192.168.1.27最低配置4核8G #书接上文&#xff0c;我们在华为云平台租了几台云服务器&#xff0c;这次买一台性能好的服务器&#xff0c;作为logstash软件部署的载体。 今天给小伙伴们带来…

【装机教程】Visual Studio Community 2019离线安装

Visual Studio 2019离线安装 由于现在 官网只支持在线安装最新版的Visual Studio 2022&#xff0c;因此 Visual Studio Community 2019需要离线安装。 下载离线安装镜像&#xff0c;并解压。点击vs_setup.exe运行。 选择安装位置&#xff0c;四处位置需要确定。 选择语言包&…

CSP-J 2019 入门级 第一轮(初赛) 完善程序(1)

【题目】 CSP-J 2019 入门级 第一轮&#xff08;初赛&#xff09; 完善程序&#xff08;1&#xff09; 1.&#xff08;矩阵变幻&#xff09;有一个奇幻的矩阵&#xff0c;在不停的变幻&#xff0c;其变幻方式为&#xff1a; 数字 0 变成矩阵 0 0 0 1 数字 1 变成矩阵 1 1 1 0 …

沉浸式利用自然语言无代码开发工具生成式AI产品应用(下)

背景 小伙伴们过去在开发应用时&#xff0c;经常需要编写大量代码文件以实现业务逻辑&#xff0c;想必肯定有小伙伴开发过类似于快消行业索赔处理、订单库存跟踪和项目审批等系统。去解决这些业务实际问题&#xff0c;我们需要定制地开发业务应用程序为这些问题提供解决方案。…

手写redis实现分布式锁详细教程,满足可续锁、可重入等分布式锁条件

前言 本文将讨论的做一个高并发场景下避不开的话题&#xff0c;即redis分布式锁。比如在淘宝 的秒杀场景、热点新闻和热搜排行榜等。可见分布式锁是一个程序员面向高级的一门必修课&#xff0c;下面请跟着本篇文章好好学习。 redis分布式锁有哪些面试题 1.Redis做分布式的时…

C语言 | Leetcode C语言题解之第404题左叶子之和

题目&#xff1a; 题解&#xff1a; bool isLeafNode(struct TreeNode *node) {return !node->left && !node->right; }int sumOfLeftLeaves(struct TreeNode *root) {if (!root) {return 0;}struct TreeNode **q malloc(sizeof(struct TreeNode *) * 2001);in…

JVM 一个对象是否已经死亡?

目录 前言 引用计数法 可达性分析法 引用 finalize() 方法区回收 前言 虚拟机中垃圾回收器是掌握对象生死的判官, 只要是垃圾回收器认为需要被回收的, 那么这个对象基本可以宣告"死亡". 但是也不是所有的对象, 都需要被回收, 因此, 我们在学习垃圾回收的时候…

Linux系统应用之知识补充——OpenEuler(欧拉)的安装和基础配置

前言 这篇文章将会对OpenEuler的安装进行详解&#xff0c;一步一步跟着走下去就可以成功 注意 &#xff1a;以下的指令操作最好在root权限下进行&#xff08;即su - root&#xff09; ☀️工贵其久&#xff0c;业贵其专&#xff01; 1、OpenEuler的安装 这里我不过多介绍&a…

markdown 使用技巧

文章目录 markdown使用技巧1.标题快捷键设置2.文档可读性设置 markdown使用技巧 1.标题快捷键设置 ctl 1:一级标题 ctl 2:二级标题 ctl 3:三级标题 ctl 4:四级标题 ...2.文档可读性设置 输入~~~pro 可选择代码框&#xff0c;并且可以选择不同的字体 ctrl shift ] : 可…

Flink学习2

创建一个无界流 package com.qyt; import org.apache.flink.api.java.functions.KeySelector; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.streaming.api.datastream.DataStreamSource; import org.apache.flink.streaming.api.datastream.Keye…

《微信小程序实战(2) · 组件封装》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

138、Java内部类源码

01.代码如下&#xff1a; package TIANPAN;class Outer { // 外部类private String msg "Hello World !";class Inner { // 定义一个内部类private String info "世界&#xff0c;你好&#xff0…

【深度分析】OpenAI o1是最强的推理模型,却不是最强模型!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…