Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrong file path and line number in Exception stack traces from dynamic code

We are using System.Reflection.Emit to generate code at runtime from source code (yes - as in a compiler). We provide correct symbol information to the ILGenerator with MarkSequencePoint etc, and enable all debug flags on the AssemblyBuilder. The assembly is kept in memory in the same process that compiled it and is executed directly.

When using the Visual Studio debugger to step through the source for the dynamically generated code it actually works perfectly, and Visual Studio seems to be fully aware of where code comes from in terms of files and line numbers.

HOWEVER - When exceptions are thrown by the generated code, the System.Exception objects contain stack traces that are completely wrong. They point to other (valid, but wrong) files and line numbers. It gets the class and method name right, but the file and line number pointed to has nothing to do with the code path the exception actually came from.

The files pointed to are so unrelated it seems it can't be linked to inlining or optimizations. The only pattern I can spot is that it seems to be offset by some files (in an imaginary alphabetically sorted list of source files the assembly was built from). However, this pattern is not 100% consistent, and it seems irrational that this is linked to the source of the problem.

If i construct a System.Diagnostics.Debug object from the Exception, it contains the same faulty information.

I am assuming that the .NET runtime uses the same metadata to construct Exception stack traces as the debugger uses for stepping through the code, in which case this behavior is really weird.

I'm trying to find out if this is a known bug in .NET when dealing with dynamic in-memory assemblies, or if anyone has seen similar problems in other areas.

like image 938
Duckers Avatar asked Jan 13 '14 13:01

Duckers


1 Answers

Okay, I was NOT able to figure out what was causing the problem nor how to make .NET behave correctly, but at least I was able to find a work-around that might also work for others experiencing the same problem.

  1. As I am generating the CIL bytecode I am building a separate database that maps method names (full path) and IL-offset back to the original file names and line numbers.

  2. When exceptions are caught I examine the stack trace and use only the GetMethod() and GetILOffset() information from the stack frame objects. This information from the CLR happens to be correct, even when GetFileName() and GetFileLineNumber() are wrong.

  3. I then for each stack frame use the Method name and IL offset retrieved from the exception to look up into my generated database to determine actual file name and line number for each stack frame I have information about.

  4. The frames I don't find information about in the database are typically stack frames in precompiled .NET modules, for which the information retrieved from CLR is actually correct. For these frames I use GetFileName() and GetFileLineNumber() on the stack frame directly.

like image 191
Duckers Avatar answered Nov 20 '22 10:11

Duckers