| File: Compiler\DependencyAnalysis\StackTraceLineNumbersNode.cs | Web Access |
| Project: src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.IO; using Internal.Text; using Internal.TypeSystem; using Internal.NativeFormat; using Internal; using Debug = System.Diagnostics.Debug; namespace ILCompiler.DependencyAnalysis { /// <summary> /// Contains information about mapping native code offsets to line numbers. /// </summary> public sealed class StackTraceLineNumbersNode : ObjectNode, ISymbolDefinitionNode { private readonly ExternalReferencesTableNode _externalReferences; private readonly StackTraceDocumentsNode _documents; public StackTraceLineNumbersNode(ExternalReferencesTableNode externalReferences, StackTraceDocumentsNode documents) { _externalReferences = externalReferences; _documents = documents; } public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix).Append("__stacktrace_line_numbers"u8); } public int Offset => 0; public override bool IsShareable => false; public override ObjectNodeSection GetSection(NodeFactory factory) => _externalReferences.GetSection(factory); public override bool StaticDependenciesAreComputed => true; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. if (relocsOnly) return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this }); NativeWriter nativeWriter = new NativeWriter(); VertexHashtable hashtable = new VertexHashtable(); Section nativeSection = nativeWriter.NewSection(); nativeSection.Place(hashtable); foreach (StackTraceMapping mapping in factory.MetadataManager.GetStackTraceMapping(factory)) { if ((factory.MetadataManager.StackTracePolicy.GetMethodVisibility(mapping.Method) & MethodStackTraceVisibilityFlags.HasLineNumbers) == 0) continue; var entrypointSymbol = factory.MethodEntrypoint(mapping.Method); if (entrypointSymbol is not INodeWithDebugInfo debugInfo) continue; Vertex blob = CreateLineNumbersBlob(nativeWriter, _documents, debugInfo); if (blob == null) continue; Vertex methodPointer = nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(entrypointSymbol)); var hashtableEntry = nativeWriter.GetTuple(methodPointer, blob); uint hashcode = VersionResilientHashCode.CombineThreeValuesIntoHash((uint)mapping.OwningTypeHandle, (uint)mapping.MethodNameHandle, (uint)mapping.MethodSignatureHandle); hashtable.Append(hashcode, nativeSection.Place(hashtableEntry)); } foreach (ReflectionStackTraceMapping mapping in factory.MetadataManager.GetReflectionStackTraceMappings(factory)) { Debug.Assert((factory.MetadataManager.StackTracePolicy.GetMethodVisibility(mapping.Method) & MethodStackTraceVisibilityFlags.HasLineNumbers) != 0); var entrypointSymbol = factory.MethodEntrypoint(mapping.Method); if (entrypointSymbol is not INodeWithDebugInfo debugInfo) continue; Vertex blob = CreateLineNumbersBlob(nativeWriter, _documents, debugInfo); if (blob == null) continue; Vertex methodPointer = nativeWriter.GetUnsignedConstant(_externalReferences.GetIndex(entrypointSymbol)); var hashtableEntry = nativeWriter.GetTuple(methodPointer, blob); uint hashcode = VersionResilientHashCode.CombineTwoValuesIntoHash((uint)mapping.OwningTypeHandle, (uint)mapping.MethodHandle); hashtable.Append(hashcode, nativeSection.Place(hashtableEntry)); } static Vertex CreateLineNumbersBlob(NativeWriter writer, StackTraceDocumentsNode documents, INodeWithDebugInfo debugInfoNode) { var encoder = default(NativePrimitiveEncoder); encoder.Init(); int currentNativeOffset = 0; int currentLineNumber = 0; string currentDocument = null; uint numEntries = 0; foreach (NativeSequencePoint sequencePoint in debugInfoNode.GetNativeSequencePoints()) { // Make sure a zero native offset delta is not possible because we use it below // to indicate an update to the current document. Debug.Assert(currentDocument == null || currentNativeOffset != sequencePoint.NativeOffset); if (currentDocument != sequencePoint.FileName) { // We start with currentDocument == null, so the reader knows the first byte of the output // is a document number. Otherwise we use NativeOffsetDelta == 0 as a marker that the next // byte is a document number and not a native offset delta. if (currentDocument != null) encoder.WriteSigned(0); encoder.WriteSigned(documents.GetDocumentId(sequencePoint.FileName)); } int nativeOffsetDelta = sequencePoint.NativeOffset - currentNativeOffset; encoder.WriteSigned(nativeOffsetDelta); int lineNumberDelta = sequencePoint.LineNumber - currentLineNumber; encoder.WriteSigned(lineNumberDelta); numEntries++; currentLineNumber = sequencePoint.LineNumber; currentNativeOffset = sequencePoint.NativeOffset; currentDocument = sequencePoint.FileName; } var ms = new MemoryStream(); encoder.Save(ms); return numEntries == 0 ? null : writer.GetTuple(writer.GetUnsignedConstant(numEntries), new BlobVertex(ms.ToArray())); } byte[] streamBytes = nativeWriter.Save(); return new ObjectData(streamBytes, Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this }); } protected internal override int Phase => (int)ObjectNodePhase.Ordered; public override int ClassCode => (int)ObjectNodeOrder.StackTraceLineNumbersNode; } }