Initializing Non-Primtive Field by ByteBuddy in Aspect

38 Views Asked by At

For monitoring and program analysis, I'm currently trying to transform an AspectJ implementation to one ByteBuddy and one javassist implementation. The original AspectJ implementation is here: https://github.com/kieker-monitoring/kieker/blob/c95292abc8df91eac82c1fa9f6ab0316768df16b/monitoring/src/kieker/monitoring/probe/aspectj/operationExecution/AbstractOperationExecutionAspect.java#L43

Unfortunately, I don't see a way to initialize non-primitive fields in ByteBuddy. In AspectJ, I can just have them inside of the advice, in javassist, I can add them using the API (https://github.com/kieker-monitoring/kieker/blob/KIEKER-1973-InstrumentationTechnologies/monitoring-probe-javassist/src/kieker/monitoring/probe/javassist/KiekerClassTransformer.java#L107), and later on use them in my string that represents program code (for example the CFREGISTRY: https://github.com/kieker-monitoring/kieker/blob/547adbf71b4347ec95abec16bfd54a5845b48f58/monitoring-probe-javassist/src/kieker/monitoring/probe/javassist/KiekerClassTransformer.java#L74).

But for ByteBuddy, I failed to do so. Getting the fields in every advice and initializing them if necessary works:

@Advice.OnMethodEnter
public static OperationStartData enter(
        @Advice.Origin String operationSignature,
        @Advice.FieldValue(value = "CTRLINST", readOnly = false) IMonitoringController CTRLINST,
        @Advice.FieldValue(value = "TIME", readOnly = false) ITimeSource TIME,
        @Advice.FieldValue(value = "VMNAME", readOnly = false) String VMNAME,
        @Advice.FieldValue(value = "CFREGISTRY", readOnly = false) ControlFlowRegistry CFREGISTRY,
        @Advice.FieldValue(value = "SESSIONREGISTRY", readOnly = false) SessionRegistry SESSIONREGISTRY) {
    if (CTRLINST == null) {
        CTRLINST = MonitoringController.getInstance();
        TIME = CTRLINST.getTimeSource();
        VMNAME = CTRLINST.getHostname();
        CFREGISTRY = ControlFlowRegistry.INSTANCE;
        SESSIONREGISTRY = SessionRegistry.INSTANCE;
    }

(https://github.com/kieker-monitoring/kieker/blob/d1eff598ce4d1bbf226cc76c51aa3ac8f8db2b7e/monitoring-probe-bytebuddy/src/kieker/monitoring/probe/bytebuddy/OperationExecutionAdvice.java#L14) but will most likely create some overhead and make JVM optimizations impossible, since the fields are set to readOnly=false.

So creating the fields would need to somehow also set the initializer:

     .transform(new AgentBuilder.Transformer() {

            @Override
            public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription,
                    ClassLoader classLoader, JavaModule module, ProtectionDomain protectionDomain) {
                Valuable<?> definedField = builder.defineField("CTRLINST", IMonitoringController.class, Modifier.STATIC | Modifier.FINAL | Modifier.PRIVATE);
                return definedField;
            }
        })

(in https://github.com/kieker-monitoring/kieker/blob/d1eff598ce4d1bbf226cc76c51aa3ac8f8db2b7e/monitoring-probe-bytebuddy/src/kieker/monitoring/probe/bytebuddy/PremainClass.java#L93). Unfortunately, I don't see an option to do so; I can only call .value(..) which is able to set a primitive type, but I cannot set an initializer.

bytebuddy definefield and initialize a complex object seems to have tried a similiar thing, but according to this, there is no way to do so.

Another option would be to add a static initialization block, which would make it possible to set the fields to readOnly=true (but the final thing wouldn't work either), like in ByteBuddy: generate a static initializer?.

So overall: Is there any way to initialize static final fields that are dynamically created by ByteBuddy (or at least making it possible to set them readOnly)?

1

There are 1 best solutions below

0
Rafael Winterhalter On

Are you adding these fields to the instrumented classes and initialize them from there? Most monitoring tools work with retransformation where adding fields is not possible and any values are rather attached to the current thread or in a weak-referenced storage.

If you want to add those fields in each class, you would need to code it out as you suggest. There is however no need to add this to every advice. You can either:

  1. Create multiple advice classes and chain your "field setting advice" prior to the actual one.
  2. Advice your advice. This normally happens during build using a tool like Maven. Byte Buddy ships with a build tool. This way you can add the repetitive code to the "enter" phase of every advice.