uftrace
概述
uftrace 工具可用来跟踪和分析用 C/C++ 编写的程序。它深受 Linux 内核的 ftrace 框架(尤其是函数图跟踪器)的启发,并支持用户空间程序。它支持各种命令和过滤器来帮助分析程序的执行和性能。它的特性如下:
- 它能跟踪可执行文件中的每个函数并显示持续时间,它也可以跟踪外部库调用(入口和出口),通常也支持外部库嵌套的外部库或者内部函数调用。
- 它可以在功能级别显示详细的执行流程,并报告哪个函数的开销最高,它还显示了与执行环境相关的各种信息。
- 可以设置过滤器,以便在跟踪时排除或包含特定函数。此外,它还可以保存和显示函数参数和返回值。
- 它支持多进程和多线程应用程序。
- 如果内核有启用函数图跟踪器(CONFIG_FUNCTION_GRAPH_TRACER=y),那么它还可以跟踪内核函数(使用 -k 选项)。
实践
toolchain:gcc-9.1.0-2019.11-x86_64_arm-linux-gnueabihf
下code
git clone https://github.com/namhyung/uftrace.git
编译
./configure --prefix=$PWD/output --host=arm-linux-gnueabihf
make -j5
make install
生成布局为:
运行
环境准备
- 板子挂载共享目录
ifconfig eth0 up
ifconfig eth0 xxx.xxx.xxx.xxx
mount -t nfs xxx.xxx.xxx.xxx:/xxx /mnt -o nolock
- 将生成的 output/* 拷贝到共享目录的对应位置,板子上布局如下:
- 编译二进制程序要带上 -pg 或者 -finstrument-functions,本文全程以下面的程序为例子:
//multi_thread.c
#include
#include
void *thread_funcA(void *arg)
{
printf("%s:%d\n", __FUNCTION__, __LINE__);
sleep(5);
printf("%s:%d\n", __FUNCTION__, __LINE__);
return NULL;
}
void *thread_funcB(void *arg)
{
printf("%s:%d\n", __FUNCTION__, __LINE__);
sleep(10);
printf("%s:%d\n", __FUNCTION__, __LINE__);
return NULL;
}
int main(void)
{
pthread_t thidA;
pthread_t thidB;
printf("%s:%d\n", __FUNCTION__, __LINE__);
pthread_create(&thidA, NULL, thread_funcA, "funcA");
printf("%s:%d\n", __FUNCTION__, __LINE__);
pthread_create(&thidB, NULL, thread_funcB, "funcB");
printf("%s:%d\n", __FUNCTION__, __LINE__);
pthread_join(thidA, NULL);
printf("%s:%d\n", __FUNCTION__, __LINE__);
pthread_join(thidB, NULL);
printf("%s:%d\n", __FUNCTION__, __LINE__);
return 0;
}
编译如下:
arm-linux-gnueabihf-gcc -pg multi_thread.c -lpthread -o multi_thread
- 将生成的 multi_thread 放到共享目录里
- 需要注意的是,uftrace 会在当前目录创建 fifo/utrace.data,如果是在 windows 共享目录下,会提示如下出错:
建议切到板子上的 /tmp 目录: cd /tmp
- 最后再设置一下环境变量和动态库路径就可以愉快玩耍了:
export PATH=/mnt/uftrace/bin:$PATH
export LD_LIBRARY_PATH=/mnt/uftrace/lib:$LD_LIBRARY_PATH
basic
目标程序执行完后,uftrace就会输出每个函数的执行时间。
uftrace /mnt/multi_thread
输出如下:
filters
- -F FUNC, --filter=FUNC
将过滤器设置为仅跟踪所选函数。
uftrace -F sleep -F printf /mnt/multi_thread
输出如下:
- -C FUNC, --caller-filter=FUNC
只显示所选函数的调用路径。
uftrace -C sleep -C printf /mnt/multi_thread
输出如下:
- -N FUNC, --notrace=FUNC
设置过滤器不跟踪所选函数
uftrace -N printf /mnt/multi_thread
输出如下:
- -t TIME, --time-filter=TIME
不显示设定时间阈值以下的短小函数。
uftrace -t 1ms /mnt/multi_thread
Arguments Detection with Debug Info
使用 -a/--auto-args 去侦测带调试信息的二进制程序的函数参数和返回值。
uftrace -a /mnt/multi_thread
输出如下:
uftrace record
可以记录下 trace data,便于后续分析。
uftrace record /mnt/multi_thread
生成如下:
uftrace replay
可以回放对应的trace data,默认是回放当前目录的 uftrace.data。
uftrace replay
uftrace report
打印 trace data 的统计信息和摘要,默认是打印当前目录的 uftrace.data。
- basic
uftrace report
输出如下:
- -s
该选项可选择按 total time/self time/calls 进行降序排序,默认按 total 降序排序。
- 按 total time 降序排序
uftrace report -s total
输出如下:
- 按 self time 降序排序
uftrace report -s self
- 按 calls 降序排序
uftrace report -s call
uftrace graph
显示函数调用图,默认是显示当前目录的 uftrace.data。
uftrace graph
输出如下:
总结
uftrace还有很多强大的特性,比如通过 dump 命令生成相应 trace data,可拿到 chrome 浏览器可视化展示或者生成火焰图;如果 configure 包含了 python,还可以编写 python 脚本定义函数入口和出口的钩子函数;如果编译加上 -mnop-mcount,还可以实现运行时动态跟踪;如果 configure 包含了 capstone,那可以实现完全动态跟踪,而不用编译器支持。因此 uftrace 还是值得尝试一下。