• 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 
  • 相关阅读:
    go相关
    mac下使用vscode技巧
    mac下secureCRT的使用技巧
    python subprocess实时输出
    python中多级目录导入模块问题
    python的print与sys.stdout
    python中类相关笔记
    python中sys.stdout.flush()的作用
    nginx+uwsgi配置
    虚拟机的 基本配置
  • 原文地址:https://www.cnblogs.com/zengkefu/p/5632194.html
Copyright © 2020-2023  润新知