神器 BTrace 快速入门

来源:互联网 发布:什么是软件功能点 编辑:程序博客网 时间:2024/05/18 09:41

原文地址:http://mp.weixin.qq.com/s?__biz=MzI0NjUxNTY5Nw==&mid=2247483848&idx=1&sn=1d566e87d37849729b15707cb472ea7e&chksm=e9bf5356dec8da40fcb4a8a3937222b8c35e74f8a6550a9422c8643ab86b1f651fbb9f32ae6a&scene=0&utm_source=tuicool&utm_medium=referral


1.使用背景

生产环境系统发生问题时,定位问题需要获取系统运行时的相关数据,如方法参数、返回值、全局变量、堆栈信息等。为了获取这些数据,需要修改代码,将数据输出到日志文件,再发布到生产环境。这种方式,一方面将增大定位问题的成本和周期,对于紧急问题无法做到及时定位及解决;另一方面重新部署后环境很大程度上已被破坏,很难重现问题。BTrace在这种背景环境下应运而生了。


2.BTrace简述

Btrace (Byte Trace)是sun推出的一款Java 动态、安全追踪(监控)工具,可以在不停机的情况下监控系统运行情况,并且做到最少的侵入,占用最少的系统资源。官方网址:https://kenai.com/projects/btrace。BTrace在使用上做了很多限制,如不能创建对象、不能使用数组、不能抛出或捕获异常、不能使用循环、不能使用synchronized关键字、脚本的属性和方法都必须使用static修饰等,具体限制条件可参考用户手册。根据官方声明,不当地使用BTrace可能导致JVM崩溃,如BTrace使用错误的.class文件,所以,可以先在本地验证BTrace脚本的正确性再使用。


3.BTrace优点

安全性:安全性不会导致对目标Java进程的任何破坏性影响;

无侵入性:无需对原有代码做任何修改,降低上线风险和测试成本,并且无需重启系统。


4.安装BTrace

1)下载地址:https://github.com/btraceio/btrace/releases/tag/v1.3.8.3-1

2)解压缩

3)设置环境变量

BTRACE_HOME=/Users/wlxs/btrace-bin-1.3.8.3

export BTRACE_HOME

export PATH=$PATH:$BTRACE_HOME/bin


5.使用btrace

作用:运行Btrace脚本

命令格式:


参数说明:



6.使用btracec

—作用:预编译BTrace脚本,在编译期验证脚本正确性。

—命令格式:


参数说明:directory指定编译结果输出路径,其它参数同btrace。


7.使用btracer

—作用:btracer命令同时启动应用程序和BTrace脚本

—命令格式:


参数说明:



8.注解说明

1)类注解

—@com.sun.btrace.annotations.BTrace指定该java类为一个btrace脚本文件。

2)属性注解

—@TLS标注的属性可以在追踪脚本的方法中通讯

3)方法注解

—@OnMethod:指定该方法在什么情况下被执行,clazz属性指定要跟踪的类的全限定类名,也可以用正则表达式,“/类名的Pattern/”匹配,如/javax\\.swing\\..*/;用”+类名”追踪所有子类,如+java.lang.Runnable;用”@xxx”追踪用该注解注解过的类,如@javax.jws.WebService。method属性指定要追踪的方法名称,也可以用正则表达式。location属性用@Location来指定该方法在目标方法执行前(后、异常、某行、某个方法调用)被执行—。

@OnTimer:定时执行该方法—。

@OnExit:当脚本运行Sys.exit(code)时执行该方法—。

@OnError:当脚本运行抛出异常时执行该方法—。

@OnEvent:脚本运行时Ctrl+C可以发送事件—。

@OnLowMemory:指定一个内存阀值,低于阀值值执行该方法—。

@OnProbe:指定一个xml文件来描述在什么时候执行该方法。

4)方法参数注解

—@Self:指目标对象本身—。

@Retrun:指目标程序方法返回值(需要配合Kind.RETURN)—。

@ProbeClassName:指目标类名。

—@ProbeMethodName:指目标方法名—。

@targetInstance:指@Location指定的clazz和method的目标(需要配合Kind.CALL)—。

@targetMethodOrField:指@Location指定的clazz和method的目标的方法或字段(需要配合Kind.CALL)—。

@Duration:指目标方法执行时间,单位是纳秒(需要需要配合Kind.RETURN或Kind.ERROR一起使用)。

—AnyType:获取对应请求的参数,泛指任意类型。


9.追踪时机参数

—Kind.Entry:开始进入目标方法时,默认值—。

Kind.Return:目标方法返回时。

—Kind.Error:异常没被捕获被抛出目标方法之外时—。

Kind.Throw:异常抛出时—。

Kind.Catch:异常被捕获时。

—Kind.Call:被调用时。

—Kind.Line:执行到某行时。


10.其它

—1)追踪构造函数—用法:@OnMethod(clazz="java.net.ServerSocket",method="<init>”)。

—2)追踪静态内部类:—在类与内部类之间加上"$"—@OnMethod(clazz="com.vip.MyServer$MyInnerClass", method="hello”)—。

3)追踪同名函数:—如果有多个同名的函数,可以在拦截函数上定义不同的参数列表—。

4)追踪结果输出—可以使用>将结果输出到指定文件。


11.示例代码

Calculator类的add方法每隔5秒对a、b两个数进行相加,代码如下。

public class Calculator {

    private int c = 1;


    public int add(int a, int b) {

        try {

            Thread.sleep(5000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        return a + b;

    }

}

BTraceDemo调用Calculator的add方法对两个随机数进行相加,代码如下。

public class BTraceDemo {

    public static void main(String[] args) {

        Calculator calculator = new Calculator();

        Random random = new Random();

        while (true) {

            System.out.println(calculator.add(random.nextInt(10), random.nextInt(10)));

        }

    }

}


1)BTraceTest则是相应的追踪脚本,代码如下。

@BTrace

public class BTraceTest {

    private static long count;

    static{

        println("---------------------------JVM properties:---------------------------");

        printVmArguments();

        println("---------------------------System properties:------------------------");

        printProperties();

        println("---------------------------OS properties:----------------------------");

        printEnv();

        exit();

    }


    @OnMethod(

            clazz = "Calculator",

            method = "add",

            location = @Location(Kind.RETURN)

    )

    public static void trace1(int a, int b, @Return int sum) {

        println("trace1:a=" + a + ",b=" + b + ",sum=" + sum);

    }

}

运行如下命令:

btrace 11308 /Users/wlxs/java/BTraceTest.java

11308是BTraceDemo的进程ID,静态块中的输出结果就不展示了。trace1方法实现Calculator类的add方法的入参和返回值进行追踪,结果如下。

trace1:a=2,b=6,sum=8


2)为了节省篇幅,下面都将只列出各个追踪的方法,trace2追踪Calculator类的add方法执行时间,默认时间单位是纳秒

  @OnMethod(

            clazz = "Calculator",

            method = "add",

            location = @Location(Kind.RETURN)

    )

    public static void trace2(@Duration long duration) {

        println(strcat("duration(nanos): ", str(duration)));

        println(strcat("duration(s): ", str(duration / 1000000000)));

    }

结果如下。

duration(nanos): 5004187000

duration(s): 5


3)trace3追踪Calculator类的add方法,并且追踪add方法中的任何类的sleep方法,代码如下。

@OnMethod(

            clazz = "Calculator",

            method = "add",

            location = @Location(value = Kind.CALL, clazz = "/.*/", method = "sleep")

    )

    public static void trace3(@ProbeClassName String pcm, @ProbeMethodName String pmn,

                              @TargetInstance Object instance, @TargetMethodOrField String method) {

        println(strcat("ProbeClassName: ", pcm));

        println(strcat("ProbeMethodName: ", pmn));

        println(strcat("TargetInstance: ", str(instance)));

        println(strcat("TargetMethodOrField : ", str(method)));

        println(strcat("count: ", str(++count)));

    }

结果如下。

ProbeClassName: Calculator

ProbeMethodName: add

TargetInstance: null

TargetMethodOrField : sleep

count: 1


4)trace4每隔6秒打印一次count的值,代码如下。

   @OnTimer(6000)

    public static void trace4() {

        println(strcat("trace4:count: ", str(count)));

    }

结果如下。

trace4:count: 1


5)trace5用于获取Calculator类的c属性的值,代码如下。

    @OnMethod(

            clazz = "Calculator",

            method = "add",

            location = @Location(Kind.RETURN)

    )

    public static void trace5(@Self Object calculator) {

        println(get(field("Calculator", "c"), calculator));

    }


6)traceMemory每隔4秒打印一次印堆和非堆内存信息,代码如下。

   @OnTimer(4000)

    public static void traceMemory() {

        println("heap:");

        println(heapUsage());

        println("no-heap:");

        println(nonHeapUsage());

    }

结果如下。

heap:

init = 10485760(10240K) used = 4430576(4326K) committed = 9961472(9728K) max = 9961472(9728K)

no-heap:

init = 24576000(24000K) used = 7813992(7630K) committed = 24576000(24000K) max = 136314880(133120K)


7)trace6每隔4秒检测是否有死锁产生,并打印产生死锁的相关类信息、对应的代码行、线程信息,代码如下。

    @OnTimer(4000)

    public static void trace6() {

        deadlocks();

    }

原创粉丝点击