R2RDump: Add support for partially interruptible regions (dotnet/coreclr#25626)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Wed, 17 Jul 2019 12:51:33 +0000 (14:51 +0200)
committerGitHub <noreply@github.com>
Wed, 17 Jul 2019 12:51:33 +0000 (14:51 +0200)
We were basically missing this block:

https://github.com/dotnet/coreclr/blob/dotnet/coreclr@030a3ea9b8dbeae89c90d34441d4d9a1cf4a7de6/src/vm/gcinfodecoder.cpp#L726-L810

Commit migrated from https://github.com/dotnet/coreclr/commit/4294348f888f4cb503a7dd43a7463451654f0bd9

src/coreclr/src/tools/r2rdump/Amd64/GcInfo.cs
src/coreclr/src/tools/r2rdump/Amd64/GcSlotTable.cs
src/coreclr/src/tools/r2rdump/GCInfoTypes.cs
src/coreclr/src/tools/r2rdump/R2RMethod.cs
src/coreclr/src/tools/r2rdump/TextDumper.cs

index 3535ef9b74e2b1aa98dd9ab8dd1fe81cc722c880..15ee18b7d5853874df3064b153c08e19e768ca03 100644 (file)
@@ -4,6 +4,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Reflection.PortableExecutable;
 using System.Text;
 using System.Xml.Serialization;
@@ -186,7 +187,15 @@ namespace R2RDump.Amd64
 
             SlotTable = new GcSlotTable(image, machine, _gcInfoTypes, ref bitOffset);
 
-            Transitions = GetTranstions(image, ref bitOffset);
+            // Try partially interruptible first
+            if (NumSafePoints > 0)
+            {
+                LiveSlotsAtSafepoints = GetLiveSlotsAtSafepoints(image, ref bitOffset);
+            }
+            else
+            {
+                Transitions = GetTransitions(image, ref bitOffset);
+            }
 
             Size = bitOffset - startBitOffset;
         }
@@ -260,6 +269,7 @@ namespace R2RDump.Amd64
             foreach (SafePointOffset offset in SafePointOffsets)
             {
                 sb.AppendLine($"\t\t{offset.Value}");
+                sb.AppendLine($"\t\tLive slots: {String.Join(", ", LiveSlotsAtSafepoints[offset.Index])}");
             }
             sb.AppendLine($"\tInterruptibleRanges:");
             foreach (InterruptibleRange range in InterruptibleRanges)
@@ -346,10 +356,116 @@ namespace R2RDump.Amd64
             return (readyToRunMajorVersion == 1) ? 1 : GCINFO_VERSION;
         }
 
+        private List<List<BaseGcSlot>> GetLiveSlotsAtSafepoints(byte[] image, ref int bitOffset)
+        {
+            // For each safe point, enumerates a list of GC slots that are alive at that point
+            var result = new List<List<BaseGcSlot>>();
+
+            uint numSlots = SlotTable.NumTracked;
+            if (numSlots == 0)
+                return null;
+
+            uint numBitsPerOffset = 0;
+            // Duplicate the encoder's heuristic to determine if we have indirect live
+            // slot table (similar to the chunk pointers)
+            if (NativeReader.ReadBits(image, 1, ref bitOffset) != 0)
+            {
+                numBitsPerOffset = NativeReader.DecodeVarLengthUnsigned(image, _gcInfoTypes.POINTER_SIZE_ENCBASE, ref bitOffset) + 1;
+                Debug.Assert(numBitsPerOffset != 0);
+            }
+
+            uint offsetTablePos = (uint)bitOffset;
+
+            for (uint safePointIndex = 0; safePointIndex < NumSafePoints; safePointIndex++)
+            {
+                bitOffset = (int)offsetTablePos;
+
+                var liveSlots = new List<BaseGcSlot>();
+
+                if (numBitsPerOffset != 0)
+                {
+                    bitOffset += (int)(numBitsPerOffset * safePointIndex);
+
+                    uint liveStatesOffset = (uint)NativeReader.ReadBits(image, (int)numBitsPerOffset, ref bitOffset);
+                    uint liveStatesStart = (uint)((offsetTablePos + NumSafePoints * numBitsPerOffset + 7) & (~7));
+                    bitOffset = (int)(liveStatesStart + liveStatesOffset);
+                    if (NativeReader.ReadBits(image, 1, ref bitOffset) != 0)
+                    {
+                        // RLE encoded
+                        bool skip = NativeReader.ReadBits(image, 1, ref bitOffset) == 0;
+                        bool report = true;
+                        uint readSlots = NativeReader.DecodeVarLengthUnsigned(image,
+                            skip ? _gcInfoTypes.LIVESTATE_RLE_SKIP_ENCBASE : _gcInfoTypes.LIVESTATE_RLE_RUN_ENCBASE, ref bitOffset);
+                        skip = !skip;
+                        while (readSlots < numSlots)
+                        {
+                            uint cnt = NativeReader.DecodeVarLengthUnsigned(image,
+                                skip ? _gcInfoTypes.LIVESTATE_RLE_SKIP_ENCBASE : _gcInfoTypes.LIVESTATE_RLE_RUN_ENCBASE, ref bitOffset) + 1;
+                            if (report)
+                            {
+                                for (uint slotIndex = readSlots; slotIndex < readSlots + cnt; slotIndex++)
+                                {
+                                    int trackedSlotIndex = 0;
+                                    foreach (var slot in SlotTable.GcSlots)
+                                    {
+                                        if (slot.Flags != GcSlotFlags.GC_SLOT_UNTRACKED)
+                                        {
+                                            if (slotIndex == trackedSlotIndex)
+                                            {
+                                                liveSlots.Add(slot);
+                                                break;
+                                            }
+                                            trackedSlotIndex++;
+                                        }
+                                    }
+                                }
+                            }
+                            readSlots += cnt;
+                            skip = !skip;
+                            report = !report;
+                        }
+                        Debug.Assert(readSlots == numSlots);
+                        result.Add(liveSlots);
+                        continue;
+                    }
+                    // Just a normal live state (1 bit per slot), so use the normal decoding loop
+                }
+                else
+                {
+                    bitOffset += (int)(safePointIndex * numSlots);
+                }
+
+                for (uint slotIndex = 0; slotIndex < numSlots; slotIndex++)
+                {
+                    bool isLive = NativeReader.ReadBits(image, 1, ref bitOffset) != 0;
+                    if (isLive)
+                    {
+                        int trackedSlotIndex = 0;
+                        foreach (var slot in SlotTable.GcSlots)
+                        {
+                            if (slot.Flags != GcSlotFlags.GC_SLOT_UNTRACKED)
+                            {
+                                if (slotIndex == trackedSlotIndex)
+                                {
+                                    liveSlots.Add(slot);
+                                    break;
+                                }
+                                trackedSlotIndex++;
+                            }
+                        }
+                    }
+                }
+
+                result.Add(liveSlots);
+            }
+
+            return result;
+        }
+
         /// <summary>
         /// based on end of <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/gcinfodecoder.cpp">GcInfoDecoder::EnumerateLiveSlots and GcInfoEncoder::Build</a>
         /// </summary>
-        public Dictionary<int, List<BaseGcTransition>> GetTranstions(byte[] image, ref int bitOffset)
+        public Dictionary<int, List<BaseGcTransition>> GetTransitions(byte[] image, ref int bitOffset)
         {
             int totalInterruptibleLength = 0;
             if (NumInterruptibleRanges == 0)
@@ -382,7 +498,7 @@ namespace R2RDump.Amd64
             int info2Offset = (int)Math.Ceiling(bitOffset / 8.0) * 8;
 
             List<GcTransition> transitions = new List<GcTransition>();
-            bool[] liveAtEnd = new bool[SlotTable.GcSlots.Count - SlotTable.NumUntracked]; // true if slot is live at the end of the chunk
+            bool[] liveAtEnd = new bool[SlotTable.NumTracked]; // true if slot is live at the end of the chunk
             for (int currentChunk = 0; currentChunk < numChunks; currentChunk++)
             {
                 if (chunkPointers[currentChunk] == 0)
@@ -439,7 +555,7 @@ namespace R2RDump.Amd64
         private uint GetNumCouldBeLiveSlots(byte[] image, ref int bitOffset)
         {
             uint numCouldBeLiveSlots = 0;
-            int numTracked = SlotTable.GcSlots.Count - (int)SlotTable.NumUntracked;
+            uint numTracked = SlotTable.NumTracked;
             if (NativeReader.ReadBits(image, 1, ref bitOffset) != 0)
             {
                 // RLE encoded
index 1a30eaec3c62beb27d5d787589470be27d8e8138..be77aa53a805b6fb2349bc1432875cfb4189ab47 100644 (file)
@@ -4,6 +4,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Reflection.PortableExecutable;
 using System.Text;
 using System.Xml.Serialization;
@@ -12,7 +13,7 @@ namespace R2RDump.Amd64
 {
     public class GcSlotTable
     {
-        public class GcSlot
+        public class GcSlot : BaseGcSlot
         {
             [XmlAttribute("Index")]
             public int Index { get; set; }
@@ -60,6 +61,16 @@ namespace R2RDump.Amd64
         public uint NumStackSlots { get; set; }
         public uint NumUntracked { get; set; }
         public uint NumSlots { get; set; }
+
+        public uint NumTracked
+        {
+            get
+            {
+                Debug.Assert(NumSlots == GcSlots.Count);
+                return NumSlots - NumUntracked;
+            }
+        }
+
         public List<GcSlot> GcSlots { get; set; }
 
         public GcSlotTable() { }
@@ -85,11 +96,11 @@ namespace R2RDump.Amd64
             {
                 DecodeRegisters(image, gcInfoTypes, ref bitOffset);
             }
-            if ((NumStackSlots > 0) && (GcSlots.Count < gcInfoTypes.MAX_PREDECODED_SLOTS))
+            if (NumStackSlots > 0)
             {
                 DecodeStackSlots(image, machine, gcInfoTypes, NumStackSlots, false, ref bitOffset);
             }
-            if ((NumUntracked > 0) && (GcSlots.Count < gcInfoTypes.MAX_PREDECODED_SLOTS))
+            if (NumUntracked > 0)
             {
                 DecodeStackSlots(image, machine, gcInfoTypes, NumUntracked, true, ref bitOffset);
             }
index f3bf54fe1e0cde8d2bbb5e4f2be4402540fdfce0..2dcdce65baa8d13e74bd8ce9b6506eb19b86895f 100644 (file)
@@ -93,7 +93,6 @@ namespace R2RDump
         internal int INTERRUPTIBLE_RANGE_DELTA1_ENCBASE { get; } = 6;
         internal int INTERRUPTIBLE_RANGE_DELTA2_ENCBASE { get; } = 6;
 
-        internal int MAX_PREDECODED_SLOTS { get; } = 64;
         internal int NUM_REGISTERS_ENCBASE { get; } = 2;
         internal int NUM_STACK_SLOTS_ENCBASE { get; } = 2;
         internal int NUM_UNTRACKED_SLOTS_ENCBASE { get; } = 1;
index 36f3dd0cb2514e3033c0acb22e2998c4c1f64b25..e8b4a4d6afc3103c89246917624fef57fc5c73ca 100644 (file)
@@ -32,6 +32,10 @@ namespace R2RDump
         }
     }
 
+    public abstract class BaseGcSlot
+    {
+    }
+
     public abstract class BaseGcInfo
     {
         public int Size { get; set; }
@@ -39,6 +43,7 @@ namespace R2RDump
         public int CodeLength { get; set; }
         [XmlIgnore]
         public Dictionary<int, List<BaseGcTransition>> Transitions { get; set; }
+        public List<List<BaseGcSlot>> LiveSlotsAtSafepoints { get; set; }
     }
 
     /// <summary>
@@ -221,6 +226,8 @@ namespace R2RDump
                 foreach (Amd64.GcInfo.SafePointOffset safePoint in gcInfo.SafePointOffsets)
                 {
                     writer.WriteLine($@"        Index: {safePoint.Index,2}; Value: 0x{safePoint.Value:X4}");
+                    if (gcInfo.LiveSlotsAtSafepoints != null)
+                        writer.WriteLine($@"        Live slots: {String.Join(", ", gcInfo.LiveSlotsAtSafepoints[safePoint.Index])}");
                 }
 
                 writer.WriteLine($@"    InterruptibleRanges: {gcInfo.InterruptibleRanges.Count}");
index b1528df37a32b3e41755f1355cf6b9605c959736..2daa3ad42d12ac7608d79e4bd8fed4995465134f 100644 (file)
@@ -203,7 +203,7 @@ namespace R2RDump
                     }
                 }
 
-                if (rtf.Method.GcInfo != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset))
+                if (rtf.Method.GcInfo?.Transitions != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset))
                 {
                     foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset])
                     {