BTrace使用总结

一、背景 
       在生产环境中可能经常遇到各种问题,定位问题需要获取程序运行时的数据信息,如方法参数、返回值、全局变量、堆栈信息等。为了获取这些数据信息,我们可以通过改写代码,增加日志信息的打印,再发布到生产环境。通过这种方式,一方面将增大定位问题的成本和周期,对于紧急问题无法做到及时响应;另一方面重新部署后环境可能已被破坏,很难重新问题的场景。 

二、BTrace功能 
       BTrace天生就为解决这类问题而来,它可以动态地跟踪java运行程序。通过hotswap技术,动态将跟踪字节码注入到运行类中,对运行代码侵入较小,对性能上的影响可以忽略不计。 
       BTrace在使用上有很多限制条件,如不能创建对象、数组、抛出和捕获异常、循环等,具体限制条件参考用户文档中的BTrace Restrictions。用户文档地址: http://kenai.com/projects/btrace/pages/UserGuide。 
       根据官方声明,不当地使用btrace可能导致jvm崩溃,如BTrace使用错误的.class文件,Hotspot JVM自身存在的hotswap bug等。可以先在本地验证BTrace脚本的正确性,再传到生产环境中定位问题。 

    
三、安装步骤 
1. 下载安装压缩包,最新版本的是1.2.1,下载地址: http://kenai.com/projects/btrace/downloads/directory/releases。 
2. 解压缩,命令脚本放在bin目录中。 
3. 设置脚本环境变量。 
4. 增加脚本可执行权限。 

四、使用方法 
BTrace主要包含btracec和btrace两个命令编译和启动BTrace脚本: 
1. btrace 
功能: 用于运行BTrace跟踪程序。 
命令格式: 
btrace [-I <include-path>] [-p <port>] [-cp <classpath>] <pid> <btrace-script> [<args>] 
示例: 
btrace -cp build/  1200 AllCalls1.java 
参数含义: 
include-path指定头文件的路径,用于脚本预处理功能,可选; 
port指定BTrace agent的服务端监听端口号,用来监听clients,默认为2020,可选; 
classpath用来指定类加载路径,默认为当前路径,可选; 
pid表示进程号,可通过jps命令获取; 
btrace-script即为BTrace脚本;btrace脚本如果以.java结尾,会先编译再提交执行。可使用btracec命令对脚本进行预编译。 
args是BTrace脚本可选参数,在脚本中可通过"$"和"$length"获取参数信息。 

2. btracec 
功能: 用于预编译BTrace脚本,用于在编译时期验证脚本正确性。 
btracec [-I <include-path>] [-cp <classpath>] [-d <directory>] <one-or-more-BTrace-.java-files> 
参数意义同btrace命令一致,directory表示编译结果输出目录。 

3. btracer 
功能: btracer命令同时启动应用程序和BTrace脚本,即在应用程序启动过程中使用BTrace脚本。而btrace命令针对已运行程序执行BTrace脚本。 
命令格式: 
btracer <pre-compiled-btrace.class> <application-main-class> <application-args> 
参数说明: 
pre-compiled-btrace.class表示经过btracec编译后的BTrace脚本。 
application-main-class表示应用程序代码; 
application-args表示应用程序参数。 
该命令的等价写法为: 
java -javaagent:btrace-agent.jar=script=<pre-compiled-btrace-script1>[,<pre-compiled-btrace-script1>]* <MainClass> <AppArguments> 

4. jvisualvm插件 
BTrace提供了jvisualvm插件,强烈推荐在jvisualvm中编写和测试BTrace脚本,启动、关闭、发送事件、增加classpath都非常方便。 


五、BTrace实战 
1. 示例代码 
示例代码定义了Counter计数器,有一个add()方法,每次增加随机值,总数保存在totalCount属性中。 
Btracetest.java代码  收藏代码
  1. package com.learnworld;  
  2. import java.util.Random;  
  3.   
  4. public class BTraceTest {  
  5.   
  6.     public static void main(String[] args) throws Exception {  
  7.         Random random = new Random();  
  8.   
  9.         // 计数器  
  10.         Counter counter = new Counter();  
  11.         while (true) {  
  12.             // 每次增加随机值  
  13.             counter.add(random.nextInt(10));  
  14.             Thread.sleep(1000);  
  15.         }  
  16.     }  
  17. }  

Counter.java代码  收藏代码
  1. package com.learnworld;  
  2. public class Counter {  
  3.     // 总数  
  4.     private static int totalCount = 0;  
  5.   
  6.     public int add(int num) throws Exception {  
  7.         totalCount += num;  
  8.         sleep();  
  9.         return totalCount;  
  10.     }  
  11.       
  12.     public void sleep() throws Exception {  
  13.         Thread.sleep(1000);  
  14.     }  
  15.   
  16. }  



2. 常见使用场景 
下面通过几个常见使用场景演示如何使用BTrace脚本。 

1) 获取add()方法参数值和返回值。 
Java代码  收藏代码
  1. import com.sun.btrace.annotations.*;  
  2. import static com.sun.btrace.BTraceUtils.*;  
  3.   
  4. @BTrace  
  5. public class TracingScript {  
  6.    @OnMethod(  
  7.       clazz="com.learnworld.Counter",  
  8.       method="add",  
  9.       location=@Location(Kind.RETURN)  
  10.    )  
  11.    public static void traceExecute(int num,@Return int result){  
  12.      println("====== ");  
  13.      println(strcat("parameter num: ",str(num)));  
  14.      println(strcat("return value:",str(result)));  
  15.    }  
  16. }  


2) 定时获取Counter类的属性值totalCount。 
Java代码  收藏代码
  1. import com.sun.btrace.annotations.*;  
  2. import static com.sun.btrace.BTraceUtils.*;  
  3.   
  4. @BTrace  
  5. public class TracingScript {  
  6.    private static Object totalCount = 0;  
  7.      
  8.    @OnMethod(  
  9.       clazz="com.learnworld.Counter",  
  10.       method="add",  
  11.       location=@Location(Kind.RETURN)  
  12.    )   
  13.    public static void traceExecute(@Self com.learnworld.Counter counter){  
  14.      totalCount = get(field("com.learnworld.Counter","totalCount"), counter);  
  15.    }   
  16.       
  17.    @OnTimer(1000)  
  18.    public static void print(){  
  19.      println("====== ");  
  20.      println(strcat("totalCount: ",str(totalCount)));  
  21.    }  
  22. }  



3) 获取add方法执行时间。 
Java代码  收藏代码
  1. import com.sun.btrace.annotations.*;  
  2. import static com.sun.btrace.BTraceUtils.*;  
  3.   
  4. @BTrace  
  5. public class TracingScript {  
  6.    @TLS private static long startTime = 0;  
  7.      
  8.    @OnMethod(  
  9.       clazz="com.learnworld.Counter",  
  10.       method="add"  
  11.    )   
  12.    public static void startExecute(){  
  13.      startTime = timeNanos();  
  14.    }   
  15.       
  16.    @OnMethod(  
  17.       clazz="com.learnworld.Counter",  
  18.       method="add",  
  19.       location=@Location(Kind.RETURN)  
  20.    )   
  21.    public static void endExecute(@Duration long duration){  
  22.      long time = timeNanos() - startTime;  
  23.      println(strcat("execute time(nanos): ", str(time)));  
  24.      println(strcat("duration(nanos): ", str(duration)));  
  25.    }   
  26. }  


4) 获取add()方法调用方法sleep()次数。 
Java代码  收藏代码
  1. import com.sun.btrace.annotations.*;  
  2. import static com.sun.btrace.BTraceUtils.*;  
  3.   
  4. @BTrace  
  5. public class TracingScript {  
  6.    private static long count;   
  7.        
  8.    @OnMethod(  
  9.       clazz="/.*/",  
  10.       method="add",  
  11.       location=@Location(value=Kind.CALL, clazz="/.*/", method="sleep")  
  12.    )  
  13.    public static void traceExecute(@ProbeClassName String pcm, @ProbeMethodName String pmn,  
  14.    @TargetInstance Object instance,  @TargetMethodOrField String method){  
  15.      println("====== ");  
  16.      println(strcat("ProbeClassName: ",pcm));  
  17.      println(strcat("ProbeMethodName: ",pmn));  
  18.      println(strcat("TargetInstance: ",str(classOf(instance))));  
  19.      println(strcat("TargetMethodOrField : ",str(method)));  
  20.      count++;  
  21.    }  
  22.      
  23.    @OnEvent  
  24.    public static void getCount(){  
  25.        println(strcat("count: ", str(count)));  
  26.    }  
  27. }  


六、参考文档 
1. userGuide: http://kenai.com/projects/btrace/pages/UserGuide 
2. JAVA doc:  http://btrace.kenai.com/javadoc/1.2/index.html 
3. BTrace用户手册<译>,http://macrochen.iteye.com/blog/838920 
4. btrace使用简介,http://rdc.taobao.com/team/jm/archives/509 
5. btrace记忆,http://agapple.iteye.com/blog/962119 
6. btrace一些你不知道的事(源码入手),http://agapple.iteye.com/blog/1005918 

posted on 2013-07-18 22:35  Java码界探秘  阅读(113)  评论(0编辑  收藏  举报

导航