Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use Instrumentation.retransformClasses() correctly from within asm code?

I'm using the asm library to perform some Java bytecode modification - specifically to modify my classes to implement a new interface and associated methods. My current approach is using the core asm API via a javaagent. I'd like to keep this dynamic approach as opposed to statically modifying .class files.

At a higher level, my problem is that if I choose to modify class A, which extends from B, I also need to modify B. (Given my understanding of how classes are loaded in the JVM, I believe that class B will always be handed to a transformer before class A. (Please correct me if I'm wrong). Given that assumption, I'm thinking that I then need to go back and retransform B. My approach is captured in this bit of code:

public byte[] transform(ClassLoader l, String name, Class<?> clazz, ProtectionDomain d, byte[] b) {
      throws IllegalClassFormatException {
    // **1**
    System.out.println("--->>> " + name);

    if (interestingClass(name)) {
        try {
            ClassReader cr = new ClassReader(b);
            ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
            PyClassVisitorAdapter pv = new PyClassVisitorAdapter(cw, name);
            cr.accept(pv, 0);

            // **2** Retrieve the superclass and try to transform that
            if (! "Ljava/lang/Object;".equals(pv.getSuperName())) {
                String cName = classJvmToCanonical(pv.getSuperName());
                Class[] classes = inst.getAllLoadedClasses();
                for (Class c : classes) {
                    if (c.getName().equals(cName)) {
                        inst.retransformClasses(c);
                        break;
                    }
                }
            }

            // Dump the transformed class
            ClassReader cr2 = new ClassReader(cw.toByteArray());
            ClassWriter cw2 = new ClassWriter(cr2, 0);
            TraceClassVisitor tcv = new TraceClassVisitor(cw2, new PrintWriter(System.out));
            cr2.accept(tcv, 0);

            return cw2.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    } else {
        return b;
    }
}

(inst is a handle to Instrumentation which gets passed in in the constructor)

The part I'm having a hard time with is the block marked in the comments with **2**. Let's say again that A extends B and I'm 'interested' in transforming A. What I'm expecting is that I would see the name of the superclass (B) being printed at **1** (but not getting transformed because I don't think it's interesting on the first pass) and then, once I get to **2** and discover that A's superclass is B, I should be trying to retransform B. At this point I'm expecting this method to be called again (via inst.retransformClasses()) and that I would see B getting printed at **1**. However, I don't. (I have added print statements and am sure I'm reaching the retransform call. I've also checked that Instrumentation.isRetransformClassesSupported() and Instrumentation.isModifiableClass(c) both return true).

I believe I've set up the agent correctly; setting both Can-Retransform-Classes and Can-Redefine-Classes to true in the manifest. Also, when I add the transformer to the Instrumentation in the agent's premain method I do this:

public static void premain(String agentArgs, Instrumentation inst) {
    inst.addTransformer(new PyClassFileTransformer(inst), true);
}

Any insights as to what I'm doing wrong here? Thanks.

like image 646
Jens Avatar asked Mar 01 '12 22:03

Jens


1 Answers

You could change your bytecode instrumentation strategy, so when class B is loaded, you find all its subclasses and decide at that point if you need to modify class B now. This can be optimized by maintaining class-metadata repository or cache in memory (i.e. information about class hierarchy), so you won't have to load metadata every time.

like image 55
Eugene Kuleshov Avatar answered Sep 23 '22 17:09

Eugene Kuleshov