内核文档:
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之前挺住,接着执行下面的命令,最后接着执行程序
然后执行下面的命令导出trace: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
对于function_graph抓到的调用栈,可以使用内核提供的vim插件来阅读:echo 0 > /sys/kernel/debug/tracing/tracing_on cat /sys/kernel/debug/tracing/trace > trace.log
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
可以通过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 |
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)
- 为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);
}