美文网首页JVM
BTrace实现原理

BTrace实现原理

作者: 十毛tenmao | 来源:发表于2018-12-22 13:29 被阅读125次

    BTrace是每个Java程序员必备的瑞士军刀,可以实现线上服务器不重启增加调试信息。本文简单介绍一下其实现原理。

    BTrace工作原理

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

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

    BTrace工作序列图

    BTrace工作序列图

    Java Compile API

    BTrace使用Compile API把用户编写的源码文件编译成字节码文件

    在 JDK 6 中,类库通过 javax.tools 包提供了程序运行时调用编译器的 API。

    public class Compiler {
        public static void main(String[] args) throws Exception {
            String fullQuanlifiedFileName = "compile" + java.io.File.separator + "Target.java";
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            FileOutputStream err = new FileOutputStream("err.txt");
            int compilationResult = compiler.run(null, null, err, fullQuanlifiedFileName);
            if (compilationResult == 0) {
                System.out.println("Done");
            } else {
                System.out.println("Fail");
            }
        }
    }
    

    Java Attach API

    BTrace使用Attach API把BTrace Agent附加到待调试的JVM上去

    Attach API不是Java的标准API,而是Sun公司提供的一套扩展API,用来向目标JVM"附着"(Attach)代理工具程序的。有了它,开发者可以方便的监控一个JVM,运行一个外加的代理程序,Sun JVM Attach API功能上非常简单,仅提供了如下几个功能:

    1. 列出当前所有的JVM实例描述
    2. Attach到其中一个JVM上,建立通信管道
    3. 让目标JVM加载Agent

    ASM

    BTrace使用ASM修改当前类,附加调试信息,得到新的类

    我们都知道,一般情况下,Class文件是通过javac编译器产生的,然后通过类加载器加载到虚拟机内,再通过执行引擎去执行。现在我们可以通过ASM的API直接生成符合Java虚拟机规范的Class字节流,这样,ASM做的事情一定程度上正是javac解释器做的工作。

    可以说ASM分析一个类、从字节码角度创建一个类、修改一个已经被编译过的类文件。

    Instrumentation

    BTrace使用JVM Instrumentation动态替换当前类

    利用 Java 代码,即 java.lang.instrument 做动态 Instrumentation 是 Java SE 5 的新特性,它把 Java 的 instrument 功能从本地代码中解放出来,使之可以用 Java 代码的方式解决问题。使用 Instrumentation,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义。有了这样的功能,开发者就可以实现更为灵活的运行时虚拟机监控和 Java 类操作了,这样的特性实际上提供了一种虚拟机级别支持的 AOP 实现方式,使得开发者无需对 JDK 做任何升级和改动,就可以实现某些 AOP 的功能了。

    关键逻辑

    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

    核心流程代码如下:

    try {
        Client client = new Client(port, OUTPUT_FILE, PROBE_DESC_PATH,
            DEBUG, TRACK_RETRANSFORM, TRUSTED, DUMP_CLASSES, DUMP_DIR, statsdDef);
        if (! new File(fileName).exists()) {
            errorExit("File not found: " + fileName, 1);
        }
        //编译Java源码
        byte[] code = client.compile(fileName, classPath, includePath);
        if (code == null) {
            errorExit("BTrace compilation failed", 1);
        }
        if (!hostDefined)
            //调用JVM的Attach API,在被调试JVM中attach一个Agent
            client.attach(pid, null, classPath);
        registerExitHook(client);
        if (con != null) {
            registerSignalHandler(client);
        }
        if (isDebug()) debugPrint("submitting the BTrace program");
        //提交调试代码(被调试JVM中的Agent收到代码后使用ASM修改类定义)
        client.submit(host, fileName, code, btraceArgs, createCommandListener(client));
    } catch (IOException exp) {
        errorExit(exp.getMessage(), 1);
    }
    

    BTrace系列

    参考

    相关文章

      网友评论

        本文标题:BTrace实现原理

        本文链接:https://www.haomeiwen.com/subject/urfikqtx.html