Using DebugInfo

Oct 12, 2009 at 10:15 AM

  Hi,

I am not able to add file/line number information to code generated using the DLR, and I can't spot the difference between my code and the code in IronPython/Ruby. The test code I am using is:

 

    class MainClass
    {
        public static void Main(string[] args)
        {
            Expression.Lambda(Dbg())
                .Compile(DebugInfoGenerator.CreatePdbGenerator())
                .DynamicInvoke();
        }

        public static void P()
        {
            var trace = new System.Diagnostics.StackTrace(true);

            foreach (var frame in trace.GetFrames())
            {
                System.Console.WriteLine(frame.ToString());
            }
        }

        static Expression Dbg()
        {
            return Expression.Block(
                Expression.DebugInfo(
                    Expression.SymbolDocument("aaa.cs"),
                    1, 1,
                    2, 2),
                Expression.Call(
                    typeof(MainClass).GetMethod("P")),
                Expression.Throw(
                    Expression.New(
                        typeof(System.Exception).GetConstructor(
                            new Type[] { typeof(string) }),
                        Expression.Constant("Test"))),
                Expression.ClearDebugInfo(
                    Expression.SymbolDocument("aaa.cs"))
                );
        }
}

all the stack frames in the stack trace contain file/line information except the one in the lambda. I am using the latest DLR (Subversion revision 32072) with Mono on Mac OS X.

 

Regards,

Mattia

Oct 12, 2009 at 4:39 PM

There is a difference (see RubyScriptCode.cs for complete source code):

<font size="2">

        internal static Delegate CompileLambda(LambdaExpression lambda, bool debugMode) {
            if (debugMode) {
#if !SILVERLIGHT
                // try to use PDBs and fallback to CustomGenerator if not allowed to:
                if (_HasPdbPermissions) {
                    try {
                        return CompilerHelpers.CompileToMethod(lambda, DebugInfoGenerator.CreatePdbGenerator(), true);
                    } catch (SecurityException) {
                        // do not attempt next time in this app-domain:
                        _HasPdbPermissions = false;
                    }
                }
#endif
                return CompilerHelpers.CompileToMethod(lambda, new CustomGenerator(), false);
            } else {
                return lambda.LightCompile(false);
            }
        }
 
Unfortunately, maintaining stack trace is not simple. The major problem is that DynamicMethods, which are produced by LambdaExpression.Compile, don't support debugging information (PDBs). Besides PDBs don't work in partial trust.
In "debug" mode IronRuby is using CompilerHelpers.CompileToMethod. This method defines a new type and emits the Expression Tree into a method on it. This works with PDBs and provides full stack trace information at runtime in full trust.
To make this work in partial trust (a SecurityException is thrown by CompilerHelpers.CompileToMethod) we use our custom debug info generator instead of PDBs.
If debug mode is off we use adaptive compilation as you can see above. That gets a little bit more complicated since we need to maintain the interpreted stack trace and combine it with the real stack trace.
</font>

Oct 17, 2009 at 7:26 AM

Thanks for the detailed explanation; unfortunately using

  CompilerHelpers.CompileToMethod(
      Expression.Lambda(Dbg()),
      DebugInfoGenerator.CreatePdbGenerator(), true)
      .DynamicInvoke();

does not throw a SecurityException bu does not add file/line information to the stack trace.

 

Regards,

Mattia

 

Oct 17, 2009 at 6:35 PM

What output do you get from your program? I get:

P at offset 37 in file:line:column C:\Dev\ConsoleApplication1\Program.cs:114:35

lambda_method$1 at offset 25 in file:line:column aaa.cs:1:1

_InvokeMethodFast at offset 0 in file:line:column <filename unknown>:0:0

InvokeMethodFast at offset 72 in file:line:column <filename unknown>:0:0

Invoke at offset 351 in file:line:column <filename unknown>:0:0

DynamicInvokeImpl at offset 85 in file:line:column <filename unknown>:0:0

Main at offset 89 in file:line:column C:\Dev\ConsoleApplication1\Program.cs:285:9


Unhandled Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.

Oct 18, 2009 at 11:22 AM

I get this (I also tried under Linux, same result):

P at offset 10 in file:line:column /Users/mbarbon/devel/perl5/p/dlrt/Test/Main.cs:104:0
lambda_method$1 at offset 0 in file:line:column <filename unknown>:0:0
InternalInvoke at offset 0 in file:line:column <filename unknown>:0:0
Invoke at offset 0 in file:line:column <filename unknown>:0:0
Invoke at offset 0 in file:line:column <filename unknown>:0:0
DynamicInvokeImpl at offset 0 in file:line:column <filename unknown>:0:0
DynamicInvokeImpl at offset 0 in file:line:column <filename unknown>:0:0
DynamicInvoke at offset 0 in file:line:column <filename unknown>:0:0
Main at offset 0 in file:line:column /Users/mbarbon/devel/perl5/p/dlrt/Test/Main.cs:71:0

 

 

Oct 20, 2009 at 4:38 PM

What is the exact .NET Framework version you use? (e.g. 3.5.30729.4918, you can run csc.exe w/o arguments to get the number).

Oct 20, 2009 at 7:30 PM

I am using Mono 2.4.3 on Mac OS X (there isn't a csc command).