(欢迎大家留言交流讨论~)
bpftrace
bpftrace可以理解为eBPF的高层次“封装”,利用LLVM将bpftrace的脚本或命令转为BPF字节码,并且利用了linux kernel 4.x 现有的kprobes/uprobes/tracepoint等机制。其设计参考了awk、c语言、dtrace和systemtap等。
bpftrace功能强大,本文给出一种实时打印某用户态进程某函数调用栈的方法。
安装bpftrace
在CentOS下,CentOS 7.6下bpftrace的官方文档并未给出安装方案,经过测试,我发现最现实的方法是借助snap安装[1],步骤如下:
sudo yum install epel-release
sudo yum install snapd
sudo systemctl enable --now snapd.socket
sudo ln -s /var/lib/snapd/snap /snap
sudo snap install --devmode bpftrace
sudo snap connect bpftrace:system-trace
使用bpftrace追踪用户态调用栈
关于基本的使用,[2][3]是bpftrace的官方例子,[2]中有很多实用”一行工具”中有很多例子,[3]中有更详细的参考文档。这些都是上手尝试的最好读物。
本文用到的是uprobe用户态追踪功能的ustack函数。比如我们要追踪/home/zjc/hello_world
这个二进制程序中的hello()
函数的调用栈,只需要写一行:
sudo bpftrace -e 'uprobe:/home/zjc/hello_world:hello {printf("%s\n", ustack());}'
为了更方便使用,我这里写了一个shell脚本注释如下[4]:
#!/bin/bash
# 将你要追踪的二进制程序路径写到这:
BIN_PATH=##HERE IS YOUR PROGRAM PATH
# 如果上边不写,脚本运行时也会问你:
if [ ! $BIN_PATH ]
then
read -p "Please tell me the program filepath: " BIN_PATH
fi
# 如输入-h,打印脚本用法(即可以输入多个要打印调用栈的函数)
if [ $1 = "-h" ]
then
echo "$0 function1 [function2] [function3] ..."
exit 0
fi
# 每个函数的追踪需求合成一个命令
CMD="sudo bpftrace -e '"
for i
do
CMD=$CMD" uprobe:${BIN_PATH}:${i} {printf(\"Traced:\n\t${i}\nStack:\n%s\n\", ustack());} "
done
CMD=$CMD"'"
# 打印一下要执行的命令
echo $CMD
# 执行这个命令
eval $CMD
[1] https://snapcraft.io/install/bpftrace/centos
[2] https://github.com/iovisor/bpftrace/blob/master/docs/tutorial_one_liners.md
[3] https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md
[4] https://gist.github.com/zhangjaycee/aaffb2ab5a8412d1c6ba6b6dc9d3bb19
[5] https://github.com/zhangjaycee/real_tech/wiki/linux_017