private unsafe void WriteEditAndContinueLambdaMap(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.EditAndContinueLambdaMap); writer.WriteStartElement("encLambdaMap"); try { if (record.Data.Length == 0) { return; } int methodOrdinal = -1; int syntaxOffsetBaseline = -1; int closureCount; fixed (byte* blobPtr = &record.Data.ToArray()[0]) { var blobReader = new BlobReader(blobPtr, record.Data.Length); if (!blobReader.TryReadCompressedInteger(out methodOrdinal)) { writer.WriteElementString("methodOrdinal", "?"); writer.WriteEndElement(); return; } // [-1, inf) methodOrdinal--; writer.WriteElementString("methodOrdinal", methodOrdinal.ToString()); if (!blobReader.TryReadCompressedInteger(out syntaxOffsetBaseline)) { writer.WriteElementString("baseline", "?"); writer.WriteEndElement(); return; } syntaxOffsetBaseline = -syntaxOffsetBaseline; if (!blobReader.TryReadCompressedInteger(out closureCount)) { writer.WriteElementString("closureCount", "?"); writer.WriteEndElement(); return; } for (int i = 0; i < closureCount; i++) { writer.WriteStartElement("closure"); try { int syntaxOffset; if (!blobReader.TryReadCompressedInteger(out syntaxOffset)) { writer.WriteElementString("offset", "?"); break; } writer.WriteAttributeString("offset", (syntaxOffset + syntaxOffsetBaseline).ToString()); } finally { writer.WriteEndElement(); } } while (blobReader.RemainingBytes > 0) { writer.WriteStartElement("lambda"); try { int syntaxOffset; if (!blobReader.TryReadCompressedInteger(out syntaxOffset)) { writer.WriteElementString("offset", "?"); return; } writer.WriteAttributeString("offset", (syntaxOffset + syntaxOffsetBaseline).ToString()); int closureOrdinal; if (!blobReader.TryReadCompressedInteger(out closureOrdinal)) { writer.WriteElementString("closure", "?"); return; } closureOrdinal--; if (closureOrdinal != -1) { writer.WriteAttributeString("closure", closureOrdinal.ToString() + (closureOrdinal < -1 || closureOrdinal >= closureCount ? " (invalid)" : "")); } } finally { writer.WriteEndElement(); } } } } finally { writer.WriteEndElement(); //encLocalSlotMap } }
/// <summary> /// Contains a list of buckets, each of which contains a number of flags, a slot ID, and a name. /// TODO: comment when the structure is understood. /// </summary> /// <remarks> /// Appears when there are dynamic locals. /// </remarks> private void WriteDynamicLocalsCustomDebugInfo(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.DynamicLocals); writer.WriteStartElement("dynamicLocals"); var buckets = CDI.DecodeDynamicLocalsRecord(record.Data); foreach (DynamicLocalBucket bucket in buckets) { ulong flags = bucket.Flags; int flagCount = bucket.FlagCount; PooledStringBuilder pooled = PooledStringBuilder.GetInstance(); StringBuilder flagsBuilder = pooled.Builder; for (int f = 0; f < flagCount; f++) { flagsBuilder.Append((flags >> f) & 1UL); } writer.WriteStartElement("bucket"); writer.WriteAttributeString("flagCount", flagCount.ToString()); writer.WriteAttributeString("flags", pooled.ToStringAndFree()); writer.WriteAttributeString("slotId", bucket.SlotId.ToString()); writer.WriteAttributeString("localName", bucket.Name); writer.WriteEndElement(); //bucket } writer.WriteEndElement(); //dynamicLocals }
private unsafe void WriteEditAndContinueLocalSlotMap(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.EditAndContinueLocalSlotMap); writer.WriteStartElement("encLocalSlotMap"); try { int syntaxOffsetBaseline = -1; fixed (byte* compressedSlotMapPtr = &record.Data.ToArray()[0]) { var blobReader = new BlobReader(compressedSlotMapPtr, record.Data.Length); while (blobReader.RemainingBytes > 0) { byte b = blobReader.ReadByte(); if (b == 0xff) { if (!blobReader.TryReadCompressedInteger(out syntaxOffsetBaseline)) { writer.WriteElementString("baseline", "?"); return; } syntaxOffsetBaseline = -syntaxOffsetBaseline; continue; } writer.WriteStartElement("slot"); if (b == 0) { // short-lived temp, no info writer.WriteAttributeString("kind", "temp"); } else { int synthesizedKind = (b & 0x3f) - 1; bool hasOrdinal = (b & (1 << 7)) != 0; int syntaxOffset; bool badSyntaxOffset = !blobReader.TryReadCompressedInteger(out syntaxOffset); syntaxOffset += syntaxOffsetBaseline; int ordinal = 0; bool badOrdinal = hasOrdinal && !blobReader.TryReadCompressedInteger(out ordinal); writer.WriteAttributeString("kind", synthesizedKind.ToString()); writer.WriteAttributeString("offset", badSyntaxOffset ? "?" : syntaxOffset.ToString()); if (badOrdinal || hasOrdinal) { writer.WriteAttributeString("ordinal", badOrdinal ? "?" : ordinal.ToString()); } } writer.WriteEndElement(); } } } finally { writer.WriteEndElement(); //encLocalSlotMap } }
/// <summary> /// Contains a name string. /// TODO: comment when the structure is understood. /// </summary> /// <remarks> /// Appears when are iterator methods. /// </remarks> private void WriteForwardIteratorCustomDebugInfo(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardIterator); writer.WriteStartElement("forwardIterator"); string name = CDI.DecodeForwardIteratorRecord(record.Data); writer.WriteAttributeString("name", name); writer.WriteEndElement(); //forwardIterator }
/// <summary> /// Appears when iterator locals have to lifted into fields. Contains a list of buckets with /// start and end offsets (presumably, into IL). /// TODO: comment when the structure is understood. /// </summary> /// <remarks> /// Appears when there are locals in iterator methods. /// </remarks> private void WriteStatemachineHoistedLocalScopesCustomDebugInfo(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.StateMachineHoistedLocalScopes); writer.WriteStartElement("hoistedLocalScopes"); var scopes = CDI.DecodeStateMachineHoistedLocalScopesRecord(record.Data); foreach (StateMachineHoistedLocalScope scope in scopes) { writer.WriteStartElement("slot"); writer.WriteAttributeString("startOffset", AsILOffset(scope.StartOffset)); writer.WriteAttributeString("endOffset", AsILOffset(scope.EndOffset)); writer.WriteEndElement(); //bucket } writer.WriteEndElement(); }
/// <summary> /// This indicates that further information can be obtained by looking at the custom debug /// info of another method (specified by token). /// </summary> /// <remarks> /// Appears when there are extern aliases and edit-and-continue is disabled. /// Emitting tokens makes tests more fragile. /// </remarks> private void WriteForwardToModuleCustomDebugInfo(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardToModuleInfo); writer.WriteStartElement("forwardToModule"); int token = CDI.DecodeForwardRecord(record.Data); WriteMethodAttributes(token, isReference: true); writer.WriteEndElement(); //forwardToModule }
/// <summary> /// For each namespace declaration enclosing a method (innermost-to-outermost), there is a count /// of the number of imports in that declaration. /// </summary> /// <remarks> /// There's always at least one entry (for the global namespace). /// </remarks> private void WriteUsingCustomDebugInfo(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.UsingInfo); writer.WriteStartElement("using"); ImmutableArray<short> counts = CDI.DecodeUsingRecord(record.Data); foreach (short importCount in counts) { writer.WriteStartElement("namespace"); writer.WriteAttributeString("usingCount", importCount.ToString()); writer.WriteEndElement(); //namespace } writer.WriteEndElement(); //using }
/// <summary> /// If the custom debug info is in a format that we don't understand, then we will /// just print a standard record header followed by the rest of the record as a /// single hex string. /// </summary> private void WriteUnknownCustomDebugInfo(CustomDebugInfoRecord record) { writer.WriteStartElement("unknown"); writer.WriteAttributeString("kind", record.Kind.ToString()); writer.WriteAttributeString("version", record.Version.ToString()); PooledStringBuilder pooled = PooledStringBuilder.GetInstance(); StringBuilder builder = pooled.Builder; foreach (byte b in record.Data) { builder.AppendFormat("{0:X2}", b); } writer.WriteAttributeString("payload", pooled.ToStringAndFree()); writer.WriteEndElement(); //unknown }