btrace使用教程

背景

在日常开发中,有一些常见的环境,比如Dev、UAT、预发、生产等,当然并不是每个公司都是这样。有时候开发环境一切正常,但是到线上的UAT环境或预发等等会出现各种问题,那么你是不是经常需要进行本地修改代码、提交、编译、打包、上传、运行、查看日志等这一系列步骤呢?这种方式不仅低效、繁琐而且容易引入诸多不可控的因素,比如你在任意一个环节出现问题,可能都会影响到程序最终的运行结果。而如果能有一种神器,可以对正在运行的程序,进行动态追踪、错误诊断、性能剖析等,是不是无形中为你延长了生命呢?如果你之前不知道也就罢了,然而如果你看到这里了,却还不学习的话,就是你自己的锅了。

Java运行时追踪工具

常见的动态追踪工具有BTraceHouseMD(该项目已经停止开发)、Greys-Anatomy(国人开发,个人开发者)、Byteman(JBoss出品),注意Java运行时追踪工具并不限于这几种,但是这几个是相对比较常用的,本文主要介绍BTrace。

BTrace

BTrace简介

BTrace是SUN Kenai云计算开发平台下的一个开源项目,旨在为java提供安全可靠的动态跟踪分析工具。先看一下BTrace的官方定义

BTrace is a safe, dynamic tracing tool for the Java platform. BTrace can be used to dynamically trace a running Java program (similar to DTrace for OpenSolaris applications and OS). BTrace dynamically instruments the classes of the target application to inject tracing code (“bytecode tracing”)

简洁明了,大意是一个Java平台的安全的动态追踪工具。可以用来动态地追踪一个运行的Java程序。BTrace动态调整目标应用程序的类以注入跟踪代码(“字节码跟踪”)。

动手之前再了解一下BTrace的主要术语

  • Probe Point: “location” or “event” at which a set of tracing statements are executed. Probe point is “place” or “event” of interest where we want to execute some tracing statements.(探测点,就是我们想要执行一些追踪语句的地方或事件)
  • Trace Actions or Actions: Trace statements that are executed whenever a probe “fires”.(当探测触发时执行追踪语句)
  • Action Methods: BTrace trace statements that are executed when a probe fires are defined inside a static method a class. Such methods are called “action” methods.(当在类的静态方法中定义了探测触发时执行的BTrace跟踪语句。这种方法被称为“操作”方法。)

安装BTrace

目前,BTrace已经托管在Github上了,主页在这里,下载地址在这里,目前最新版本是V1.3.9。新建环境变量BTRACE_HOME值为E:/btrace-bin-1.3.9,然后编辑Path变量,在值的末尾追加;%BTRACE_HOME%/bin即可,验证是否安装成功,打开cmd,输入btrace,显示如下则证明配置成功

Usage: btrace 
where possible options include:
–version Show the version
-v Run in verbose mode
-o The path to store the probe output (will disable showing the output in console)
-u Run in trusted mode
-d Dump the instrumented classes to the specified path
-pd The search path for the probe XML descriptors
-classpath Specify where to find user class files and annotation processors
-cp Specify where to find user class files and annotation processors
-I Specify where to find include files
-p Specify port to which the btrace agent listens for clients
-statsd Specify the statsd server, if any

根据上面的提示,btrace使用起来很简单,而且官方提供了一个简易的使用指南,在解压下载的压缩包中E:/btrace-bin-1.3.9/docs下有usersguide.html,用浏览器打开即可。BTrace支持四种方式的注解,分别是

扫描二维码关注公众号,回复: 2806647 查看本文章
  • Method Annotations
    • @com.sun.btrace.annotations.OnMethod
    • @com.sun.btrace.annotations.OnTimer
    • @com.sun.btrace.annotations.OnError
    • @com.sun.btrace.annotations.OnExit
    • @com.sun.btrace.annotations.OnEvent
    • @com.sun.btrace.annotations.OnLowMemory
    • @com.sun.btrace.annotations.OnProbe
  • Argument Annotations
    • @com.sun.btrace.annotations.Self
    • @com.sun.btrace.annotations.Return
    • @com.sun.btrace.annotations.CalledInstance
    • @com.sun.btrace.annotations.CalledMethod
  • Field Annotations
    • @com.sun.btrace.annotations.Export
    • @com.sun.btrace.annotations.Property
    • @com.sun.btrace.annotations.TLS
  • Class Annotations
    • @com.sun.btrace.annotations.DTrace
    • @com.sun.btrace.annotations.DTraceRef
    • @com.sun.btrace.annotations.BTrace

关于这些注解的具体解释可以去翻看docs目录下的用户指南,好了,废话不多说,下面简单操练起来。

使用示例

如果是在Maven项目中开发,那么首先需要引入BTrace的Jar包,由于Maven的中央仓库中只有1.x版本的BTrace,并没有高版本的,所以一般的做法是自己编译BTrace源码,将高版本的Jar发布到私服(Nexus)中,为简单起见,此处通过Maven指定依赖本地Jar即可,修改pom.xml

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

 

<dependency>

<groupId>com.sun.tools.btrace</groupId>

<artifactId>btrace-agent</artifactId>

<version>1.3.9</version>

<scope>system</scope>

<systemPath>E:/btrace-bin-1.3.9/build/btrace-agent.jar</systemPath>

</dependency>

<dependency>

<groupId>com.sun.tools.btrace</groupId>

<artifactId>btrace-boot</artifactId>

<version>1.3.9</version>

<scope>system</scope>

<systemPath>E:/btrace-bin-1.3.9/build/btrace-boot.jar</systemPath>

</dependency>

<dependency>

<groupId>com.sun.tools.btrace</groupId>

<artifactId>btrace-client</artifactId>

<version>1.3.9</version>

<scope>system</scope>

<systemPath>E:/btrace-bin-1.3.9/build/btrace-client.jar</systemPath>

</dependency>

首先编写想要追踪的示例,如下

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

 

import java.util.concurrent.TimeUnit;

public class BTraceOnMethodDemo {

public static void main(String[] args) {

try {

TimeUnit.SECONDS.sleep(15);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("start main method...");

new Thread(() -> {

while (true) {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}).start();

}

}

再编写追踪代码示例,如下

 

1

2

3

4

5

6

7

8

9

10

11

12

 

import com.sun.btrace.annotations.BTrace;

import com.sun.btrace.annotations.OnMethod;

import static com.sun.btrace.BTraceUtils.println;

@BTrace

public class Tracer {

@OnMethod(clazz = "java.lang.Thread", method = "start")

public static void onThreadStart() {

println("tracing method start");

}

}

注意点

  • Tracer类中的println方法并不是JDK中的方法,而是BTraceUtils中的静态方法
  • 在追踪代码,原则上只能使用BTraceUtils中的静态方法,如果想要使用JDK中的方法,那么在命令行中需要使用-cp指定依赖的Jar
  • OnMethod这个注解只会在方法启动的时候被触发,如果该方法已经启动,再运行追踪代码是无法触发的,所以在示例中,先休息了15s钟

运行BTraceOnMethodDemo,打开cmd,到Tracer类所在的目录下,运行

D:/Demo/src/main/java>jps
39808 Launcher
15480 RemoteMavenServer
40280 Jps
41880 AppMain
4764
D:/Demo/src/main/java>btrace -v 41880 Tracer.java
DEBUG: assuming default port 2020
DEBUG: assuming default classpath ‘.’
DEBUG: compiling Tracer.java
DEBUG: compiled Tracer.java
DEBUG: attaching to 41880
DEBUG: checking port availability: 2020
DEBUG: attached to 41880
DEBUG: loading E:\btrace-bin-1.3.9\build\btrace-agent.jar
DEBUG: agent args: port=2020,statsd=,debug=true,bootClassPath=.,systemClassPath=
C:\Program Files\Java\jdk1.8.0_45\jre/../lib/tools.jar,probeDescPath=.
DEBUG: loaded E:\btrace-bin-1.3.9\build\btrace-agent.jar
DEBUG: registering shutdown hook
DEBUG: registering signal handler for SIGINT
DEBUG: submitting the BTrace program
DEBUG: opening socket to 2020
DEBUG: setting up client settings
DEBUG: sending instrument command
DEBUG: entering into command loop
DEBUG: received com.sun.btrace.comm.OkayCommand@396f6598
DEBUG: received com.sun.btrace.comm.RetransformationStartNotification@394e1a0f
DEBUG: received com.sun.btrace.comm.OkayCommand@27a5f880
DEBUG: received com.sun.btrace.comm.MessageCommand@53f65459
DEBUG: received com.sun.btrace.comm.MessageCommand@3b088d51
tracing method start

注意要带上-v参数,否则控制台看不到任何输出,另外还可以利用-o参数将信息输出到指定的文件,运行BTraceOnMethodDemo,打开cmd,到Tracer类的目录下,运行

D:/Demo/src/main/java>jps
12064 Jps
24560 AppMain
41764 Launcher
15480 RemoteMavenServer
4764
D:/Demo/src/main/java>btrace -o out.csv 24560 Tracer.java

注意这时候out.csv文件时在Tracer.java所在目录的根目录下,也就是在D:/Demo下,在当前目录下是找不到的,是不是很变态。找到out.csv打开看看,就是追踪代码的输出内容

BTrace Log: 17-9-20 下午3:16
tracing method start

好了,整个流程就打通了,剩下的就是自己动手实战吧。此处仅给出一个简单示例,详情可以参看BTrace的用户指南,里面给出了更多详细的示例,只要打开动手一一实战即可。

BTrace虽然功能强大,但是并不完美,这是因为它有着诸多的限制,例如

  • can not create new objects.
  • can not create new arrays.
  • can not throw exceptions.
  • can not catch exceptions.
  • can not make arbitrary instance or static method calls - only the public static methods of com.sun.btrace.BTraceUtils class may be called from a BTrace program.
  • can not assign to static or instance fields of target program’s classes and objects. But, BTrace class can assign to it’s own static fields (“trace state” can be mutated).
  • can not have instance fields and methods. Only static public void returning methods are allowed for a BTrace class. And all fields have to be static.
  • can not have outer, inner, nested or local classes.
  • can not have synchronized blocks or synchronized methods.
  • can not have loops (for, while, do..while)
  • can not extend arbitrary class (super class has to be java.lang.Object)
  • can not implement interfaces.
  • can not contains assert statements.
  • can not use class literals.

BTrace命令详解

  • btrace
    功能:用于运行BTrace跟踪程序。
    命令格式:
    btrace [-I <include-path>] [-p <port>] [-cp <classpath>] <pid> <btrace-script> [<args>]
    示例:
    btrace -cp build/ 1200 AllCalls1.java
    参数含义:
    include-path指定头文件的路径,用于脚本预处理功能,可选;
    port指定BTrace agent的服务端监听端口号,用来监听clients,默认为2020,可选;
    classpath用来指定类加载路径,默认为当前路径,可选;
    pid表示进程号,可通过jps命令获取;
    btrace-script即为BTrace脚本;btrace脚本如果以.java结尾,会先编译再提交执行。可使用btracec命令对脚本进行预编译。
    args是BTrace脚本可选参数,在脚本中可通过$$length获取参数信息。

  • btracec
    功能:用于预编译BTrace脚本,用于在编译时期验证脚本正确性。
    btracec [-I <include-path>] [-cp <classpath>] [-d <directory>] <one-or-more-BTrace-.java-files>
    参数意义同btrace命令一致,directory表示编译结果输出目录。

  • btracer
    功能:btracer命令同时启动应用程序和BTrace脚本,即在应用程序启动过程中使用BTrace脚本。而btrace命令针对已运行程序执行BTrace脚本。
    命令格式:
    btracer <pre-compiled-btrace.class> <application-main-class> <application-args>
    参数说明:
    pre-compiled-btrace.class表示经过btracec编译后的BTrace脚本。
    application-main-class表示应用程序代码;
    application-args表示应用程序参数。
    该命令的等价写法为:
    java -javaagent:btrace-agent.jar=script=<pre-compiled-btrace-script1>[,<pre-compiled-btrace-script1>]* <MainClass> <AppArguments>

BTrace基本就介绍完了,但是BTrace并不是完美的,比如当你想要追踪一个局部变量的,查看具体值的时候,却无能为力,不仅扼腕叹息,真是天妒英才啊,这么小的一个需求都无法cover?不用着急,后面就介绍一个更加强大的工具,Byteman。

Btrace使用教程

下载

下载链接:https://github.com/btraceio/btrace/releases/tag/v1.3.9

安装及环境配置

1.下载一个压缩包

2.解压

3.配置环境变量 sudo vi /etc/profile 添加   export BTRACE\_HOME=/home/josonliu/btrace     export PATH=$PATH:$BTRACE\_HOME/bin PS:BTRACE\_HOME必须是你解压的路径

4.使配置生效  source /etc/profile  这样就可以在任何地方使用 btrace 命令了

BTrace简介及使用须知

BTrace是一个可以对 JAVA 进行安全、动态追踪的工具。为了保证在追踪动作的只读性,追踪动作不能改变程序的状态。一般来说 BTrace 具体有以下限制(禁令):

1.不准创建新对象!

2.不准创建新数组!

3.不准抛出异常!

4.不准捕捉导常!

5.不准使用断言或静态方法 只准使用 com.sun.btrace.BTraceUtils 中定义的类和方法及脚本里定义的 static 方法。

6.不准对追踪的类或对象进行赋值操作

7.不准使用外部、内部、嵌入或本地类

8.不准实现接口

9.不准使用循环

其实就是一句话 只使用 println\(\) 方法进行打印信息就好 哈哈

使用方法

1.找到要监控的 JVM进程 PID 

    a.通过 top -c 命令找到

    b.通过 ps -ef \| grep 对应进程标识

2.切换到进程拥有账户 一般为 www-data

    a.sudo -s 切换到 root 账户

    b.btracec 监控脚本  对监控脚本进行预编译 这一点很重要,可以在运行前发现错误。特别是应用到线上环境,必须强制先预编译一下,看是否报错。

    c.sudo -u www-data btrace $PID $监控脚本

3.如需修改监控只需要停止运行后 修改脚本 然后运行脚本即可。

4.BTrace脚本在进程重启后会失效。

使用场景

1.查看某一个方法中入参

2.查看某一个方法的响应时间

3.查看某一个方法中所有外部调用的响应时间,方便定位方法响应慢的具体位置及原因

4.查看谁调用了 System.gc(),及其对应的调用栈

实战DEMO

1.监控指定方法的耗时

import com.sun.btrace.annotations.\*;

import static com.sun.btrace.BTraceUtils.\*;

@BTrace

public class CheckOnlineStatus{

    //监控某一个方法的执行时间

    @OnMethod\(clazz = "com.joson.btrace.service.impl.BtraceServiceImpl",method = "getCount",location=@Location\(Kind.RETURN\)\)

    public static void printMethodRunTime\(@ProbeClassName String probeClassName,@Duration long duration\){

        println\(probeClassName + ",duration:" + duration / 1000000 + " ms"\);

    }

}

这里是监控 BtraceServiceImpl 方法中 getCount 的调用情况。duration是以纳秒为单位的,所以换算成 MS 比较好看一点 ,其他例子也是如此考虑。

2.监控指定函数中所有外部调用的耗时情况.PS:这里最好只监控一个函数 太多的话 性能没法看

import com.sun.btrace.annotations.\*;

import static com.sun.btrace.BTraceUtils.\*;

@BTrace

public class CheckOnlineStatus{

    //监控某一个方法的执行时间

    @OnMethod\(clazz = "com.joson.btrace.service.impl.BtraceServiceImpl",method = "getCount",

    location=@Location\(value=Kind.CALL,clazz="/.\*/",method="/.\*/",where = Where.AFTER\)\)

    public static void printMethodRunTime\(@Self Object self,@TargetInstance Object instance,@TargetMethodOrField String methon,@Duration long duration\){

        if\( duration &gt; 5000000 \){//如果耗时大于 5 毫秒则打印出来 这个条件建议加 否则打印的调用函数太多 具体数值可以自己调控

            println\(methon + ",cost:" + duration / 1000000 + " ms"\);

        }



    }

}

这里是监控 BtraceServiceImpl 类中 getCount 方法内的外部方法调用情况并打印出响应时间大于 5 MS 的外部调用方法名 。

通过注入 @TargetInstance 和 @TargetMethodOrField 参数,告诉脚本实际匹配到的外部函数调用的类及方法名\(或属性名\)

3.按接口、父类监控方法的执行

import com.sun.btrace.annotations.\*;

import static com.sun.btrace.BTraceUtils.\*;

@BTrace

public class InterfaceMonitor{

    //监控某一个方法的执行时间

    @OnMethod\(clazz = "+com.joson.btrace.service.BtraceService",method = "getCount",location=@Location\(Kind.RETURN\)\)

    public static void printMethodRunTime\(@ProbeClassName String probeClassName,@Duration long duration\){

        println\(probeClassName + ",cost time:" + duration / 1000000 + " ms"\);

    }

}

这里是监控 BtraceService 接口的所有实现类中 对 getCount 方法的调用情况。

4.正则表达式定位监控

通过正则表达式可以实现批量定位,正则表达式需要写在两个 "/" 中间。PS:建议正则表达式的范围要尽可能的小,不然会非常慢。

import com.sun.btrace.annotations.\*;

import static com.sun.btrace.BTraceUtils.\*;

@BTrace

public class ServiceMonitor{

    //监控某一个方法的执行时间

    @OnMethod\(clazz = "/com.joson.btrace.service.\*/",method = "/.\*/",location=@Location\(Kind.RETURN\)\)

    public static void printMethodRunTime\(@ProbeClassName String probeClassName,@ProbeMethodName String probeMethod,@Duration long duration\){

        println\( probeClassName + "." + probeMethod + " cost time: " + duration / 1000000 + " ms."\);

    }

}



这里是监控 com.joson.btrace.service 包下的所有类与方法,并打印其调用时间 以 MS 为单位。

通过在函数里注入 @ProbeClassName,@ProbeMethodName 参数,告诉脚本实际匹配到的类和方法名。

5.监控代码是否到达了某类的某一行

import com.sun.btrace.annotations.\*;

import static com.sun.btrace.BTraceUtils.\*;

@OnMethod\(clazz = "java.net.ServerSocket", location = @Location\(value = Kind.LINE, line = 363\)\)

public static void onBind4\(\) {

   println\("socket bind reach line:363"\);



}

这里是监控代码是否到达了 Stock类的 363 行。

6.打印某个类中 某一方法的入参

import com.sun.btrace.AnyType;

import com.sun.btrace.annotations.\*;

import static com.sun.btrace.BTraceUtils.\*;

@BTrace

public class ServiceMonitor{

    //监控某一个方法的执行时间

    @OnMethod\(clazz = "com.joson.btrace.service.BtraceService",method = "getCount",location=@Location\(Kind.RETURN\)\)

    public static void printMethodRunTime\(@Self Object self,String type,Integer limit,@Return AnyType result \){

        println\( "type: " + type + " ,limit: " + limit  \);

        println\("result : " + result \);

    }

}

这里是监控 BtraceService 类中 getCount 方法的所有入参及返回值

对于入参,不需要打印的也可以不定义 但是定义一定要按顺序。比如参数列表不能放在返回值的后面。

对于返回值类型 如果是非基本类型 则直接用 AnyType 类型即可。

7.查看谁调用了 GC

import com.sun.btrace.annotations.\*;

import static com.sun.btrace.BTraceUtils.\*;

@OnMethod\(clazz = "java.lang.System", method = "gc"\)

public static void onSystemGC\(\) {

    println\("entered System.gc\(\)"\);

    jstack\(\);// print the stack info.

}

8.其他用法参考链接:https://github.com/btraceio/btrace/tree/master/samples

猜你喜欢

转载自blog.csdn.net/ZYC88888/article/details/81662671
今日推荐