I am seeing Cannot resolve type description errors from AgentBuilder.Listener.onError(...) when installing an AgentBuilder transformation for an Instrumentation.

The agent code is:

  public static void premain(final String agentArgs, final Instrumentation inst) {
    System.out.println("from bytebuddy: agent premain");
    new AgentBuilder.Default(new ByteBuddy().with(TypeValidation.DISABLED))
      .disableClassFormatChanges()
      .ignore(none())
      .with(RedefinitionStrategy.RETRANSFORMATION)
      .with(InitializationStrategy.NoOp.INSTANCE)
      .with(TypeStrategy.Default.REDEFINE)
      .with(new AgentBuilder.Listener() {
        public void onDiscovery(final String typeName, final ClassLoader classLoader, final JavaModule module, final boolean loaded) {
          log("Event::onDiscovery(" + typeName + ", " + getNameId(classLoader) + ", " + module + ", " + loaded + ")");
        }

        public void onTransformation(final TypeDescription typeDescription, final ClassLoader classLoader, final JavaModule module, final boolean loaded, final DynamicType dynamicType) {
          log("Event::onTransformation(" + typeDescription.getName() + ", " + getNameId(classLoader) + ", " + module + ", " + loaded + ", " + dynamicType + ")");
        }

        public void onIgnored(final TypeDescription typeDescription, final ClassLoader classLoader, final JavaModule module, final boolean loaded) {
          log("Event::onIgnored(" + typeDescription.getName() + ", " + getNameId(classLoader) + ", " + module + ", " + loaded + ")");
        }

        public void onError(final String typeName, final ClassLoader classLoader, final JavaModule module, final boolean loaded, final Throwable throwable) {
          log("Event::onError(" + typeName + ", " + getNameId(classLoader) + ", " + module + ", " + loaded + ")", throwable);
        }

        public void onComplete(final String typeName, final ClassLoader classLoader, final JavaModule module, final boolean loaded) {
          log("Event::onComplete(" + typeName + ", " + getNameId(classLoader) + ", " + module + ", " + loaded + ")");
        }
      })
      .type(hasSuperType(named("com.ning.http.client.AsyncHttpClientConfig$Builder")))
      .transform(new Transformer() {
        public DynamicType.Builder<?> transform(final DynamicType.Builder<?> builder, final     
          System.out.println("from bytebuddy: transform");
          TypeDescription typeDescription, final ClassLoader classLoader, final JavaModule module) {
          return builder.visit(Advice.to(AHCBuilderExit.class).on(isDefaultConstructor()));
        }})
      .installOn(inst);
  }

This is using ByteBuddy v1.10.2 with Mule 4 (jdk1.8) as the target application.

There is a similar post regarding this same issue here, but the responses have not led me to a solution.

The output shows "from bytebuddy: agent premain", but it does not show "from bytebuddy: transform". Instead, the logs show exceptions for many classes that belong in the bootstrap class loader, such as:

Event::onError(org.mule.weave.v2.el.WeaveExpressionLanguage, org.mule.runtime.module.artifact.api.classloader.MuleArtifactClassLoader@6d4b1d14, null, false)
java.lang.IllegalStateException: Cannot resolve type description for java.lang.Exception
    at net.bytebuddy.pool.TypePool$Resolution$Illegal.resolve(TypePool.java:159)
    at net.bytebuddy.pool.TypePool$Default$WithLazyResolution$LazyTypeDescription.delegate(TypePool.java:914)
    at net.bytebuddy.description.type.TypeDescription$AbstractBase$OfSimpleType$WithDelegation.getSuperClass(TypeDescription.java:8031)
    at net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType.getSuperClass(TypeDescription.java:3619)
    at net.bytebuddy.description.type.TypeDefinition$SuperClassIterator.next(TypeDefinition.java:314)
    at net.bytebuddy.description.type.TypeDefinition$SuperClassIterator.next(TypeDefinition.java:281)
    at net.bytebuddy.matcher.HasSuperTypeMatcher.matches(HasSuperTypeMatcher.java:53)
...

It seems that the class loader that's being queried for the type description is not providing the desired classes. Is there a way to specify an alternate ClassFileLocator for these lookups?

The specific class loader that's resulting in the error is: org.mule.runtime.module.artifact.api.classloader.MuleArtifactClassLoader

Interestingly, this instrumentation works as expected when using Byteman as the agent, with the following rule file:

RULE AsyncHttpClientConfig.Builder.<init>
CLASS com.ning.http.client.AsyncHttpClientConfig$Builder
METHOD <init>
AT EXIT
IF TRUE
DO
  traceln("from byteman: AsyncHttpClientConfig$Builder.<init> triggered: " + getClass().getClassLoader());
ENDRULE

Any advice would be appreciated! Thank you for your help!

1

There are 1 answers

1
Rafael Winterhalter On BEST ANSWER

The stack trace shows that you are applying a super type check with Byte Buddy that you do not use with Byteman. If you substitute your hasSuperType(named(...)) matcher with just named(...), it might work without errors as this is where the error seems to happen. Byte Buddy needs to resolve all classes in a class hierarchy for being able to apply this matcher what seems to be impossible in your case.

Yet, that a ClassLoader cannot resolve the class file for java.lang.Exception seems weird; probably the class loader you are using is a custom implementation that does not delegate to the boot loader.

You can circumvent this by defining a custom LocationStrategy in your agent that always queries the boot loader as an alternative option:

class BootFallbackLocationStrategy implements AgentBuilder.LocationStrategy {

  @Override
  public ClassFileLocator classFileLocator(ClassLoader classLoader, JavaModule module) {
     return new ClassFileLocator.Compound(
       ClassFileLocator.ForClassLoader.of(classLoader),  
       ClassFileLocator.ForClassLoader.ofBootLoader())
  }
}

As mentioned in my comment, if you can name the type of the class loader that is failing to resolve the class file, I might be able to provide a more detailed answer.

I will add a standard implementation of such a location strategy in a future version of Byte Buddy to make working around this problem easier. The progress is tracked in this ticket: https://github.com/raphw/byte-buddy/issues/794