经由ftrace将动态追踪事件添加到内核中
下面介绍经由ftrace的特殊文件添加动态追踪事件的方法。动态追踪事件在ftrace中的接口为/sys/kernel/debug/tracing/kprobe_events。经由ftrace定义动态追踪事件,就是按照下列格式向这个文件中写入信息。
[p|r][:[组名/]事件名]符号[+偏移量]|内存地址[参数]
开头的文字如果为p,就表示定义在指定动态追踪事件的地址上。开头的文字如果为r,则表示定义在指定动态追踪事件的函数(符号)返回调用起点的地方。未指定事件名时,从符号名称和偏移量生成任意的事件名。另外,参数的格式如表8-13所示。
例如,在vfs_read的开头定义记录ax寄存器(x86-64作为RAX, x86-32作为EAX处理)和栈开头要素的动态追踪事件event1,使用下列方法进行确认。
[~]#cd/sys/kernel/debug/tracing/
[tracing]#echo p:event1 vfs_read%ax\$stack0>kprobe_events
[tracing]#cat kprobe_events
p:kprobes/event1 vfs_read arg1=%ax arg2=$stack0
使用$stack或$retval时,请不要忘记转义特殊字符($)。另外,如果不指定参数的类型,基本上就全部作为unsigned long类型进行处理,因此x86-64作为u64,x86-32作为u32处理。如果不向参数指定名称,就会自动分配arg1、arg2的名称。
下面讨论新添加的动态追踪事件是不是真的与其他事件相同。
[tracing]#fnd events/kprobes/
events/kprobes/
events/kprobes/event1
events/kprobes/event1/format
events/kprobes/event1/filter
events/kprobes/event1/id
events/kprobes/event1/enable
events/kprobes/enable
events/kprobes/filter
[tracing]#cat events/kprobes/event1/format
name:event1
ID:834
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:int common_lock_depth;offset:8;size:4;signed:1;
field:unsigned long__probe_ip;offset:16;size:8;signed:0;
field:u64 arg1;offset:24;size:8;signed:0;
field:u64 arg2;offset:32;size:8;signed:0;
print fmt:"(%lx)arg1=%llx arg2=%llx",REC->__probe_ip, REC->arg1,REC->arg2
可以看出,确实连格式的定义都是相同的。这个事件可以从ftrace使用,如下所示。
[tracing]#echo kprobes:event1>set_event
[tracing]#head trace
tracer:nop
#
TASK-PID CPU#TIMESTAMP FUNCTION
|||||
libvirtd-1242[003]22550.135210:event1:(vfs_read+0x0/0x190)arg1=0
arg2=ffffffff81148f81
libvirtd-1242[003]22550.135286:event1:(vfs_read+0x0/0x190)
arg1=400 arg2=ffffffff81148f81
libvirtd-1242[003]22550.135316:event1:(vfs_read+0x0/0x190)
arg1=800 arg2=ffffffff81148f81
libvirtd-1242[003]22550.135343:event1:(vfs_read+0x0/0x190)
arg1=c00 arg2=ffffffff81148f81
libvirtd-1242[003]22550.135346:event1:(vfs_read+0x0/0x190)
arg1=d28 arg2=ffffffff81148f81
libvirtd-1242[003]22550.135420:event1:(vfs_read+0x0/0x190)arg1=0
arg2=ffffffff81148f81
在上述示例中,向符号开头的地址添加了事件,也可以指定符号+偏移量。x86采用的是命令长度各不相同的CISC方式,因此必须进行反汇编才能知道可以向哪个地址添加事件。但是,即使指定了错误的地址,由于内核内部会进行命令范围的检查,因此会作为定义时的错误被排除。
要删除这样定义的动态追踪事件,可以使用两种方法。
如果想删除所有定义,可以像下面这样向kprobe_events写入空白。
[tracing]#echo>/sys/kernel/debug/tracing/kprobe_events
想删除单个事件,可以向kprobe_events追加“-:[组名/]事件名”命令。例如,只删除event1的方法如下。
[tracing]#echo-:kprobes/event1>>kprobe_events