使用自己的脚本处理数据

有时我们会打算自己写脚本来实现事件的统计处理,而不是执行现存的脚本。perf script考虑这样的需求,准备了根据记录的事件生成脚本模型的功能。

下面查看一个用来调查系统调用和内核中分配内存的kmalloc()关系的脚本实例。首先生成使用perf record记录必要事件的数据文件。


perf record-e kmem:kmalloc-e raw_syscalls:sys_enter ls/

bin dev home lib32 media opt root selinux sys tracing var

boot etc lib lib64 mnt proc sbin srv tmp usr

[perf record:Woken up 1 times to write data]

[perf record:Captured and wrote 0.037 MB perf.data(~1633 samples)]


然后,向perf script传递-g选项,根据数据文件生成脚本的模型。-g选项将生成的脚本语言(当前为Perl或Python)作为参数。在下例中,生成的是perf脚本。


perf script-g perl

generated Perl script:perf-script.pl


生成的脚本直接就具有显示事件内容的功能。


perf script event handlers, generated by perf script-g perl

Licensed under the terms of the GNU GPL License version 2

The common_*event handler fields are the most useful fields common to

all events.They don't necessarily correspond to the'common_*'fields

in the format files.Those fields not available as handler params can

be retrieved using Perl functions of the form common_*($context).

See Context.pm for the list of available functions.

use lib"$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";

use lib"./Perf-Trace-Util/lib";

use Perf:Trace:Core;

use Perf:Trace:Context;

use Perf:Trace:Util;

sub trace_begin

{

optional

}

sub trace_end{

optional

}

sub raw_syscalls:sys_enter

{

my($event_name,$context,$common_cpu,$common_secs,$common_nsecs,

$common_pid,$common_comm,

$id,$args)=@_;

print_header($event_name,$common_cpu,$common_secs,$common_nsecs,

$common_pid,$common_comm);

printf("id=%d, args=%s\n",

$id,$args);

}

sub kmem:kmalloc

{

my($event_name,$context,$common_cpu,$common_secs,$common_nsecs,

$common_pid,$common_comm,

$call_site,$ptr,$bytes_req,$bytes_alloc,

$gfpflags)=@

print_header($event_name,$common_cpu,$common_secs,$common_nsecs,

$common_pid,$common_comm);

printf("call_site=%u, ptr=%u, bytes_req=%u, bytes_alloc=%u,".

"gfp_flags=%s\n",

$call_site,$ptr,$bytes_req,$bytes_alloc,

flag_str("kmem:kmalloc","gfp_flags",$gfp_flags));

}

sub trace_unhandled

{

my($event_name,$context,$common_cpu,$common_secs,$common_nsecs,

$commonpid,$common_comm)=@

print_header($event_name,$common_cpu,$common_secs,$common_nsecs,

$common_pid,$common_comm);

}

sub print_header

{

my($eventname,$cpu,$secs,$nsecs,$pid,$comm)=@

printf("%-20s%5u%05u.%09u%8u%-20s",

$event_name,$cpu,$secs,$nsecs,$pid,$comm);

}


从生成的脚本的内容,可以看出是事件触发类型的程序,perf script依次处理事件,然后将信息传递给与事件名称相对应的函数。通过更改处理这个事件的函数的内容,可以执行任意的处理。例如,要统计哪个系统调用调用了几次kmalloc(),可以将生成的脚本修改为如下内容。


use lib"$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";

use lib"./Perf-Trace-Util/lib";

use Perf:Trace:Core;

use Perf:Trace:Context;

use Perf:Trace:Util;

@count=();

$current_id=-1;

sub trace_end

{

for($id=0;$id<1024;$id++){

if(@count[$id]!=0){

printf("syscall%d invokes kmalloc%d times.\n",

$id,@count[$id]);

}

}

}

sub raw_syscalls:sys_enter

{

my($event_name,$context,$common_cpu,$common_secs,$common_nsecs,

$common_pid,$common_comm,

$id,$args)=@_;

$current_id=$id;

}

sub kmem:kmalloc

{

my($event_name,$context,$common_cpu,$common_secs,$common_nsecs,

$common_pid,$common_comm,

$call_site,$ptr,$bytes_req,$bytes_alloc,

$gfpflags)=@

if($current_id!=-1){

@count[$current_id]++;

}

}


将这个脚本另存为kmalloc-syscall.pl,并使用perf script来尝试运行。


perf script-s kmalloc-syscall.pl

syscall 0 invokes kmalloc 1 times.

syscall 2 invokes kmalloc 25 times.

syscall 9 invokes kmalloc 16 times.

syscall 10 invokes kmalloc 16 times.

syscall 21 invokes kmalloc 9 times.

syscall 137 invokes kmalloc 1 times.


可以像这样使用现有的脚本语言非常简单地记述事件的处理方式,就是perf script的特点。从这个功能也可以使用Hack#71介绍的通过perf probe生成的事件,因此实质上也就是可以使用脚本来处理内核中的大部分信息。