Creation, dynamic loading and instrumentation with javaagents

Source: Internet
Author: User
Tags vcard

Copy From:http://dhruba.name/2010/02/07/creation-dynamic-loading-and-instrumentation-with-javaagents/creation, Dynamic loading and instrumentation with javaagentsposted on February 7, inDhruba Bandopadhyay 19 Comments

referrers: Reverseengineeringstackexchange, StackOverflow.

Sometime back I had to delve into the depths of what one could dynamically and programmatically load a javaagent at runtime –in other words how a javaagent could is attached to a running process. Since then I ' ve been meaning to blog my findings. Finally, after quite some time, I ' ve got around to it but here I take the opportunity to expand the scope of the article T o A complete treatment of what to create a javaagent, load it statically at startup or dynamically at runtime and also how To perform instrumentation of a simple class. As always the code would reside in a eclipse MAVEN project that would be made available for download.

Introduction

Java agents is self contained components through which application classes pass at the level of byte code instructions in The form of byte arrays. They were introduced in Java5 along with the powerful java.lang.instrument package. Java agents can loaded statically at startup or dynamically (programmatically) at runtime to attach to a running proces s in a fail-safe fashion.

Lifecycle of a Javaagent

The most important thing to understand are the lifecycle of a javaagent and its behaviour in relation to the application it Self. The lifecycle hook points is as follows.

    • Premain–the Premain method is a invoked first prior to running the main method. The Hookpoint for loading the javaagent statically at startup.
    • Main–the Main method is always invoked after the Premain invocation.
    • Agentmain–the Agentmain method can be invoked at any time before or after the Main method. This hookpoint are for loading the javaagent dynamically at runtime and attaching to a running process.
Creation of a Javaagent

In order to create a Java agent must first define your chosen hooks points from the lifecycle above as below.

Package Name.dhruba.javaagent;import Java.lang.instrument.instrumentation;import Org.slf4j.logger;import Org.slf4j.loggerfactory;public class Myjavaagent {static final Logger Logger = Loggerfactory.getlogger (myjavaagent.cla    SS);    private static instrumentation instrumentation;     /** * JVM hook to statically load the javaagent at startup. * * After the Java Vsan (JVM) has initialized, the Premain method * would be called.     Then the real application main method would be called. * * @param args * @param inst * @throws Exception */public static void Premain (String args, instrumen        Tation Inst) throws Exception {Logger.info ("Premain method invoked with args: {} and Inst: {}", args, inst);        instrumentation = Inst;    Instrumentation.addtransformer (New Myclassfiletransformer ());     }/** * JVM hook to dynamically load javaagent at runtime. * * The Agent class may has a Agentmain method for use when the AGent is * started after VM startup. * * @param args * @param inst * @throws Exception */public static void Agentmain (String args, Instrum        Entation Inst) throws Exception {Logger.info ("Agentmain method invoked with args: {} and Inst: {}", args, inst);        instrumentation = Inst;    Instrumentation.addtransformer (New Myclassfiletransformer ());     }/** * Programmatic hook to dynamically load javaagent at runtime.        */public static void Initialize () {if (instrumentation = = null) {myjavaagentloader.loadagent (); }    }}

Once you ' ve defined your hooks points you must make the JVM aware by putting them in a manifest file.

Main-class:name.dhruba.javaagent.mymainclassagent-class:name.dhruba.javaagent.myjavaagentcan-redefine-classes: Truecan-retransform-classes:truepremain-class:name.dhruba.javaagent.myjavaagent

In the example project I m doing so using Maven and the MAVEN assembly plugin which also packages the project as a single All inclusive Uber executable jar for ease of testing.

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactid>maven-assembly-plugin      </artifactId> <executions> <execution> <goals> <goal>attached</goal>          </goals> <phase>package</phase> <configuration> <descriptorRefs>          <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>name.dhruba.javaagent.MyMainClass</mainClass> </ma Nifest> <manifestEntries> <premain-class>name.dhruba.javaagent.myjavaagent</premain- Class> <Agent-Class>name.dhruba.javaagent.MyJavaAgent</Agent-Class> <can-redefine- Classes>true</can-redefine-classes> <can-retransform-classes>true</can-retransform-classes&gt          ; </manifestentries> </archive> </configuration> </execution> </executions></plugin>
instrumenting classes

Within the Javaagent hook points One can define implementations that is invoked for all java.lang.instrument.ClassFileTransformer classes being loaded within An application. These receive classes as byte arrays and has the option of redefining them also in terms of byte arrays. Here I provide an example class file transformation.

The following User Pojo returns ' Foo ' as its name.

Package Name.dhruba.user;public class MyUser {public    String getName () {        return ' foo ';    }}

The following class file transformer (using ASM) redefines the User Pojo to return ' bar ' instead.

Package Name.dhruba.javaagent;import Java.lang.instrument.classfiletransformer;import Java.lang.instrument.illegalclassformatexception;import Java.security.protectiondomain;import Org.objectweb.asm.classwriter;import Org.objectweb.asm.label;import Org.objectweb.asm.methodvisitor;import Org.objectweb.asm.opcodes;import Org.slf4j.logger;import Org.slf4j.loggerfactory;public Class Myclassfiletransformer implements Classfiletransformer, opcodes {static final Logger Logger = Loggerfactory.getlogger (    Myclassfiletransformer.class); @Override public byte[] Transform (ClassLoader loader, String className, Class classbeingredefined, Protectio Ndomain Protectiondomain, byte[] classfilebuffer) throws Illegalclassformatexception {Logger.info ("Clas        s file transformer invoked for ClassName: {} ", className);            if (Classname.equals ("Name/dhruba/user/myuser")) {Classwriter CW = new Classwriter (0);            Methodvisitor MV; Cw.visit (V1_6, Acc_public + acc_super, "Name/dhruba/user/myuser", NULL, "Java/lang/object", null);            Cw.visitsource (null, NULL);                {mv = Cw.visitmethod (Acc_public, "", "() V", NULL, NULL);                Mv.visitcode ();                Label L0 = new label ();                Mv.visitlabel (L0);                Mv.visitlinenumber (3, L0);                MV.VISITVARINSN (aload, 0);                MV.VISITMETHODINSN (invokespecial, "Java/lang/object", "" "," () V ");                MV.VISITINSN (RETURN);                Label L1 = new label ();                Mv.visitlabel (L1);                Mv.visitlocalvariable ("This", "lname/dhruba/user/myuser;", null, L0, L1, 0);                MV.VISITMAXS (1, 1);            Mv.visitend ();                } {mv = Cw.visitmethod (acc_public, "GetName", "() ljava/lang/string;", null, NULL);                Mv.visitcode ();                Label L0 = new label ();             Mv.visitlabel (L0);   Mv.visitlinenumber (6, L0);                MV.VISITLDCINSN ("Bar");                MV.VISITINSN (Areturn);                Label L1 = new label ();                Mv.visitlabel (L1);                Mv.visitlocalvariable ("This", "lname/dhruba/user/myuser;", null, L0, L1, 0);                MV.VISITMAXS (1, 1);            Mv.visitend ();            } cw.visitend ();        return Cw.tobytearray ();    } return Classfilebuffer; }}

Note that this is not a standard AOP or proxy logic. The class file transformer is literally redefining the bytecode instructions incrementally in the form of byte arrays.

Static loading of a javaagent at startup

Static loading of a javaagent is do by using the -javaagent=/path/to/file.jar=options command line option to the Java executable as below.

$ Java-javaagent:target/javaagent-examples-jar-with-dependencies.jar=foobarbaz Name.dhruba.javaagent.MyMainClass Foo bar baz21:37:50.783 [main] INFO Name.dhruba.javaagent.myjavaagent-premain method invoked with Args:foobarbaz and I NST: [email protected]21:37:50.789 [main] INFO N.d.javaagent.myclassfiletransformer-class file transformer Invoked for classname:name/dhruba/javaagent/mymainclass21:37:50.789 [main] INFO Name.dhruba.javaagent.MyMainClass- Main method invoked with args: [foo, bar, baz]21:37:50.789 [main] INFO N.d.javaagent.myclassfiletransformer-class file Transformer invoked for classname:name/dhruba/user/myuser21:37:50.800 [main] INFO Name.dhruba.javaagent.MyMainClass-  username:bar21:37:50.801 [DESTROYJAVAVM] INFO N.d.javaagent.myclassfiletransformer-class file transformer invoked for classname:java/lang/shutdown21:37:50.801 [DESTROYJAVAVM] INFO n.d.javaagent.myclassfiletransformer-class file Transformer invoked for Classname:java/lang/shutdown$Lock 

Above, the javaagent lifecycle is clearly visible. The Premain (and not the Agentmain) method is invoked first. The class file transformer is passed classes as they is loaded. The transformer chooses to redefine the User object prior to their use. Subsequently the classes loaded at shutdown also pass through the transformer.

Dynamic loading of a javaagent at runtime

Dynamic loading of a javaagent at runtime can be do quite easily in a programmatic fashion but requires the sun Tools ja R to is present on the classpath. Certain libraries like Jmockit has avoided this by opting to absorb the relevant classes from the Sun tools jar into its Library under the same package names. In Maven the tools jar can is added to the classpath very easily.

<dependency>  <groupId>com.sun</groupId>  <artifactId>tools</artifactId>  <version>1.6.0</version>  <scope>system</scope>  <systempath>${ java.home}/. /lib/tools.jar</systempath></dependency>

The Sun Tools Jar API can then is used to load the Java agent simply by provided the path to the jar file as follows.

 Package Name.dhruba.javaagent;import Java.lang.management.managementfactory;import Org.slf4j.logger;import Org.slf4j.loggerfactory;import Com.sun.tools.attach.virtualmachine;public class Myjavaagentloader {static final    Logger Logger = Loggerfactory.getlogger (Myjavaagentloader.class); private static final String Jarfilepath = "/home/dhruba/.m2/repository/" + "Javaagent-examples/javaagent-exampl    es/1.0/"+" Javaagent-examples-1.0-jar-with-dependencies.jar ";        public static void Loadagent () {Logger.info ("dynamically Loading Javaagent");        String NAMEOFRUNNINGVM = Managementfactory.getruntimemxbean (). GetName ();        int p = nameofrunningvm.indexof (' @ ');        String pid = nameofrunningvm.substring (0, p);            try {virtualmachine vm = Virtualmachine.attach (PID);            Vm.loadagent (Jarfilepath, "");        Vm.detach ();        } catch (Exception e) {throw new RuntimeException (e); }    }}

As you can see–the code above are querying the PID of the running process and attaching the javaagent to the process. If any kind of failure occurs at the it is ignored silently whereas if the agent is loaded on startup failures Resul T in termination of startup.

We can now initialise the Java agent prior to invoking our main method using the MyJavaAgent.initialize() Hookpoint we declared earlier.

Package Name.dhruba.javaagent;import Java.util.arrays;import Name.dhruba.user.myuser;import Org.slf4j.Logger; Import Org.slf4j.loggerfactory;public class Mymainclass {    static final Logger Logger = Loggerfactory.getlogger ( Mymainclass.class);    static {        myjavaagent.initialize ();    }    /**     * Main method.     *      * @param args */public    static void Main (string[] args) {        Logger.info ("Main method invoked with args: { } ", Arrays.aslist (args));        Logger.info ("UserName: {}", new MyUser (). GetName ());}    }

The output is very similar but with a subtely different path through the code.

20:58:50.923 [main] INFO  n.dhruba.javaagent.myjavaagentloader-dynamically loading javaagent20:58:51.249 [Attach Listener] INFO  Name.dhruba.javaagent.myjavaagent-agentmain method invoked with args: and  inst: [Email protected]20:58:51.266 [main] INFO  Name.dhruba.javaagent.mymainclass-main method invoked with args: [] 20:58:51.267 [main] INFO  n.d.javaagent.myclassfiletransformer-class file transformer invoked for classname:name/ dhruba/user/myuser20:58:51.276 [main] INFO  name.dhruba.javaagent.mymainclass-username:bar20:58:51.276 [ DESTROYJAVAVM] INFO  n.d.javaagent.myclassfiletransformer-class file transformer invoked for classname:java/lang/ shutdown20:58:51.276 [DESTROYJAVAVM] INFO  n.d.javaagent.myclassfiletransformer-class file transformer invoked For Classname:java/lang/shutdown$lock

Note that this time the Agentmain method is invoked instead of the Premain method. The rest of the output is the same.

Conclusion

Javaagents along with the Java.lang.instrument package is a powerful feature set of the Java language that allow you comp Lete control over the classes for any given application. Instrumentation also have far more liberty with its capabilities than proxying or pointcut weaving. This gives complete dynamic capability to an otherwise very static language. It has allowed development of powerful non-intrusive tools like Jmockit which immediately gains numerous advantages over O Ther mocking tools largely because it is based on instrumentation. I look forward to further exploring the possibilities going forward.

Resources

ASM, Bytecodeoutline Plugin for Eclipse (Eclipse 3.5 Update site), Java.lang.instrument, Jmockit.

Update [2/7/2011]: It's nice to see this featured on StackOverflow.

Thanks to Anton Arhipov, software Engineer at JRebel, for kindly linking to this post.

Filed Under:java, lab49

Creation, dynamic loading and instrumentation with javaagents

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.