Runtime: inject code to method before JIT - Add feature Like Java ClassFileTransformer

Created on 20 Mar 2019  路  10Comments  路  Source: dotnet/runtime

In Java we can Create Agent and add ClassFileTransformer before method been JIT. .Net Core added startup Hook but there aren't method to inject code to method before JIT.
It's a vary import feature for AOP, such as generic Logging, Metric, Trace.
So are there plan to add feature like this? If not we strongly recommend to add this.

area-Tracing-coreclr enhancement

Most helpful comment

@juliusfriedman

patch method pointer is good for some case, however it's only suite for method is non virtual, non generic, not in generic type, not inlined so cannot using in some scenarios.

TypeLoader-like solution is better.

The focus is .Net CoreFx should make a official stable managed specification that suite for different scenarios .net core and .net framework, windows and linux.

All 10 comments

@stephentoub @jkotas anything we considered?

BTW you can do this today using the CLR Profiling API, here's an example showing how to re-write IL (see ILRewriter.cpp.

The main difference is that in .NET you currently have to do this via C++ code (unmanaged), AFAIK it can't be done in managed code (C#/F#/VB.NET). Unless you resort to hacks like this (which I'm sure is very unsupported!!)

I agree that being able to do it from managed code, like the Java Agent API lets you do, would be nice and would open up the functionality to more developers

And AOP is usually done by IL weaving ahead of time, not during JIT time.

@Suchiman you are right, that's "before JIT" meaning, exactly says before Type been loaded. However most exist solutions doing this after compile such as PostSharp not dynamically.

@mattwarren
I Investigated methods to do what like Java Agent also include the CLR Profiling API, however it's too complex.

@karelz @mattwarren
Let's look back the Java Agent, in java each class compile to a class file, Java ClassLoader take responsibility to load it. Java just provide a cut point before load class to change the byte code, no JIT involved. And we usually modify byte code by Javassist it's another library.

Similar in .Net type IL code store in Assembly file, the easiest way is provide a event that we can modify Assembly file content for all Assemblies like AssemblyLoadContext.Resolving (for example using Mono.Cecil), but in AssemblyLoadContext.Resolving we can customized load assembly that load failed.

In assembly some Type may not be used in some program, if modify all type in assembly lead to low performance, so it's efficiently to modify type by type before load.

In .Net the TypeLoader take responsibility to load Type, maybe it's better to provide a Managed Event or Hook to modify Type IL if possible.

Java Sample:

//equals to .Net StartupHook class
public class MyAgent {
    //equals to .Net StartupHook.Initialize
    public static void premain(String agentOps, Instrumentation inst) {
        inst.addTransformer(new PointClassFileTransformer());
    }
}
public class PointClassFileTransformer implements ClassFileTransformer {

    /**
     * modify method print's byte code by javassist
     */
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
        ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        System.out.println("Point Transformer load class:" + className);

        if ("chris/Point".equals(className)) {
            try {
                //*** modify Class byte Code (IL code) by third-party library Javassist
                CtClass ctClass = ClassPool.getDefault().get(className.replace('/', '.'));
                CtMethod printMethod = ctClass.getDeclaredMethod("print");
                printMethod.insertBefore("System.out.println(\"before print\");");
                printMethod.insertAfter("System.out.println(\"after print\");");

                byte[] newCode = ctClass.toBytecode();
                System.out.println(newCode.length);
                return newCode; //*** returns modified byte code equals .Net IL code
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return classfileBuffer;
    }
}

You can also patch the method pointer.

https://github.com/juliusfriedman/net7mma/blob/master/Concepts/Classes/MethodHelper.cs

See also

https://blog.adamfurmanek.pl/2016/05/07/custom-memory-allocation-in-c-part-3/

I think assembly fusion and cer are the two most complex things in .Net and love them until something goes wrong either with strong naming or otherwise such as the need to augment but at the same time I have never found myself in a place where I needed to use C++ or result even to using unsafe code for that matter

See https://stackoverflow.com/questions/25803/how-do-i-intercept-a-method-call-in-c/5345043#5345043

However I can also see how allowing this can allow for more advanced scenarios of which in of itself I cannot say that having an implementation will satisfy everyone.

Where there is a will there is always a way, you can dynamically patch any method to first call another method and this has little to nothing to do with c#.

See also

https://stackoverflow.com/questions/11016078/is-it-possible-to-create-a-function-dynamically-during-runtime-in-c/11020335#11020335

Tldr;

I would like the ability ( just as we have custom reflection provider's today ) the ability to provide my own TypeLoader implementation which mayornot derive from an existing one for various reasons.

I suppose an interface would suit so long as it doesn't contain any default implementations, please use static extension methods.

Also I would like the ability to unload my TypeLoader and replace it with another or the default so that once I load my type I can do other fun stuff like pretend I can't find that type anymore by just unloading... It would be useful for the types to outlive the loader in short.

@juliusfriedman

patch method pointer is good for some case, however it's only suite for method is non virtual, non generic, not in generic type, not inlined so cannot using in some scenarios.

TypeLoader-like solution is better.

The focus is .Net CoreFx should make a official stable managed specification that suite for different scenarios .net core and .net framework, windows and linux.

Mark! AOP is a very important feature. Maybe one day we can see the official plan.

@jkotas happy new year, .net core 3.1 was released and .net 5.0 is on the way, are there any news or plan for this feature?

Moving this to the Tracing space, since this is more about adding probes here. Assume you have already looked at the profiling APIs but you are looking at something in managed code?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GitAntoinee picture GitAntoinee  路  3Comments

yahorsi picture yahorsi  路  3Comments

omajid picture omajid  路  3Comments

EgorBo picture EgorBo  路  3Comments

bencz picture bencz  路  3Comments