BTrace原理浅析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

之前在看agentzh的此篇博文动态追踪技术漫谈时,领会到了动态追踪技术的强大之处,也一直由于无法在不重启线上服务器的情况下排查线上问题在寻找Java中的动态追踪工具。在公司内部的JavaEE性能检测框架中,我们使用了asm做字节码注入来做线上性能的监测,沿着这个思路,如果要做到动态追踪应该是需要做字节码注入的,但是额外的一点是需要动态加载字节码替换掉原有的类的。此外,性能监测框架是需要耦合到业务应用中的,无法做到一个监测工具的灵活性。

后来听同事提到了BTrace这个工具,于是去尝试了一下。BTrace是SUN Kenai云计算开发平台下的一个开源项目,旨在为java提供安全可靠的动态跟踪分析工具。江南白衣的这篇文章http://calvin1978.blogcn.com/articles/btrace1.html做了比较详细的描述。

那么,BTrace这么神奇的功能是如何实现的呢?既然这是个开源的代码,那么直接从代码找原理。BTrace代码开源在https://github.com/btraceio/btrace

总体来说,BTrace是基于动态字节码修改技术(Hotswap)来实现运行时java程序的跟踪和替换。大体的原理可以用下面的公式描述:

Client(Java compile api + attach api) + Agent(脚本解析引擎 + ASM + JDK6 Instumentation) + Socket

BTrace的入口类在https://github.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/client/Main.java中。在其main方法中,可以看到起最终的核心逻辑是在https://github.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/client/Client.java中。方法调用如下:

  • client.compile
  • client.attach
  • client.submit

Client

首先是client.compile方法,使用的是Java compile api,将我们传递的java源文件编译为.class文件,当然你如果使用btracec提前编译了源代码,那么这里就不会有这一步。

针对官方脚本的一个例子:

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class HelloWorld {
    @OnMethod(
        clazz="java.lang.Thread",
        method="start"
    )
    public static void func() {
        println("about to start a thread!");
    }
}

@OnMethod告诉Btrace解析引擎需要代理的类和方法。 这个例子的作用是当java.lang.Thread类的任意一个对象调用 start 方法后,会调用func方法。

client端在编译完脚本之后,进行了一次字节码修改,但是仅仅是做了一些兼容性,例如域访问控制器、简写等。

接着client.attach中使用java的attach api将agent动态attach到目标jvm进程中(ava agent,通常有两种方式添加到jvm进程中:动态attach;在目标jvm启动之前添加agent参数)。

VirtualMachine vm  = VirtualMachine.attach(pid);
...
vm.loadAgent(agentPath, agentArgs);

最后client的submit方法,会向agent发送监控命令以及传递对应code的字节码。

Agent

BTrace的agent实现类就在https://github.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/agent/Main.java中,具体的实现可以看其main方法,此agent的premain和agentmain方法都是调用了这个方法。这里需要注意的一点:必须要上jdk6,因为jdk5虽然已经有了instrument api,但是其仅仅支持premain方法,也就是仅仅支持在main方法运行之前执行一些动作,而jdk6后加入了agentmain方法和VirtualMachine,是可以在main方法运行后执行的(如果是通过命令行启动的,那么agentmain方法不会被调用)。此外,在jdk6之前,程序启动之后是无法再设置boot class加载路径和system class加载路径的。而jdk6之后,instrument新增的appendToBootstrapClassLoaderSearch和appendToSystemClassLoaderSearch是可以动态添加classpath的。

agent被提交到目标jvm进程后,首先会添加boot classpath.

...
inst.appendToBootstrapClassLoaderSearch(jf);
...
inst.appendToSystemClassLoaderSearch(jf);

接着开启一个serversocket等待client的连接。之后client和agent之间的数据通讯,比如生成.class发送到agent,agent将线上程序打印的数据回传给 client都是通过socket来进行的。当agent接收到监控命令后,主要有以下两部分工作:

  • 重写类:遍历当前所有的class,根据正则找到匹配的类,用asm重写
  • 替换类:替换掉原来的class

agent接受到client发来的监控指令以及对应的参数后,会load所有的class,根据正则去匹配指定的类和方法,并使用脚本解析引擎去处理发送过来的字节码然后使用ASM将脚本里标注的类java.lang.Thread的字节码重写,植入跟踪代码或新的逻辑。在上面那个例子中,Java.lang.Thread这个类的字节码被重写并在start方法体尾部植入了func方法的调用。

BTrace的agent利用instrumentation的retransformClasses方法将原始字节码替换掉,使用的transfomer见https://github.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/runtime/BTraceTransformer.java。如下:

new ClassFileTransformer() {
    public byte[] transform(ClassLoader l, String className, Class c, ProtectionDomain pd, byte[] b) throws IllegalClassFormatException {
        // BTrace解析脚本,利用asm重写bytecode,然后classLoader加载
    }
}, true);

其中,在agent的agentmain中通过handleNewClient方法启动一个异步线程进行class transformer,而在这个异步线程中最终是通过调用https://github.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/agent/Client.java中的retransformLoaded()来进行的。

总结

其实BTrace就是使用了java attach api附加agent.jar,然后使用脚本解析引擎+asm来重写指定类的字节码,再使用instrument实现对原有类的替换。借鉴这些,我们也完全可以实现自己的动态追踪工具。









原文出处:后端技术杂谈
转载请与作者联系,同时请务必标明文章原始出处和原文链接及本声明。
目录
相关文章
|
21天前
|
Java
BTrace如何使用?
BTrace如何使用?
30 0
|
3月前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
|
7月前
|
监控 Java 测试技术
性能工具之Java分析工具BTrace入门
【5月更文挑战第25天】性能工具之Java分析工具BTrace入门
130 2
|
7月前
|
存储 XML 监控
JVM工作原理与实战(三):字节码文件的组成
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了字节码文件的基础信息、常量池、方法、字段、属性等内容。
108 6
|
7月前
|
Arthas 运维 监控
JVM工作原理与实战(四):字节码常用工具
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了字节码常用工具javap、jclasslib、Arthas等内容。
89 3
|
7月前
|
监控 安全 Java
JVM工作原理与实战(三十八):JIT即时编译器原理
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了JIT即时编译器、HotSpot中的JIT编译器、JIT优化技术、JIT优化建议等内容。
129 0
|
缓存 监控 前端开发
JVM学习日志(三) Java代码执行流程
简述 Java代码执行流程
106 0
JVM学习日志(三) Java代码执行流程
|
安全 IDE Java
【Java应用诊断工具】「BTrace」基本概念和原理的介绍(1)
【Java应用诊断工具】「BTrace」基本概念和原理的介绍(1)
178 0
|
Java
Java GC 日志详解(一图读懂)
每一种回收器的日志格式都是由其自身的实现决定的,换而言之,每种回收器的日志格式都可以不一样。但虚拟机设计者为了方便用户阅读,将各个回收器的日志都维持一定的共性。本文简单介绍下这些共性。
11536 0
|
小程序 Java 程序员
BTrace 入门教程
很久没发文了,不知道小伙伴们是不是忘记我们了?主要是最近我和znlover在利用业余时间开发一款小程序,一直没时间写文章,小程序目前在内测阶段,在接下来的时间,我们会持续更新文章。在此,给支持我们的读者说声谢谢,感谢你们一直在默默支持我们。
322 0
BTrace 入门教程