參考
- 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
- 參考
- 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
trace_point
時間延遲標志
標記 | 含義 |
---|---|
+ | > 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
trace-cmd
是kernelshark的后端,可以用kernelshark解析trace-cmd生成的文件。
參考
事件列舉
# 列出所有跟蹤事件的來源和選項
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
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/format
可以通過filter設置過濾條件:name: 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)
echo 'file=="setproxy.sh"' > events/kprobes/my_probe/filter
使能:
echo 1 > events/kprobes/my_probe/enable
下面是輸出的log:
cat trace
或者cat trace_pipe
可以在輸出log的時候,打印open的調用棧:cat-753 [001] .... 3406.761327: my_probe: (do_sys_open+0x0/0x80) file="setproxy.sh"
echo 1 > options/stacktrace
可以從trace中看到如下的log:
對於不同的體系架構,進行函數調用時使用的傳參規則並不相同,所以在使用kprobe提取函數參數時使用的方式也不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
相同,為了解決這一問題,內核引入了一個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
-
下面是提前編譯好可以直接使用的bpftrace可執行程序
-
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
-
使用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
BCC
perf
參考
- 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 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 實現原理
火焰圖
- Flame Graphs
- On-CPU火焰圖
- Off-CPU火焰圖
- 內存泄漏火焰圖
- gitee鏡像:https://gitee.com/mirrors/FlameGraph
perf stat
perf kvm
- perf kvm查看虛機熱點調用
- 紅帽的技術文檔,其中介紹的guestmount的用法可以學習一下:
- 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
surftrace
這是是阿里雲開發的,是對ftrace的二次封裝。
https://github.com/aliyun/surftrace
systemtap
- https://sourceware.org/systemtap/
- SystemTap使用指南
- Systemtap原理簡介
- https://sourceware.org/systemtap/getinvolved.html
- https://gitlab.com/fche/systemtap
uftrace
- https://uftrace.github.io/slide/#1
- https://github.com/namhyung/uftrace
- https://github.com/namhyung/uftrace/wiki/Tutorial
- 業務時延檢測利器-uftrace
Systrace/Perfetto
LTTng
Trace Compass
- https://eclipse.dev/tracecompass/index.html
- 使用trace compass分析ftrace
- Linux Networking: How The Kernel Handles A TCP Connection