Is it possible to intercept array constructor with ByteBuddy?

183 views Asked by At

I have a regression test where I'm trying to count the number of instantiations of arrays. Specifically, I'm interested in counting how many times new int[..] is called. I have put together the following ByteBuddy advice-based instrumentation, which I believe should catch all constructors, but this is not working for arrays (of any kind):

public class InstanceCounter {
  public static void load() {
    ClassInjector.UsingUnsafe.ofBootLoader().injectRaw(Collections.singletonMap("foo.bar.BootstrapState", readBytes(InstanceCounter.class.getClassLoader().getResource("foo/bar/BootstrapState.class"))));
    new AgentBuilder.Default()
      .ignore(none())
      .disableClassFormatChanges()
      .with(RedefinitionStrategy.RETRANSFORMATION)
      .with(InitializationStrategy.NoOp.INSTANCE)
      .with(TypeStrategy.Default.REDEFINE)
    .type(any())
      .transform(new Transformer() {
        @Override
        public Builder<?> transform(final Builder<?> builder, final TypeDescription typeDescription, final ClassLoader classLoader, final JavaModule module) {
          return builder.visit(Advice.to(InstanceCounter.class).on(any()));
        }})
    .installOn(ByteBuddyAgent.install());
  }

  @Advice.OnMethodEnter
  public static void enter(final @Advice.Origin Class<?> origin) {
    if (!BootstrapState.trace || BootstrapState.lock)
      return;

    BootstrapState.lock = true;
    System.out.println(">>> " + origin);
    BootstrapState.lock = false;
  }
}

public class InstanceCounterTest {
  @Test
  public void test() {
    InstanceCounter.load();
    System.out.println("Creating array...");
    BootstrapState.trace = true;
    final Object[] y = new Object[3];
    BootstrapState.trace = false;
    System.out.println("Printing array: " + y);
  }
}

When I run this test, I get the following results:

Creating array...
Printing array: [Ljava.lang.Object;@7f5eae0f

If I change final Object[] y = new Object[3] to new Object(), then the output is:

Creating array...
>>> class java.lang.Object
Printing array: [Ljava.lang.Object;@68562ad5

I've made the advice instrumentation as open as possible (at least as open as I think I can make it), but this is still not instrumenting construction of array objects.

Is is even possible to instrument construction of array objects?

Update 2020-07-28

As per the context of the original question, the provided answers express that ByteBuddy does not have direct support for automatic instrumentation of new int[]. However, for those interested in an alternate solution, I was able to achieve this kind of instrumentation with Byteman.

3

There are 3 answers

0
Rafael Winterhalter On BEST ANSWER

As pointed out, you cannot change array classes as they are not represented in a class file. Instead, you'd need to intercept their creation which requires some byte code parsing.

Byte Buddy exposes ASM, which it uses itself to do this kind of work, but it is much more manual work than intercepting a constructor.

0
chrylis -cautiouslyoptimistic- On

Arrays don't have constructors; they're created with the newarray (for primitives) or anewarray (for reference types) (and there's also multianewarray if you want to get creative).

If you want to modify array creation, you'll have to find some way to instrument these bytecode instructions.

0
Stephen C On

Instrumentation of the construction of regular objects involves injecting a call into the constructors for the relevant class. This is feasible because every class declares at least one constructor, and because a constructor is always invoked during object creation.

By contrast, arrays don't have constructors so there is nowhere to inject the call. That explains why your instrumentation of all constructors is not picking up array creations.

Hypothetically, the other way to instrument array creation would be find all places where the 3 "newarray" bytecodes are used, and precede them with an instrumentation call.