• 动态追踪技术之BTrace


    BTrace是什么

    BTrace 是一个开源项目。旨在为 java 提供安全可靠的动态跟踪分析工具。 Btrace 基于动态字节码修改技术 (Hotswap) 来实现运行时 java 程序的跟踪和替换。Btrace的脚本是用纯java 编写的,基于一套官方提供的 annotation,使跟踪逻辑实现起来异常简单。

    BTrace安装

    下载链接:https://github.com/btraceio/btrace/releases , 目前最新版本为2.2.2

    配置BTRACE_HOME环境变量

    Path添加%BTRACE_HOME%\bin

    cmd命令行输入btrace --version,出现以下界面表示成功

    BTrace 注解

    BTrace 注解指定应该将工具放置在何处,以及应该向追踪操作提供哪些数据。BTrace 注解可以分为3类:类注解、方法注解和参数注解。

    类注解

    @Btrace:表示这个类是一个BTrace脚本

    方法注解

    1. @OnMethod:用于指定跟踪方法到目标类,目标方法和目标位置
    2. @OnTimer:用于指定跟踪操作定时执行。
    3. @OnError:当 trace的代码抛异常或者错误时,该注解的方法会被执行,如果同一个trace脚本中其他方法抛异常,该注解方法也会被执行。
    4. @OnEvent:用于将跟踪方法与BTrace客户端发送的“外部”事件关联起来。
    5. OnExit:用于指定BTrace代码调用"exit(int)"内置函数以完成跟踪"session"时运行的操作。
    6. @OnLowMemory:用于跟踪超过内存阈值事件
    7. @OnProbe:用于指定以避免在BTrace脚本中使用实现内部类
    8. @Sampled:为带注释的处理程序启用采样。与@OnMethod注释一起使用

    参数注解

    1. @ProbeClassName:用于标记处理方法的参数,仅用户@OnMethod,该参数的值就是被跟踪的类名称
    2. @ProbeMethodName:用于表姐处理方法的参数,仅用户 @OnMethod,该参数值是被跟踪方法名称
    3. @Self:当前截取方法的封闭实例参数
    4. @Return:当前截取方法的的返回值,只对location=@Location(Kind.RETURN)生效
    5. @Duration:当前截取方法的执行时间
    6. @TargetInstance:当前截取方法内部调用的实例
    7. @TargetMethodOrField:当前截取方法内部被调用的方法名

    BTrace启动方式

    上篇文章介绍Java Agent的时候说过,它有两种加载方式:静态加载和动态加载。Java Agent又是BTrace底层技术之一,所以BTrace也有类似于Java Agent的加载方式的启动方式:动态启动方式和静态启动方式。

    动态启动方式

    动态启动方式用于快速附加到已经运行的应用程序、获取感兴趣的数据和分离、删除任何跟踪代码。

    语法:btrace [-p ] [-cp ] []

    • port是 BTrace 代理监听的端口,可选参数。classpath 是 BTrace 在编译期间搜索类的目录和 jar文件的集合。默认为“.”
    • pid是被追踪的 Java 程序的进程ID
    • btrace-script是btrace脚本程序。

    静态启动方式

    静态启动方式和Java Agent静态加载方式一样,在这种模式下,BTrace 甚至在应用程序启动代码运行之前就已启动。这使我们有机会追踪在应用程序生命周期的早期执行的代码。

    语法:java -javaagent:btrace-agent.jar=[[,]*]?

    agent-arg参数之间采用逗号进行分隔:

    • noServer - 不启动socket套接字服务器
    • bootClassPath - 要使用的引导类路径
    • systemClassPath - 要使用的系统类路径
    • debug - boolean类型(true/false),是否打开详细的调试消息
    • trusted- boolean类型(true/false),是否检查 btrace 限制违规
    • dumpClasses - boolean类型(true/false),是否将转换后的字节码转储到文件中
    • dumpDir - 指定转换后的类将转储到的文件夹
    • stdout - boolean类型(true/false),是否将 btrace 输出重定向到 stdout,而不是将其写入任意文件
    • probeDescPath - 搜索探测描述符 XML 的路径
    • startupRetransform - boolean类型(true/false),是否在附加时启用所有已加载类的重新转换
    • scriptdir - 包含要在代理启动时运行的脚本的目录的路径
    • scriptOutputFile - btrace脚本运行结果将要存储的路径
    • script - 在代理启动时运行的追踪脚本,脚本之间使用冒号进行分隔

    要运行的脚本必须已经被btracec编译为字节码(一个*.class*文件)。

    实战测试

    这里只测试动态运行方式。

    1. 新建一个maven项目,引入BTrace的包

    解压后会有个libs文件夹,BTrace的包可以引入本地的

    <dependency>
        <groupId>org.openjdk.btracegroupId>
        <artifactId>btrace-agentartifactId>
        <version>${btrace.version}version>
        <scope>systemscope>
        <systemPath>D:\software\btrace-v2.2.1-bin\libs\btrace-agent.jarsystemPath>
    dependency>
    
    <dependency>
        <groupId>org.openjdk.btracegroupId>
        <artifactId>btrace-bootartifactId>
        <version>${btrace.version}version>
        <scope>systemscope>
        <systemPath>D:\software\btrace-v2.2.1-bin\libs\btrace-boot.jarsystemPath>
    dependency>
    
    <dependency>
        <groupId>org.openjdk.btracegroupId>
        <artifactId>btrace-clientartifactId>
        <version>${btrace.version}version>
        <scope>systemscope>
        <systemPath>D:\software\btrace-v2.2.1-bin\libs\btrace-client.jarsystemPath>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    1. 新建一个MainTest 测试类
    public class MainTest {
    
        public static void main(String[] args) throws InterruptedException {
            while (true){
                print(UUID.randomUUID().toString());
                TimeUnit.SECONDS.sleep(2);
            }
        }
    
        private static void print(String name){
            System.out.println("时间:"+LocalDateTime.now()+","+"hello "+name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. 新建一个打印方法耗时的BTrace脚本
    import org.openjdk.btrace.core.BTraceUtils;
    import org.openjdk.btrace.core.annotations.*;
    
    @BTrace
    public class PrintMethodTime {
    
        @OnMethod(
                clazz = "com.example.jvmlearing.agent.MainTest",
                method = "print",
                location = @Location(value = Kind.CALL
                        , clazz = "/.*/" , method = "/.*/"
                        , where = Where.AFTER)
        )
        public static void method(@ProbeClassName String probeClass,
                             @ProbeMethodName String probeMethod ,
                             @Duration long duration) {
            BTraceUtils.print("class name= " + probeClass);
            BTraceUtils.print("method name =" + probeMethod);
            BTraceUtils.print("duration="+duration);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    参数解释:

    • @Location:拦截的时机,类似于AOP,在方法的执行前,执行后等时机进行拦截。
    • Kind@Location作用的探测点的种类
    • Where:探测点的位置
    1. 进入脚本所在文件夹,测试脚本
      先通过jps命令获取到MainTest 测试类的进程id,然后通过btrace命令绑定该进程id,之后就能看到脚本运行结果了

    这里只是简单的使用一下BTrace,在BTrace解压后的samples文件夹下有很多例子,有兴趣的可以去看一下。

    BTrace限制

    BTrace最终借Instrument实现class的替换。出于安全考虑,Instrument在使用上存在诸多的限制。因此BTrace脚本也有很多限制,限制如下:

    • 不允许创建对象
    • 不允许创建数组
    • 不允许抛异常
    • 不允许catch异常
    • 不允许随意调用其他对象或者类的方法,只允许调用com.sun.btrace.BTraceUtils中提供的静态方法(一些数据处理和信息输出工具)
    • 不允许改变类的属性
    • 不允许有成员变量和方法,只允许存在static public void方法
    • 不允许有内部类、嵌套类
    • 不允许有同步方法和同步块
    • 不允许有循环(for, while, do…while)
    • 不允许随意继承其他类(当然,java.lang.Object 除外)
    • 不允许实现接口
    • 不允许使用 assert
    • 不允许使用 Class 对象
      如此多的限制,其实可以理解。BTrace要做的是,虽然修改了字节码,但是除了输出需要的信息外,对整个程序的正常运行并没有影响。

    总结

    其实作为 Java的动态追踪技术,站在比较底层的角度上来说,底层无非就是基ASM、Java Attach API、Instrument开发的创建。Arthas 都是针前面这些技术的一个封装而已。

  • 相关阅读:
    WS-FLV直播协议分析
    基于天鹰算法优化概率神经网络PNN的分类预测 - 附代码
    星际编码:Swifter.Json,.NET宇宙中的数据处理新星
    高端装备制造企业项目管理实践案例(二)
    电脑基础知识—————— 删除文件
    css定位详解
    第五章:最新版零基础学习 PYTHON 教程—Python 字符串操作指南(第七节 - Python 中使用 % 进行字符串格式化)
    如何构建城市经济大脑分析指标框架?六大分析主题
    专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(十四)
    DRF02-请求响应与路由
  • 原文地址:https://blog.csdn.net/qq_39654841/article/details/127260732