zoukankan      html  css  js  c++  java
  • zorka源码解读之Instrument实现原理

    主要用到三方面技术:

    1. beanshell来实现可扩展:告诉zorkaAgent插桩的具体需求,包括插桩的方法和值、插桩的时机、插桩追踪记录方式等。
    2. Instrument来通过代理的方式访问JVM,实现在类加载的时候访问类字节码。
    3. ASM字节码操纵框架,用于实现真正底层的类字节码的修改。

    一、beanshell实现插桩具体需求(bsh如何将要插桩的内容告诉代理程序的?)
    给classTransformer添加SpyDefinition的实例,SpyDefinition里面定义了各种插桩行为,这些行为传递到底层主要封装到SpyProbe和SpyProcessor中了。SpyProbe的实例集合和SpyProcessor的实例集合都是SpyDefinition的属性。

    二、Instrument插桩过程

    1、AgentMain.premain方法的执行
    在JVM执行ClassLoader.defineClass之后,应用程序main方法执行之前,premain方法开始执行。
    premain方法中主要执行两个任务:
    1) AgentInstance.start()加载beanshell,给beanshell注册zorkaAgent中的lib,并执行bsh。bsh的执行,主要是往SpyClassTransformer(start的时候实例化transformer)中添加SpyDefinition。
    SpyDefinition中包含了插桩的所有信息。
    2)给instrumentation添加transformer。

    2、classTransformer.transform方法的执行
    每装载一个类,transformer的transform方法就会执行一次,看看是否需要转换。transform函数的最后,若返回null值,表示不需要进行类字节码的转化,否则返回转换后的class字节数组。
    transform中核心的代码是调用ASM的字节码访问技术:

    ClassReader cr = new ClassReader(cbf);//cbf是类字节码的字节数组
    ClassWriter cw = new ClassWriter(cr, cbf[7] > (byte)0x32 ? ClassWriter.COMPUTE_FRAMES : 0);
    ClassVisitor scv = createVisitor(classLoader, clazzName, found, tracer, cw);//found就是SpyDefinition的集合
    cr.accept(scv, 0);//给classReader传递字节码访问者,这个方法里面开始正式的字节码访问。
    buf = cw.toByteArray();

    1)accept中开始visitor(SpyClassVisitor)的各种visit方法的调用。比如visit(访问类声明)、visitSource、visitOuterClass、visitInnerClass、readMethod(读取方法,让指定visitor访问)、readField、visitAnnotaiton、visitTypeAnnotation、visitAttribute、visitEnd,这些方法按一定顺序被执行。在这里我们关注readMethod的调用。
    2)readMethod调用spyClassVisitor的visitMethod方法。visitMethod解析SpyDefinition,根据SpyMatcher匹配目标方法,获取SpyContext上下文传递给下一个节点访问者。比如方法访问者SpyMethodVisitor。这个对象会返回到readMethod方法中继续执行访问。
    3)SpyMethodVisitor中的visit方法会被调用。其中有visitAnnotation、visitCode、visitInsn、visitMaxs等。
    4)上面的visit访问会调用emitProbes方法。该访问会根据SpyContext中定义的SpyDefinition,SpyDefinition里面的probe进行插桩。

    private int emitProbes(int stage, SpyContext ctx)

    5)emitProbes方法遍历SpyContext里面的probe,调用probe.emit方法进行插桩。

  • 相关阅读:
    【STL源码剖析读书笔记】【第6章】算法之inplace_merge算法
    Python学习 过程中零散知识点的总结
    Python 从零学起(纯基础) 笔记 (二)
    Python 从零学起(纯基础) 笔记(一)
    用select实现监控终端输入
    实现socket非阻塞设置
    exit(0)、exit(1)、exit(-1)的区别
    解决bind错误 bind: Address already in use
    UDP编程中client和server中使用recvfrom和sendto的区别
    linux安装zookeeper
  • 原文地址:https://www.cnblogs.com/peterpanzsy/p/4594915.html
Copyright © 2011-2022 走看看