基于uprobe的调试调优浅析

uprobe与krobe对应,动态附加到用户态调用函数的切入点称为uprobe,相比如kprobe 内核函数的稳定性,uprobe 的函数由开发者定义。uprobe是用户态的探针,它和kprobe是相对应的,kprobe是内核态的探针。uprobe需要制定用户态探针在执行文件中的位置,插入探针的原理和kprobe类似。

下面是对uprobe使用方法的简单介绍。比如跟踪下面程序中的callee函数调用:

#include 

int callee(void)
{
    printf("hello world.\n");

    return 0;
}

int main(void)
{    
    callee();

    return 0;
}

Makefile

分别编译地址加载无关和地址相关两个版本的应用程序,分别抓取其PROBE结果。

all:
	gcc -O0 -no-pie -fno-pic -g main.c -o main-nopie
	gcc -O0 -g main.c -o main
clean:
	rm -fr main main-nopie

增加一个新的uprobe event,命令如下(在可执行文件main的0x63a偏移处增加一个uprobe探针),当callee函数被调用时,截获调用事件信息:

$ readelf -s main|grep callee
    64: 000000000000063a    23 FUNC    GLOBAL DEFAULT   14 callee
$ 
# echo 'p:callee /home/zlcao/uprobe/main:0x63a' > /sys/kernel/debug/tracing/uprobe_events

增加一个返回事件信息:

echo 'r:callee_ret /home/zlcao/uprobe/main:0x63a' >> /sys/kernel/debug/tracing/uprobe_events

之后使用如下命令查看注册的EVENT事件

cat /sys/kernel/debug/tracing/uprobe_events

定义以后,使能所有的events:

echo 1 > /sys/kernel/debug/tracing/events/uprobes/enable

之后执行测试用例,执行完毕后,通过如下命令查看探测记录。

cat /sys/kernel/debug/tracing/trace

可以看到,callee函数的调用和返回事件都被触发了,输出显示给我们uprobe被触发时:main-27731、程序PC 0x55f1b22cb63a,uretprobe被触发时:PC从函数入口0x55f1b22cb63a返回0x55f1b22cb65a。

之后,通过如下命令关闭探测。

echo 0 > /sys/kernel/debug/tracing/events/uprobes/enable

最后,使用如下命令清除掉所有注册的events。

echo > /sys/kernel/debug/tracing/uprobe_events

添加探测点的步骤比较麻烦,perf很贴心地添加了一键添加探测点的功能,只需要执行一个简单的命令即可:

# perf probe -x ./main-nopie callee
# perf probe -x ./main-nopie callee%return

之后使能探测,运行用例。

 echo 1 > /sys/kernel/debug/tracing/events/probe_main/enable

PIE和NO-PIE应用的区别

通过上面的例子可以看到,默认编译的PIE应用和非PIE应用都可以被事件机制探测调试,那么它们的区被是什么呢?通过下面几幅图可以看出,PIE应用的符号地址是相对的,加载地址是随机的,而非PIE应用的加载地址和运行地址完全是由应用符号表提供的,运行时加载器必须遵守,不能更改。


参考资料

Linux内核文档,Documentation/trace/uprobetracer.rst

结束

你可能感兴趣的:(uprobe,嵌入式系统,Linux,算法,c++,开发语言)