初识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/bt… 中。在其main方法中,可以看到起最终的核心逻辑是在 github.com/btraceio/bt… 中。方法调用如下:

  • 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

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

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

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

原文 

https://juejin.im/post/5bd958c46fb9a022523c4a66

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 初识btrace

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址