zoukankan      html  css  js  c++  java
  • 初识btrace

    此文已由作者易国强授权网易云社区发布。

    欢迎访问网易云社区,了解更多网易技术产品运营经验。


    1 btrace简介

      BTrace是一个非常不错的java诊断工具。BTrace 中的B表示bytecode,它是在字节码层面上对代码进行trace ,通过在运行中的java类中注入trace代码, 并对运行中的目标程序进行热交换(hotswap)来达到对代码的跟踪 。BTrace应用较为广泛的原因应该是其安全性和无侵入性,以及热交互技术,使得我们无需启动Agent的情况下动态跟踪分析,其安全性不会导致对目标Java进程的任何破坏性影响,使得BTrace成为我们线上产品问题定位的利器。无侵入性无需我们对原有代码做任何修改,降低上线风险和测试成本,并且无需重启启动目标Java进程进行Agent加载即可动态分析和跟踪目标程序,可以说BTrace可以满足大部分的应用场景。


    2 btrace原理

        总体来说,BTrace是基于动态字节码修改技术(Hotswap)来实现运行时java程序的跟踪和替换。大体的原理如下所示:

    Client(Java compile api + attach api) + Agent(脚本解析引擎 + ASM + JDK6 Instumentation) + Socket

    BTrace的入口类在https://gi

    thub.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/client/Main.java中。在其main方法中,可以看到起最终的核心逻辑是在https://github.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/client/Client.java中。方法调用如下:

    • client.compile

    • client.attach

    • client.submit

        具体来说,针对官网给出的示例进行说明,有以下脚本:


    import com.sun.btrace.annotations.*;import static com.sun.btrace.BTraceUtils.*;@BTracepublic class HelloWorld {    @OnMethod(
            clazz="java.lang.Thread",
            method="start"
        )    public static void func() {
            println("about to start a thread!");
        }
    }

         @OnMethod告诉Btrace解析引擎需要代理的类和方法。 这个例子的作用是在需要监控的程序中的java.lang.Thread类的任意一个对象调用 start 方法后,都会调用func方法。首先client会编译上述脚本,然后client.attach使用java的attach api将agent动态attach到目标jvm进程中,最后client的submit方法,会向agent发送监控命令以及传递对应code的字节码。总的来说其实BTrace就是使用了java attach api附加agent.jar,然后使用脚本解析引擎+asm来重写指定类的字节码,再使用instrument实现对原有类的替换。


    3 安装并启动btrace

       1.     安装BTrace,下载tar.gz包,在服务器上解压即可运行。

         2.     确认需要监控的java应用,获取进程PID。

         3.     使用java语言写一个BTrace脚本,如Demo.java。

        进入$BTRACE_HOME/bin目录 
        执行 ./btrace [目标进程的PID] Demo.java 


    4 btrace实例

       首先我们编写一个简单的java程序作为被监控的对象:


    import java.util.Random;  
      
    public class HelloWorld {  
        public static void main(String[] args) throws Exception {  
            //CaseObject object = new CaseObject();  
            while (true) {  
                Random random = new Random();  
                execute(random.nextInt(4000));  
                  
                //object.execute(random.nextInt(4000));  
            }        
        }  
        public static Integer execute(int sleepTime) {  
            try {  
                Thread.sleep(sleepTime);  
            } catch (Exception e) {  
            }  
            System.out.println("sleep time is=>"+sleepTime);  
            return 0;  
        }  
    }

      其次需要编写一个btrace脚本,如下所示:


    import static com.sun.btrace.BTraceUtils.println;import static com.sun.btrace.BTraceUtils.str;import static com.sun.btrace.BTraceUtils.strcat;import static com.sun.btrace.BTraceUtils.timeMillis;import com.sun.btrace.annotations.BTrace;import com.sun.btrace.annotations.Kind;import com.sun.btrace.annotations.Location;import com.sun.btrace.annotations.OnMethod;import com.sun.btrace.annotations.ProbeClassName;import com.sun.btrace.annotations.ProbeMethodName;import com.sun.btrace.annotations.TLS;@BTracepublic class TraceHelloWorld {	
    	@TLS
    	private static long startTime = 0;	
    	@OnMethod(clazz = "my.app.test.HelloWorld", method = "execute")	public static void startMethod(){
    		startTime = timeMillis();
    	}	
    	@OnMethod(clazz = "my.app.test.HelloWorld", method = "execute", location = @Location(Kind.RETURN))	public static void endMethod(){
    		println(strcat("the class method execute time=>", str(timeMillis()-startTime)));
    		println("-------------------------------------------");
    	}	
    	@OnMethod(clazz = "my.app.test.HelloWorld", method = "execute", location = @Location(Kind.RETURN))	public static void traceExecute(@ProbeClassName String name,@ProbeMethodName String method,int sleepTime){
    		println(strcat("the class name=>", name));
    		println(strcat("the class method=>", method));
    		println(strcat("the class method params=>", str(sleepTime)));
    		
    	}
    }
    以下是需要注意的几个点:

    1、@btrace这个annotation表明这个类是btrace脚本,

    2、@OnMethod(clazz = "my.app.test.HelloWorld", method = "execute")

    中clazz标明要监控那个类,也可以用正则匹配的方式,method标明要监控类的哪个方法

    3、其中用到的几个方法timeMillis(),获取时间,println(str)输出

      其中clazz=...  method=... 这两个属性,指定了这个BTrace方法的注入位置,这个注入的位置叫probe point, 具体来讲,excute()方法叫probed method, TraceHelloWorld类叫probed class。 也就是说, Btrace脚本中的方法endMethod()会注入在目标JVM的com.netease.qa.btrace.Demo1.add()调用处。注入操作是通过修改excute()方法的字节码实现的。

      一旦注入成功,action method会在被监控应用执行到probe point的时候被触发执行。但是,这里具体是add()方法调用前,还是调用完成时呢? 这个由@Location注解指定,这里的value=Kind.RETURN,指定了endMethod方法会在调用结束后执行。

     启动btrace脚本后,可以看到以下输出:

      可以看到这样就完成了一个简单的btrace脚本的编写以及监控应用实践的工作。


    免费体验云安全(易盾)内容安全、验证码等服务

    更多网易技术、产品、运营经验分享请点击




    相关文章:
    【推荐】 Vue 依赖收集原理分析
    【推荐】 3招搞定APP注册作弊

  • 相关阅读:
    DataGridView重绘painting简单实例
    C#实现万年历(农历、节气、节日、星座、属相、生肖、闰年等)
    《开源框架那点事儿11》:软件开发杂谈
    半年总结——欲戴王冠,必承其重
    三天学会HTML5 之第一天
    读书笔记 -《高效程序猿的45个习惯-敏捷开发修炼之道》
    Opengl ES 1.x NDK实例开发之七:旋转的纹理立方体
    我与小娜(08):人工智能的伟大胜利
    阿里云 oss 小文件上传进度显示
    模仿猫眼电影App一个动画效果
  • 原文地址:https://www.cnblogs.com/163yun/p/9883409.html
Copyright © 2011-2022 走看看