book
- http://docs.oracle.com/cd/E19253-01/819-6959/
- https://docs.oracle.com/cd/E19253-01/819-6959/819-6959.pdf
- DTrace-Dynamic-Tracing-in-Oracle-Solaris-Mac-OS-X-and-FreeBSD
pragma
- pragma D 指定特定的编译指令
- pragma D attributes 名称稳定性/数据稳定性/依赖 provider providername (provider、module、function、name、args) 稳定性包括:Internal(内部)、Private(专用)、Obsolete(过时)、External(外部)、Unstable(不稳定)、Evolving(发展中)、Stable(稳定)、Standard(标准) 依赖包括:Unknown(未知)、CPU(特性cpu)、Platform(系列cpu)、Group(某种架构的cpu)、ISA(特定指令集)、Common(通用)。
- pragma D option 选项: aggrate(聚合读取的速率)、aggsize(聚合缓冲区大小)、bufresize(缓冲区调整大小策略)、bufsize(主体缓冲区大小)、cleanrate(清除速率hz)、cpu(启用跟踪的CPU)、defaultargs(允许引用未指定的宏参数)、destructive(允许破坏性操作)、dynvarsize(动态变量空间大小)、flowindent(缩进函数输入并加前缀 ->;取消缩进函数返回并加前缀 <-)、grabanon(声明匿名状态)、jstackframes(jstack() 的缺省栈帧数)、jstackstrsize(jstack() 的缺省字符串空间大小)、nspec(推理数)、quiet(仅输出显式跟踪的数据)、specsize(推理缓冲区大小)、strsize(字符串大小)、stackframes(栈帧数)、stackindent(缩进 stack() 和 ustack() 输出时要使用的空格字符数)、statusrate(状态检查的速率)、switchrate(缓冲区切换的速率)、ustackframes(用户栈帧数)、version(dtrace版本)
探针描述
- dtrace:::BEGIN 脚本开始时执行
- dtrace:::END 脚本退出时执行
- dtrace:::ERROR 脚本错误时执行
-
sudo dtrace -l awk ‘{ match($2, “([a-z,A-Z]*)”);print substr($2, RSTART, RLENGTH); }’ sort -u 列出所有可用探针 - tick-10sec 每10秒执行 在一个cpu上触发
- profile-1001 每1001赫兹执行 在所有cpu上触发
- pid123:libSystem:printf:return pid为123的print函数
- objc123:NSTableView:draw?:entry pid为123obj类函数
- io::: start done wait-start wait-done journal-start journal-done
- ip:::send ip:::receive arg0-arg5
- 自定义静态探针 provider name{probe name(param)} name.d dtrace -h -s name.d
- 多个探针可以用’,’进行分隔
- :entry :return 函数进入和退出
断言
-
条件可使用 && / a == b && c == d /
语句
- sum(…) 集积函数 对key对应的值累加
- count() 集积函数 对key对应的函数调用次数
- avg(…) 集积函数 对key对应的值平均值
- min(…) 集积函数 对key对应的值最小
- max(…) 集积函数 对key对应的值最大
- lquantize() 集积函数 线性统计
- quantize() 集积函数 非线性统计
- trunc(@name, number)集积函数 截断
- printa(…) 打印集积
- clear(@name) 清除集积
- normalize(@name, .) 标准化集积
- trace() 打印信息
- exit(0) 退出Dtrace
- printf格式化输出
- tracemem(addr, len) 打印内存
- stack(deepth) 打印内核栈 可以不指定深度
- ustack(deepth, strsize) 打印用户栈,参数可不指定
- jstack(deepth, strsize) ustack别名, strsize默认>0
- stop()触发探测器的进程在退出内核时被强制停止
- raise(int signal)发送信号给进程
- copyout(buf,addr,size) 从buf拷贝size到addr内存
- copystr(str,addr,maxlen)从str拷贝maxlen到addr
- system(program,…)执行系统命令可以用格式化字符生成命令,只在处理Dtrace缓存区时才开始执行,并不在调用当时执行
- breakpoint() 内核断点
- panic()系统奔溃转储
- chill(nanoseconds) 相当于sleeep
- void* alloca(size) 分配零时空间,出{}会自动释放
- basename(str)复制str不包括/结尾的前缀,临时空间自动释放
- bcopy(str,dest,size)str在临时内存外,dest在零时内存内
- cleanpath(str)返回有效路径 临时内存外部
- void* copyin(addr, size)拷贝addr到临时缓存区
- string copyinstr(addr)
- void copyinto(saddr,size,daddr)
- string dirname(path)返回目录内容
- size_t msgdsize(mblk_t *mp) 返回数据字节数
- size_t msgsize(mblk_t *mp) 返回字节总数
- int mutex_owned(kmutex_t *mutex)返回当前内核mutex
- kthread_t* mutex_owner(kmutext_t *mutex)返回内核mutext的线程指针
- int mutex_type_adaptive(kmutex_t *mutex)判断内核互斥是否为MUTEX_ADAPTIVE类型
- int progenyof(pid_t pid)判断是否是指定的进程
- int rand(void)返回伪随机数
- int rw_iswriter(krwlock_t *rwlock)是否是写锁或写等待锁
- int rw_write_held(krwlock_t *rwlock)是否写锁定
- int speculation()
- string strjoin(str1, str2)连接两个字符串 临时缓存外部
- size_t strlen(str)字符长度
- translator structtype1 < structtype2 name > 自动结构转换 2->1
- xlate
(structtype2)->name 运算符自动转换 - inline 指令定义 D 命名的常量
变量
- 全局(name)、线程本地(self->name)、 探针语句本地{this->name}
- 集积 @name 对某个key进行累计
- 联合数组 int x[unsigned long long, char];
- $1 表示Dtrace 命令的第一个参数
- errno int 系统调用的当前 errno 值
- ‘name 访问操作系统的变量 数据结构 类型
- int64_t arg0, …, arg9 探测器的前10个输入参数表示为原始的64位整数
- args[] 当前探测器键入的参数 实际类型
- uintptr_t caller 当前线程的程序计数器位置
- chipid_t chip 当前物理芯片的 CPU 芯片标识符
- processorid_t cpu 当前 CPU 的 CPU 标识符
- cpuinfo_t *curcpu 当前 CPU 的 CPU 信息
- lwpsinfo_t *curlwpsinfo 当前线程LWP 状态
- psinfo_t *curpsinfo 当前线程关联的进程的进程状态
- kthread_t *curthread 当前线程的操作系统内核的内部数据结构的地址
- string cwd 与当前线程关联的进程的当前工作目录名称。
- uint_t epid 当前探测器的已启用的探测器 ID
- int errno 线程最后一次执行的系统调用返回的错误值
- string execname 当前进程的名称
- gid_t gid 当前进程的实际组 ID
- uint_t id 探测器ID。系统范围内的唯一标识符
- uint_t ipl 触发探测器时当前 CPU 的中断优先级
- lgrp_id_t lgrp 当前 CPU 所属的延迟组的延迟组 ID
- pid_t pid 当前进程的进程 ID
- pid_t ppid 当前进程的父进程 ID
- string probefunc 探测器函数名称部分
- string probemod 探测器模块名称部分
- string probename 探测器名称部分
- string probeprov 探测器提供器名称部分
- psetid_t pset 当前 CPU 的处理器集的处理器集 ID
- string root 当前线程关联的进程的根目录名称
- uint_t stackdepth 触发探测器时当前线程的栈帧深度
- id_t tid 当前线程的线程 ID
- uid_t uid 当前进程的实际用户 ID
- uint64_t uregs[] 当前线程已保存的用户模式寄存器值
- uint64_t timestamp 纳秒相对时间
- uint64_t vtimestamp 纳秒当前线程已运行时间减去花费时间
- uint64_t walltimestamp 1970-1-1-00:00纳秒
xcode
- 创建d文件
- provider 名称 {}; 定义提供者
- probe name(args…) 定义探针
- 将d文件添加进xcode
- ios项目head search path add $(DERIVED_FILES_DIR)
- 使用的地方#import d文件名.h
- providename_probename、 providename_probename_enabled
- DTRACE_PROBES_DISABLED 关闭dtrace
linux 系统追踪
- kprobes ftrace perf_events cBPF eBPF
- ebpf 需要内核在3.15以上, 5.5以上才好用, 5.8引入了ring buffer
- ebpf字节码
- c 直接开发
- libbpf 纯C库, 生成BPF字节码, 运行时动态判断内核版本, 更新BPF字节码, 加载内核编译
- centos 升级内核
yum update -y rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-5.el7.elrepo.noarch.rpm rpm -qa |grep kernel yum remove -y kernel-headers kernel-tools kernel-tools-libs kernel-debug-devel kernel-debuginfo kernel-debuginfo-common-x86_64 旧内核名(不含arch名) clang llvm yum --enablerepo=elrepo-kernel install -y kernel-ml kernel-ml-devel kernel-ml-headers kernel-ml-tools kernel-ml-tools-libs grub2-mkconfig -o /boot/grub2/grub.cfg awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg grub2-set-default 0 grub2-editenv list cat /boot/config-xxx | grep KPROBES 确定内核编译是否开启内核跟踪 cat /boot/config-xxx | grep BPF 确定内核编译是否开启BPF yum -y groupinstall "Development Tools" yum -y install zlib-devel openssl-devel python3 elfutils-libelf-devel #gcc 11 无法直接用gcc 4.x编译 wget https://mirrors.aliyun.com/gnu/gcc/gcc-10.3.0/gcc-10.3.0.tar.xz wget https://mirrors.aliyun.com/gnu/gcc/gcc-11.1.0/gcc-11.1.0.tar.xz tar -xvf gcc-11.1.0.tar.xz cd gcc-11.1.0 ./contrib/download_prerequisites mkdir build cd build ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,go --enable-plugin --enable-initfini-array --disable-libgcj --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux --disable-multilib --with-default-libstdcxx-abi=gcc4-compatible --with-abi=m64 --enable-offload-targets=nvptx-none --without-cuda-driver --enable-checking=release --with-target-system-zlib --enable-objc-gc=auto --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-nls --without-included-gettext unset LIBRARY_PATH CPATH C_INCLUDE_PATH PKG_CONFIG_PATH CPLUS_INCLUDE_PATH INCLUDE make -j16 make install wget https://github.com/Kitware/CMake/releases/download/v3.20.2/cmake-3.20.2.tar.gz tar -xvf cmake-3.20.2.tar.gz cd cmake-3.20.2 ./bootstrap --prefix=/usr make -j16 make install wget https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.0/llvm-12.0.0.src.tar.xz wget https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.0/clang-12.0.0.src.tar.xz wget https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.0/clang-tools-extra-12.0.0.src.tar.xz wget https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.0/compiler-rt-12.0.0.src.tar.xz wget https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.0/libcxx-12.0.0.src.tar.xz wget https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.0/libcxxabi-12.0.0.src.tar.xz wget https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.0/lldb-12.0.0.src.tar.xz tar -xvf llvm-12.0.0.src.tar.xz tar -xf clang-12.0.0.src.tar.xz mv clang-12.0.0.src clang tar -xf clang-tools-extra-12.0.0.src.tar.xz mv clang-tools-extra-12.0.0.src clang-tools-extra tar -xf compiler-rt-12.0.0.src.tar.xz mv compiler-rt-12.0.0.src compiler-rt tar -xf libcxx-12.0.0.src.tar.xz mv libcxx-12.0.0.src libcxx tar -xf libcxxabi-12.0.0.src.tar.xz mv libcxxabi-12.0.0.src libcxxabi tar -xf lldb-12.0.0.src.tar.xz mv lldb-12.0.0.src lldb cd llvm-12.0.0.src mkdir build cd build unset LIBRARY_PATH CPATH C_INCLUDE_PATH PKG_CONFIG_PATH CPLUS_INCLUDE_PATH INCLUDE cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=on -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;compiler-rt;lldb;libcxx;libcxxabi" .. make -j16 make install git clone https://github.com/lldb-tools/lldb-mi cd lldb-mi mkdir build cd build cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_PREFIX_PATH=/usr .. make -j16 make install
- c 语言
- 直接编译进内核
yum install -y epel-release yum update -y yum groupinstall -y "Development tools" yum install -y elfutils-libelf-devel cmake3 git bison flex ncurses-devel zlib libz-dev zlib-devel libelf libelf-devel futils-libelf-devel libbpf install or build install llvm >= 10 install or build install clang >= 10 linux 内核代码 make defconfig && make headers_install 编写内核bpf探针 samples/bpf/xxx_kern.c #include <linux/bpf.h> #include "bpf_helpers.h" #define SEC(NAME) __attribute__((section(NAME), used)) SEC("tracepoint/syscalls/sys_enter_execve") int bpf_prog(void *ctx){ char msg[] = "nomadli's bpf program!\n"; bpf_trace_printk(msg, sizeof(msg)); return 0; } char _license[] SEC("license") = "GPL"; 编写用户态使用 samples/bpf/xxx_user.c #include <stdio.h> #include "bpf_load.h" int main(int argc, char **argv){ if(load_bpf_file("xxx_kern.o")!=0){ printf("The kernel didn't load BPF program\n"); return -1; } read_trace_pipe(); return 0; } 修改Makefile 添加 hostprogs-y += hello hello-objs := bpf_load.o hello_user.o always += hello_kern.o make M=samples/bpf
- 手动加载如内核
#与直接加载内核相同, 写xxx_kern.c #always += hello_kern.o #make M=samples/bpf llm-objdump -arch-name=bpf -S hello_kern.o 获取函数开始及长度 dd if=/xx/hello_kern.o of=/xx/hell_kern bs=1 count=函数二进制字节长度 skip=函数开始位置(头64字节) #获取相关event id, 如kprodb的id echo 'p:sys_clone sys_clone' >> /sys/kernel/debug/tracing/kprobe_events cat /sys/kernel/debug/tracing/events/kprobes/sys_clone/id #客户端代码 #include <unistd.h> #include <string.h> #include <sys/syscall.h> #include <stdlib.h> #include <stdio.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/bpf.h> #include <linux/version.h> #include <linux/perf_event.h> #include <linux/hw_breakpoint.h> #include <errno.h> int main() { int bfd = open("/xx/hell_kern", O_RDONLY); if (bfd < 0) { printf("open eBPF program error: %s\n", strerror(errno)); exit(-1); } unsigned char buf[1024] = {}; int n = read(bfd, buf, 1024); for (int i = 0; i < n; ++i) { printf("%02x ", buf[i]); if (i % 8 == 0) printf("\n"); } close(bfd); struct bpf_insn *insn = (struct bpf_insn*)buf; unsigned char log_buf[4096] = {}; union bpf_attr attr = {}; attr.prog_type = BPF_PROG_TYPE_KPROBE; attr.insns = (unsigned long)insn; attr.insn_cnt = n / sizeof(struct bpf_insn); attr.license = (unsigned long)"GPL"; attr.log_size = sizeof(log_buf); attr.log_buf = (unsigned long)log_buf; attr.log_level = 1; attr.kern_version = 264656; int pfd = syscall(SYS_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); if (pfd < 0) { printf("bpf syscall error: %s\n", strerror(errno)); printf("log_buf = %s\n", log_buf); exit(-1); } struct perf_event_attr pattr = {}; pattr.type = PERF_TYPE_TRACEPOINT; pattr.sample_type = PERF_SAMPLE_RAW; pattr.sample_period = 1; pattr.wakeup_events = 1; pattr.config = 1234;#kprodb 的 id pattr.size = sizeof(pattr); int efd = syscall(SYS_perf_event_open, &pattr, -1, 0, -1, 0); if (efd < 0) { printf("perf_event_open error: %s\n", strerror(errno)); exit(-1); } int ret = ioctl(efd, PERF_EVENT_IOC_ENABLE, 0); if (ret < 0) { printf("PERF_EVENT_IOC_ENABLE error: %s\n", strerror(errno)); exit(-1); } ret = ioctl(efd, PERF_EVENT_IOC_SET_BPF, pfd); if (ret < 0) { printf("PERF_EVENT_IOC_SET_BPF error: %s\n", strerror(errno)); exit(-1); } while(1); #不退出看结果 }
- 直接编译进内核
- libbpf
- linux bpf 内核锚点
- 从/sys/kernel/debug/tracing/events/目录下开始 tp/目录/目录…
- 可以一次跟踪父目录的所有锚点 如tp/目录
- 最后的文件描述了锚点的文本信息 enable filter format id trigger
- bpf_printk 打印到/sys/kernel/debug/tracing/下的pip
- kprobes 内核跟踪
- /sys/kernel/debug/kprobes/ blacklist enabled list
#添加一个调用do_sys_open函数前监控,打印参数 寄存器%ax、%dx、%cx 3个参数,堆栈$stack 从4个参数开始 echo 'p:namein do_sys_open dfd=%ax filename=%dx flags=%cx mode=+4($stack)' > /sys/kernel/debug/tracing/kprobe_events #添加一个调用do_sys_open返回值 $retval 返回值 echo 'r:nameout do_sys_open $retval' >> /sys/kernel/debug/tracing/kprobe_events #启用 echo 1 > /sys/kernel/debug/tracing/events/kprobes/namein/enable echo 1 > /sys/kernel/debug/tracing/events/kprobes/nameout/enable #输出 tail -f /sys/kernel/debug/tracing/trace #关闭监控 echo 0 > /sys/kernel/debug/tracing/events/kprobes/namein/enable echo 0 > /sys/kernel/debug/tracing/events/kprobes/nameout/enable echo > /sys/kernel/debug/tracing/kprobe_events | echo -:namein >> /sys/kernel/debug/tracing/kprobe_events echo > /sys/kernel/debug/tracing/kprobe_events | echo -:nameout >> /sys/kernel/debug/tracing/kprobe_events
```C #include<linux/init.h> #include<linux/module.h> #include<linux/kernel.h> #include <linux/kprobes.h>
//统计do_fork()总共执行了几次 static int total_count = 0;
//前置方法,这里可以拿到方法入参和栈,每次执行do_fork() total_count++ static int handler_pre(struct kprobe *p, struct pt_regs *regs) { total_count++; //printk 打印的日志 可以通过dmesg 命令查看 printk(KERN_INFO “累计调用do_fork[%d]次\n”,total_count); return 0; }
//后置方法,这里可以拿到方法返回值 static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags) { } //方法执行失败的回调函数 static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr) { printk(KERN_INFO “fault_handler: p->addr = 0x%p, trap #%dn”,p->addr, trapnr); return 0; } //通过kprobe这个数据结构,定义要hook的内核方法名称 static struct kprobe kp = { .symbol_name = “do_fork”, }; //通过register_kprobe 方法更改内核对应方法的指令 static int kprobe_init(void){ int ret; kp.pre_handler = handler_pre; kp.post_handler = handler_post; kp.fault_handler = handler_fault;
ret = register_kprobe(&kp); if (ret < 0) { printk(KERN_INFO "register_kprobe failed, returned %d\n", ret); return ret; } printk(KERN_INFO "Planted kprobe at %p\n", kp.addr); return 0; } //通过unregister_kprobe卸载hook static void kprobe_exit(void){ unregister_kprobe(&kp); printk(KERN_INFO "kprobe at %p unregistered\n", kp.addr); }
//构造内核模块 module_init(kprobe_init); module_exit(kprobe_exit); MODULE_LICENSE(“GPL”);
obj-m := xxxx.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -f *.mod.c *.ko *.o
su root insmod xxx.ko cat /var/log/messages 查看消息 lsmod | grep krpobe rm xxx.ko ```
- /sys/kernel/debug/kprobes/ blacklist enabled list
- uprobe 用户态追踪
objdump -d /root/test #调用前 打印参数 寄存器%ax、%dx、%cx 3个参数,堆栈$stack 从4个参数开始 echo 'p:namein /root/test:func_addr' >> /sys/kernel/debug/tracing/uprobe_events #返回时 $retval 返回值 echo 'r:nameout /root/test:func_addr' >> /sys/kernel/debug/tracing/uprobe_events run /root/test #启动监控 echo 1 > /sys/kernel/debug/tracing/events/enable echo 1 > /sys/kernel/debug/tracing/events/uprobes/namein/enable echo 1 > /sys/kernel/debug/tracing/events/uprobes/nameout/enable #显示信息 tail -f /sys/kernel/debug/tracing/trace #停止 echo 0 > /sys/kernel/debug/tracing/events/uprobes/namein/enable echo 0 > /sys/kernel/debug/tracing/events/uprobes/nameout/enable echo 0 > /sys/kernel/debug/tracing/events/enable echo -:namein >> /sys/kernel/debug/tracing/uprobe_events echo -:nameout >> /sys/kernel/debug/tracing/uprobe_events
ebpf
- 字节码相关
- 寄存器64位 无论cpu、内核是否是64位的
- r0 函数返回值
- r1-r5 函数参数, 数字或栈指针,内核代码只使用栈
- r6-r9 内核调用后保持不变
- r10 栈指针只读, 栈大小512字节
- 指令64位 最多4096条指令,5.1内核100万条,op:8,dreg:4,sreg:4,off:16,imm:32
- op code:4,source:1,class:3 source表明是寄存器还是立即数
- 尾调用 一个bpf程序结束调用另一个bpf, 最多33个调用嵌套
- map 主要用于bpf间传输数据,单个bpf可用64个map
- /proc/sys/net/core/bpf_jit_enable echo 1 »
- Netronome’s nfp driver supper jit Offloads to NIC network divce
- libbpf BPF CO-RE(Compile Once – Run Everywhere)
- 内核导出 BTF(BPF Type Format)类似DWARF调试信息
- 比老的内核设置CONFIG_DEBUG_INFO_BTF=y编译内核
- 新内核直接携带并暴露在/sys/kernel/btf/vmlinux
- bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
- clang __builtin_preserve_access_index 重定位信息
- 导出字段重定位信息(field offset relocation)
- 用户空间 libbpf加载器
- 加载 clang -march=bpf xxx.c 编译后的 BPF ELF
- 根据BTF clang重定位信息 裁剪信息进行 bpf字节码重定向和裁剪
- 内核导出 BTF(BPF Type Format)类似DWARF调试信息
- libbpf 代码移植写法
- 内核已经支持BPF_PROG_TYPE_TRACING
- pid_t pid = __builtin_preserve_access_index(({ task->pid; }));
- 内核不支持BPF_PROG_TYPE_TRACING
- bpf_core_read(&pid, sizeof(pid), &task->pid); ==== bpf_probe_read(&pid, sizeof(pid), __builtin_preserve_access_index(&task->pid));
- inode = BPF_CORE_READ(task, mm, exe_file, f_inode, i_ino);
- BPF_CORE_READ_INTO(&inode, task, mm, exe_file, f_inode, i_ino);
- 声明内核全局变量
- extern u32 LINUX_KERNEL_VERSION __kconfig;
- if (LINUX_KERNEL_VERSION >= KERNEL_VERSION(4, 11, 0)){}
- 结构体带版本
- struct thread_struct {} 最新版系统线程结构体
- struct thread_struct___v46 {} <= 4.6版本内核的结构体
- 使用时thread_struct,loader时会自动选择
- 使用全局静态变量
- const bool xxx;
- if (xxx) {}
- 用户空间控制程序在加载前修改 xxx;
- 其它宏
- BPF_CORE_READ_STR_INTO()
- bpf_core_read_str()
- bpf_core_field_exists() 判断字段是否存在,
- bpf_core_field_size() 判断字段大小
- BPF_CORE_READ_BITFIELD() 直接内存读取
- BPF_CORE_READ_BITFIELD_PROBED() 底层会调用 bpf_probe_read()
- 内核已经支持BPF_PROG_TYPE_TRACING
libbpf
#libbpf
git clone --recurse-submodules git@github.com:libbpf/libbpf.git
#高版本系统一般已经自带
yum -y install elfutils-libelf-devel zlib-devel
cd src
BUILD_STATIC_ONLY=y OBJDIR=/a/b PKG_CONFIG_PATH=/c/d DESTDIR=/e/f make install
#不编译只需头文件
cp libbpf/src/libbpf_version.h libbpf/src/libbpf_coommon.h libbpf/src/libbpf_legacy.h libbpf/src/libbpf.h ./bpf/
# bpftool
yum -y install elfutils-libelf-devel zlib-devel binutils-devel libcap-devel
git clone --recurse-submodules git@github.com:libbpf/bpftool.git
git submodule update --init
cd src
#OUTPUT/libbpf/include/bpf OUTPUT/libbpf/libbpf.a
mkdir -p /a/b
vi ./src/skeleton/pid_iter.bpf.c <vmlinux.h> -> "../libbpf/.github/actions/build-selftests/vmlinux.h"
OUTPUT=/a/b/ make install
android ebpf
- android 9.0以上
- 需root
- 可以使用libbpf