0%

使用yocto为arm平台编译bpftrace

bpftrace最新发布的0.17.0加入了对arm平台的支持,所以就想为arm平台编译一版bpftrace。值得注意的是,bpftrace才加入对arm 32位的支持(#2360#2361),由于之前一直是基于64位系统,所以现在工作的还不是很好,还有一些bug。

yocto环境准备

bpftrace相关的bb在meta-clang。同时我想使用一个真实arm平台的BSP,而不是yocto提供的虚拟机qemuarm,所以我还加入了meta-raspberrypi。考虑到需要克隆很多仓库,所以借助repo工具。

1
2
3
4
5
6
7
mkdir bpftrace-arm
cd bpftrace-arm
repo init -u https://gitlab.com/pkemb/yocto-manifest.git -b master
repo sync
repo start master --all
export TEMPLATECONF=${PWD}/meta/meta-pkemb/conf/templates/bpftrace-arm
source meta/poky/oe-init-build-env

说明:

  • pkemb/yocto-manifest默认使用内网的镜像地址。
  • 环境变量TEMPLATECONF用于指定示例配置文件的位置,详细说明参考yocto文档
  • repo sync默认会从Google服务器下载最新版的repo工具。由于一些原因,国内无法下载。可以设置环境变量export REPO_URL=https://mirrors.tuna.tsinghua.edu.cn/git/git-repo,从清华源下载。
  • 现在(2023年2月12日)meta-clang中的bpftrace版本是0.16.0,0.16.0 ~ 0.17.0有一些关于32位的修改,这些补丁在meta-pkemb中有加入。

修改bb文件

修改bpftrace及其相关依赖的bb文件,在COMPATIBLE_HOST变量中加入arm

meta/meta-clang

bcc_0.26.0.bbbpftrace_0.16.0.bb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
diff --git a/dynamic-layers/openembedded-layer/recipes-devtools/bcc/bcc_0.26.0.bb b/dynamic-layers/openembedded-layer/recipes-devtools/bcc/bcc_0.26.0.bb
index 36c6192..23009c6 100644
--- a/dynamic-layers/openembedded-layer/recipes-devtools/bcc/bcc_0.26.0.bb
+++ b/dynamic-layers/openembedded-layer/recipes-devtools/bcc/bcc_0.26.0.bb
@@ -68,4 +68,4 @@ do_install_ptest() {
FILES:${PN} += "${PYTHON_SITEPACKAGES_DIR}"
FILES:${PN}-doc += "${datadir}/${PN}/man"

-COMPATIBLE_HOST = "(x86_64.*|aarch64.*|powerpc64.*|riscv64.*)-linux"
+COMPATIBLE_HOST = "(x86_64.*|aarch64.*|arm.*|powerpc64.*|riscv64.*)-linux"
diff --git a/dynamic-layers/openembedded-layer/recipes-devtools/bpftrace/bpftrace_0.16.0.bb b/dynamic-layers/openembedded-layer/recipes-devtools/bpftrace/bpftrace_0.16.0.bb
index ca324bc..19e8c64 100644
--- a/dynamic-layers/openembedded-layer/recipes-devtools/bpftrace/bpftrace_0.16.0.bb
+++ b/dynamic-layers/openembedded-layer/recipes-devtools/bpftrace/bpftrace_0.16.0.bb
@@ -54,5 +54,5 @@ EXTRA_OECMAKE = " \
-DENABLE_MAN=OFF \
"

-COMPATIBLE_HOST = "(x86_64.*|aarch64.*|powerpc64.*|riscv64.*)-linux"
+COMPATIBLE_HOST = "(x86_64.*|aarch64.*|arm.*|powerpc64.*|riscv64.*)-linux"
COMPATIBLE_HOST:libc-musl = "null"

meta/meta-openembedded

libbpf_0.8.0.bb

1
2
3
4
5
6
7
8
9
10
11
12
diff --git a/meta-oe/recipes-kernel/libbpf/libbpf_0.8.0.bb b/meta-oe/recipes-kernel/libbpf/libbpf_0.8.0.bb
index 3aea7c079..909478be3 100644
--- a/meta-oe/recipes-kernel/libbpf/libbpf_0.8.0.bb
+++ b/meta-oe/recipes-kernel/libbpf/libbpf_0.8.0.bb
@@ -12,7 +12,7 @@ SRC_URI = "git://github.com/libbpf/libbpf.git;protocol=https;branch=master"
SRCREV = "86eb09863c1c0177e99c2c703092042d3cdba910"

PACKAGE_ARCH = "${MACHINE_ARCH}"
-COMPATIBLE_HOST = "(x86_64|i.86|aarch64|riscv64|powerpc64).*-linux"
+COMPATIBLE_HOST = "(x86_64|i.86|aarch64|arm|riscv64|powerpc64).*-linux"

S = "${WORKDIR}/git/src"

启动编译

yocto是在编译前通过do_fetch任务下载bb指定的源代码,下载很有可能会失败。所以建议把所有的fetch任务跑完之后,再开始编译。建议晚上睡觉前编译,因为clang需要编译非常非常非常久。

1
2
3
4
# 下载所有的源代码
bitbake bpftrace --runall=fetch
# 开始编译
bitbake bpftrace

编译成功之后,bpftrace可执行文件在 build/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/bpftrace/0.16.0+gita277ec42102c463d656df8f64eb2f7e87e322210-r0/package/usr/bin

运行bpftrace

yocto编译时所使用的glibc版本,与平台里的glibc版本可能不一致,所以bpftrace是无法直接运行的。为了将bpftrace跑起来,需要一些技巧,这里提供两个参考的方法。

复制文件到arm平台

为了缩短路径长度,以下相对路径均基于bpftrace的工作目录build/tmp/work/cortexa7t2hf-neon-vfpv4-poky-linux-gnueabi/bpftrace/0.16.0+gita277ec42102c463d656df8f64eb2f7e87e322210-r0/

  1. 在编译主机打包recipe-sysroot目录,复制到arm平台并解压。假设是放在arm平台的/root/bpf/recipe-sysroot目录。
  2. 将编译主机的文件package/usr/bin/bpftrace复制到arm平台的/root/bpf/recipe-sysroot/usr/bin目录。

方法一:chroot

通过chroot命令,为bpftrace打造一个专属的rootfs,包含bpftrace对应的glibc,以及依赖的库和头文件。

编写wrapper脚本/usr/bin/bpftrace_chroot.sh,内容如下。记得给脚本加可执行权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/sh

SYSROOT="/root/bpf/recipe-sysroot"

mkdir -p $SYSROOT/proc $SYSROOT/sys

if ! mount | grep -q $SYSROOT/proc; then
mount --bind /proc $SYSROOT/proc
fi

if ! mount | grep -q $SYSROOT/sys; then
mount --bind /sys $SYSROOT/sys
fi

if ! mount | grep -q $SYSROOT/sys/kernel/debug; then
mount --bind /sys/kernel/debug $SYSROOT/sys/kernel/debug
fi

chroot /root/bpf/recipe-sysroot /usr/bin/bpftrace "$@"

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@raspberrypi:~# bpftrace_chroot.sh -l '*sleep*'
hardware:*sleep*:
kprobe:__rpc_sleep_on_priority
kprobe:alarm_timer_nsleep
kprobe:alarm_timer_nsleep_restart
kprobe:alarmtimer_do_nsleep
kprobe:alarmtimer_nsleep_wakeup
kprobe:brcmf_sdio_bus_sleep
kprobe:brcmf_sdio_sleep
kprobe:common_nsleep
kprobe:do_cpu_nanosleep
kprobe:do_nanosleep
kprobe:dwc_otg_get_lpm_portsleepstatus
kprobe:fscache_object_sleep_till_congested
kprobe:gpiod_cansleep
......

方法二:patchelf

首先使用patchelf修改bpftrace的解释器,然后修改LD_LIBRARY_PATH。具体步骤如下。

1
2
3
4
cd /root/bpftrace/recipe-sysroot/usr/bin
# 备份一下
cp bpftrace bpftrace_patchelf
patchelf --set-interpreter /root/bpf/recipe-sysroot/lib/ld-linux-armhf.so.3 ./bpftrace_patchelf

编写wrapper脚本/usr/bin/bpftrace_patchelf.sh,内容如下。记得给脚本加可执行权限。

1
2
3
4
5
#!/bin/bash

export LD_LIBRARY_PATH=/root/bpf/recipe-sysroot/lib:/root/bpf/recipe-sysroot/usr/lib

/root/bpftrace/recipe-sysroot/usr/bin/bpftrace_patchelf "$@"

测试:

1
2
3
4
5
6
7
8
9
10
11
12
root@raspberrypi:~# bpftrace_patchelf.sh -l '*sleep*'
hardware:*sleep*:
kprobe:__rpc_sleep_on_priority
kprobe:alarm_timer_nsleep
kprobe:alarm_timer_nsleep_restart
kprobe:alarmtimer_do_nsleep
kprobe:alarmtimer_nsleep_wakeup
kprobe:brcmf_sdio_bus_sleep
kprobe:brcmf_sdio_sleep
kprobe:common_nsleep
kprobe:do_cpu_nanosleep
......

已知问题

无法获取到正确的数值型数据

如下面的命令,pid是一个非常大的值,系统中并没有这个进程。tidnsecs等参数也是类似的。

1
2
3
4
5
6
7
8
9
root@raspberrypi:~# bpftrace_patchelf.sh -e 'kprobe:do_nanosleep { printf("PID %s(%d) sleeping...\n", comm, pid); }'
sh: relocation error: /root/bpf/recipe-sysroot/lib/libc.so.6: symbol __nptl_set_robust_list_avail version GLIBC_PRIVATE not defined in file ld-linux-armhf.so.3 with link time reference
Attaching 1 probe...
PID sleep(2128712024) sleeping...
PID sleep(2128712024) sleeping...
PID sleep(2128712024) sleeping...
PID cron(2128712024) sleeping...
PID sleep(2128712024) sleeping...
PID sleep(2128712024) sleeping...

参考