ftrace、perf、bcc、bpftrace、ply的使用


參考

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
    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)
    
    可以通過filter設置過濾條件:
    echo 'file=="setproxy.sh"' > events/kprobes/my_probe/filter
    使能:
    echo 1 > events/kprobes/my_probe/enable
    下面是輸出的log:
    cat trace 或者 cat trace_pipe
    cat-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

bpftrace

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)
#include <linux/mm.h>

kprobe:shmem_writepage
{
        printf("Page: 0x%x, Index: 0x%x\n", arg0, ((struct page *)arg0)->index);
}

ply

BCC

perf

image

參考

火焰圖

perf stat

perf kvm

perf trace

perf ftrace

perf probe的使用

image

  • 查看一個內核函數中指定行可以訪問哪些變量
    perf probe --var kernel_function:lineno -k <kernel_source_path>/vmlinux -s <kernel_source_path>

image

  • 添加追蹤事件

在上面第5行插入事件:
image

可以使用perf probe --list查看當前注冊了哪些事件:

image

然后使用perf record -e probe:vfs_symlink_L5開啟事件,再在另外一個窗口執行一個創建軟連接的操作,然后
停止perf probe,最后用perf script查看事件:

image

  • 刪除事件

image

perf sched使用

getdelays

surftrace

這是是阿里雲開發的,是對ftrace的二次封裝。
https://github.com/aliyun/surftrace

systemtap

uftrace

Systrace/Perfetto

LTTng

Trace Compass

B站某UP主寫的profile工具,可以學習一下


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM