博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
初识btrace
阅读量:4959 次
发布时间:2019-06-12

本文共 4125 字,大约阅读时间需要 13 分钟。

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

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

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的入口类在

中。在其main方法中,可以看到起最终的核心逻辑是在中。方法调用如下:

  • 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,,在服务器上解压即可运行。

     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脚本后,可以看到以下输出:

ff0a74bd-f4e2-470a-9cd9-ce9fc4badda1

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

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

相关文章:

【推荐】 
【推荐】 

转载于:https://www.cnblogs.com/zyfd/p/9883411.html

你可能感兴趣的文章
链表操作
查看>>
Tautology(structure)
查看>>
MySQL:binlog 和 redo log
查看>>
爬新浪
查看>>
Lnmp.org 最稳定方式呈现
查看>>
OpenGL ES着色器语言之语句和结构体(官方文档第六章)内建变量(官方文档第七、八章)...
查看>>
面试题目总结(前端)
查看>>
Makefile学习笔记
查看>>
找出一个整数数组的和最大的连续子数组
查看>>
MyBatis的入门案例
查看>>
我的 nginx 配置
查看>>
[NOI2001]食物链(种类并查集)
查看>>
VMware workstation 与 VMware GSX Server 的区别
查看>>
OOA/OOD/OOP的区别
查看>>
hint指定index的深入理解
查看>>
ASP.NET Excel数据导入数据库---2
查看>>
Instagram的技术架构
查看>>
嘉定三屠与扬州十屠
查看>>
Lua学习笔记9:多文件
查看>>
Qt 3D研究(九):尝试第二边缘检测方法
查看>>