Java程序排查问题利器之Btrace

转自:http://qindongliang.iteye.com/blog/2408834

(一)Btrace的介绍

BTrace是Java的安全可靠的动态跟踪工具。 他的工作原理是通过 instrument + asm 来对正在运行的java程序中的class类进行动态增强,可以在不用重启的情况下监控系统运行情况,方便的获取程序运行时的数据信息,如方法参数、返回值、全局变量和堆栈信息等,并且做到最少的侵入,占用最少的系统资源。

正如上面描述的一些特性,所以btrace一般是用来排查生产环境jvm问题的一款利器,使用它不用再担心应用程序的日志打的不够全,不够细,也不用为了排查问题一遍遍的重启程序。

(二)Btrace的一些限制

由于Btrace会把脚本逻辑直接侵入到运行的代码中,所以在使用上做很多限制:

1、不能创建对象

2、不能使用数组

3、不能抛出或捕获异常

4、不能使用循环

5、不能使用synchronized关键字

6、属性和方法必须使用static修饰

此外不恰当的使用BTrace可能导致生产程序直接挂掉,所以在上生产环境之前,务必在开发环境充分验证脚本的正确性。

(三)Btrace能干什么

(1)分析任意接口或者方法的耗时情况

(2)分析不断的添加数据时,Map的扩容情况

(3)分析那个方法里面调用了System.gc()方法,并打印出其调用栈

(4)某些方法抛出异常时,分析其运行参数

(5)统计一些接口的调用次数

(6)分析一些方法的代码是否执行到了某一行

(7)……….

注意上面的所有操作,都是监控正在运行中的程序,基本不会产生任何影响。

(四)Btrace的安装

(1)从github上下载最新的二进制包 ,版本是1.3.10.2

wget https://github.com/btraceio/btrace/releases/download/v1.3.10.2/btrace-bin-1.3.10.2.tgz

(2)解压到指定路径

(3)设置环境变量
Java代码 收藏代码


BTRACE_HOME=/root/btrace
export BTRACE_HOME
export PATH=$PATH:$BTRACE_HOME/bin

(4)验证安装是否成功

Java代码 收藏代码

[root@es1 ~]# btrace --version
BTrace v.1.3.10 (20171121)

(5)查看btrace的帮助文档
Java代码 收藏代码

[root@es1 build]# btrace
Usage: btrace <options> <pid> <btrace source or .class file> <btrace arguments>
where possible options include:
--version Show the version
-v Run in verbose mode
-o <file> The path to store the probe output (will disable showing the output in console)
-u Run in trusted mode
-d <path> Dump the instrumented classes to the specified path
-pd <path> The search path for the probe XML descriptors
-classpath <path> Specify where to find user class files and annotation processors
-cp <path> Specify where to find user class files and annotation processors
-I <path> Specify where to find include files
-p <port> Specify port to which the btrace agent listens for clients
-statsd <host[:port]> Specify the statsd server, if any

(五)Btrace使用的一个例子

注意btrace在maven上发布的jar,版本非常低,都是几年前的版本了,如果要编写btrace脚本,建议把安装目录下三个核心jar直接拷贝到工程中临时使用即可,如果想要随时使用,也可以将其上传到你们公司的maven私服上。
Java代码 收藏代码

[root@es1 btrace]# ll build/
总用量 1572
-rw-r--r--. 1 root root 460271 12月 16 00:44 btrace-agent.jar
-rw-r--r--. 1 root root 358679 12月 16 00:44 btrace-boot.jar
-rw-r--r--. 1 root root 785219 12月 16 00:44 btrace-client.jar

(1)在使用btrace之前,我们先需要写一个简单的java程序,模拟成是线上正在跑的程序

代码比较简单,就是每隔随机的时间,随机生成两个整数,进行求和:
Java代码 收藏代码
““
package com.test;
import java.util.Random;

public class BtraceTest {

public static Random random=new Random();  

public int add (int a, int b)   {  
    int sum=a+b;  
    System.out.println("和:"+sum);  
    return a+b;  
}  

public void run(){  
    try {  
        while (true) {  
            add(random.nextInt(10), random.nextInt(10));  
            Thread.sleep(random.nextInt(10) * 100);  
        }  

    }catch (Exception e){  
        e.printStackTrace();  
    }  
}  


public static void main(String[] args)     {  
    new BtraceTest().run();  

}  

}

““

(2)主程序已经有了,下面就需要我们写btrace脚本,来监控”我们的线上程序了”
注意,需要在工程里面引入上面提到的3个jar。

监控的代码如下:
Java代码 收藏代码
““
package samples;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.*;
import com.sun.btrace.annotations.Export;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class OnlineDebug {

@OnExit//当成程序退出时,执行一些命令  
public static void onexit(int code) {  
    println("BTrace program exits! with code: " + code);  
}  

@Export //可以用来统计调用次数  
public static long counter;  

@OnMethod(clazz="com.test.BtraceTest", method="add",  
        location=@Location(value=Kind.RETURN))  
public static void m(@Self Object self, int a,int b,@Return int result,@Duration long time) {  
    BTraceUtils.println("参数: a: "+a+"  b: "+b);  
    BTraceUtils.println("花费时间:  "+time*1.0/1000+"ms");  
    jstack();//打印堆栈信息  
    counter++;  
}  

@OnEvent("1")//通过事件触发,打印当前的程序调用次数  
public static void setL1() {  
    BTraceUtils.println("executor count:  "+counter);  
}  

//监控程序是否走到第22行代码  
@OnMethod(clazz = "com.test.BtraceTest", location = @Location(value = Kind.LINE, line = 22))  
public static void onBind() {  
    println("执行到第22行");  
}  

//每隔指定时间打印一下调用次数  
 @OnTimer(5000)  
public static void run(){  
    BTraceUtils.println("executor count:  "+counter);  
}  

// //慎用 监控程序里面是否调用了,java.lang下面的一些类或方法
// @OnMethod(clazz=”/java\.lang\../”, method=”/./”)
// public static void swingMethods( @ProbeClassName String probeClass, @ProbeMethodName String probeMethod) {
// print(“entered ” + probeClass + “.” + probeMethod);
// }
}

““

(六)模拟测试

经过步骤五,我们已经准备好了模拟程序和监控脚本,下面来部署测试下:

(1)首先,通过maven将模拟程序打包成一个jar

(2)选择一台linux机器将jar包上传

(3)执行命令,启动模拟程序
Java代码 收藏代码

java -cp xxx.jar com.test.BtraceTest

linux终端输出内容如下:
Java代码 收藏代码

和:8
和:11
和:3
和:12
和:14
和:15
和:8
和:2
和:8
和:7
和:18
和:4

(4)打开另一个linux终端部署监控脚本OnlineDebug.java到指定目录

赋予执行权限:
Java代码 收藏代码

chmod +x OnlineDebug.java

执行jps命令,查看模拟程序的进程id

启动监控脚本:
Java代码 收藏代码

btrace 2139 OnlineDebug.java

等待几秒后,控制台输出如下:

Java代码 收藏代码

参数: a: 0 b: 8
花费时间: 101.973ms
com.test.BtraceTest.add(BtraceTest.java:11)
com.test.BtraceTest.run(BtraceTest.java:17)
com.test.BtraceTest.main(BtraceTest.java:28)
参数: a: 3 b: 4
花费时间: 116.611ms
com.test.BtraceTest.add(BtraceTest.java:11)
com.test.BtraceTest.run(BtraceTest.java:17)
com.test.BtraceTest.main(BtraceTest.java:28)
参数: a: 0 b: 2
花费时间: 113.168ms
com.test.BtraceTest.add(BtraceTest.java:11)
com.test.BtraceTest.run(BtraceTest.java:17)
com.test.BtraceTest.main(BtraceTest.java:28)
参数: a: 7 b: 7
花费时间: 113.434ms
com.test.BtraceTest.add(BtraceTest.java:11)
com.test.BtraceTest.run(BtraceTest.java:17)
com.test.BtraceTest.main(BtraceTest.java:28)
参数: a: 6 b: 1
花费时间: 181.184ms
com.test.BtraceTest.add(BtraceTest.java:11)
com.test.BtraceTest.run(BtraceTest.java:17)
com.test.BtraceTest.main(BtraceTest.java:28)
参数: a: 7 b: 6
花费时间: 190.881ms
com.test.BtraceTest.add(BtraceTest.java:11)
com.test.BtraceTest.run(BtraceTest.java:17)
com.test.BtraceTest.main(BtraceTest.java:28)
executor count: 44

可以看到监控脚本已经生效,成功获取到了模拟程序的内部参数,以及执行时间等。此刻另一个终端的模拟程序还在
正常运行,但内部其实已经被增强了。

如何退出监控脚本?

非常简单,执行ctrl+c命令,选择1退出即可:
Java代码 收藏代码

参数: a: 9 b: 9
花费时间: 246.743ms
com.test.BtraceTest.add(BtraceTest.java:11)
com.test.BtraceTest.run(BtraceTest.java:17)
com.test.BtraceTest.main(BtraceTest.java:28)
参数: a: 3 b: 1
花费时间: 251.039ms
^CPlease enter your option:
1. exit
2. send an event
3. send a named event
4. flush console output
1

总结:

通过上面的例子,相信大家可以感受到btrace的强大,用其来排查正在运行的程序问题非常方便,感兴趣的朋友们可以自己试一试。

参考链接:

https://github.com/btraceio/btrace/wiki/BTrace-Annotations

https://yq.aliyun.com/articles/7569

https://www.jianshu.com/p/93e94b724476

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值