ftrace、perf、bcc、bpftrace、ply、simple_perf如何灵活运用?
摘要:目录参考Ftrace经典用法function_graphtrace_event时间延迟标志histtrace_optionperf-toolstrace-cmd参考事件列举函数图示跟踪函数跟踪事件跟踪远程kernelsharkkprobeu
目录参考Ftrace经典用法function_graphtrace_event时间延迟标志histtrace_optionperf-toolstrace-cmd参考事件列举函数图示跟踪函数跟踪事件跟踪远程kernelsharkkprobeuprobeeBPFbpftraceplyBCCsimpleperfperf参考perf_event_open学习火焰图perf statperf kvmperf traceperf ftraceperf probe的使用perf sched使用getdelayssurftraceSystemtapstapbpfDtraceuftraceutraceSystrace/PerfettoLTTngTrace CompassB站某UP主写的profile工具,可以学习一下
参考
https://www.brendangregg.com
Linux Performance
https://github.com/iovisor/bcc/blob/master/docs/tutorial.md
https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md
采用了eBPF技术的项目
Linux Performance框图
影响程序性能的几个关键因素
https://blog.csdn.net/rikeyone/category_10317059.html
linux内核调试追踪技术20讲
Dive into BPF: a list of reading material
perf性能分析工具使用分享
深入字节版atop: 线上系统的性能监控实践
Linux tracing/profiling 基础:符号表、调用栈、perf/bpftrace 示例等(2022)
Linux tracing systems & how they fit together
https://jvns.ca/#linux-debugging---tracing-tools
trace专栏
boot time tracing 和 bootconfig
Ftrace
参考
https://www.kernel.org/doc/html/latest/trace/ftrace.html#ftrace-function-tracer
https://www.kernel.org/doc/html/latest/trace/ftrace.html
Linux内核 eBPF基础:ftrace源码分析:过滤函数和开启追踪
Linux内核 eBPF基础:ftrace基础-ftrace_init初始化
Linux启动时追踪
Debugging the kernel using Ftrace - part 1
Debugging the kernel using Ftrace - part 2
Secrets of the Ftrace function tracer
openEuler kernel技术分享-第13期-ftrace框架及指令修改机制
ftrace源码实现 —— 跟踪器的实现
经典用法
function_graph
获取某个进程调用sys_open的调用栈
运行要trace的程序,然后在调用open之前挺住,接着执行下面的命令,最后接着执行程序
echo function_graph > /sys/kernel/debug/tracing/current_tracer
echo *sys_open > /sys/kernel/debug/tracing/set_graph_function
#echo 1 > /sys/kernel/debug/tracing/options/funcgraph-tail
echo <pid> > /sys/kernel/debug/tracing/set_ftrace_pid
echo 0 > /sys/kernel/debug/tracing/tracing_on
echo > /sys/kernel/debug/tracing/trace
echo 3 > /proc/sys/vm/drop_caches
echo 1 > /sys/kernel/debug/tracing/tracing_on
然后执行下面的命令导出trace:
echo 0 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace > trace.log
对于function_graph抓到的调用栈,可以使用内核提供的vim插件来阅读:Documentation/trace/function-graph-fold.vim
用法: vim trace.log -S Documentation/trace/function-graph-fold.vim
查看不同进程在某个函数上消耗的时间
比如以rtnl_lock为例:
echo rtnl_lock > set_graph_function
echo rtnl_trylock >> set_graph_function
# 设置过滤
echo rtnl_lock > set_ftrace_filter
echo rtnl_trylock >> set_ftrace_filter
# 输出调用者的信息
echo 1 > options/funcgraph-proc # 或者 echo funcgraph-proc > trace_options
# 输出延迟标记(+ ! # * @ $)
echo 1 > options/funcgraph-overhead # 或者 echo funcgraph-overhead > trace_options
# 设置tracer
echo function_graph > current_tracer
# 启动
echo 1 > tracing_on
下面是输出:
root@ubuntu:/sys/kernel/debug/tracing# cat trace
# tracer: function_graph
#
# CPU TASK/PID DURATION FUNCTION CALLS
# | | | | | | | | |
4) kworker-3574 | + 53.651 us | rtnl_lock();
4) kworker-3574 | + 11.752 us | rtnl_lock();
4) kworker-3574 | 3.186 us | rtnl_lock();
4) kworker-3574 | 5.460 us | rtnl_lock();
4) kworker-3574 | 5.090 us | rtnl_lock();
4) kworker-3574 | 2.755 us | rtnl_lock();
4) kworker-3574 | 3.246 us | rtnl_lock();
4) kworker-3574 | 6.502 us | rtnl_lock();
4) kworker-3574 | 3.016 us | rtnl_lock();
4) kworker-3574 | 2.936 us | rtnl_lock();
4) kworker-3574 | 2.455 us | rtnl_lock();
4) kworker-3574 | + 42.089 us | rtnl_lock();
4) kworker-3574 | 2.495 us | rtnl_lock();
4) kworker-3574 | 8.997 us | rtnl_lock();
4) kworker-3574 | 3.166 us | rtnl_lock();
4) kworker-3574 | 3.908 us | rtnl_lock();
过滤影响分析的函数
echo *spin* >> set_graph_notrace
echo *rcu* >> set_graph_notrace
echo mutex* >> set_graph_notrace
echo *alloc* >> set_graph_notrace
echo security* >> set_graph_notrace
echo *might* >> set_graph_notrace
echo __cond* >> set_graph_notrace
echo preempt* >> set_graph_notrace
echo kfree* >> set_graph_notrace
不跟硬件中断
echo 0 > options/funcgraph-irqs
如果不希望跟踪流程里有硬件中断处理,那么可以设置这个。这样在硬件中断中的函数不会被跟踪,
这里判断是否在硬件中断,用的时in_hardirq,而设置hardirq标志是在irq_enter_rcu,所以
还是会看到irq_enter_rcu被跟踪到,但是再往下就不会被跟踪了。
trace_event
Linux内核 eBPF基础:Tracepoint原理源码分析
Linux ftrace 1.2、trace event
Using the TRACE_EVENT() macro (Part 1)
Using the TRACE_EVENT() macro (Part 2)
Using the TRACE_EVENT() macro (Part 3)
时间延迟标志
标记
含义
+
> 10us
!
> 100us
#
> 1ms
*
> 10ms
@
> 100ms
$
> 1s
hist
在事件上创建自定义的直方图。
使用hist触发器通过raw_syscalls:sys_enter跟踪点来计数系统调用数量,并提供按进程pid分类的直方图
echo 'hist:keys=common_pid' > events/raw_syscalls/sys_enter/trigger
sleep 10
cat events/raw_syscalls/sys_enter/hist
...
echo '!hist:keys=command_pid' > events/raw_syscalls/sys_enter/trigger
trace_option
参考: https://www.kernel.org/doc/html/latest/trace/ftrace.html#trace-options
func_stack_trace: 输出函数的调用栈,配合function tracer使用,也可以用于trace-event
bash-840761 [006] .... 101123.681864: ldsem_down_read <-tty_ldisc_ref_wait
bash-840761 [006] .... 101123.681866: <stack trace>
=> 0xffffffffc09d6099
=> ldsem_down_read
=> tty_ldisc_ref_wait
=> tty_ioctl
=> __x64_sys_ioctl
=> do_syscall_64
=> entry_SYSCALL_64_after_hwframe
stacktrace: 输出函数的调用栈,配合trace-event用
perf-tools
https://github.com/brendangregg/perf-tools
trace-cmd
是kernelshark的后端,可以用kernelshark解析trace-cmd生成的文件。
参考
trace-cmd
kernelshark
trace-cmd 恢复
使用ftrace分析函数性能
事件列举
# 列出所有跟踪事件的来源和选项
trace-cmd list
# 列出Ftrace跟踪器
trace-cmd list -t
# 列出事件源
trace-cmd list -e
# 列出系统调用跟踪点
trace-cmd list -e syscalls
# 显示指定跟踪点的格式文件
trace-cmd list -e syscalls:sys_enter_nanosleep -F
函数图示跟踪
抓取某个进程调用sys_open的调用栈
sudo trace-cmd record -p function_graph -g *sys_open -P <pid>
可以先让进程在调用open之前停住,用sleep或者getchar,在脚本里可以用echo $$; read con; exec xxx,执行上面的命令后,再继续执行
此外,还可以直接跟命令,比如:
sudo trace-cmd record -p function_graph -g *sys_open -F ls
最后执行下面的命令导出trace:
trace-cmd report > trace.log
函数跟踪
trace-cmd record -p function -l function_name
比如:
# 为ls命令跟踪所有一个vfs_开头的内核函数
trace-cmd record -p function -l 'vfs_*' -F ls
# 跟踪以tcp_开头的所有内核函数,持续10秒
trace-cmd record -p function -l 'tcp_*' sleep 10
# 跟踪bash及其子程序的所有一个vfs_开头的内核函数
trace-cmd record -p function -l 'vfs_*' -F -c bash
# 跟踪PID为21124的所有一个vfs_开头的内核函数
trace-cmd record -p function -l 'vfs_*' -P 21124
事件跟踪
trace-cmd record -e sched:sched_process_exec
远程
# 在tcp 8081端口监听
trace-cmd listen -p 8081
# 连接到远程主机以运行记录子命令
trace-cmd record ... -N addr:port
kernelshark
使用ftrace分析函数性能
kprobe
kprobe 的 3 种使用
内核调试之kprobe
Linux内核 eBPF基础:kprobe原理源码分析:源码分析
Linux内核 eBPF基础:kprobe原理源码分析:基本介绍与使用示例
打印open时的文件名
echo 'p:myprobe do_sys_open file=+0(%si):string' > kprobe_events
可以通过format查看log的输出格式:
root@ubuntu:/sys/kernel/debug/tracing# cat events/kprobes/my_probe/formatname: my_probe
ID: 2072
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:unsigned long __probe_ip; offset:8; size:8; signed:0;
field:__data_loc char[] file; offset:16; size:4; signed:1;
print fmt: "(%lx) file=\"%s\"", REC->__probe_ip, __get_str(file)
可以通过filter设置过滤条件:
echo 'file=="setproxy.sh"' > events/kprobes/my_probe/filter
使能:
echo 1 > events/kprobes/my_probe/enable
下面是输出的log:
cat trace 或者 cat trace_pipecat-753 [001] .... 3406.761327: my_probe: (do_sys_open+0x0/0x80) file="setproxy.sh"
可以在输出log的时候,打印open的调用栈:
echo 1 > options/stacktrace
可以从trace中看到如下的log: cat-772 [000] .... 3650.530789: my_probe: (do_sys_open+0x0/0x80) file="setproxy.sh"
cat-772 [000] .... 3650.530980: <stack trace>
=> do_sys_open
=> do_syscall_64
=> entry_SYSCALL_64_after_hwframe
对于不同的体系架构,进行函数调用时使用的传参规则并不相同,所以在使用kprobe提取函数参数时使用的方式也不
相同,为了解决这一问题,内核引入了一个patch:a1303af5d79eb13a658633a9fb0ce3aed0f7decf解决了这一问题,
使用argX来代表参数,比如上面的这个file=+0(%si):string可以替换为file=+0($arg2):string,因为
filename是第2个参数,这里参数编号 是从1开始的。关于参数的解析可以参考内核代码:
kernel\trace\trace_probe.c:parse_probe_arg
下面是常见的处理器的函数传参规则:
体系架构
参数1
参数2
参数3
参数4
参数5
参数6
参数7
参数8
参数9
x86
stack
x86_64
rdi
rsi
rdx
rcx
r8
r9
stack
arm
r0
r1
r2
r3
stack
arm64
x0
x1
x2
x3
x4
x5
x6
x7
stack
下面是不同架构的处理器上执行系统调用时的传参规则:
Architecture
Syscall instruction
Syscall number in
return value
arg0
arg1
arg2
arg3
arg4
arg5
x86_64
syscall
rax
rax
rdi
rsi
rdx
rcx
r8
r9
x86
int 0x80
eax
eax
ebx
ecx
edx
esi
edi
ebp
arm
svc 0
r7
r0
r0
r1
r2
r3
r4
r5
arm64
svc 0
x8
x0
x0
x1
x2
x3
x4
x5
uprobe
eBPF
https://ebpf.io/
https://www.bolipi.com/ebpf/index
BPF CO-RE reference guide
BPF and XDP Reference Guide
bpftrace
MAN手册
https://bpftrace.org/
bpftrace使用案例学习
openEuler kernel 技术分享-第9期-Bpf+trace介绍
下面是提前编译好可以直接使用的bpftrace可执行程序
x86_64
纯静态编译版本的bpftrace:v0.12.0
半静态编译的bpftrace:v0.16.0
容器版本的bpftrace:v0.16.0
AArch64
纯静态编译版本的bpftrace:v0.12.0
bpftrace做完strip后无法执行BEGIN和END
使用strip过程的bpftrace遇到如下问题:
ERROR: Could not resolve symbol: /proc/self/exe:BEGIN_trigger
在bpftrace运行时会调用bcc的接口bcc_resolve_name来解析BEGIN_trigger和END_trigger,传入的文件路径是/proc/self/exe,即会从本身的elf文件中去解析,但是strip后,符号表被删除,自然无法解析到需要的符号。同理,如果bpftrace被upx加了壳也会出现这个问题,因为此时exe指向的是加过壳的可执行程序,自然无法找到需要的符号。解决办法:
不strip
在strip的时候使用-K参数保留这两个符号:strip -K BEGIN_trigger -K END_trigger bpftrace
可以从ExtendedAndroidTools下载编译好的bpftrace可执行程序:
bpftrace官网也提供了静态编译的二进制文件,不过只有x86的
https://github.com/bpftrace/bpftrace/releases/download/v0.21.2/bpftrace
使用kprobe打印函数入口参数:
bpftrace -e 'kprobe:shmem_user_xattr_handler_set {printf("filename = %s, value = %s size = %d flag = %x\n", str(arg3), str(arg4), arg5, sarg0)}''
Attaching 1 probe...
filename = shmem_backend_file, value = ./memory_backend.img size = 20 flag = 1
filename = shmem_backend_file, value = size = 0 flag = 0
需要注意的是,arg从0开始,对于存放在栈里的参数,可以使用sarg来提取,函数原型:
static int shmem_user_xattr_handler_set(const struct xattr_handler *handler,
>------->------->------->------- struct dentry *unused, struct inode *inode,
>------->------->------->------- const char *name, const void *value,
>------->------->------->------- size_t size, int flags)
为bpfrace安装头文件
有时在bpftrace中需要解析一些内核结构体,比如page,此时就需要依赖内核头文件,可以参考下面的链接来安装:
https://github.com/iovisor/bpftrace/blob/master/INSTALL.md#kernel-headers-install
比如下面的bt文件:
#include <linux/mm.h>
kprobe:shmem_writepage
{
printf("Page: 0x%x, Index: 0x%x\n", arg0, ((struct page *)arg0)->index);
}
ply
https://wkz.github.io/ply/
https://github.com/iovisor/ply
BCC
simpleperf
配合kprobe抓取特定事件时的调用栈
simpleperf record --kprobe 'p set_user_nice $arg2:s32' -e 'kprobes:p_set_user_nice_0' --tp-filter 'arg1 == 4' -g -p `pidof xxx`
配合uprobe抓取特定事件的调用栈
由于simpleperf没有直接支持uprobe参数,所以我们可以利用ftrace的uprobe_events
创建一个uprobe跟踪点,然后再用simpleperf利用这个跟踪点。
以android的bionic的__kill函数为例:
root@localhost:~# nm /apex/com.android.runtime/lib64/bionic/libc.so | grep kill
00000000000e8e60 t __kill
00000000000e8e80 t __tgkill
00000000000ac1e0 T kill
000000000009f8ec T killpg
00000000000fe9b0 T pthread_kill
00000000000ac410 T tgkill
然后创建uprobe跟踪点:
echo 'p:kill /apex/com.android.runtime/lib64/bionic/libc.so:0xe8e60' >> uprobe_events
跟踪:
# simpleperf list | grep kill
cfg80211:rdev_rfkill_poll
uprobes:kill
# simpleperf record -e 'uprobes:kill' -g -a
perf
参考
在ubuntu上面的安装方法
https://www.brendangregg.com/perf.html
perf的基本使用方法
Linux kernel profiling with perf
linux性能分析工具:perf入门一页纸
2022最火的Linux性能分析工具--perf
perf_event内核框架
Linux Perf (目录)
Linux内核性能架构:perf_event
Linux内核 eBPF基础
perf(1)perf_event在内核中的初始化
perf(2)perf性能管理单元PMU的注册
perf(3)用户态指令分析
perf(4)perf_event_open系统调用与用户手册详解
perf(5)perf_event_open系统调用内核源码分析
PERF EVENT API篇
PERF EVENT 硬件篇
PERF EVENT 内核篇
PERF EVENT 硬件篇续 -- core/offcore/uncore
man perf_event_open
内核文档 Performance monitor support
系统级性能分析工具perf的介绍与使用
在Linux下做性能分析3:perf
深入探索 perf CPU Profiling 实现原理
Perf IPC以及CPU性能
ARM SPE技术
视频
perf_event_open学习
perf_event_open 学习 —— 手册学习
perf_event_open学习 —— mmap方式读取
perf_event_open学习 —— 缓冲区管理
perf_event_open 学习 —— 通过read的方式读取硬件技术器
火焰图
Flame Graphs
On-CPU火焰图
Linux下用火焰图进行性能分析
CPU Flame Graphs
Off-CPU火焰图
Off-CPU Flame Graphs
内存泄漏火焰图
Memory Leak (and Growth) Flame Graphs
gitee镜像:https://gitee.com/mirrors/FlameGraph
perf stat
perf kvm
perf kvm查看虚机热点调用
红帽的技术文档,其中介绍的guestmount的用法可以学习一下:
https://www.linux-kvm.org/page/Perf_events
perf-kvm(1) — Linux manual page
perf trace
perf ftrace
perf probe的使用
https://man7.org/linux/man-pages/man1/perf-probe.1.html
perf probe实例
查看一个内核函数中哪些位置可以插入事件
perf probe --line kernel_function -k <kernel_source_path>/vmlinux -s <kernel_source_path>
查看一个内核函数中指定行可以访问哪些变量
perf probe --var kernel_function:lineno -k <kernel_source_path>/vmlinux -s <kernel_source_path>
添加追踪事件
在上面第5行插入事件:
可以使用perf probe --list查看当前注册了哪些事件:
然后使用perf record -e probe:vfs_symlink_L5开启事件,再在另外一个窗口执行一个创建软连接的操作,然后
停止perf probe,最后用perf script查看事件:
删除事件
perf sched使用
Linux perf sched Summary
perf sched for Linux CPU scheduler analysis
perf sched查看调度延迟与唤醒延迟
Linux 的调度延迟 - 原理与观测
perf之sched
getdelays
Linux任务调度延时分析工具getdelays
surftrace
这是是阿里云开发的,是对ftrace的二次封装。
https://github.com/aliyun/surftrace
Systemtap
https://sourceware.org/systemtap/wiki/HomePage
https://sourceware.org/systemtap/
SystemTap使用指南
Systemtap原理简介
https://sourceware.org/systemtap/getinvolved.html
https://gitlab.com/fche/systemtap
stapbpf
stapstapbpfComparison
Introducing stapbpf - SystemTap's new BPF backend
SystemTap's BPF Backend Introduces Tracepoint Support
What are BPF Maps and how are they used in stapbpf
Dtrace
https://dtrace.org/
https://illumos.org/books/dtrace/preface.html#preface
介绍
DTrace on Fedora
uftrace
https://uftrace.github.io/slide/#1
https://github.com/namhyung/uftrace
https://github.com/namhyung/uftrace/wiki/Tutorial
业务时延检测利器-uftrace
utrace
https://github.com/Gui774ume/utrace
UTrace is a tracing utility that leverages eBPF to trace both user space and kernel space functions
Systrace/Perfetto
Systrace 和 Perfetto的使用
LTTng
https://lttng.org/features/
Documentation
Trace Compass
https://eclipse.dev/tracecompass/index.html
Trace Visualization Labs
使用手册
使用trace compass分析ftrace
Linux Networking: How The Kernel Handles A TCP Connection
B站某UP主写的profile工具,可以学习一下
https://github.com/zq-david-wang/linux-tools/tree/main
https://space.bilibili.com/2073420539
