Hi,
I'm creating a simple tool using Byte Buddy to trace method calls.
While testing it on an application that uses the Guava library's `ImmutableList`, I encountered the following `java.lang.ClassFormatError`.
``` shell
$ ./gradlew run
Reusing configuration cache.
> Task :app:run FAILED
premain!! arges:null
tracer start!!
org.example.App#main :: is called.
tracer end!!
Exception in thread "main" java.lang.ClassFormatError: Duplicate method name "listIterator" with signature "(I)Ljava.util.ListIterator;" in class file com/google/common/collect/ImmutableList$ReverseImmutableList
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1027)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
at org.example.App.main$original$LSGQf8fD(App.java:16)
at org.example.App.main(App.java)
FAILURE: Build failed with an exception.
...
```
Is there a way to work around this issue?
I would greatly appreciate any advice.
For reference, my execution environment is as follows:
- OS: macOS Ventura (version 13.4)
- Java: OpenJDK version 21.0.6 2025-01-21 LTS
Here is the sample application code.
```
import com.google.common.collect.ImmutableList;
public class App {
public static void main(String[] args) {
ImmutableList<String> alist = ImmutableList.of("a", "b", "c");
System.out.println(alist.get(0));
}
}
```
And here is the `build.gradle` file.
```
plugins {
id 'application'
}
repositories {
mavenCentral()
}
dependencies {
implementation libs.guava
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
application {
mainClass = '
org.example.App'
applicationDefaultJvmArgs = ["-javaagent:../my-tracer-all.jar"]
}
```
Here is the code for the tracing tool.
```java
package org.example;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.matcher.ElementMatchers;
public class TraceAgent {
public static void premain(String args, Instrumentation instrumentation) {
System.out.println("premain!!");
AgentBuilder agentBuilder =
new AgentBuilder.Default()
.type(ElementMatchers.any())
.transform(
(builder, typeDescription, classLoader, module, protectionDomain) ->
builder
.method(ElementMatchers.any())
.intercept(
MethodDelegation.to(MethodLogger.class)
.andThen(SuperMethodCall.INSTANCE)));
agentBuilder.with(AgentBuilder.Listener.StreamWriting.toSystemError());
agentBuilder.installOn(instrumentation);
}
public static class MethodLogger {
@RuntimeType
public static void onMethodEnter(@Origin Method method) {
System.out.println("tracer start!!");
try {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StackTraceElement caller = null;
if (stackTrace.length > 3) {
caller = stackTrace[3];
}
if (caller != null) {
System.out.printf(
"caller::%s#%s (%s:%d), callee::%s#%s\n",
caller.getClassName(),
caller.getMethodName(),
caller.getFileName(),
caller.getLineNumber(),
method.getDeclaringClass().getCanonicalName(),
method.getName());
} else {
System.out.printf(
"%s#%s :: is called.\n",
method.getDeclaringClass().getCanonicalName(), method.getName());
}
} catch (Exception e) {
System.out.println("An exception occurred: " + e.getMessage());
}
System.out.println("tracer end!!");
}
}
}
```
Best regards,
Satoshi Hikida