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

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

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注册作弊

初识btrace的更多相关文章

  1. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

  2. 初识Hadoop

    第一部分:              初识Hadoop 一.             谁说大象不能跳舞 业务数据越来越多,用关系型数据库来存储和处理数据越来越感觉吃力,一个查询或者一个导出,要执行很长 ...

  3. python学习笔记(基础四:模块初识、pyc和PyCodeObject是什么)

    一.模块初识(一) 模块,也叫库.库有标准库第三方库. 注意事项:文件名不能和导入的模块名相同 1. sys模块 import sys print(sys.path) #打印环境变量 print(sy ...

  4. 初识IOS,Label控件的应用。

    初识IOS,Label控件的应用. // // ViewController.m // Gua.test // // Created by 郭美男 on 16/5/31. // Copyright © ...

  5. UI篇(初识君面)

    我们的APP要想吸引用户,就要把UI(脸蛋)搞漂亮一点.毕竟好的外貌是增进人际关系的第一步,我们程序员看到一个APP时,第一眼就是看这个软件的功能,不去关心界面是否漂亮,看到好的程序会说"我 ...

  6. Python导出Excel为Lua/Json/Xml实例教程(一):初识Python

    Python导出Excel为Lua/Json/Xml实例教程(一):初识Python 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出 ...

  7. 初识SpringMvc

    初识SpringMvc springMvc简介:SpringMVC也叫Spring Web mvc,属于表现层的框架.Spring MVC是Spring框架的一部分,是在Spring3.0后发布的 s ...

  8. 初识redis数据类型

    初识redis数据类型 1.String(字符串) string是redis最基本的类型,一个key对应一个value. string类型是二进制安全的.意思是redis的string可以包含任何数据 ...

  9. Redis初识、设计思想与一些学习资源推荐

    一.Redis简介 1.什么是Redis Redis 是一个开源的使用ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库,并提供多种语言的API.从2010 年 ...

随机推荐

  1. MyBatis学习(一):简单的运行

    1.准备工作 jar包: mybatis-3.4.4.jar,下载地址:https://github.com/mybatis/ignite-cache/releases mysql-connector ...

  2. CASIO fx-991es Plus科学计算器使用技巧

    关于输出: 默认是按照自然书写格式显示的,计算结果是按照分数形式显示,如0.5x0.5,会显示=1/4.虽然很直观,但是在测量和估算上略有不便.此时用 SHIFT --> MODE (也就是se ...

  3. 自译Solr in action中文版

    文件夹 Part 1 初识 SOLR 1 Solr 简单介绍 2 開始熟悉 Solr 3 Solr 核心概念 4 配置 Solr 5 建立索引 6 文本分析 Part 2 Solr 核心功能 7 发起 ...

  4. 公网RTSP地址(持续更新)

    H264+AAC: rtsp://a2047.v1412b.c1412.g.vq.akamaistream.net/5/2047/1412/1_h264_350/1a1a1ae555c53196016 ...

  5. mongodb分页

    1 什么是mongodb的分页 就是一次返回表中的连续若干行. 2 什么是sql分页 同样是返回表中的连续若干行. 3 如何实现sql分页 利用order by xxx limit xxx 4 如何实 ...

  6. Dropout: A Simple Way to Prevent Neural Networks fromOverfitting

    https://www.cs.toronto.edu/~hinton/absps/JMLRdropout.pdf Deep neural nets with a large number of par ...

  7. css zoom 属性

    oom这个属性是ie专有属性,除了设置或者检索对象的缩放比例之外,它还有可以触发ie的haslayout属性,清除浮动,清除margin重叠等作用. 不过值得注意的一点就是火狐浏览器不支持zoom属性 ...

  8. JDBC编程步奏、问题总结(一)

    jdbc编程步骤: 1. 加载数据库驱动 2. 创建并获取数据库链接 3. 创建jdbc statement对象 4. 设置sql语句 5. 设置sql语句中的参数(使用preparedStateme ...

  9. 分词系统简介:PHPAnalysis分词程序

    分词系统简介:PHPAnalysis分词程序使用居于unicode的词库,使用反向匹配模式分词,理论上兼容编码更广泛,并且对utf-8编码尤为方便. 由于PHPAnalysis是无组件的系统,因此速度 ...

  10. javascript 正则表达式 进阶教程

    学习之前先来说一说一些概念 子项 1.正则的一个分组为一个子项,子项的匹配结果可以在这个子项之后被使用 2.子项是有顺序的,以(出现的位置顺序从左到右,第一个'()'--分组 包含的为第一子项,第二个 ...