简体   繁体   中英

Decorating ClassLoader methods using ByteBuddy

I have a project which allows one to log classes as they are loaded by the JVM ( https://github.com/jdeppe-pivotal/classload-tracer ). The limitation with this is that a class has to be loaded successfully in order to be logged.

Now I'd like to try and expand this and instrument various ClassLoader methods in order to log classes as they are attempted to by loaded (regardless of success). I'm hoping to use ByteBuddy for this. Unfortunately I'm not having much luck.

Here is the code for the agent:

    public class ScratchAgent {

      private static final PrintWriter out;
      public static final ByteArrayOutputStream baos;

      static {
        baos = new ByteArrayOutputStream(10);
        out = new PrintWriter(baos);
      }

      public static void premain(String arg, Instrumentation inst) throws Exception {
        File temp = Files.createTempDirectory("tmp").toFile();

        ClassInjector.UsingInstrumentation
            .of(temp, ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, inst)
            .inject(Collections.singletonMap(
                new TypeDescription.ForLoadedType(MyInterceptor.class),
                ClassFileLocator.ForClassLoader.read(MyInterceptor.class)));

        new AgentBuilder.Default()
            .disableClassFormatChanges()
            .with(AgentBuilder.Listener.StreamWriting.toSystemOut().withTransformationsOnly())
            .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
            .with(AgentBuilder.TypeStrategy.Default.REDEFINE)
            .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
            .ignore(ElementMatchers.nameStartsWith("net.bytebuddy."))
            .enableBootstrapInjection(inst, temp)

            .type(is(TestClass.class))
            .or(ElementMatchers.isSubTypeOf(ClassLoader.class)
                .or(ElementMatchers.nameContainsIgnoreCase("classloader"))
            )

            .transform((builder, type, classLoader, module) -> builder
                .visit(Advice.to(MyInterceptor.class)
                    .on(hasMethodName("fooMethod")))
                .visit(Advice.to(MyInterceptor.class)
                    .on(hasMethodName("loadClass"))))
            .installOn(inst);
      }

      public static class MyInterceptor {
        @Advice.OnMethodEnter
        public static void decorate(
            @Advice.Argument(0) String arg,
            @Advice.This Object thisThis,
            @Advice.Origin Method method,
            @Advice.Origin Class<?> clazz) {
          System.out.println("--->>> OK " + method.getName()
              + "(" + arg + ") " + thisThis);
          out.println("--->>> OK " + arg);
          out.flush();
        }
      }
    }

And here is a test:

    @Test
    public void sanity() throws Exception {
      ScratchAgent.premain(null, ByteBuddyAgent.install());

      String result = new TestClass().fooMethod("world");
      assertThat(result).isEqualTo("Hello world");
      assertThat(ScratchAgent.baos.toString()).isEqualTo("--->>> OK world\n");
    }

But this produces the following error:

[Byte Buddy] TRANSFORM io.pivotal.test.TestClass [sun.misc.Launcher$AppClassLoader@18b4aac2, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader$2 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader$3$1 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader$3 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.ClassLoader$2 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader$1 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.SystemClassLoaderAction [null, null, loaded=true]
[Byte Buddy] TRANSFORM sun.misc.Launcher$AppClassLoader$1 [null, null, loaded=true]
[Byte Buddy] TRANSFORM sun.misc.Launcher$ExtClassLoader$1 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader$7 [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.ClassLoader$ParallelLoaders [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.ClassLoader$NativeLibrary [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.ClassLoader$3 [null, null, loaded=true]
[Byte Buddy] TRANSFORM sun.misc.Launcher$ExtClassLoader [null, null, loaded=true]
[Byte Buddy] TRANSFORM sun.misc.Launcher$AppClassLoader [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.net.URLClassLoader [null, null, loaded=true]
[Byte Buddy] TRANSFORM sun.reflect.DelegatingClassLoader [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.security.SecureClassLoader [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.lang.ClassLoader [null, null, loaded=true]
--->>> OK fooMethod(world) io.pivotal.test.TestClass@2ca26d77
--->>> OK loadClass(org.assertj.core.api.Assertions) sun.misc.Launcher$AppClassLoader@18b4aac2
--->>> OK loadClass(org.junit.runners.model.MultipleFailureException) sun.misc.Launcher$AppClassLoader@18b4aac2
--->>> OK loadClass(org.junit.runner.notification.RunNotifier$7) sun.misc.Launcher$AppClassLoader@18b4aac2
--->>> OK loadClass(net.bytebuddy.pool.TypePool$Default$WithLazyResolution$LazyResolution) sun.misc.Launcher$AppClassLoader@18b4aac2
[Byte Buddy] ERROR java.lang.Throwable$WrappedPrintStream [null, null, loaded=false]
[Byte Buddy] ERROR java.lang.Throwable$PrintStreamOrWriter [null, null, loaded=false]
[Byte Buddy] ERROR java.util.IdentityHashMap$KeySet [null, null, loaded=false]
Exception in thread "main" java.lang.NoClassDefFoundError: org/junit/runners/model/MultipleFailureException
        at org.junit.internal.runners.model.EachTestNotifier.addFailure(EachTestNotifier.java:20)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:369)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
        at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
        at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
        at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
        at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
[Byte Buddy] ERROR java.lang.Shutdown [null, null, loaded=false]
java.lang.NoClassDefFoundError: net/bytebuddy/pool/TypePool$Default$WithLazyResolution$LazyResolution
        at net.bytebuddy.pool.TypePool$Default$WithLazyResolution.doDescribe(TypePool.java:1319)
        at net.bytebuddy.pool.TypePool$AbstractBase.describe(TypePool.java:408)
        at net.bytebuddy.pool.TypePool$AbstractBase$Hierarchical.describe(TypePool.java:471)
        at net.bytebuddy.agent.builder.AgentBuilder$DescriptionStrategy$Default$1.apply(AgentBuilder.java:3373)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.resolve(AgentBuilder.java:10499)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:10469)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10432)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1500(AgentBuilder.java:10198)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10807)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10754)
        at java.security.AccessController.doPrivileged(Native Method)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10355)
        at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
        at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
[Byte Buddy] ERROR java.lang.Shutdown$Lock [null, null, loaded=false]
java.lang.NoClassDefFoundError: net/bytebuddy/pool/TypePool$Default$WithLazyResolution$LazyResolution
        at net.bytebuddy.pool.TypePool$Default$WithLazyResolution.doDescribe(TypePool.java:1319)
        at net.bytebuddy.pool.TypePool$AbstractBase.describe(TypePool.java:408)
        at net.bytebuddy.pool.TypePool$AbstractBase$Hierarchical.describe(TypePool.java:471)
        at net.bytebuddy.agent.builder.AgentBuilder$DescriptionStrategy$Default$1.apply(AgentBuilder.java:3373)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.resolve(AgentBuilder.java:10499)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:10469)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10432)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1500(AgentBuilder.java:10198)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10807)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:10754)
        at java.security.AccessController.doPrivileged(Native Method)
        at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10355)
        at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
        at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
        at java.lang.Shutdown.<clinit>(Shutdown.java:61)

Process finished with exit code 1

I realize my test isn't doing anything to test the ClassLoader decoration but I'm just iterating on using a 'normal' class first and slowly adding in the necessary bits to instrument ClassLoader classes.

What am I doing wrong? Is it even feasible to instrument ClassLoader s? Thanks!

When you instrument a class, additional class might be loaded during the instrumentation. If this triggers class loading of classes that you rely on, for example classes of your own agent that is not in the Byte Buddy namespace or classes of the boot loader, this might trigger an instrumentation circularity and therefore a no class def found error that you experience.

Try to use a POOL_ONLY TypeStrategy that avoids class loading for the most and you probably need to reduce the amount of classes being instrumented to avoid instrumenting classes that are needed to establish your logging.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM