trace、perf、eBPF使用

内核文档:
https://www.kernel.org/doc/html/latest/trace/ftrace.html
https://www.kernel.org/doc/html/latest/trace/kprobetrace.html
https://www.kernel.org/doc/html/latest/trace/uprobetracer.html
https://www.kernel.org/doc/html/latest/trace/tracepoints.html
https://www.kernel.org/doc/html/latest/trace/events.html
https://www.kernel.org/doc/html/latest/trace/events-kmem.html
https://www.kernel.org/doc/html/latest/trace/mmiotrace.html
https://www.kernel.org/doc/html/latest/bpf/index.html#
https://github.com/iovisor/bpftrace
https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md
https://github.com/iovisor/bcc
https://github.com/iovisor/bcc/blob/master/docs/tutorial.md

ftrace

  • 获取某个进程调用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

trace-cmd

  • 抓取某个进程调用sys_open的调用栈
    sudo trace-cmd record -p function_graph -g *sys_open -P <pid>
    可以先让进程在调用open之前停住,用sleep或者getchar,执行上面的命令后,再继续执行
    最后执行下面的命令导出trace:
    trace-cmd report > trace.log

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

uprobe

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

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

perf

原文地址:https://www.cnblogs.com/pengdonglin137/p/15533392.html