Instein
Instein

Reputation: 3564

"LinkageError: attempted duplicate class definition" when dynamically instrumenting java classes with ASM

I wrote a javaagent by myself to dynamically instrumenting the java classes with ASM (I am not using the COMPUTE_MAXS or COMPUTE_FRAMES of ASM, I do that manually by myself). Actually, I am just trying to use a big try-catch block for non-constructor methods to capture the uncaught Exceptions or Errors and record such events (my code is actually the revised version of the code in this question).

However, when I tried to use my javaagent in the testing process of an open-source project joda-time, the following error occured:

org.apache.maven.surefire.testset.TestSetFailedException: org.joda.time.TestAllPackages
    at org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:116)
    at org.apache.maven.surefire.junit.JUnit3Provider.executeTestSet(JUnit3Provider.java:140)
    at org.apache.maven.surefire.junit.JUnit3Provider.invoke(JUnit3Provider.java:113)
    at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:379)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:340)
    at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:125)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:413)
Caused by: java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "org/joda/time/DateTimeZone"
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at org.joda.time.TestChronology.<clinit>(TestChronology.java:47)
    at org.joda.time.TestAll.suite(TestAll.java:37)
    at org.joda.time.TestAllPackages.suite(TestAllPackages.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.maven.surefire.common.junit3.JUnit3Reflector.createInstanceFromSuiteMethod(JUnit3Reflector.java:157)
    at org.apache.maven.surefire.common.junit3.JUnit3Reflector.constructTestObject(JUnit3Reflector.java:124)
    at org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:75)
    ... 6 more

To my understanding, each time when sun/misc/Launcher$AppClassLoader is trying to load a class, the transform method of ClassFileTransformer is entered. The class is finally loaded after I modified the class and return the modified byte array. Therefore, every class should be loaded only once.

I further tried to print out the class name and the loader which loads it in the begining of the transform method, I found that org/joda/time/DateTimeZone appears only once, which is consistent with my understanding.

So now the only thing inconsistent with my understanding is the error. Why sun/misc/Launcher$AppClassLoader attempted duplicate class definition with my agent? Every went so soomthly when I removed my -javaagent option.

Upvotes: 0

Views: 1519

Answers (2)

Line
Line

Reputation: 1651

I had this issue when trying to run configuration from IntelliJ IDEA - reloading the project with maven helped.

Upvotes: 0

Rafael Winterhalter
Rafael Winterhalter

Reputation: 44042

To compute frames, ASM needs to find a common super class of several classes at targets of jump instructions. To do so, ASM loads the classes to browse their hierarchy. If you load a class this way while it is also instrumented during its first load, the class will slready be loaded post instrumentation and you will end up with this error.

To avoid this, you can override the getCommonSuperClass method of ASM's ClassWriter. You'd need to parse the class files of these classes passed to the method rather then loading them. If you wanted and out-of-box implementation of this, you could use Byte Buddy which exposes ASM and resolves its ClassWriter that way.

Upvotes: 2

Related Questions