在CentOS 7.6中使用bpftrace打印函数调用栈

(欢迎大家留言交流讨论~)

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

Leave a Reply

Your email address will not be published. Required fields are marked *