Java运行时如何使用追踪工具BTrace

发布时间:2021-09-27 09:47:59 作者:柒染
来源:亿速云 阅读:247

这期内容当中小编将会给大家带来有关Java运行时如何使用追踪工具BTrace,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

1. BTrace是什么?

Btrace是由sundararajan在2009年6月开发的一个开源项目,是一种动态跟踪分析一个运行中的Java应用程序的工具。 BTrace是一个为Java平台开发的安全、动态的追踪工具。BTrace动态地向目标应用程序的字节码注入追踪代码(字节码追踪),这些追踪字节码追踪代码使用Java语言表达,也就是BTrace的脚本。

2. BTrace约束

为了保证追踪动作是“只读”的(也就是这些动作不可以修改被追踪程序的状态)和有限度的(比如在固定时间里结束)。一个BTrace程序只允许完成一些指定的动作。下面是BTrace一些不可以完成的事情:

这上面的种种限制可以通过一个配置改变:unsafe=true,在使用BTrace注解时修改该属性的默认值(false)为true,即@BTrace(unsafe=true);也可以启动选项中显式声明-Dcom.sun.btrace.unsafe=true(响应也有-u参数);现在你可以为所欲为了。BUT,这样做之前最好考虑好风险并再三检查脚本,请斟酌使用!

3. BTrace安装

btrace git下载地址 ,下载release版本下来直接解压就可以使用。

3.1 基本语法
btrace <pid> <btrace-script>脚本

btrace命令行工具运行命令如下:

btrace <options> <pid> <btrace source or .class file> <btrace arguments>
常用选项:
[-I <include-path>] [-p <port>] [-cp <classpath>]

参数说明:

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

在samples目录下有很多示例,并且有的跟踪很有用可直接使用.

4. BTrace的注解

4.1 方法注解
4.2 参数相关的注解
4.3 无注解的参数

没有注解的BTrace探测函数参数是用来作签名匹配的,因为他们必须必须在固定的位置上出现。然而,它们可以和其他的注解的参数进行交换。如果一个参数的类型是_AnyType[]_,它就会“吃”掉所所有剩下的参数。没有注解的参数的具体含义与他们所在的位置有关:

名称作用
Kind.ARRAY_GET数组元素加载
Kind.ARRAY_SET数组元素存储
Kind.CALL方法调用
Kind.CATCH异常捕获
Kind.CHECKCASTcheckcast
Kind.ENTRY方法进入。意指进入匹配probe点,跟你@Location设置的clazz和method没有任何关系
Kind.ERROR错误,异常没有捕获,返回
Kind.FIELD_GETfield获取
Kind.FIELD_SETfield设置
Kind.INSTANCEOF实例检测
Kind.LINE源代码行号
Kind.NEW创建新实例
Kind.NEWARRAY新的数组对象被创建
Kind.RETURN意指从某个匹配probe的方法中调用了匹配A class method的点,一定要和claz
Kind.SYNC_ENTRY进入一个同步方法锁
Kind.SYNC_EXIT离开一个同步方法锁
Kind.THROW抛出异常
4.4 字段相关的注解
4.5 类相关的注解

5. 使用示例

5.1 准备示例代码
package com.gitee.funcy.jtools.btrace;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

/**
 * 该程序周期性地进行网络读取操作
 *
 * @author chengyan
 * @date 2019-11-03 10:41 下午
 */
public class HoldNetTask implements Runnable{

    public void visitWeb(String strUrl) {
        URL url = null;
        URLConnection urlconn = null;
        InputStream is = null;
        try {
            url = new URL(strUrl);
            urlconn = url.openConnection();
            is = urlconn.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
            StringBuffer bs = new StringBuffer();
            String l = null;
            while((l = bufferedReader.readLine()) != null) {
                bs.append(l).append("\r\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(is != null) {
                try {
                    is.close();
                } catch (Exception e) {

                }
            }
        }
    }

    @Override
    public void run() {
        while(true) {
            visitWeb("http://www.sina.com.cn");
        }
    }

    public static void main(String[] args) {
        new Thread(new HoldNetTask()).start();
    }

}
5.2 导入相关jar包
1. 直接导入jar包

BTrace对应的java包在Btrace release文件的build目录下,一共有三个jar包:

btrace-agent.jar
btrace-boot.jar
btrace-client.jar
2. 使用maven引入jar包

也可使用maven引入依赖包,在pom文件中添加以下内容:

<properties>
	...省略其他
	<btrace.version>1.3.11.3</btrace.version>
</properties>

<dependencies>
	...省略其他
	<dependency>
		<groupId>com.sun.tools.btrace</groupId>
		<artifactId>btrace-agent</artifactId>
		<version>${btrace.version}</version>
	</dependency>
	<dependency>
		<groupId>com.sun.tools.btrace</groupId>
		<artifactId>btrace-boot</artifactId>
		<version>${btrace.version}</version>
	</dependency>
	<dependency>
		<groupId>com.sun.tools.btrace</groupId>
		<artifactId>btrace-client</artifactId>
		<version>${btrace.version}</version>
	</dependency>
</dependencies>

<repositories>
	<repository>
		<id>btrace-repo</id>
		<name>btrace-repo</name>
		<url>https://dl.bintray.com/btraceio/maven/</url>
		<releases>
			<enabled>true</enabled>
		</releases>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
	</repository>
</repositories>

需要注意的是,maven中央仓库中可能没有btrace的最新版本,为此需要配置额外的仓库。

5.3 监控函数的耗时

使用BTrace脚本可以通过正则表达式指定监控特定类的特定方法的耗时,以下代码将监控所有类中名为visitWeb()的函数的执行时间。

package com.gitee.funcy.jtools.btrace;

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;

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

/**
 * {这里添加描述}
 *
 * @author chengyan
 * @date 2019-11-03 12:13 上午
 */
@BTrace
public class PrintTimes {

    @TLS
    private static long startTime = 0;

    /**
     * 方法调用开始
     * clazz = "/.+/" : 监控做任意类
     * method="/visitWeb/":监控visitWeb方法
     */
    @OnMethod(clazz = "/.+/", method="/visitWeb/")
    public static void startMethod() {
        startTime = timeMillis();
    }

    @OnMethod(clazz = "/.+/", method="/visitWeb/", location=@Location(Kind.RETURN))
    public static void endMethod(@ProbeClassName String pcm, @ProbeMethodName String pmn) {
        println(pcm + "." + pmn + " [Time taken: " + str(timeMillis() - startTime) + "ms]");
    }
}

运行结果:

$ btrace 42188 PrintTimes.java
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 55ms]
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 50ms]
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 52ms]
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 49ms]
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 54ms]
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 54ms]
5.4 监控函数参数

除了执行时间,BTrace也可输出函数的参数:

package com.gitee.funcy.jtools.btrace;

import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;

/**
 * {这里添加描述}
 *
 * @author chengyan
 * @date 2019-11-03 10:43 下午
 */
@BTrace
public class PrintArgs {
    @OnMethod(clazz = "/.*HoldNetTask/",method = "/visitWeb/")
    public static void anyWriteFile(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {
        BTraceUtils.print(pcn + "-" + pmn);
        BTraceUtils.printArray(args);
    }
}

运行结果:

$ btrace 46877 PrintArgs.java
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
5.5 取得做任意行代码信息

通过BTrace脚本@Location注解,可以指定程序运行到某一行代码时,触发某一行为。下例显示了通过BTrace脚本获取HoldNetTask 类第27行代码的信息(当目标程序运行到第27行时,触发BTrace脚本)。

package com.gitee.funcy.jtools.btrace;

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

import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;

/**
 * {这里添加描述}
 *
 * @author chengyan
 * @date 2019-11-04 10:02 下午
 */
@BTrace
public class AllLines {

    @OnMethod(clazz = "/.*HoldNetTask/", location = @Location(value = Kind.LINE, line = 27))
    public static void online(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) {
        println(pcn + "." + pmn + ":" + line);
    }
}

运行结果:

$ btrace 46877 AllLines.java
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27

由脚本输出可以看到,BTrace正确识别出HoldNetTask类的第27行代码,正处于visitWeb() 函数的运行区间中。若将 @Location 中line的值设置为-1,则BTrace脚本将在每一行触发。

5.6 定时触发

BTrace脚本支持定时触发。可以周期性地执行某一行为,获取系统信息。下例使用@OnTimer 标记制定两个周期性任务,分别为每秒运行一次和每3秒运行一次。

package com.gitee.funcy.jtools.btrace;

import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnTimer;

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

/**
 * {这里添加描述}
 *
 * @author chengyan
 * @date 2019-11-04 10:11 下午
 */
@BTrace
public class Timers {

    @OnTimer(1000)
    public static void getUpTime() {
        println(BTraceUtils.Strings.strcat("1000 msec:", BTraceUtils.Strings.str(BTraceUtils.Sys.VM.vmUptime())));
    }

    @OnTimer(3000)
    public static void getStack() {
        jstackAll();
    }
}

运行结果:

$ btrace 46969 Timers.java
1000 msec:27654
1000 msec:28647
Thread[Attach Listener,9,system]


Thread[Signal Dispatcher,9,system]


Thread[Thread-1,9,system]

	java.net.PlainSocketImpl.socketAccept(Native Method)
	java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
	java.net.ServerSocket.implAccept(ServerSocket.java:545)
	java.net.ServerSocket.accept(ServerSocket.java:513)
	com.sun.btrace.agent.Main.startServer(Main.java:668)
	com.sun.btrace.agent.Main.access$000(Main.java:63)
	com.sun.btrace.agent.Main$2.run(Main.java:128)
	java.lang.Thread.run(Thread.java:748)

getUpTime()方法中,指定了一个每秒运行一次的任务,并打印虚拟机的启动时间。在getStack()方法中,指定了一个每3秒运行一次的任务,每次都将导出系统的线程快照。

5.7 获取类的属性

在程序运行过程中,BTrace可以在指定的位置获得对象实例的字段信息。比如,在本实例中,可以在调用URL.openConnection()时查看实际打开的URL地地址。

package com.gitee.funcy.jtools.btrace;

import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;
import com.sun.btrace.annotations.Self;
import static com.sun.btrace.BTraceUtils.*;

/**
 * {这里添加描述}
 *
 * @author chengyan
 * @date 2019-11-04 10:20 下午
 */
@BTrace
public class PrintField {
    @OnMethod(clazz = "/.*URL/", method = "/.*openConnection/", location = @Location(value = Kind.ENTRY))
    public static void visitWebEntry(@Self Object self, @ProbeClassName String pcn, @ProbeMethodName String pmn) {
        println(pcn + "." + pmn);
        println(self);
        // 只能取值static变量
        println(get(field(classOf(self), "protocolPathProp")));
        // 获得实例变量
        println(get(field(classOf(self), "host"), self));
        println("===============");
    }
}

运行结果:

$ btrace 46969 PrintField.java
java.net.URL.openConnection
http://www.sina.com.cn
java.protocol.handler.pkgs
www.sina.com.cn
===============

这里监控URL对象,在调用openConnection()方法时,获得当前对象实例self,然后获取对应实例的host等属性。

6. 注意事项

  1. 脚本中方法参数需要跟原方法参数类型保持一致

  2. 脚本中不允许使用除btrace之外的类,拼接字符串使用BTraceUtils.strcat(),打印使用BTraceUtils.println(),获取线程使用BTraceUtils.Threads

  3. BTrace植入过的代码,会一直在,直到应用重启为止。所以即使Btrace退出了,业务函数每次执行时都会执行Btrace植入的代码

上述就是小编为大家分享的Java运行时如何使用追踪工具BTrace了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。

推荐阅读:
  1. js 追踪脚本
  2. Btrace安装与使用

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java btrace

上一篇:Linux系统中的文件系统路径是什么

下一篇:如何使用Java实现Word/Excel/TXT转PDF功能

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》