/// <summary> /// After the global header (see <see cref="ReadGlobalHeader"/> comes list of custom debug info record. /// Each record begins with a standard header. /// </summary> public static void ReadRecordHeader(byte[] bytes, ref int offset, out byte version, out CustomDebugInfoKind kind, out int size) { version = bytes[offset + 0]; kind = (CustomDebugInfoKind)bytes[offset + 1]; size = BitConverter.ToInt32(bytes, offset + 4); //note: two bytes of padding after kind offset += CdiRecordHeaderSize; }
/// <summary> /// After the global header (see <see cref="ReadGlobalHeader"/> comes list of custom debug info record. /// Each record begins with a standard header. /// </summary> private static void ReadRecordHeader(byte[] bytes, ref int offset, out byte version, out CustomDebugInfoKind kind, out int size, out int alignmentSize) { version = bytes[offset + 0]; kind = (CustomDebugInfoKind)bytes[offset + 1]; alignmentSize = bytes[offset + 3]; // two bytes of padding after kind size = BitConverter.ToInt32(bytes, offset + 4); offset += CDI.CdiRecordHeaderSize; }
/// <exception cref="InvalidOperationException"></exception> public static ImmutableArray<byte> TryGetCustomDebugInfoRecord(byte[] customDebugInfo, CustomDebugInfoKind recordKind) { foreach (var record in GetCustomDebugInfoRecords(customDebugInfo)) { if (record.Kind == recordKind) { return record.Data; } } return default(ImmutableArray<byte>); }
public CustomDebugInfoRecord(CustomDebugInfoKind kind, byte version, ImmutableArray <byte> data) { this.Kind = kind; this.Version = version; this.Data = data; }
/// <exception cref="InvalidOperationException"></exception> public static ImmutableArray <byte> TryGetCustomDebugInfoRecord(byte[] customDebugInfo, CustomDebugInfoKind recordKind) { foreach (var record in GetCustomDebugInfoRecords(customDebugInfo)) { if (record.Kind == recordKind) { return(record.Data); } } return(default(ImmutableArray <byte>)); }
public CustomDebugInfoRecord(CustomDebugInfoKind kind, byte version, ImmutableArray<byte> data) { this.Kind = kind; this.Version = version; this.Data = data; }
/// <summary> /// Contains a name string. /// TODO: comment when the structure is understood. /// </summary> /// <remarks> /// Appears when are iterator methods. /// </remarks> private void WriteForwardIteratorCustomDebugInfo(byte version, CustomDebugInfoKind kind, int size, byte[] bytes, ref int offset) { Debug.Assert(kind == CustomDebugInfoKind.ForwardIterator); writer.WriteStartElement("forwardIterator"); WriteCustomDebugInfoRecordHeaderAttributes(version, kind, size); string name; CDI.ReadForwardIteratorRecord(bytes, ref offset, size, out name); writer.WriteAttributeString("name", name); writer.WriteEndElement(); //forwardIterator }
/// <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(byte version, CustomDebugInfoKind kind, int size, byte[] bytes, ref int offset) { Debug.Assert(kind == CustomDebugInfoKind.DynamicLocals); writer.WriteStartElement("dynamicLocals"); WriteCustomDebugInfoRecordHeaderAttributes(version, kind, size); ImmutableArray<DynamicLocalBucket> buckets; CDI.ReadDynamicLocalsRecord(bytes, ref offset, size, out buckets); writer.WriteAttributeString("bucketCount", buckets.Length.ToString()); 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 }
/// <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(byte version, CustomDebugInfoKind kind, int size, byte[] bytes, ref int offset) { Debug.Assert(kind == CustomDebugInfoKind.ForwardToModuleInfo); writer.WriteStartElement("forwardToModule"); WriteCustomDebugInfoRecordHeaderAttributes(version, kind, size); int token; CDI.ReadForwardRecord(bytes, ref offset, size, out token); WriteMethodAttributes(token, isReference: true); writer.WriteEndElement(); //forwardToModule }
/// <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 WriteIteratorLocalsCustomDebugInfo(byte version, CustomDebugInfoKind kind, int size, byte[] bytes, ref int offset) { Debug.Assert(kind == CustomDebugInfoKind.IteratorLocals); writer.WriteStartElement("iteratorLocals"); WriteCustomDebugInfoRecordHeaderAttributes(version, kind, size); ImmutableArray<IteratorLocalBucket> buckets; CDI.ReadIteratorLocalsRecord(bytes, ref offset, size, out buckets); writer.WriteAttributeString("bucketCount", buckets.Length.ToString()); foreach (IteratorLocalBucket bucket in buckets) { writer.WriteStartElement("bucket"); writer.WriteAttributeString("startOffset", AsILOffset(bucket.StartOffset)); writer.WriteAttributeString("endOffset", AsILOffset(bucket.EndOffset)); writer.WriteEndElement(); //bucket } writer.WriteEndElement(); //iteratorLocals }
/// <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(byte version, CustomDebugInfoKind kind, int size, byte[] bytes, ref int offset) { Debug.Assert(kind == CustomDebugInfoKind.UsingInfo); writer.WriteStartElement("using"); WriteCustomDebugInfoRecordHeaderAttributes(version, kind, size); ImmutableArray<short> counts; CDI.ReadUsingRecord(bytes, ref offset, size, out counts); writer.WriteAttributeString("namespaceCount", counts.Length.ToString()); 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(byte version, CustomDebugInfoKind kind, int size, byte[] bytes, ref int offset) { writer.WriteStartElement("unknown"); WriteCustomDebugInfoRecordHeaderAttributes(version, kind, size); ImmutableArray<byte> body; CDI.ReadUnknownRecord(bytes, ref offset, size, out body); PooledStringBuilder pooled = PooledStringBuilder.GetInstance(); StringBuilder builder = pooled.Builder; foreach (byte b in body) { builder.AppendFormat("{0:X2}", b); } writer.WriteAttributeString("payload", pooled.ToStringAndFree()); writer.WriteEndElement(); //unknown }
/// <summary> /// Write version, kind, and size attributes. /// </summary> private void WriteCustomDebugInfoRecordHeaderAttributes(byte version, CustomDebugInfoKind kind, int size) { writer.WriteAttributeString("version", version.ToString()); writer.WriteAttributeString("kind", kind.ToString()); writer.WriteAttributeString("size", size.ToString()); }
private static PooledBlobBuilder SerializeRecord( CustomDebugInfoKind kind, EditAndContinueMethodDebugInformation debugInfo, Action<EditAndContinueMethodDebugInformation, BlobBuilder> recordSerializer) { var cmw = PooledBlobBuilder.GetInstance(); cmw.WriteByte(CustomDebugInfoConstants.Version); cmw.WriteByte((byte)kind); cmw.WriteByte(0); // alignment size and length (will be patched) var alignmentSizeAndLengthWriter = new BlobWriter(cmw.ReserveBytes(sizeof(byte) + sizeof(uint))); recordSerializer(debugInfo, cmw); int length = cmw.Count; int alignedLength = 4 * ((length + 3) / 4); byte alignmentSize = (byte)(alignedLength - length); cmw.WriteBytes(0, alignmentSize); // fill in alignment size and length: alignmentSizeAndLengthWriter.WriteByte(alignmentSize); alignmentSizeAndLengthWriter.WriteUInt32((uint)alignedLength); return cmw; }