-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can Jitex still intercept methods that have already been JIT-compiled? #85
Comments
Hi! Thanks a lot! Yes, Jitex can intercept methods already compiled by JIT. You can use: See this example intercepting a method Sum: using Jitex;
var result = MyMath.Sum(1, 1);
Console.WriteLine(result);
JitexManager.MethodResolver += context =>
{
if (context.Method.Name == "Sum")
context.InterceptCall();
};
JitexManager.Interceptor += async context =>
{
if (context.Method.Name == "Sum")
{
Console.WriteLine("Method sum intercepted");
context.SetReturnValue(-1);
}
};
//Will not be intercepted, because was already compiled.
result = MyMath.Sum(10, 10);
Console.WriteLine(result);
class MyMath
{
public static int Sum(int a, int b)
{
Console.WriteLine("Sum called");
return a + b;
}
} Output:
Calling MethodHelper.ForceRecompile: using System.Reflection;
using Jitex;
using Jitex.Utils;
var result = MyMath.Sum(1, 1);
Console.WriteLine(result);
JitexManager.MethodResolver += context =>
{
if (context.Method.Name == "Sum")
context.InterceptCall();
};
JitexManager.Interceptor += async context =>
{
if (context.Method.Name == "Sum")
{
Console.WriteLine("Method sum intercepted");
context.SetReturnValue(-1);
}
};
var sumMethod = typeof(MyMath).GetMethod("Sum", (BindingFlags)(-1));
//Force jit compile method again
MethodHelper.ForceRecompile(sumMethod);
result = MyMath.Sum(10, 10);
Console.WriteLine(result);
class MyMath
{
public static int Sum(int a, int b)
{
Console.WriteLine("Sum called");
return a + b;
}
} Output:
If your method is a R2R, you can try: MethodHelper.DisableReadyToRun(sumMethod)
MethodHelper.ForceRecompile(sumMethod) Tell me if worked. |
Thanks a lot, I'll try it out and let you know the result |
Yes, it worked, at first I was using .NET Core 3.1 version but it reported error
Are there any plans for .NET Core 2.1/3.1 support for the Recompile method in the future, and will Jitex support .NET 7.0? |
Thanks! For .NET Core 2.1/3.1, when I developed that feature, I couldn't make it work with .NET Core; it only works with .NET 5 or above. That's the reason it doesn't work on those versions. I will try to make it compatible with these versions again. Regarding .NET 7, yes, I can make it work on .NET 7. Honestly, Jitex development was paused on .NET 6 because no one was using Jitex. I'll work to add support for .NET 7 this week. It will probably take 2 weeks to be completed. After that, I will try to enable ForceRecompile on versions below .NET 5. |
I am highly anticipating the support of .NET 7.0, and the release of .NET 8.0 in just a few months' time. I recognize that the development of Jitex is a formidable and challenging undertaking, and many people have yet to fully realize its value. The ability to non-invasively modify any method's IL is truly remarkable, and there is no other project that can achieve this functionality. I am currently planning to utilize Jitex for a project of my own, as it perfectly satisfies my requirements. |
I have another concern, and I am unsure if this is a problem with my usage or something else, or if I should open a new issue. This is a test project for .NET 6.0, and I am attempting to retrieve the result of DateTime.Now. However, it seems that using System.Reflection;
using Jitex;
using Jitex.Utils;
Console.WriteLine(DateTime.Now);
var nowMethod = typeof(DateTime).GetProperty("Now", (BindingFlags)(-1)).GetGetMethod();
JitexManager.MethodResolver += context =>
{
Console.WriteLine(context.Method.Name);
if (context.Method.Name == nowMethod.Name)
{
Console.WriteLine("MethodResolver");
context.InterceptCall();
}
};
JitexManager.Interceptor += async context =>
{
if (context.Method.Name == nowMethod.Name)
{
Console.WriteLine("Before interceptor! ");
await context.ContinueAsync();
>>>> Console.WriteLine($"After interceptor! Result:{context.GetReturnValue()}"); <<<<
}
};
if (MethodHelper.IsReadyToRun(nowMethod))
{
MethodHelper.DisableReadyToRun(nowMethod);
}
//Force jit compile method again
MethodHelper.ForceRecompile(nowMethod);
Console.WriteLine(DateTime.Now); The output of the project appears as follows: 2023/7/25 8:32:21
MethodResolver
Before interceptor!
2023/7/25 8:32:21 I am not sure whether the issue lies with my usage or with something else. Thank you for reading! |
Yes, it's a bug. There are multiple 'ret' instructions in the body from DateTime.Now:
Currently, Jitex only expects one 'ret' instruction and just replace the last, not covering all paths. I'll open a new issue for that. |
Okay, thank you very much for your time. There is another scenario, if the target method throws an exception, the I just read through the source code and noticed that your implementation of the Intercepter aspect is different from other AOP frameworks. Is it because of support for async/await? If we want to make the program more robust, we might need to modify the target method like this: public int Sum(int n1, int n2)
{
try
{
CallContext context = new CallContext(methodHandle, Pointer.Box((void*)this), Pointer.Box((void*)n1),Pointer.Box((void*)n2));
CallManager callManager = new CallManager(context);
callManager.CallInteceptorsAsync();
}
catch (System.Exception ex)
{
// log exception
}
try
{
if(context.ProceedCall){
int result = n1+n2;
context.SetResult(Pointer.Box((void*)result);
}
}
catch (System.Exception ex)
{
// log exception
context.SetException(ex);
}
callManager.ReleaseTask();
if(context.Exception != null)
throw context.Exception;
return context.GetResult<int>();
} I have seen the implementation of Datadog before, and this is how they did it: Rewrite the target method body with the calltarget implementation. (This is function is triggered by the ReJIT
handler) Resulting code structure:
- Add locals for TReturn (if non-void method), CallTargetState, CallTargetReturn/CallTargetReturn<TReturn>,
Exception
- Initialize locals
try
{
try
{
try
{
- Invoke BeginMethod with object instance (or null if static method) and original method arguments
- Store result into CallTargetState local
}
catch when exception is not Datadog.Trace.ClrProfiler.CallTarget.CallTargetBubbleUpException
{
- Invoke LogException(Exception)
}
- Execute original method instructions
* All RET instructions are replaced with a LEAVE_S. If non-void method, the value on the stack is first stored
in the TReturn local.
}
catch (Exception)
{
- Store exception into Exception local
- throw
}
}
finally
{
try
{
- Invoke EndMethod with object instance (or null if static method), TReturn local (if non-void method),
CallTargetState local, and Exception local
- Store result into CallTargetReturn/CallTargetReturn<TReturn> local
- If non-void method, store CallTargetReturn<TReturn>.GetReturnValue() into TReturn local
}
catch when exception is not Datadog.Trace.ClrProfiler.CallTarget.CallTargetBubbleUpException
{
- Invoke LogException(Exception)
}
}
- If non-void method, load TReturn local
- RET I will also try to see if I can solve this problem later, but I am not very familiar with MSIL. If you have time to do all of this, that would be great. Thank you for reading! |
That's a good approach using exceptions. We can try to implement that in the near future.
I'm not familiar with how other frameworks implement Interceptors, as they require a lot of work (interfaces, virtual methods, ...) to intercept methods, and I never dug too deep to understand how they work. Perhaps we can explore new approaches in the future to simplify and enhance our implementation.
I'll open a new issue for this bug. Thanks! |
Hi!
This is a great project and work. I have a question about Jitex: can it be used for methods that have already been JIT-compiled? Because many times, we may not know in advance which methods need to be intercepted; it's only during the program's runtime that we discover the methods that need to be intercepted, and by that time, they may have already been JIT-compiled. I would like to know if Jitex can be applied in such a scenario. If it is currently not supported, are there any plans to implement this feature in the future?
Thank you for reading!
The text was updated successfully, but these errors were encountered: