先科普下 BCC

BCC 官方:

BCC is a toolkit for creating efficient kernel tracing and manipulation programs, and includes several useful tools and examples. It makes use of extended BPF (Berkeley Packet Filters), formally known as eBPF, a new feature that was first added to Linux 3.15. Much of what BCC uses requires Linux 4.1 and above.

BCC 利用 eBPF 跟踪和操纵内核, 里面有很多工具。

Wiki eBPF:

eBPF was built on top of the Berkeley Packet Filter (cBPF). At the lowest level, it introduced the use of ten 64-bit registers (instead of two 32-bit long registers for cBPF), different jump semantics, a call instruction and corresponding register passing convention, new instructions, and a different encoding for these instructions.[12] A number of additional features were subsequently added.

eBPF 就是 BPF 的扩展版。

Wiki BPF 描述:

BPF supports filtering packets, allowing a userspace process to supply a filter program that specifies which packets it wants to receive. For example, a tcpdump process may want to receive only packets that initiate a TCP connection. BPF returns only packets that pass the filter that the process supplies. This avoids copying unwanted packets from the operating system kernel to the process, greatly improving performance. The filter program is in the form of instructions for a virtual machine, which are interpreted, or compiled into machine code by a just-in-time (JIT) mechanism and executed, in the kernel.

BPF 就是跑在内核里的虚拟机,使用 JIT 编译机制,最早用来过滤报文的。

Wiki JIT 编译解释:

In computing, just-in-time (JIT) compilation (also dynamic translation or run-time compilations)1 is a way of executing computer code that involves compilation during execution of a program (at run time) rather than before execution

JIT 是运行时编译。

JIT compilation is a combination of the two traditional approaches to translation to machine code—ahead-of-time compilation (AOT), and interpretation—and combines some advantages and drawbacks of both.[2]

Wiki AOT 编译:

In computer science, ahead-of-time compilation (AOT compilation) is the act of compiling an (often) higher-level programming language into an (often) lower-level language before execution of a program, usually at build-time, to reduce the amount of work needed to be performed at run time.
AOT 编译是运行前编译 (like gcc)。

安装过程

在 Android 上运行 BCC 可以选择安装 adeb,参考 https://github.com/joelagnel/adeb/ (不过现在不维护了,此为后话)

按官方文档 adeb prepare –build –bcc 带 BCC 编译安装时有如下错误,主机是 Ubuntu 18.04,板子安卓11, 内核 4.14。

-- Detecting CXX compile features - done
error: could not lock config file /home/tj/.gitconfig: No such file or directory
CMake Warning at CMakeLists.txt:29 (message):
Failed to add root source directory to safe.directory

这个半天没发现怎么 fix,转变思路,尝试直接从 BCC 源码编译,不过有如下错误:

/home/tj/code/bcc/src/cc/bpf_module.cc: In member function ‘virtual void ebpf::MyMemoryManager::notifyObjectLoaded(llvm::ExecutionEngine*, const llvm::object::ObjectFile&)’:
/home/tj/code/bcc/src/cc/bpf_module.cc:121:46: error: no matching function for call to ‘llvm::object::SectionRef::getName() const’

是最新版本 BCC 不支持 llvm6 了,可以使用版本 v0.24.0,编译果然没问题(也没有上面那个不能lock的问题),那直接把 adeb build 脚本改成这样最直接:

diff --git a/buildstrap b/buildstrap
index bdbc554..a25e67c 100755
--- a/buildstrap
+++ b/buildstrap
@@ -61,7 +61,7 @@ echo "nameserver 4.2.2.2" > $OUT_TMP/etc/resolv.conf

# Clone BCC if needed
if [ $INSTALL_BCC -eq 1 ]; then
- git clone https://github.com/iovisor/bcc.git $TDIR/debian/bcc-master
+ git clone -b v0.24.0 https://github.com/iovisor/bcc.git $TDIR/debian/bcc-master
cp $spath/bcc/build-bcc.sh $TDIR/debian/bcc-master/
chroot $OUT_TMP /bcc-master/build-bcc.sh

或者自行 checkout,clean 下 if needed. 一路下来,没有问题,能看到 bcc tools 已经安装上了:

-- Installing: /usr/share/bcc/tools/javastat
-- Installing: /usr/share/bcc/tools/javathreads
-- Installing: /usr/share/bcc/tools/nodegc
-- Installing: /usr/share/bcc/tools/nodestat
-- Installing: /usr/share/bcc/tools/perlcalls
-- Installing: /usr/share/bcc/tools/perlflow

ok, 我们跑个工具看下:

root@localhost:/# opensnoop
Traceback (most recent call last):
File "/usr/share/bcc/tools/opensnoop", line 19, in <module>
from bcc import ArgString, BPF
ImportError: No module named bcc

少东西,默认安装的的 Debian buster,换个国内的源 (/etc/apt/sources.list),我用的 163:

deb http://mirrors.163.com/debian/ buster main non-free contrib
deb http://mirrors.163.com/debian/ buster-updates main non-free contrib
deb http://mirrors.163.com/debian/ buster-backports main non-free contrib
deb http://mirrors.163.com/debian-security/ buster/updates main non-free contrib

deb-src http://mirrors.163.com/debian/ buster main non-free contrib
deb-src http://mirrors.163.com/debian/ buster-updates main non-free contrib
deb-src http://mirrors.163.com/debian/ buster-backports main non-free contrib
deb-src http://mirrors.163.com/debian-security/ buster/updates main non-free contrib

Android 系统连上网络后 apt-get update 更新没错后安装:

apt-get install python-bpfcc

不过这个竟然有错误:

Err:1 http://mirrors.163.com/debian buster/main arm64 libbpfcc arm64 0.8.0-4
Temporary failure resolving 'mirrors.163.com'
Get:2 http://mirrors.163.com/debian buster/main arm64 python-bpfcc all 0.8.0-4 [29.4 kB]
Fetched 29.4 kB in 11s (2710 B/s)
E: Failed to fetch http://mirrors.163.com/debian/pool/main/b/bpfcc/libbpfcc_0.8.0-4_arm64.deb Temporary failure resolving 'mirrors.163.com'
E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?

按提示用下面这个修复就好了。

apt-get install python-bpfcc --fix-missing

最近 chatgpt 比较火,我也问了下,相比 web search,还是比较高效的。

chat-no-module-bcc.png

再次运行出错如下:

root@localhost:/# opensnoop
Traceback (most recent call last):
File "/usr/share/bcc/tools/opensnoop", line 19, in <module>
from bcc import ArgString, BPF
File "/usr/lib/python2.7/dist-packages/bcc/__init__.py", line 27, in <module>
from .libbcc import lib, bcc_symbol, bcc_symbol_option, _SYM_CB_TYPE
File "/usr/lib/python2.7/dist-packages/bcc/libbcc.py", line 20, in <module>
lib.bpf_module_create_b.restype = ct.c_void_p
File "/usr/lib/python2.7/ctypes/__init__.py", line 379, in __getattr__
func = self.__getitem__(name)
File "/usr/lib/python2.7/ctypes/__init__.py", line 384, in __getitem__
func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /usr/lib/aarch64-linux-gnu/libbcc.so.0: undefined symbol: bpf_module_create_b

是 python 默认版本 2.x 导致,切到 3.x:

root@localhost:/# update-alternatives --install /usr/bin/python python /usr/bin/python2 1
update-alternatives: using /usr/bin/python2 to provide /usr/bin/python (python) in auto mode
root@localhost:/# update-alternatives --install /usr/bin/python python /usr/bin/python3 2
update-alternatives: using /usr/bin/python3 to provide /usr/bin/python (python) in auto mode
root@localhost:/# update-alternatives --config python
There are 2 choices for the alternative python (providing /usr/bin/python).

Selection Path Priority Status
------------------------------------------------------------
* 0 /usr/bin/python3 2 auto mode
1 /usr/bin/python2 1 manual mode
2 /usr/bin/python3 2 manual mode

Press <enter> to keep the current choice[*], or type selection number: 2
root@localhost:/#
root@localhost:/# python
Python 3.7.3 (default, Jan 22 2021, 20:04:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> quit()

再运行还有错:

root@localhost:/# filetop
sh: modprobe: command not found
Unable to find kernel headers. Try rebuilding kernel with CONFIG_IKHEADERS=m (module) or installing the kernel development package for your running kernel version.
chdir(/lib/modules/4.14.xxx/build): No such file or directory
Traceback (most recent call last):
File "/usr/share/bcc/tools/filetop", line 164, in <module>
b = BPF(text=bpf_text)
File "/usr/lib/python3/dist-packages/bcc/__init__.py", line 475, in __init__
raise Exception("Failed to compile BPF module %s" % (src_file or "<text>"))
Exception: Failed to compile BPF module <text>

modeprobe 命令找不到,安装:

apt-get install kmod

再跑出错:

root@localhost:/# opensnoop
modprobe: ERROR: ../libkmod/libkmod.c:586 kmod_search_moddep() could not open moddep file '/lib/modules/4.14.xxx/modules.dep.bin'
modprobe: FATAL: Module kheaders not found in directory /lib/modules/4.14.xxx
Unable to find kernel headers. Try rebuilding kernel with CONFIG_IKHEADERS=m (module) or installing the kernel development package for your running kernel version.
chdir(/lib/modules/4.14.xxx/build): No such file or directory
Traceback (most recent call last):
File "/usr/share/bcc/tools/opensnoop", line 261, in <module>
b = BPF(text='')
File "/usr/lib/python3/dist-packages/bcc/__init__.py", line 475, in __init__
raise Exception("Failed to compile BPF module %s" % (src_file or "<text>"))
Exception: Failed to compile BPF module <text>

modprobe 找不到目录 /lib/modules/4.14.xxx,看提示我的板子内核已经使能 CONFIG_IKHEADERS, 而且 linux header package 按下面命令搜索也没有匹配的包:

apt-cache search linux-headers-

我们先来了解下 kernel header 相关修改:

commit f63868480dd3c52324d9bb8cdabb7c9950e5f751
Author: Joel Fernandes <joelaf@google.com>
Date: Wed Jul 10 22:52:30 2019 -0400

Remove all code todo with kernel headers or sources

BCC now can use headers from CONFIG_IKHEADERS so no need for adeb to
package them.

Signed-off-by: Joel Fernandes <joelaf@google.com>

adeb 的 –kernel-src 是在 BCC 支持 CONFIG_IKHEADERS 前使用的,当前情况:

host: adeb(no –kernel-src) + bcc 0.24.0 (支持CONFIG_IKHEADERS)
target: CONFIG_IKHEADERS 已经使能

bcc v0.24.0 使用 sysfs:

commit 0797d4acbde025fbaa36e3c26257e74fb62645b2
Author: Joel Fernandes (Google) <joel@joelfernandes.org>
Date: Wed Jun 12 15:54:14 2019 -0400

build: Rename kheaders location from proc to sys

In upstream kernel, the path has been renamed as the kheaders got moved
to sysfs. Let us update BCC with the new path.

Signed-off-by: Joel Fernandes (Google) <joel@joelfernandes.org>

因为 CONFIG_IKHEADERS=m 按模块编译,应该要加载这个模块,看看板子生成文件中确有 kheaders.ko,直接把这个 ko 推进板子上:

tj@u1804:~/adeb$ adeb push ~/kheaders.ko /data/
|--------------|
| adeb: v0.99h |
|--------------|
/home/tj/kheaders.ko: 1 file pushed. 26.1 MB/s (3279568 bytes in 0.120s)

然后 insmod 这个 ko:

root@localhost:/# insmod /data/kheaders.ko

确认:

root@localhost:/# ls /sys/kernel/kheaders.tar.xz
/sys/kernel/kheaders.tar.xz

再运行出错:

root@localhost:/# opensnoop
tar (child): xz: Cannot exec: No such file or directory
tar (child): Error is not recoverable: exiting now
tar: Child returned status 2
tar: Error is not recoverable: exiting now
Unable to find kernel headers. Try rebuilding kernel with CONFIG_IKHEADERS=m (module) or installing the kernel development package for your running kernel version.
chdir(/lib/modules/4.14.xxx/build): No such file or directory
Traceback (most recent call last):
File "/usr/share/bcc/tools/opensnoop", line 261, in <module>
b = BPF(text='')
File "/usr/lib/python3/dist-packages/bcc/__init__.py", line 475, in __init__
raise Exception("Failed to compile BPF module %s" % (src_file or "<text>"))
Exception: Failed to compile BPF module <text>

这个看起来比较明显,直接安装 xz 命令包:

apt-get install xz-utils

now, it’s working.

root@localhost:/# opensnoop
PID COMM FD ERR PATH
792 vmstat 4 0
792 vmstat 4 0
792 vmstat 4 0
792 vmstat 4 0
792 vmstat 4 0
792 vmstat 4 0
792 vmstat 4 0
792 vmstat 4 0

btw: 同样的,可以安装bpftrace, build such as:

cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang-7 -DCMAKE_CXX_COMPILER=clang++-7 -DBUILD_TESTING=OFF

参考文档