神器BTrace快速入门

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脚本
命令格式:


Paste_Image.png

参数说明:


Paste_Image.png

6.使用btracec
�作用:预编译BTrace脚本,在编译期验证脚本正确性。
�命令格式:

Paste_Image.png

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

7.使用btracer
�作用:btracer命令同时启动应用程序和BTrace脚本
�命令格式:

Paste_Image.png

参数说明:

Paste_Image.png

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();
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容