deploy the transformation code in an agent

Sometimes, we cannot touch the source code and place the transformation code in.

In this case, we can use the agent to do the transformation at the class loading time.


An agent should contain a function called premain. The classloader will execute it before executing the main method of the main class. One exemplary premain is:

 public static void premain(String agentArgs, Instrumentation inst) {
    AllocationRecorder.setInstrumentation(inst);
   System.out.println("premain invoked");
    // Force eager class loading here; we need this class to do
    // instrumentation, so if we don't do the eager class loading, we
    // get a ClassCircularityError when trying to load and instrument
    // this class.
    try {     
      Class.forName("sun.security.provider.PolicyFile");
    } catch (Throwable t) {
      // NOP
    }

    if (!inst.isRetransformClassesSupported()) {
      System.err.println("Some JDK classes are already loaded and " +
          "will not be instrumented.");
    }

    // Don't try to rewrite classes loaded by the bootstrap class
    // loader if this class wasn't loaded by the bootstrap class
    // loader.

    // For classes loaded by bootclassloader, getClassLoader()==null.
    // The following tests if the transformation class is loaded by the bootclassloader
    // if not, the transformation code cannot transform the classes loaded by the bootclassloader, which include the main class 
    // and jdk classes. 
    if (AllocationRecorder.class.getClassLoader() != null) {
      canRewriteBootstrap = false;
      // The loggers aren't installed yet, so we use println.
      System.err.println("Class loading breakage: " +
          "Will not be able to instrument JDK classes");
      return;
    }

    canRewriteBootstrap = true;

    inst.addTransformer(new ConstructorInstrumenter(),
        inst.isRetransformClassesSupported());  	  
    inst.addTransformer(new AllocationInstrumenter(),
      inst.isRetransformClassesSupported());

  }
For now, let us ignore the statements at the beginning, which correspond to some checking.

The core part in premain is the last four statements:

   inst.addTransformer(new ConstructorInstrumenter(),
        inst.isRetransformClassesSupported());  	  
    inst.addTransformer(new AllocationInstrumenter(),
      inst.isRetransformClassesSupported());

These statements register the instrumentors. Internally, such instrumentors are stored in the instrumentor manager.

Later, for each class, the manager will apply each instrumentor on it.

Such processing flow is very similar to Soot. First, we register the packs, then we apply the packs on each class.

The dynamic trace discloses more insights:

java.lang.Exception: Stack trace
	at java.lang.Thread.dumpStack(Thread.java:1206)
	at com.google.monitoring.runtime.instrumentation.AllocationInstrumenter.transform(AllocationInstrumenter.java:146)
	at sun.instrument.TransformerManager.transform(TransformerManager.java:169)
	at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:365)
	at sun.misc.Launcher$ExtClassLoader.findClass(Launcher.java:229)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:300)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:252)

Note the TransformerManager is what I mean the instrumentor manager.




Now, the functional code of each instrumentor is implemented in its "transform" method. It can be implemented based on asm library as explained in previous blogs.

For now, we skip the details of how to implement it.


Suppose, we already get all transform methods implemented.
To run the agent, we need an agent jar, which includes the transformation code and the supporting libraries. One simple ant file can be used to produce such agent jar. (put it in the project folder in eclipse's workspace).The destiny jar file is named gen.jar, as specified in the jar task. The gen.jar should also contains the manifest info so that the runtime knows which entry to enter.

Note the Boot-Class-Path attribute in the manifest part. It forces the boot classloader to search the path (e.g., gen.jar) to load classes. In this way, the classes in the gen.jar and the main class are both loaded by the same boot classloader, so that, the transformation is applicable. (Same-class-loader requirement: the transformation code and the code being transformed should be loaded by the same classloader.)


<project name="project" default="default">                             
    <!--property file="ant.settings"/-->     
	<property name="tf.version" value="1.0"/>
        
        <target name="default" depends="agent-jar"/>

        <target name="agent">
        <javac                  
            destdir="bin" 
                source="1.5"    
                target="1.5"    
                debug="true"    
                        debuglevel="lines,vars,source"        
        >                                                     
            <src path="src"/>                                 
            <classpath>
                <pathelement location="lib/asm-3.3.jar"/>
                <pathelement location="lib/asm-analysis-3.3.jar"/>
                <pathelement location="lib/asm-commons-3.3.jar"/>
                <pathelement location="lib/asm-tree-3.3.jar"/>
                <pathelement location="lib/asm-util-3.3.jar"/>
                <pathelement location="lib/guava-r06.jar"/>
                <pathelement location="lib/jarjar-1.0.jar"/>
            </classpath>
        </javac>
    </target>

    <target name="agent-jar" depends="agent">
        <mkdir dir="META-INF"/>
        <manifest file="META-INF/MANIFEST.MF">
           <!--attribute name="Premain-Class" value="com.google.monitoring.runtime.instrumentation.AllocationInstrumenter"/>
           <attribute name="Can-Retransform-Classes" value="true"/>
           <attribute name="Implementation-Version" value="${tf.version}"/-->
          <attribute name="Boot-Class-Path" value="./gen.jar"/>
        <attribute name="Premain-Class" value="com.google.monitoring.runtime.instrumentation.AllocationInstrumenter"/>
        <attribute name="Can-Redefine-Classes" value="true" />
        <attribute name="Can-Retransform-Classes" value="true" />
        <attribute name="Main-Class" value="NotSuitableAsMain" />
        
       </manifest>

        <jar destfile="gen.jar" manifest="META-INF/MANIFEST.MF">
            <fileset dir="bin"/>
            
                <zipfileset  src="lib/asm-3.3.jar"/>
                <zipfileset  src="lib/asm-analysis-3.3.jar"/>
                <zipfileset  src="lib/asm-commons-3.3.jar"/>
                <zipfileset  src="lib/asm-tree-3.3.jar"/>
                <zipfileset  src="lib/asm-util-3.3.jar"/>
                <zipfileset  src="lib/guava-r06.jar"/>
                <zipfileset  src="lib/jarjar-1.0.jar"/>
        </jar>

    </target>


</project>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值