推荐一款轻量级 eBPF 前端工具 ply【转】

转自:https://tinylab.org/ply-intro/

推荐一款轻量级 eBPF 前端工具 ply

Sun Jianguo 创作于 2020/10/21

By Jianguo Sun of TinyLab.org Oct 11, 2020

1 Overview

ply 是 eBPF 的 front-end 前端工具之一,专为 embedded Linux systems 开发,采用 C 语言编写,只需 libc 和内核支持 BPF 就可以运行,不需要外部 kernel 模块,不需要 LLVM,不需要 python。

ply 由瑞典工程师 Tobias Waldekranz 开发,其项目主页是 PLY Light-weight Dynamic Tracer for Linux 

使用非常灵活和轻量级,编辑一种类 C 语言的脚本,然后利用内核 eBPF 来收集和探测内核数据,比如打印出内核函数的调用栈,获取内核变量等,是学习内核,进行嵌入式 Linux 系统开发调试的利器!

本文记录 ply 的编译及使用过程,实验例程源码都已上传到 https://github.com/jgsun/buildroot

2 内核配置

注意: 为顺利使用 ply,内核需要比较新的版本,并且支持 eBPF 和 FTRACE,选择如下配置:

  1. CONFIG_KPROBES=y
  2. CONFIG_HAVE_DYNAMIC_FTRACE=y
  3. CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y
  4. CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
  5. CONFIG_FTRACE=y
  6. CONFIG_DYNAMIC_FTRACE=y
  7. CONFIG_BPF=y
  8. CONFIG_BPF_SYSCALL=y

本文实验中所采用内核版本为 Linux v5.8.4。

3 ply 编译

3.1 交叉编译

请提前准备好交叉编译器,大部分 Linux 发行版都提供 ARM64 交叉编译器,直接用包管理工具安装即可,以 Ubuntu 为例:

  1. $ sudo apt-get install gcc-aarch64-linux-gnu

交叉编译步骤举例如下:

  1. $ git clone https://github.com/wkz/ply
  2. $ ./autogen.sh
  3. // 先安装到本地目录下
  4. $ ./configure --host=aarch64-linux-gnu --prefix=~/usr
  5. $ make
  6. $ make install

查看安装目录 ~/usr

  1. ~/usr$ tree
  2. .
  3. |-- include
  4. | `-- ply
  5. | |-- arch.h
  6. | |-- buffer.h
  7. | |-- func.h
  8. | |-- internal.h
  9. | |-- ir.h
  10. | |-- kallsyms.h
  11. | |-- node.h
  12. | |-- perf_event.h
  13. | |-- ply.h
  14. | |-- printxf.h
  15. | |-- provider.h
  16. | |-- sym.h
  17. | |-- syscall.h
  18. | |-- type.h
  19. | `-- utils.h
  20. |-- lib
  21. | |-- libply.a
  22. | |-- libply.la
  23. | |-- libply.so -> libply.so.0.0.0
  24. | |-- libply.so.0 -> libply.so.0.0.0
  25. | `-- libply.so.0.0.0
  26. |-- sbin
  27. | `-- ply
  28. `-- share
  29. `-- doc
  30. `-- ply
  31. |-- COPYING
  32. `-- README.md
  33. 7 directories, 23 files

将 lib 目录的库文件和 sbin 目录的可执行文件 ply 拷贝到 target 板卡的文件系统即可使用,例如:

主机:

  1. ~/usr$ scp -P 22 lib/* root@192.168.122.46:/lib
  2. ~/usr$ scp -P 22 sbin/ply root@192.168.122.46:~/bin

目标板卡:

  1. root@~/bin# ./ply -v
  2. ply 2.1.1-11-g6aabe5f (linux-version:267277~4.20.13)
  3. root@~/bin# uname -a
  4. Linux qemu-aarch64 5.8.4 #4 SMP Fri Oct 16 11:05:52 CST 2020 aarch64 GNU/Linux

3.2 使用 buildroot 编译

ply 项目采用 GNU’s autotools build 系统,非常容易集成到 buildroot,首先在 buildroot 添加 ply package,然后执行 make ply 编译即可。

可以从 buildroot/package/ply 下载,也可以参考下述 patch 修改:

  1. diff --git a/package/Config.in b/package/Config.in
  2. index cb6d8e0e01..7dd278242e 100644
  3. --- a/package/Config.in
  4. +++ b/package/Config.in
  5. @@ -2321,6 +2321,7 @@ menu "System tools"
  6. source "package/openrc/Config.in"
  7. source "package/openvmtools/Config.in"
  8. source "package/pamtester/Config.in"
  9. + source "package/ply/Config.in"
  10. source "package/polkit/Config.in"
  11. source "package/powerpc-utils/Config.in"
  12. source "package/procps-ng/Config.in"
  13. diff --git a/package/ply/.Config.in.swp b/package/ply/.Config.in.swp
  14. new file mode 100644
  15. index 0000000000..1193f698fb
  16. Binary files /dev/null and b/package/ply/.Config.in.swp differ
  17. diff --git a/package/ply/Config.in b/package/ply/Config.in
  18. new file mode 100644
  19. index 0000000000..258a59b6fd
  20. --- /dev/null
  21. +++ b/package/ply/Config.in
  22. @@ -0,0 +1,11 @@
  23. +config BR2_PACKAGE_PLY
  24. + bool "ply"
  25. + depends on BR2_x86_64 || BR2_aarch64 || BR2_arm || BR2_ppc # needs <cpuid.h>
  26. + depends on BR2_TOOLCHAIN_USES_UCLIBC || BR2_TOOLCHAIN_USES_GLIBC
  27. + help
  28. + ply dynamically instruments the running kernel to aggregate and
  29. + extract user-defined data. It compiles an input program to one or
  30. + more Linux bpf(2) binaries and attaches them to arbitrary points
  31. + in the kernel using kprobes and tracepoints.
  32. +
  33. + https://wkz.github.io/ply/
  34. diff --git a/package/ply/ply.mk b/package/ply/ply.mk
  35. new file mode 100644
  36. index 0000000000..298ea4acf2
  37. --- /dev/null
  38. +++ b/package/ply/ply.mk
  39. @@ -0,0 +1,15 @@
  40. +################################################################################
  41. +#
  42. +# ply
  43. +#
  44. +################################################################################
  45. +
  46. +PLY_VERSION = 2.1.1
  47. +PLY_SITE = https://github.com/wkz/ply/releases/download/$(PLY_VERSION)
  48. +# fetched from Github, with no configure script
  49. +PLY_AUTORECONF = YES
  50. +PLY_DEPENDENCIES = host-bison host-flex
  51. +PLY_LICENSE = GPL-2.0+
  52. +PLY_LICENSE_FILES = COPYING
  53. +
  54. +$(eval $(autotools-package))

4 ply 示例

网址 root/ply 有部分示例。clone 编译可直接使用:

  1. $ git clone https://github.com/jgsun/buildroot
  2. $ cd buildroot && make qemu_aarch64_virt-fun_defconfig && make
  3. $ qemu-system-aarch64 -M virt \
  4. -cpu cortex-a57 -nographic -smp 4 -m 512 \
  5. -kernel output/images/Image \
  6. -append "root=/dev/ram0 console=ttyAMA0 kmemleak=on loglevel=8" \
  7. -netdev type=tap,ifname=tap0,id=eth0,script=board/qemu/scripts/qemu-ifup_virbr0,queues=2 \
  8. -device virtio-net-pci,netdev=eth0,mac='00:00:00:01:00:01',vectors=6,mq=on

4.1 打印出内核函数的调用栈

本 ply 示例打印出函数 rtnetlink_rcv 的调用栈:

  1. root@~# cat netlink.ply
  2. #!/usr/bin/env ply
  3. kprobe:rtnetlink_rcv
  4. {
  5. print(stack);
  6. }

运行 sshnetlink.ply,然后在另外一个终端通过 ssh 登录 qemu aarch64 board,执行 ip addr show dev eth0 命令:

  1. $ ssh -p 22 root@192.168.122.46
  2. root@~# ip addr show dev eth0
  3. 3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
  4. link/ether 00:00:00:01:00:01 brd ff:ff:ff:ff:ff:ff
  5. inet 192.168.122.46/24 brd 192.168.122.255 scope global eth0
  6. valid_lft forever preferred_lft forever
  7. inet6 fe80::200:ff:fe01:1/64 scope link
  8. valid_lft forever preferred_lft forever

netlink.ply 就会打印出 rtnetlink_rcv 的调用栈:

  1. root@~# ./netlink.ply
  2. ply: active
  3. rtnetlink_rcv
  4. netlink_sendmsg+408
  5. ____sys_sendmsg+592
  6. ___sys_sendmsg+136
  7. __sys_sendmsg+112
  8. __arm64_sys_sendmsg+40
  9. el0_svc_common.constprop.3+144
  10. do_el0_svc+116
  11. el0_sync_handler+280
  12. el0_sync+320
  13. rtnetlink_rcv
  14. netlink_sendmsg+408
  15. ____sys_sendmsg+592
  16. ___sys_sendmsg+136
  17. __sys_sendmsg+112
  18. __arm64_sys_sendmsg+40
  19. el0_svc_common.constprop.3+144
  20. do_el0_svc+116
  21. el0_sync_handler+280
  22. el0_sync+320
  23. rtnetlink_rcv
  24. netlink_sendmsg+408
  25. __sys_sendto+224
  26. __arm64_sys_sendto+44
  27. el0_svc_common.constprop.3+144
  28. do_el0_svc+116
  29. el0_sync_handler+280
  30. el0_sync+320

4.2 获取打开文件的进程信息

本 ply 示例打印出发起系统调用 do_sys_open 打开文件的进程名,进程 pid 和文件名(第一个参数):

  1. #!/usr/bin/env ply
  2. kprobe:do_sys_open
  3. {
  4. printf("%v(%v): %s\n",
  5. comm, pid, str(arg1));
  6. }

运行结果:

  1. root@~/ply# ./opensnoop.ply
  2. ply: active
  3. dropbear ( 128): /dev/urandom
  4. dropbear ( 128): /proc/timer_list
  5. syslogd ( 60): /var/log/messages
  6. dropbear ( 128): /proc/interrupts
  7. dropbear ( 128): /proc/loadavg
  8. dropbear ( 128): /proc/sys/kernel/random/entropy_avail
  9. dropbear ( 128): /proc/net/netstat
  10. dropbear ( 128): /proc/net/dev
  11. dropbear ( 128): /proc/net/tcp
  12. dropbear ( 128): /proc/net/rt_cache

5 参考文档

posted @ 2022-05-24 16:13  Sky&Zhang  阅读(297)  评论(0编辑  收藏  举报