Failed to redefine class When I try to retransform class
Asked Answered
E

1

2

I was trying to modify class dynamically, such as call sleep() before a line. I attached agent to a jvm during runtime using Attach method. Then I got target class from jvm, and modified it(Add a line to call sleep()). And I got redine class error. I am using JDK1.6. I am using ASM core API to modify class. The Error:

Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
    at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
    at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:124)

Is there something wrong with ASM code? Actually my ASM code finished its job(to Add a line to call sleep()). Does current jvm not support retransform class? It seems failed to execute retransformClasses(). Does retransformClasses() not support the ASM operation(to add a line into a method to call sleep())? Any ideas? thx

EDIT: The class which I want modify:

import java.util.concurrent.TimeUnit;

public class Person {
    public String name = "abc";
    public String address = "xxxxx" ;

    public void setName(String name) {
    this.name = name;
    }

    public String getName() {
    return name;
    }

    public void sayHello() throws InterruptedException {  
    System.out.println("aaaaaaaaaa");
            System.out.println("Hello World!");
            TimeUnit.SECONDS.sleep(120);
            System.out.println("dd");
        }  
    public void sayHello2() {
            System.out.println("aaaaaaaaaa1");
                System.out.println("Hello World!2");  
        }  

    public static void main (String args[]) {
        try {
            Person p = new Person();
            p.sayHello(); // linenumber #9. A line to call Sleep() should be added before #here.

            p.sayHello2();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }   
}

My ASM code:

public void visitMethodInsn(int arg0, String arg1, String arg2, String arg3) {

    Label la=new Label();
    mv.visitLabel(la);
    int linenumber=la.getOffset();
    if(linenumber==9) {
        mv.visitFieldInsn(Opcodes.GETSTATIC, "java/util/concurrent/TimeUnit", "SECONDS", "Ljava/util/concurrent/TimeUnit;");
        mv.visitLdcInsn(new Long("5"));
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/concurrent/TimeUnit", "sleep", "(J)V");

        super.visitMethodInsn(arg0, arg1, arg2, arg3);

    }
}
Enyo answered 4/9, 2013 at 3:40 Comment(0)
P
6

Not looking at your code yet, I think I can suggest something. When a class is first loaded, in addition to storing the class's byte codes, the JVM also has tables where it keeps track of the types of fields and the signatures of methods in each class.

The error you are seeing would suggest that the class was loaded, this signature information was stored and then you tried to add the method after that.

If you instead put your agent jar onto the command line, you can do things before the class is loaded for the first time. If you add your method before the signature info is stored away, you should be good.

If you have to connect the agent after the process is already launched, you may be able to transform the class but you may only be able to transform it without changing the set of fields, their types, or the methods, or their signatures. In other words, you may be able to change the byte codes but you have to not invalidate the previously-stored meta information.

Polypody answered 19/4, 2014 at 21:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.