Shri Borde
2008-Dec-30 22:27 UTC
[Ironruby-core] Interpreter, backtrace, and call site caching
For backtraces to work correctly in interpreter mode, it is required that the interpreter guard every call to C# code with a try-catch, so that the catch block has a chance to stash away the backtrace if an exception is thrown. This is done in Interpreter.InvokeMethod, and the catch block gives the language a chance to save the backtrace by calling LanguageContext.InterpretExceptionThrow. However, with call-site caching enabled in interpreter mode, Interpreter.InterpretMetaAction can directl invoke the compiled delegate instead of calling Interpreter.InvokeMethod. This breaks the backtrace. private static object InterpretMetaAction(InterpreterState state, DynamicMetaObjectBinder action, DynamicExpression node, object[] argValues) { ... callSiteInfo.Counter++; if (callSiteInfo.Counter > SiteCompileThreshold) { if (callSiteInfo.CallSite == null) { SetCallSite(callSiteInfo, node); } return callSiteInfo.CallerTarget(callSiteInfo.CallSite, argValues); } ... var result = Interpret(state, binding.Expression); return result; } Is this a known issue? The interpreter stack trace in the unoptimized case is shown below. The fix could be for the interpreter to maintain a separate cache of compiled rules which have a try-catch generated for all MethodCallExpression nodes so as to match the unoptimized code behavior. Thanks, Shri Microsoft.Scripting.dll!Microsoft.Scripting.Utils.ReflectedCaller.Invoke(object[] args = {object[0x00000001]}) Line 46 + 0x19 bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InvokeMethod(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Reflection.MethodInfo method = {System.Reflection.RuntimeMethodInfo}, object instance = null, object[] parameters = {object[0x00000001]}) Line 135 + 0xb bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretMethodCallExpression(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.MethodCallExpressionN}) Line 251 + 0x30 bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.MethodCallExpressionN}) Line 38 + 0xb bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretAndCheckFlow(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression node = {System.Linq.Expressions.MethodCallExpressionN}, out object result = null) Line 76 + 0x11 bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretBlockExpression(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.Block2}) Line 1256 + 0xf bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.Block2}) Line 79 + 0xb bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretAndCheckYield(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression target = {System.Linq.Expressions.Block2}, out object res = true) Line 86 + 0x11 bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretConditionalExpression(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.FullConditionalExpression}) Line 113 + 0x22 bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.FullConditionalExpression}) Line 40 + 0xb bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretMetaAction(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Dynamic.DynamicMetaObjectBinder action = {IronRuby.Runtime.Calls.RubyCallAction}, System.Linq.Expressions.DynamicExpression node = {System.Linq.Expressions.DynamicExpression2}, object[] argValues = {object[0x00000002]}) Line 810 + 0x2a bytes C# -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/ironruby-core/attachments/20081230/53c351a0/attachment.html>
Shri Borde
2008-Dec-31 08:05 UTC
[Ironruby-core] Interpreter, backtrace, and call site caching
Adding a try-catch around the optimized case (callSiteInfo.CallerTarget) fixes the problem. However, this points out that we need to have a try-catch around every operation in Interpreter.cs that could cause an exception and have the catch call LanguageContext.InterpretExceptionThrow. From: ironruby-core-bounces at rubyforge.org [mailto:ironruby-core-bounces at rubyforge.org] On Behalf Of Shri Borde Sent: Tuesday, December 30, 2008 2:28 PM To: ironruby-core at rubyforge.org Subject: [Ironruby-core] Interpreter, backtrace, and call site caching For backtraces to work correctly in interpreter mode, it is required that the interpreter guard every call to C# code with a try-catch, so that the catch block has a chance to stash away the backtrace if an exception is thrown. This is done in Interpreter.InvokeMethod, and the catch block gives the language a chance to save the backtrace by calling LanguageContext.InterpretExceptionThrow. However, with call-site caching enabled in interpreter mode, Interpreter.InterpretMetaAction can directl invoke the compiled delegate instead of calling Interpreter.InvokeMethod. This breaks the backtrace. private static object InterpretMetaAction(InterpreterState state, DynamicMetaObjectBinder action, DynamicExpression node, object[] argValues) { ... callSiteInfo.Counter++; if (callSiteInfo.Counter > SiteCompileThreshold) { if (callSiteInfo.CallSite == null) { SetCallSite(callSiteInfo, node); } return callSiteInfo.CallerTarget(callSiteInfo.CallSite, argValues); } ... var result = Interpret(state, binding.Expression); return result; } Is this a known issue? The interpreter stack trace in the unoptimized case is shown below. The fix could be for the interpreter to maintain a separate cache of compiled rules which have a try-catch generated for all MethodCallExpression nodes so as to match the unoptimized code behavior. Thanks, Shri Microsoft.Scripting.dll!Microsoft.Scripting.Utils.ReflectedCaller.Invoke(object[] args = {object[0x00000001]}) Line 46 + 0x19 bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InvokeMethod(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Reflection.MethodInfo method = {System.Reflection.RuntimeMethodInfo}, object instance = null, object[] parameters = {object[0x00000001]}) Line 135 + 0xb bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretMethodCallExpression(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.MethodCallExpressionN}) Line 251 + 0x30 bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.MethodCallExpressionN}) Line 38 + 0xb bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretAndCheckFlow(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression node = {System.Linq.Expressions.MethodCallExpressionN}, out object result = null) Line 76 + 0x11 bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretBlockExpression(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.Block2}) Line 1256 + 0xf bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.Block2}) Line 79 + 0xb bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretAndCheckYield(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression target = {System.Linq.Expressions.Block2}, out object res = true) Line 86 + 0x11 bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretConditionalExpression(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.FullConditionalExpression}) Line 113 + 0x22 bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Linq.Expressions.Expression expr = {System.Linq.Expressions.FullConditionalExpression}) Line 40 + 0xb bytes C# Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretMetaAction(Microsoft.Scripting.Interpretation.InterpreterState state = {Microsoft.Scripting.Interpretation.InterpreterState}, System.Dynamic.DynamicMetaObjectBinder action = {IronRuby.Runtime.Calls.RubyCallAction}, System.Linq.Expressions.DynamicExpression node = {System.Linq.Expressions.DynamicExpression2}, object[] argValues = {object[0x00000002]}) Line 810 + 0x2a bytes C# -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/ironruby-core/attachments/20081231/8abb9514/attachment-0001.html>