Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Line Number is not included in Exception Stacktrace (Code Dynamically Compiled with Roslyn)

Tags:

c#

.net

roslyn

I use the roslyn compiler to compile code on demand.

Now when there is an Exception in the compiled code (example: Divided by Zero Exception) I get in Visual Studio the line number displayed:

StackTrace

But when I do stacktrace.ToString() the line information is not included. In frame.GetLineNumber the line number is also 0.

The code for handling the exception:

    try
     {
        int i = 0;
        int iiii = 5 / i; 
     }
     catch (Exception ex)
     {
        var stackTrace = new StackTrace(ex, true);
        var frame = stackTrace.GetFrame(0);

        Console.WriteLine("Exception message: {0}", ex.Message);
        Console.WriteLine("Exception in file: {0}", frame.GetFileName());
        Console.WriteLine("Exception in method: {0}", frame.GetMethod());
        Console.WriteLine("Exception at line numer: {0}", frame.GetFileLineNumber());
     }

The Code for Compiling my Code on demand

 CSharpParseOptions po = new CSharpParseOptions(LanguageVersion.CSharp7, DocumentationMode.Parse, SourceCodeKind.Regular);
     SyntaxTree parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(code, po);

     List<string> defaultNamespaces = GetUsings(parsedSyntaxTree);

     //// Referenzen über Kommentare heraussuchen:
     List<MetadataReference> defaultReferences = GetReferences(parsedSyntaxTree, rootPfad);

     var encoding = Encoding.UTF8;

     var assemblyName = Path.GetRandomFileName();
     var symbolsName = Path.ChangeExtension(assemblyName, "pdb");
     var sourceCodePath = "generated.cs";

     var buffer = encoding.GetBytes(code);
     var sourceText = SourceText.From(buffer, buffer.Length, encoding, canBeEmbedded: true);

     var syntaxTree = CSharpSyntaxTree.ParseText(
         sourceText,
         new CSharpParseOptions(),
         path: sourceCodePath);

     var syntaxRootNode = syntaxTree.GetRoot() as CSharpSyntaxNode;
     var encoded = CSharpSyntaxTree.Create(syntaxRootNode, null, sourceCodePath, encoding);


     CSharpCompilationOptions defaultCompilationOptions =
           new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
                   .WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Debug).WithPlatform(Platform.AnyCpu)
                   .WithUsings(defaultNamespaces);

     CSharpCompilation compilation = CSharpCompilation.Create(
         assemblyName,
         syntaxTrees: new[] { encoded },
         references: defaultReferences,

         options: defaultCompilationOptions
     );

     using (var assemblyStream = new MemoryStream())
     using (var symbolsStream = new MemoryStream())
     {
        var emitOptions = new EmitOptions(
                debugInformationFormat: DebugInformationFormat.PortablePdb,
                pdbFilePath: symbolsName);

        var embeddedTexts = new List<EmbeddedText> { EmbeddedText.FromSource(sourceCodePath, sourceText) };

        EmitResult result = compilation.Emit(
            peStream: assemblyStream,
            pdbStream: symbolsStream,
            embeddedTexts: embeddedTexts,
            options: emitOptions);


        if (result.Success)
        {
           Console.WriteLine("Kompilierung erfolgreich!");
           try
           {
              var assembly = Assembly.Load(assemblyStream.ToArray(), symbolsStream.ToArray());
              var parsedCodeInfo = new IM3ParsedCodeInfo() { Assembly = assembly, Erfolgreich = true };

              return parsedCodeInfo;
           }
           catch (Exception ex)
           {
              Console.WriteLine("Ausnahme aufgetreten:");
              Console.WriteLine(ex);

              var parsedCodeInfo = new IM3ParsedCodeInfo() { ErrorException = ex, Erfolgreich = false };
              return parsedCodeInfo;
           }
        }
        else
        {
           Console.WriteLine("Kompilierung nicht erfolgreich!");
           foreach (var diagnostic in result.Diagnostics)
           {
              Console.WriteLine(diagnostic.ToString());
           }

           var parsedCodeInfo = new IM3ParsedCodeInfo() { ErrorDiagnostics = result.Diagnostics, Erfolgreich = false };
           return parsedCodeInfo;
        }
     }

Picture for provided Solution Suggestion: Line Numbers not available

like image 796
MarkusE Avatar asked Oct 27 '25 23:10

MarkusE


1 Answers

UPDATE: As per Hans Passant comment:

If you are running in .NET Core you'll need PortablePDB files; for .NET Framework you'll need PDB files

According to MSDN StackTrace.GetFrame(0) returns the most recent function call, which would be IEnumerator<T>.MoveNext() and you need the PDB file for .NET Framework or PortablePDB for .NET Core for that function (but you don't have it).

You should iterate over the StackFrames from 0 to StackTrace.FrameCount - 1, stopping at the first StackFrame.GetFileLineNumber() > 0:

var stack = new StackFrame(ex, true);
StackFrame frame = null;
for (int i = 0; i < stack.FrameCount; i++)
{
    frame = stack.GetFrame(i);
    if (frame.GetFileLineNumber() > 0) break;
}

Console.WriteLine("Exception message: {0}", ex.Message);
Console.WriteLine("Exception in file: {0}", frame.GetFileName());
Console.WriteLine("Exception in method: {0}", frame.GetMethod());
Console.WriteLine("Exception at line number: {0}", frame.GetFileLineNumber());

This correctly prints

Exception message: Sequence contains no elements

Exception in file: [FilePath]\Root.cs

Exception in method: System.Object GetModuleDescription(System.Object[])

Exception at line number: 70

like image 58
Ivan García Topete Avatar answered Oct 30 '25 13:10

Ivan García Topete