博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
BTrace 原理浅析
阅读量:6528 次
发布时间:2019-06-24

本文共 2884 字,大约阅读时间需要 9 分钟。

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

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

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

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

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

BTrace的入口类在中。在其main方法中,可以看到起最终的核心逻辑是在中。方法调用如下:

  • 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.*;@BTracepublic 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实现类就在中,具体的实现可以看其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见。如下:

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,而在这个异步线程中最终是通过调用中的retransformLoaded()来进行的。

总结

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

转载地址:http://jwxbo.baihongyu.com/

你可能感兴趣的文章
iOS.ReactNative-3-about-viewmanager-uimanager-and-bridgemodule
查看>>
透视校正插值
查看>>
【转载】WinCE6.0 Camera驱动源码分析(二)
查看>>
Cobertura代码覆盖率测试
查看>>
【selenium学习笔记一】python + selenium定位页面元素的办法。
查看>>
Linux禁止ping
查看>>
【Matplotlib】 标注一些点
查看>>
[AX]乐观并发控制Optimistic Concurrency Control
查看>>
自定义类加载器
查看>>
MySQL数据库事务各隔离级别加锁情况--Repeatable Read && MVCC(转)
查看>>
C++构造函数例程
查看>>
把某一列值转换为逗号分隔字符串
查看>>
DLL,DML,DCL,TCL in Oracle
查看>>
android之存储篇_存储方式总览
查看>>
SSE指令集学习:Compiler Intrinsic
查看>>
两种attach to process的方法
查看>>
WCF如何使用X509证书(安装和错误)(二)
查看>>
Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)
查看>>
iOS中--NSArray调用方法详解 (李洪强)
查看>>
java异步操作实例
查看>>