• libbpf-tools编译和使用步骤


    之前尝试了两种方法使用ePBF对内核数据进行提取和分析,第一个是bcc,具体信息见我的另一个blog:eBPF初体验_mozart1756的博客-CSDN博客

    再复习一下大致的步骤:

    首先,确认内核包含如下编译选项:

    1. CONFIG_BPF=y
    2. CONFIG_BPF_SYSCALL=y
    3. # [optional, for tc filters]
    4. CONFIG_NET_CLS_BPF=m
    5. # [optional, for tc actions]
    6. CONFIG_NET_ACT_BPF=m
    7. CONFIG_BPF_JIT=y
    8. # [for Linux kernel versions 4.1 through 4.6]
    9. CONFIG_HAVE_BPF_JIT=y
    10. # [for Linux kernel versions 4.7 and later]
    11. CONFIG_HAVE_EBPF_JIT=y
    12. # [optional, for kprobes]
    13. CONFIG_BPF_EVENTS=y
    14. # Need kernel headers through /sys/kernel/kheaders.tar.xz
    15. CONFIG_IKHEADERS=y

    这样保证内核是一个支持ebpf的内核,否则无论什么形式的ebpf程序,都无法在该内核上正常运行。

    第二,安装必要的编译工具,至少要安装clang和llvm,我目前安装的版本是clang-12,同时,bcc是在python下运行,还有安装python。

    最后,准备bcc的代码和运行环境:

    1. git clone https://github.com/iovisor/bcc.git
    2. mkdir bcc/build; cd bcc/build
    3. cmake ..
    4. make
    5. sudo make install
    6. cmake -DPYTHON_CMD=python3 .. # build python3 binding
    7. pushd src/python/
    8. make
    9. sudo make install
    10. popd

    最后,看一下运行的结果(以biolatency.py为例):

    1. sudo python3 biolatency.py
    2. Tracing block device I/O... Hit Ctrl-C to end.
    3. ^C
    4. usecs : count distribution
    5. 0 -> 1 : 0 | |
    6. 2 -> 3 : 0 | |
    7. 4 -> 7 : 0 | |
    8. 8 -> 15 : 0 | |
    9. 16 -> 31 : 0 | |
    10. 32 -> 63 : 3 |** |
    11. 64 -> 127 : 15 |*********** |
    12. 128 -> 255 : 50 |************************************* |
    13. 256 -> 511 : 54 |****************************************|
    14. 512 -> 1023 : 49 |************************************ |
    15. 1024 -> 2047 : 3 |** |
    16. 2048 -> 4095 : 4 |** |
    17. 4096 -> 8191 : 33 |************************ |
    18. 8192 -> 16383 : 2 |* |

    bcc的有点是操作简单,容易上手,而且,由于bcc是在待测的内核上编译和运行,所以没有内核跨版本的兼容性问题。缺点也是由于由于bcc是在待测的内核上编译和运行,会消耗很多的系统资源,在一些场景下并不适用。

    我对ebpf的第二个尝试是适用C代码直接在内核代码树中编写ebpf程序,代码的位置在内核代码的sample/bpf目录下,你需要把你的代码放到这个目录下,每个工具应该包括两个c文件,一个运行在内核空间,另一个运行在用户空间,然后在Makefile中添加相应的信息,最后运行

    make M=sample/bpf编译即可。

    我做了5个实时内核相关的小工具,包括:

    • 中断响应时间
    • 任务抢占和切换时间
    • 死锁解除时间
    • 信号混洗时间
    • 消息传递时间

    这种方式的好处是比较简单,对于C语言比较友好,且仅需编译一次,运行时资源消耗较少。缺点是兼容性不好,内核版本更换之后,工具无法正常使用,需要移植和适配。

    既然说到了兼容性问题,那内核的兼容性问题是什么的?ebpf工具大部分是依赖于内核的数据结构的,比如我从task_struct这个结构中读取pid这个字段,在4.19内核中,这个字段在task_struct offset 8 地址,在5.4内核,也许会由于版本的演进,前面多了16个自己的字段,那么pid就变成task_struct offset 24(这是个例子,pid在这两个版本之间并没有变换位置)。或者,某些字段的名字变了,比如thread_structfs 字段(获取 thread-local storage 用), 在 4.6 到 4.7 内核升级时就被重命名为了 fsbase

    这些问题,都导致了ebpf的兼容性和移植性的问题。

    目前社区致力于使用BTF和libbpf来达到CO-RE(compile once -- run everywhere)的效果,其中BTF在内核编译的时候记录了内核的调试信息,比如内核的结构,字段,偏移量等信息,同时,这些信息内核会输出到/sys/kernel/btf/vmlinux中,clang在编译ebpf的程序时,会对ebpf程序所使用的内核结构字段做一个记录,比如如果想访问 task_struct->pid,那clang 将做如下记录:这是一个位于结构体 struct task_struct 中、类型为 pid_t、名为 pid 的字段。libbpf会查看 ebpf 程序记录的 BTF 的重定位信息,对ebpf中使用内核数据的部分进行地址的重定位。这个就是CO-RE解决兼容和移植问题的方法。具体的细节描述见下面两篇blog:

    BPF 可移植性和 CO-RE(一次编译,到处运行)_米开朗基杨的博客-CSDN博客

    BPF BTF 详细介绍_dwh0403的博客-CSDN博客_bpf是什么文件格式

    在bcc中,由一个目录为libbpf-tools,就是bcc使用CO-RE来使用C语言编写可移植的ebpf程序,我对此做了一些尝试:

    1. 1,获取最新的bcc代码
    2. git clone https://github.com/iovisor/bcc.git
    3. 2,在bcc中获取libbpf和bpftool的代码
    4. git submodule update --init --recursive
    5. 此时你再查看bcc/libbpf-tools/bpftool和bcc/src/cc/libbpf中也已经下载好了需要使用的源码。
    6. 回到bcc/libbpf-tools,运行make进行编译。

    此时,我遇到了一个编译错误,没有函数__builtin_preserve_type_info的定义。

    查阅了一些资料,找到__builtin_preserve_type_info是一个clang提供的builtin函数,需要至少clang12及以上的版本,我检查了一下我使用设备的clang的版本,发现我安装了两个clang,一个是clang10,另一个是clang-12,编译时使用的是clang10,所以没有__builtin_preserve_type_info的定义。

    使用如下命令切换clang的版本:

    sudo update-alternatives  --install /usr/bin/clang clang /usr/bin/clang-12 100 --slave  /usr/bin/clang++ clang++ /usr/bin/clang++-12

    编译成功,运行其中一个工具试一下,发现如下错误:

    1. libbpf: failed to find valid kernel BTF
    2. libbpf: Error loading vmlinux BTF: -3
    3. libbpf: failed to load object 'wakeuptime_bpf'
    4. libbpf: failed to load BPF skeleton 'wakeuptime_bpf': -3
    5. failed to load BPF object: -3

    这个是由于当前的内核在编译的时候没有支持BTF,所以需要重新配置和编译内核:

    1. make menuconfig
    2. 选中kernel hacking --> Compile-time checks and compiler option -->Generate BTF typeinfo后,打开了编译开关CONFIG_DEBUG_INFO_BTF。

    然后重新编译内核,又遇到了问题:

    1. LD .tmp_vmlinux.btf
    2. BTF .btf.vmlinux.bin.o
    3. Killed
    4. LD .tmp_vmlinux.kallsyms1
    5. KSYMS .tmp_vmlinux.kallsyms1.S
    6. AS .tmp_vmlinux.kallsyms1.S
    7. LD .tmp_vmlinux.kallsyms2
    8. KSYMS .tmp_vmlinux.kallsyms2.S
    9. AS .tmp_vmlinux.kallsyms2.S
    10. LD vmlinux
    11. BTFIDS vmlinux
    12. FAILED: load BTF from vmlinux: No such file or directory
    13. make: *** [Makefile:1169:vmlinux] 错误 255
    14. make: *** 正在删除文件“vmlinux”

    不能正常的生成带有BTF信息的内核,这个问题困扰了我很久,百度也查询不到,后来我使用bing,查到了一些信息,生成BTF信息所使用的工具pahole有可能在使用中出了一些故障,我通过命令“dmesg|grep pahole”查了一下,发现它被oom killer杀掉了,而且根据dmesg信息,pahole确实消耗了很多的物理内存。

    我关掉了一些正在使用的应用,重新编译,通过。

    或者也可以使用如下命令,在编译内核时出现信息“BTF     .btf.vmlinux.bin.o”的时候,用如下命令提高pahole的oom score。

    1. sudo su
    2. ps -aux|grep pahole
    3. echo -17 >/proc/pahole_pid/oom_adj

    最后终于编译成功了,安装,重启,使用新的内核再运行一下libbpf-tools中的工具:

    1. sudo ./softirqs
    2. [sudo] kylin 的密码:
    3. Tracing soft irq event time... Hit Ctrl-C to end.
    4. ^C
    5. SOFTIRQ TOTAL_usecs
    6. hi 505
    7. timer 4425
    8. net_rx 1021
    9. tasklet 36
    10. sched 18034
    11. rcu 3407

    成功。

  • 相关阅读:
    Windows 修改系统默认字体
    人工神经网络
    【云原生】Kubernetes----Metrics-Server组件与HPA资源
    C++ builder 常见问题汇总
    OptiMode应用矢量有限元法模拟表面等离子体激元
    【云原生】设备入云之FlexManager实际项目操作流程
    RWLinno的退役贴
    【LeetCode】50. Pow(x, n)
    使用MASA Stack+.Net 从零开始搭建IoT平台 第五章 使用时序库存储上行数据
    聊聊logback的MarkerFilter
  • 原文地址:https://blog.csdn.net/chensong_2000/article/details/126927681