用dTrace捕捉内存分配于释放

来源:互联网 发布:javascript switch语句 编辑:程序博客网 时间:2024/06/06 11:03

dTrace是Solaris 10中一个非常强大的工具,有了它,不需要更改代码,就可以“打入”程序内部“探听”我所要知道的一切。其中一个非常重要的就是记录内存分配与释放,然后方便我进一步的分析。

 

我们所要做的就是写一个很简单的dtrace的脚本,比如叫memlog.d

 

上王道:

 

pid$1:libc:malloc:entry    //->当malloc被调用的时候执行的动作
{    
        self->trace = 1;      //设一个标志位,用于确定只捕捉已经被置位的return动作
        self->size = arg0;   //arg0就是传给malloc的第一个参数,就是需要分配的内存大小
}


pid$1:libc:malloc:return     //->当malloc返回的时候
/self->trace == 1 && self->size > 0/     //->条件:只捕捉trace==1并且内存分配>0的动作
{
        printf("Ptr=0x%p Size=%d TS=%d AllocTime=%Y", arg1, self->size, timestamp, walltimestamp);         //打印需要的信息,Ptr和Size很重要,能够方便我们查找内存泄漏
        ustack();           //打印调用栈,这点很重要,能够帮助我们对应源代码
        self->trace = 0;
        self->size = 0; 
}

 

以上两段代码就能捕捉所有通过malloc来分配内存的情况。pid$1是一个pid provider,用来探听给定的进程,进程ID 是调用dtrace 脚本的第一个参数。这样我们就可以通过命令参数传入我关心的进程ID。

 

下面看内存释放:->相信一看就懂了

pid$1:libc:free:entry
{
        printf("Ptr=0x%p Size=%d TS=%d FreeTime=%Y", arg0, self->size, timestamp, walltimestamp);
        ustack();
}

 

 

其实分配内存还有calloc和realloc,都是类似的:

pid$1:libc:realloc:entry
/self->trace == 1 && self->size > 0/
{
        self->trace = 1;
        self->size = arg1;
        self->oldptr = arg0;
}

 

pid$1:libc:realloc:return
/self->trace == 1 && self->size > 0/
{
        printf("Ptr=0x%p Oldptr=0x%p Size=%d TS=%d ReallocTime=%Y", arg1, self->oldptr, self->size, timestamp, walltimestamp);
        ustack();
        self->trace = 0;
        self->size = 0;
}

 

pid$1:libc:calloc:entry
/self->trace == 1 && self->size > 0/
{
        self->trace = 1;
        self->size = arg0 * arg1;
}

 

pid$1:libc:calloc:return
/self->trace == 1 && self->size > 0/
{
        printf("Ptr=0x%p Size=%d TS=%d CallocTime=%Y", arg1, self->size, timestamp, walltimestamp);
        ustack();
        self->trace = 0;
        self->size = 0;
}

 

 

好了,代码贴完了,看看怎么运行吧~假设我的程序名字叫jianxu_arrowpig

-bash-3.00$ ps -ef | grep jianxu_arrowpig

就可以拿到进程号,假设进程ID=6666

运行dtrace:

-bash-3.00$ dtrace -s memlog.d 6666

 

 

给一段实例输出吧:

-bash-3.00$ sudo dtrace -s memlog.d 6666   //–>我这里用sudo是因为dtrace 需要 root运行权限。
dtrace: script ‘mallco.d’ matched 7 probes
CPU     ID                    FUNCTION:NAME
  0    534                    malloc:return Ptr=0×9890130 Size=20 TS=27532231842644751 AllocTime=2008 Jun 27 02:25:17
              libc.so.1`malloc+0×49
              scrubber`_ZN23PartitionOutputDbAction4InitEv+0x69a
              scrubber`_ZN23PartitionOutputDbActionC1EP7XMLNode+0x10b6
              scrubber`_Z26ParseSearchTransformationsP7XMLNodePFvPKcE+0×3442
              scrubber`_Z8ReadFileR10FileReader+0x3fb
              scrubber`_Z10RefreshXMLPKc+0xc4
              scrubber`_Z13DaemonProcessv+0x1f9b
              scrubber`main+0xbd6
              scrubber`_start+0×80

 

 

注意,给出的 trace是c++ mangled的symbol如果要看unmangled name:

-bash-3.00$ gc++filt _ZN23PartitionOutputDbActionC1EP7XMLNode
PartitionOutputDbAction::PartitionOutputDbAction(XMLNode*)

 

通常我会重定向输出,这样可以用perl做offline的分析:

-bash-3.00$ sudo dtrace -s memlog.d 6666 > mem.log

 

还真巧了,今天就在查memory leak.
egrep ‘malloc:return|free:entry’ mem.log > mem.log.simple  //–>先弄个小点的
cat mem.log.simple | perl memanalyze.pl > memleak.log
 
##memanalyze.pl ##写了个很傻的,不知道好用不好用
#! /usr/bin/perl -wuse strict;use warnings;
my $line;

my %result;

while ($line=<STDIN>)

{   

     if(  $line=~/malloc/:return/sPtr=0x([0-9a-f]+)/sSize=(/d+)/sTS=(/d+)/)  

    {       

             #Use Ptr as the Key, Hash value is Ts, so I can locate in the original log       

             $result{$1}= $3;   

     }   

     elsif( $line=~/free/:entryn/sPtr=0x([0-9a-f]+)/sSize=(/d+)/sTS=(/d+)/)  

     {       

             delete $result{$1};  #delete the entry   

      }

}


my $MyPtr;

my $MyTs;

while ( ($MyPtr, $MyTs) = each %result)

{   

     print "Ptr=0x$MyPtr, TS=$MyTs/n";

}