How is DebugInfo used?

Jan 14, 2010 at 4:35 PM

My language is going along nicely, and the time to add more meaningful error messages then "An uncaught exception has been thrown" has come. As there seems to be no usage of Expression.DebugInfo() in the SymPL code and the IronPython and IronRuby sources are huge to say the least I have a hard time finding out how to use/add debug info to my code.

I've deducted as much that I can create and add DebugInfoExpression objects to the expression tree, which contains info like filename, source code positions, etc. But how do I gain access to them during binding/runtime (if a binding fails I want to be able to, in the specific binder, create a proper throw expression that contains source file, line and column for example... right now it just says "you made a bobo" and throws you out - while fun, not very helpful to the end user)

If anyone would be able to explain in a few lines how it works and provide a code example I would be more then grateful.

P.S. thanks for all the patience with all my questions about the DLR, it's one of the best libraries I've worked with - but there's very little documentation available D.S.

Coordinator
Jan 14, 2010 at 4:46 PM

I apologize for Sympl not showing them off yet, but they do something different than I think you expected.  All they do is cause the compiler to emit sequence points into the IL so that you can get line and source location.  This minimal debug info should enable really simple stepping in VS too, but I might be confused since I'm not sure what IronPython does to get that.

Bill

From: fholm [mailto:notifications@codeplex.com]
Sent: Thursday, January 14, 2010 9:36 AM
To: Bill Chiles
Subject: How is DebugInfo used? [dlr:80850]

From: fholm

My language is going along nicely, and the time to add more meaningful error messages then "An uncaught exception has been thrown" has come. As there seems to be no usage of Expression.DebugInfo() in the SymPL code and the IronPython and IronRuby sources are huge to say the least I have a hard time finding out how to use/add debug info to my code.

I've deducted as much that I can create and add DebugInfoExpression objects to the expression tree, which contains info like filename, source code positions, etc. But how do I gain access to them during binding/runtime (if a binding fails I want to be able to, in the specific binder, create a proper throw expression that contains source file, line and column for example... right now it just says "you made a bobo" and throws you out - while fun, not very helpful to the end user)

If anyone would be able to explain in a few lines how it works and provide a code example I would be more then grateful.

P.S. thanks for all the patience with all my questions about the DLR, it's one of the best libraries I've worked with - but there's very little documentation available D.S.

Read the full discussion online.

To add a post to this discussion, reply to this email (dlr@discussions.codeplex.com)

To start a new discussion for this project, email dlr@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com

Jan 14, 2010 at 5:02 PM

billchi: Nothing to apologize for, SymPL is a great example and was way more then i expected when I started digging in the DLR - the idea to use sexpressions and a lisp like syntax was also very good since it seems to fit nicely over the DLR expression tree model.

But, onto my follow up question then: How does IronPython/IronRuby achieve their ability to give nice error messages to the end user? One way would be to pass the metadata into the binders, but that would only solve the expressions that need to be dynamic - and that would make it impossible to use canonical binders, so it's not really an option.

Use a "debug-mode" and wrap all expressions in some kind of try/catch semantics? Seems like a lot of hassle and not very dynamic.

A third way might be to wrap all expressions in your own expression classes instead? But from what I could tell of the IronPython sources it doesn't look like it's done that way, and it also seems rather cumbersome creating your own subclasses of all expressions...

After these three ideas I'm pretty much drawing a blank here.

Coordinator
Jan 14, 2010 at 5:34 PM

You definitely don’t want to include line number information in your binder because you would not got sharing of rules across the same binders w/ different line numbers – although you can extra information above and beyond what the standard binders include if you wanted, C# does this for getting the exact semantics you would usually have at compile time.

I generally suggest relying upon the stack trace to include the line information – your exceptions should just tell the user what went wrong, the stack trace will tell them where.  This works great for code which is compiled ahead of time because it can include the DebugInfoExpressions which mark the line number.  These are just used to mark the start / end of line number information, so you could put them at the start of a method, embed them in a block before you start emitting the next line, etc…  In .NET 4.0 you could also compile into collectible assemblies and still include debug info.  But if you’re using DynamicMethod’s that doesn’t work.

For that IronPython and IronRuby are currently doing two entirely different things.   IronPython tracks line numbers in a local variable and updates the stack trace in a try/catch/rethrow block (because try/fault isn’t available in dynamic methods either).  IronRuby maintains a mapping of IL offset to line number by providing a DebugInfoGenerator when compiling.  When an exception occurs it then maps back to the line number although this can be imprecise.  So both of those approaches have their own benefits and drawbacks.

From: fholm [mailto:notifications@codeplex.com]
Sent: Thursday, January 14, 2010 10:03 AM
To: Dino Viehland
Subject: Re: How is DebugInfo used? [dlr:80850]

From: fholm

billchi: Nothing to apologize for, SymPL is a great example and was way more then i expected when I started digging in the DLR - the idea to use sexpressions and a lisp like syntax was also very good since it seems to fit nicely over the DLR expression tree model.

But, onto my follow up question then: How does IronPython/IronRuby achieve their ability to give nice error messages to the end user? One way would be to pass the metadata into the binders, but that would only solve the expressions that need to be dynamic - and that would make it impossible to use canonical binders, so it's not really an option.

Use a "debug-mode" and wrap all expressions in some kind of try/catch semantics? Seems like a lot of hassle and not very dynamic.

A third way might be to wrap all expressions in your own expression classes instead? But from what I could tell of the IronPython sources it doesn't look like it's done that way, and it also seems rather cumbersome creating your own subclasses of all expressions...

After these three ideas I'm pretty much drawing a blank here.

Read the full discussion online.

To add a post to this discussion, reply to this email (dlr@discussions.codeplex.com)

To start a new discussion for this project, email dlr@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com

Jan 14, 2010 at 6:07 PM
Edited Jan 14, 2010 at 6:07 PM

A binder should produce rules that throw an exception - a runtime exception. Such exception (like any other CLR exception) captures stack trace when thrown. You can get StackTrace from an exception by calling new StackTrace(exception, true). "True" argument means you want line information to be included in the frames (note that this API doesn't work in partial trust). So, when you display the error to the user you can display the stack trace including source line and file information. There are some gotchas though.

LambdaExpression.Compile() produces a delegate to a DynamicMethod. DynamicMethods don't allow debugging information to be set on them (the standard PDB debug info). So even if you use DebugInfoExpressions in your trees they are ignored in this mode. You can use LambdaExpression.CompileToMethod to compile an expression tree to a non-dynamic method. You need to create a MethodBuilder via Reflection.Emit or you can use a utility Microsoft.Scripting.Generation.CompilerHelpers.CompileToMethod from Microsoft.Dynamic.dll that creates it for you. Note that emitting types via Reflection.Emit is very very slow. So you don't want to do this at runtime in "release" mode, when you don't need the debugging information. ScriptRuntime has a property DebugMode which IronRuby and IronPython use to determine if they should emit debug information this way. This kind of debug information allows you to get the line numbers and file paths filled in StackTrace and also to attach a debugger (like VS) to the process and step thru your code (each DebugInfoExpression marks a sequence point).

 

Jan 14, 2010 at 6:15 PM

dinov & TomasMatousek: wow, thanks! exactly what I was looking for, again you have set me on the right path! Most you're appreciated for taking time out of your day to help guide me through the DLR, I'm incredibly impressed by the DLR and how (relatively) easy it makes the task of implementing a dynamic language!

Coordinator
Jan 14, 2010 at 7:09 PM

Thanks!!  I forget if you said, but do you mind sharing a brief description of the language your building and how you plan to use it.  If you want, feel free to send me mail at billchi@microsoft.com

Bill

Jan 15, 2010 at 7:37 AM

Bill: I'll send over a brief description sometime over the weekend, just gotta find time between work and hacking on my language :)