很多时候在online的应用出现问题时,很多时候我们需要知道更多的程序的运行细节,但又不可能在开发的时候就把程序中所有的运行细节都打印到日志上,通常这个时候能采取的就是修改代码,重新部署,然后再观察,但这种方法对于online应用来说不是很好,另外一方面如果碰到不好改的代码,例如引用的其他的外部的包什么的,就很麻烦了,BTrace就是一个可以在不改代码、不重启应用的情况下,动态的查看程序运行细节的工具,其官方网站在此:http://kenai.com/projects/btrace/,在这篇blog中,就来看看如何用BTrace来动态的监测方法的一些运行细节状况。
BTrace通过动态的挂接用java写的代码到运行时上来获取到一些运行细节,例如典型的使用btrace的方法为:
btrace -cp [btrace的jar所在的路径,默认为btrace/build下] [pid] [需要运行的java代码]
例如一段这样的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.Random;
public class Case1{
 
    public static void main(String[] args) throws Exception{
       Random random= new Random();
       CaseObject object= new CaseObject();
       boolean result= true ;
       while (result){
          result=object.execute(random.nextInt( 1000 ));
          Thread.sleep( 1000 );
       }
    }
 
}
public class CaseObject{
 
    private static int sleepTotalTime= 0 ;  
 
    public boolean execute( int sleepTime) throws Exception{
        System.out.println( "sleep: " +sleepTime);
        sleepTotalTime+=sleepTime;
        Thread.sleep(sleepTime);
        return true ;
    }
 
}

如在程序运行的情况下,想知道调用CaseObject的execute方法的以下几种情况,在BTrace中可以这么做:
1、调用此方法时传入的是什么参数,返回的是什么值,当时sleepTotalTime是多少?
BTrace脚本如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
     import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
 
@BTrace public class TraceMethodArgsAndReturn{
    @OnMethod (
       clazz= "CaseObject" ,
       method= "execute" ,
       location= @Location (Kind.RETURN)
    )
    public static void traceExecute( @Self CaseObject instance, int sleepTime, @Return boolean result){
      println( "call CaseObject.execute" );
      println(strcat( "sleepTime is:" ,str(sleepTime)));
      println(strcat( "sleepTotalTime is:" ,str(get(field( "CaseObject" , "sleepTotalTime" ),instance))));
      println(strcat( "return value is:" ,str(result)));
    }
}

然后直接执行btrace -cp btrace/build [pid] TraceMethodArgsAndReturn.java就可以了。
当程序中调用到caseobject的execute方法时,就会在btrace的console中输出相应的信息。
2、execute方法执行耗时是多久?
BTrace脚本如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
     import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
 
@BTrace public class TraceMethodExecuteTime{
 
    @TLS static long beginTime;
 
    @OnMethod (
       clazz= "CaseObject" ,
       method= "execute"
    )
    public static void traceExecuteBegin(){
      beginTime=timeMillis();
    }
 
    @OnMethod (
       clazz= "CaseObject" ,
       method= "execute" ,
       location= @Location (Kind.RETURN)
    )
    public static void traceExecute( int sleepTime, @Return boolean result){
       println(strcat(strcat( "CaseObject.execute time is:" ,str(timeMillis()-beginTime)), "ms" ));
    }
}

3、谁调用了execute方法?
BTrace脚本如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
      import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
 
@BTrace public class TraceMethodCallee{
    @OnMethod (
       clazz= "CaseObject" ,
       method= "execute"
    )
    public static void traceExecute(){
      println( "who call CaseObject.execute :" );
      jstack();
    }
}

4、有没有人调用CaseObject中的哪一行代码?
BTrace脚本如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
     import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
 
@BTrace public class TraceMethodLine{
    @OnMethod (
       clazz= "CaseObject" ,
       location= @Location (value=Kind.LINE,line= 5 )
    )
    public static void traceExecute( @ProbeClassName String pcn, @ProbeMethodName String pmn, int line){
      println(strcat(strcat(strcat( "call " ,pcn), "." ),pmn));
    }
}

从上面可看出,在有了BTrace后,要动态的跟踪代码的运行细节还是非常爽的,更多的细节的操作请大家查看BTrace的User Guide