之前在看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
首先是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吸收到监控呼吁后,主要有以下两部门事情:
agent接管到client发来的监控指令以及对应的参数后,会load所有的class,按照正则去匹配指定的类和要领,并利用剧本理会引擎去处理惩罚发送过来的字节码然后利用ASM将剧本里标注的类java.lang.Thread的字节码重写,植入跟踪代码或新的逻辑。在上面谁人例子中,Java.lang.Thread这个类的字节码被重写并在start要领体尾部植入了func要领的挪用。