Enable disasm mixed gc info (#403)
authorBruce Forstall <brucefo@microsoft.com>
Thu, 18 Jul 2019 19:04:49 +0000 (12:04 -0700)
committerGitHub <noreply@github.com>
Thu, 18 Jul 2019 19:04:49 +0000 (12:04 -0700)
* Enable gcinfo mixed in disassembly on non-Windows platforms

Rewrite the "u -gcinfo" code to not require Windows fibers,
and enable it for non-Windows platforms.

Also,
1. Enable "u -gcinfo" for ARM.
2. Fix minor issues with EHinfo/GCinfo to ensure end-of-function info is displayed

src/SOS/Strike/disasm.cpp
src/SOS/Strike/disasm.h
src/SOS/Strike/disasmARM.cpp
src/SOS/Strike/disasmARM64.cpp
src/SOS/Strike/disasmX86.cpp
src/SOS/Strike/strike.cpp

index 0d8356ea6a6bbf5085b1588d846ae6815675e951..817d3be03e4ee5eaffa908f7b1e54d44ee81c3f0 100644 (file)
@@ -1138,4 +1138,148 @@ LPCSTR ARM64Machine::s_SPName           = "sp";
 
 #endif // SOS_TARGET_ARM64
 
+//
+// GCEncodingInfo class member implementations
+//
+
+bool GCEncodingInfo::Initialize()
+{
+    buf = nullptr;
+    cchBufAllocation = 0;
+    cchBuf = 0;
+    curPtr = nullptr;
+    done = false;
+    hotSizeToAdd = 0;
+
+    return ReallocBuf();
+}
+
+void GCEncodingInfo::Deinitialize()
+{
+    delete[] buf;
+    buf = nullptr;
+    cchBufAllocation = 0;
+    cchBuf = 0;
+    curPtr = nullptr;
+    done = false;
+    hotSizeToAdd = 0;
+}
+
+bool GCEncodingInfo::ReallocBuf()
+{
+    size_t newSize;
+    if (!ClrSafeInt<size_t>::multiply(cchBufAllocation, 2, newSize))
+    {
+        ExtOut("<integer overflow>\n");
+        return false;
+    }
+
+    newSize = _max(1000, newSize);
+    char* newbuffer = new char[newSize];
+    if (newbuffer == NULL)
+    {
+        ExtOut("Could not allocate memory for the gc info dump.\n");
+        return false;
+    }
+
+    if (buf != nullptr)
+    {
+        memcpy(newbuffer, buf, cchBufAllocation);
+        delete[] buf;
+    }
+    buf = newbuffer;
+    cchBufAllocation = newSize;
+
+    // Make sure it is null terminated. It should already be, unless this is the first time.
+    buf[cchBuf] = '\0';
+
+    return true;
+}
 
+bool GCEncodingInfo::EnsureAdequateBufferSpace(SIZE_T count)
+{
+    while (cchBuf + count + 1 > cchBufAllocation) // +1 for null terminator
+    {
+        if (!ReallocBuf())
+            return false;
+    }
+
+    return true;
+}
+
+void GCEncodingInfo::DumpGCInfoThrough(SIZE_T curOffset)
+{
+    if (done)
+    {
+        // We've already output all the GC info
+        return;
+    }
+
+    if (curPtr == nullptr)
+    {
+        // We're just starting iteration.
+        curPtr = buf;
+    }
+
+    for (;;)
+    {
+        char *pNewLine = strchr(curPtr, '\n');
+        if (pNewLine != nullptr)
+            *pNewLine = '\0';
+        SIZE_T cchLine = strlen(curPtr);
+
+        // There are two kinds of lines: those that start with an offset, and
+        // those that don't. It should be the case that all lines without
+        // offset come first, but that's certainly not guaranteed.
+        //
+        // For the lines with offset, it will be a 16-bit (x86) or 32-bit (non-x86) hex
+        // offset.  strtoul returns ULONG_MAX or 0 on failure.  0 is a valid
+        // offset for the first encoding.
+
+        char *pEnd;
+        ULONG ofs = strtoul(curPtr, &pEnd, /* base */ 16);
+
+        if ((pEnd != curPtr) && isspace(*pEnd) && (ULONG_MAX != ofs))
+        {
+            // We got a number. Assume it's an offset.
+            if (ofs <= curOffset)
+            {
+                ExtOut(curPtr);
+                ExtOut("\n");
+                curPtr += cchLine + 1; // +1 to pass the terminating null
+            }
+            else
+            {
+                // We'll come back and output this one later. Restore the newline, if any.
+                // Leave curPtr alone.
+                if (pNewLine != nullptr)
+                    *pNewLine = '\n';
+                break;
+            }
+        }
+        else
+        {
+            // This is something else. Output it.
+            ExtOut(curPtr);
+            ExtOut("\n");
+            curPtr += cchLine + 1; // +1 to pass the terminating null
+        }
+
+        if (pNewLine == nullptr)
+        {
+            // There was no trailing newline, and we output some text; we must be done
+            // (if we didn't output any text, we wouldn't get here).
+            done = true;
+            break;
+        }
+        else if (*curPtr == '\0')
+        {
+            // We must have output a line with a newline, and now we're pointing at the
+            // trailing null.
+            done = true;
+            break;
+        }
+
+        // Otherwise, we have text left to output.
+    }
+}
index 2a2d9f7779fc9b9f78eeccc309fdda6104cfe5dc..2dfc8d6b223cde49223776fd18327c3c2ab360d5 100644 (file)
@@ -26,21 +26,41 @@ struct DumpStackFlag
 
 struct GCEncodingInfo
 {
-    LPVOID pvMainFiber;
-    LPVOID pvGCTableFiber;
+    GCEncodingInfo() : buf(nullptr), cchBufAllocation(0), cchBuf(0), curPtr(nullptr), done(false), hotSizeToAdd(0)
+    {
+        // We don't call Initialize() here because we want to call it somewhere
+        // we can handle memory allocation or other failures.
+    }
+
+    ~GCEncodingInfo()
+    {
+        Deinitialize();
+    }
+
+    bool Initialize();
+    void Deinitialize();
+    
+    // Reallocate the buffer. Double the size. Returns 'true' on success, 'false' on failure.
+    // This is also called to initialize the buffer the first time.
+    bool ReallocBuf();
+
+    // Ensure there are at least 'count' characters available in the buffer, by reallocating
+    // if necessary. Returns 'true' on success, 'false' on failure (e.g., failure to allocate memory).
+    bool EnsureAdequateBufferSpace(SIZE_T count);
 
-    BYTE *table;
-    unsigned int methodSize;
+    // Output all GC info from the current position up to and including 'curOffset'.
+    void DumpGCInfoThrough(SIZE_T curOffset);
 
-    char buf[1000];
-    int cch;
+    char* buf;                 // GC info textual output memory.
+    SIZE_T cchBufAllocation;   // Number of characters allocated to buf.
+    SIZE_T cchBuf;             // Number of characters stored in 'buf' (not including terminating null).
 
-    SIZE_T ofs;
+    char* curPtr;              // Current pointer in 'buf', when iterating through the GC info.
+    bool done;                 // Have we output all the GC info?
     
     // When decoding a cold region, set this to the size of the hot region to keep offset
     // calculations working.
     SIZE_T hotSizeToAdd;    
-    bool fDoneDecoding;
 };
 
 // Returns:
index e9a7a690a129dbbcebde2c1047123ce8629d1082..1cb5c0ed1ca058b6f2d5d10e394f4bd9b89f34bf 100644 (file)
@@ -24,7 +24,6 @@
 #include "../../../inc/cor.h"
 #include "../../../inc/dacprivate.h"
 
-#ifndef FEATURE_PAL
 namespace ARMGCDump
 {
 #undef _TARGET_X86_
@@ -39,7 +38,6 @@ namespace ARMGCDump
 #define DAC_ARG(x)
 #include "gcdumpnonx86.cpp"
 }
-#endif // !FEATURE_PAL
 
 #if defined(_TARGET_WIN64_)
 #error This file does not support SOS targeting ARM from a 64-bit debugger
@@ -385,22 +383,15 @@ void ARMMachine::Unassembly (
             }
         }
 
-#ifndef FEATURE_PAL
         //
         // Print out any GC information corresponding to the current instruction offset.
         //
         if (pGCEncodingInfo)
         {
             SIZE_T curOffset = (PC - PCBegin) + pGCEncodingInfo->hotSizeToAdd;
-            while (   !pGCEncodingInfo->fDoneDecoding
-                   && pGCEncodingInfo->ofs <= curOffset)
-            {
-                ExtOut(pGCEncodingInfo->buf);
-                ExtOut("\n");
-                SwitchToFiber(pGCEncodingInfo->pvGCTableFiber);
-            }
+            pGCEncodingInfo->DumpGCInfoThrough(curOffset);
         }
-#endif //!FEATURE_PAL
+
         //
         // Print out any EH info corresponding to the current offset
         //
@@ -532,6 +523,22 @@ void ARMMachine::Unassembly (
 
         ExtOut ("\n");
     }
+
+    //
+    // Print out any "end" GC info
+    //
+    if (pGCEncodingInfo)
+    {
+        pGCEncodingInfo->DumpGCInfoThrough(PC - PCBegin);
+    }
+
+    //
+    // Print out any "end" EH info (where the end address is the byte immediately following the last instruction)
+    //
+    if (pEHInfo)
+    {
+        pEHInfo->FormatForDisassembly(PC - PCBegin);
+    }
 }
 
 #if 0 // @ARMTODO: Figure out how to extract this information under CoreARM
@@ -611,7 +618,6 @@ BOOL ARMMachine::GetExceptionContext (TADDR stack, TADDR PC, TADDR *cxrAddr, CRO
 ///
 void ARMMachine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const
 {
-#ifndef FEATURE_PAL
     if (bPrintHeader)
     {
         ExtOut("Pointer table:\n");
@@ -621,7 +627,6 @@ void ARMMachine::DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printf
     gcDump.gcPrintf = gcPrintf;
 
     gcDump.DumpGCTable(dac_cast<PTR_BYTE>(gcInfoToken.Info), methodSize, 0);
-#endif // !FEATURE_PAL
 }
 
 #endif // SOS_TARGET_ARM
index 7f8accdcab6a83645258fa459db38bc02cd4a68c..499eab9be4a23334cf543307b10d44b644d893c6 100644 (file)
@@ -41,14 +41,6 @@ namespace ARM64GCDump
 #include "gcdumpnonx86.cpp"
 }
 
-#ifdef FEATURE_PAL
-void SwitchToFiber(void*)
-{
-    // TODO: Fix for linux
-    assert(false);
-}
-#endif
-
 #if !defined(_TARGET_WIN64_)
 #error This file only supports SOS targeting ARM64 from a 64-bit debugger
 #endif
@@ -224,13 +216,7 @@ void ARM64Machine::Unassembly (
         if (pGCEncodingInfo)
         {
             SIZE_T curOffset = (currentPC - PCBegin) + pGCEncodingInfo->hotSizeToAdd;
-            while (   !pGCEncodingInfo->fDoneDecoding
-                   && pGCEncodingInfo->ofs <= curOffset)
-            {
-                ExtOut(pGCEncodingInfo->buf);
-                ExtOut("\n");
-                SwitchToFiber(pGCEncodingInfo->pvGCTableFiber);
-            }
+            pGCEncodingInfo->DumpGCInfoThrough(curOffset);
         }
 
         //
@@ -363,6 +349,22 @@ void ARM64Machine::Unassembly (
                 
     }
     ExtOut ("\n");
+
+    //
+    // Print out any "end" GC info
+    //
+    if (pGCEncodingInfo)
+    {
+        pGCEncodingInfo->DumpGCInfoThrough(PC - PCBegin);
+    }
+
+    //
+    // Print out any "end" EH info (where the end address is the byte immediately following the last instruction)
+    //
+    if (pEHInfo)
+    {
+        pEHInfo->FormatForDisassembly(PC - PCBegin);
+    }
 }
 
 
index 0934a824ea79da62dfe4cf394186233f040bd826..afc01a722eb793ca562f834ac20801c89699ba5c 100644 (file)
@@ -545,21 +545,11 @@ void
         // Print out any GC information corresponding to the current instruction offset.
         //
 
-#ifndef FEATURE_PAL
         if (pGCEncodingInfo)
         {
             SIZE_T curOffset = (IP - IPBegin) + pGCEncodingInfo->hotSizeToAdd;
-            while (   !pGCEncodingInfo->fDoneDecoding
-                   && pGCEncodingInfo->ofs <= curOffset)
-            {
-                ExtOut(pGCEncodingInfo->buf);
-                ExtOut("\n");
-                SwitchToFiber(pGCEncodingInfo->pvGCTableFiber);
-            }
+            pGCEncodingInfo->DumpGCInfoThrough(curOffset);
         }
-#endif // FEATURE_PAL        
-
-        ULONG_PTR InstrAddr = IP;
 
         //
         // Print out any EH info corresponding to the current offset
@@ -582,6 +572,8 @@ void
             ExtOut("%04x ", IP - IPBegin);
         }
 
+        ULONG_PTR InstrAddr = IP;
+
         DisasmAndClean (IP, line, _countof(line));
 
         // look at key word
@@ -741,6 +733,14 @@ void
         ExtOut ("\n");
     }
 
+    //
+    // Print out any "end" GC info
+    //
+    if (pGCEncodingInfo)
+    {
+        pGCEncodingInfo->DumpGCInfoThrough(IP - IPBegin);
+    }
+
     //
     // Print out any "end" EH info (where the end address is the byte immediately following the last instruction)
     //
index a9e31bb5f5d0976f29caf485482a8f7fbfbcb819..88dae937c7a5599db0a470097b197841bd6bd08c 100644 (file)
@@ -8991,89 +8991,64 @@ DECLARE_API(GCInfo)
     return Status;
 }
 
-#if !defined(FEATURE_PAL)
+GCEncodingInfo g_gcEncodingInfo; // The constructor should run to create the initial buffer allocation.
 
 void DecodeGCTableEntry (const char *fmt, ...)
 {
-    GCEncodingInfo *pInfo = (GCEncodingInfo*)GetFiberData();
     va_list va;
 
     //
-    // Append the new data to the buffer
+    // Append the new data to the buffer. If it doesn't fit, allocate a new buffer that is bigger and try again.
     //
 
     va_start(va, fmt);
 
-    int cch = _vsnprintf_s(&pInfo->buf[pInfo->cch], _countof(pInfo->buf) - pInfo->cch, _countof(pInfo->buf) - pInfo->cch - 1, fmt, va);
-    if (cch >= 0)
-        pInfo->cch += cch;
-
-    va_end(va);
-
-    pInfo->buf[pInfo->cch] = '\0';
-
-    //
-    // If there are complete lines in the buffer, decode them.
-    //
-
-    for (;;)
+    // Make sure there's at least a minimum amount of free space in the buffer. We need to minimally
+    // ensure that 'maxCchToWrite' is >0. 20 is an arbitrary smallish number.
+    if (!g_gcEncodingInfo.EnsureAdequateBufferSpace(20))
     {
-        char *pNewLine = strchr(pInfo->buf, '\n');
-
-        if (!pNewLine)
-            break;
+        ExtOut("Could not allocate memory for GC info\n");
+        return;
+    }
 
-        //
-        // The line should start with a 16-bit (x86) or 32-bit (non-x86) hex
-        // offset.  strtoul returns ULONG_MAX or 0 on failure.  0 is a valid
-        // offset for the first encoding, or while the last offset was 0.
-        //
+    while (true)
+    {
+        char* buffer = &g_gcEncodingInfo.buf[g_gcEncodingInfo.cchBuf];
+        size_t sizeOfBuffer = g_gcEncodingInfo.cchBufAllocation - g_gcEncodingInfo.cchBuf;
+        size_t maxCchToWrite = sizeOfBuffer - 1; // -1 to leave space for the null terminator
+        int cch = _vsnprintf_s(buffer, sizeOfBuffer, maxCchToWrite, fmt, va);
 
-        if (isxdigit(pInfo->buf[0]))
+        // cch == -1 should be the only negative result, but checking < 0 is defensive in case some runtime returns something else.
+        // We should also check "errno == ERANGE", but it seems that some runtimes don't set that properly.
+        if (cch < 0)
         {
-            char *pEnd;
-            ULONG ofs = strtoul(pInfo->buf, &pEnd, 16);
-
-            if (   isspace(*pEnd)
-                && -1 != ofs 
-                && (   -1 == pInfo->ofs
-                    || 0 == pInfo->ofs
-                    || ofs > 0))
+            if (sizeOfBuffer > 1000)
             {
-                pInfo->ofs = ofs;
-                *pNewLine = '\0';
-
-                SwitchToFiber(pInfo->pvMainFiber);
+                // There must be some unexpected problem if we can't write the GC info into such a large buffer, so bail.
+                ExtOut("Error generating GC info\n");
+                break;
             }
+            else if (!g_gcEncodingInfo.ReallocBuf())
+            {
+                // We couldn't reallocate the buffer; skip the rest of the text.
+                ExtOut("Could not allocate memory for GC info\n");
+                break;
+            }
+
+            // If we get here, we successfully reallocated the buffer larger, so we'll try again to write this entry
+            // into the larger buffer.
         }
-        else if (0 == strncmp(pInfo->buf, "Untracked:", 10))
+        else
         {
-            pInfo->ofs = 0;
-            *pNewLine = '\0';
-
-            SwitchToFiber(pInfo->pvMainFiber);
+            // We successfully added this entry to the GC info we're accumulating.
+            // cch is the number of characters written, not including the terminating null.
+            g_gcEncodingInfo.cchBuf += cch;
+            break;
         }
-
-        //
-        // Shift the remaining data to the start of the buffer
-        //
-
-        strcpy_s(pInfo->buf, _countof(pInfo->buf), pNewLine+1);
-        pInfo->cch = (int)strlen(pInfo->buf);
     }
-}
 
-
-VOID CALLBACK DumpGCTableFiberEntry (LPVOID pvGCEncodingInfo)
-{
-    GCEncodingInfo *pInfo = (GCEncodingInfo*)pvGCEncodingInfo;
-    GCInfoToken gcInfoToken = { pInfo->table, GCINFO_VERSION };
-    g_targetMachine->DumpGCInfo(gcInfoToken, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
-
-    pInfo->fDoneDecoding = true;
-    SwitchToFiber(pInfo->pvMainFiber);
+    va_end(va);
 }
-#endif // !FEATURE_PAL
 
 BOOL gatherEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
 {
@@ -9124,9 +9099,7 @@ DECLARE_API(u)
 
     CMDOption option[] = 
     {   // name, vptr, type, hasValue
-#ifndef FEATURE_PAL
         {"-gcinfo", &fWithGCInfo, COBOOL, FALSE},
-#endif
         {"-ehinfo", &fWithEHInfo, COBOOL, FALSE},
         {"-n", &bSuppressLines, COBOOL, FALSE},
         {"-o", &bDisplayOffsets, COBOOL, FALSE},
@@ -9241,17 +9214,74 @@ DECLARE_API(u)
         ExtOut("Begin %p, size %x\n", SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.MethodSize);
     }
 
-#if !defined(FEATURE_PAL)
     //
-    // Set up to mix gc info with the code if requested
+    // Set up to mix gc info with the code if requested. To do this, we first generate all the textual
+    // gc info up front. This text is the same as the "!gcinfo" command, and looks like:
     //
-
-    GCEncodingInfo gcEncodingInfo = {0};
+    // Prolog size: 0
+    // Security object: <none>
+    // GS cookie: <none>
+    // PSPSym: <none>
+    // Generics inst context: <none>
+    // PSP slot: <none>
+    // GenericInst slot: <none>
+    // Varargs: 0
+    // Frame pointer: rbp
+    // Wants Report Only Leaf: 0
+    // Size of parameter area: 20
+    // Return Kind: Scalar
+    // Code size: 1ec
+    // Untracked: +rbp-10 +rbp-30 +rbp-48 +rbp-50 +rbp-58 +rbp-60 +rbp-68 +rbp-70
+    // 0000001e interruptible
+    // 0000003c +rax
+    // 0000004d +rdx
+    // 00000051 +rcx
+    // 00000056 -rdx -rcx -rax
+    // 0000005a +rcx
+    // 00000067 -rcx
+    // 00000080 +rcx
+    // 00000085 -rcx
+    // 0000009e +rcx
+    // 000000a3 -rcx
+    // 000000bc +rcx
+    // 000000c1 -rcx
+    // 000000d7 +rcx
+    // 000000e5 -rcx
+    // 000000ef +rax
+    // 0000010a +r8
+    // 00000119 +rcx
+    // 00000120 -r8 -rcx -rax
+    // 0000012f +rax
+    // 00000137 +r8
+    // 00000146 +rcx
+    // 00000150 -r8 -rcx -rax
+    // 0000015f +rax
+    // 00000167 +r8
+    // 00000176 +rcx
+    // 00000180 -r8 -rcx -rax
+    // 0000018f +rax
+    // 00000197 +r8
+    // 000001a6 +rcx
+    // 000001b0 -r8 -rcx -rax
+    // 000001b4 +rcx
+    // 000001b8 +rdx
+    // 000001bd -rdx -rcx
+    // 000001c8 +rcx
+    // 000001cd -rcx
+    // 000001d2 +rcx
+    // 000001d7 -rcx
+    // 000001e5 not interruptible
+    //
+    // For the entries without offset prefixes, we output them before the first offset of code.
+    // (Previously, we only displayed the "Untracked:" element, but displaying all this additional
+    // GC info is useful, and then the user doesn't need to also do a "!gcinfo" to see it.)
+    // For the entries with offset prefixes, we parse the offset, and display all relevant information
+    // before the current instruction offset being disassembled, that is, all the lines of GC info
+    // with an offset greater than the previous instruction and with an offset less than or equal
+    // to the offset of the current instruction.
 
     // The actual GC Encoding Table, this is updated during the course of the function.
-    gcEncodingInfo.table = NULL;
-
-    // The holder to make sure we clean up the memory for the table
+    // Use a holder to make sure we clean up the memory for the table.
     ArrayHolder<BYTE> table = NULL;
 
     if (fWithGCInfo)
@@ -9265,20 +9295,18 @@ DECLARE_API(u)
             return E_FAIL;
         }
 
-
         // Assign the new array to the mutable gcEncodingInfo table and to the
         // table ArrayHolder to clean this up when the function exits.
-        table = gcEncodingInfo.table = new NOTHROW BYTE[tableSize];
-        
-        if (gcEncodingInfo.table == NULL)
+        table = new NOTHROW BYTE[tableSize];
+        if (table == NULL)
         {
             ExtOut("Could not allocate memory to read the gc info.\n");
             return E_OUTOFMEMORY;
         }
         
-        memset (gcEncodingInfo.table, 0, tableSize);
+        memset (table, 0, tableSize);
         // We avoid using move here, because we do not want to return
-        if (!SafeReadMemory(TO_TADDR(codeHeaderData.GCInfo), gcEncodingInfo.table, tableSize, NULL))
+        if (!SafeReadMemory(TO_TADDR(codeHeaderData.GCInfo), table, tableSize, NULL))
         {
             ExtOut("Could not read memory %p\n", SOS_PTR(codeHeaderData.GCInfo));
             return Status;
@@ -9287,34 +9315,16 @@ DECLARE_API(u)
         //
         // Skip the info header
         //
-        gcEncodingInfo.methodSize = (unsigned int)codeHeaderData.MethodSize;
+        unsigned int methodSize = (unsigned int)codeHeaderData.MethodSize;
 
-        //
-        // DumpGCTable will call gcPrintf for each encoding.  We'd like a "give
-        // me the next encoding" interface, but we're stuck with the callback.
-        // To reconcile this without messing up too much code, we'll create a
-        // fiber to dump the gc table.  When we need the next gc encoding,
-        // we'll switch to this fiber.  The callback will note the next offset,
-        // and switch back to the main fiber.
-        //
-
-        gcEncodingInfo.ofs = -1;
-        gcEncodingInfo.hotSizeToAdd = 0;
-        
-        gcEncodingInfo.pvMainFiber = ConvertThreadToFiber(NULL);
-        if (!gcEncodingInfo.pvMainFiber && ERROR_ALREADY_FIBER == GetLastError())
-            gcEncodingInfo.pvMainFiber = GetCurrentFiber();
+        if (!g_gcEncodingInfo.Initialize())
+        {
+            return E_OUTOFMEMORY;
+        }
         
-        if (!gcEncodingInfo.pvMainFiber)
-            return Status;
-
-        gcEncodingInfo.pvGCTableFiber = CreateFiber(0, DumpGCTableFiberEntry, &gcEncodingInfo);
-        if (!gcEncodingInfo.pvGCTableFiber)
-            return Status;
-
-        SwitchToFiber(gcEncodingInfo.pvGCTableFiber);
+        GCInfoToken gcInfoToken = { table, GCINFO_VERSION };
+        g_targetMachine->DumpGCInfo(gcInfoToken, methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
     }    
-#endif
 
     SOSEHInfo *pInfo = NULL;
     if (fWithEHInfo)
@@ -9339,10 +9349,7 @@ DECLARE_API(u)
                 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.MethodSize,
                 dwStartAddr,
                 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
-#if !defined(FEATURE_PAL)
-                fWithGCInfo ? &gcEncodingInfo : 
-#endif            
-                NULL,
+                fWithGCInfo ? &g_gcEncodingInfo : NULL,
                 pInfo,
                 bSuppressLines,
                 bDisplayOffsets
@@ -9356,10 +9363,7 @@ DECLARE_API(u)
                 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.HotRegionSize,
                 dwStartAddr,
                 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
-#if !defined(FEATURE_PAL)
-                fWithGCInfo ? &gcEncodingInfo : 
-#endif            
-                NULL,
+                fWithGCInfo ? &g_gcEncodingInfo : NULL,
                 pInfo,
                 bSuppressLines,
                 bDisplayOffsets
@@ -9367,20 +9371,16 @@ DECLARE_API(u)
 
         ExtOut("Cold region:\n");
         
-#if !defined(FEATURE_PAL)
         // Displaying gcinfo for a cold region requires knowing the size of
         // the hot region preceeding.
-        gcEncodingInfo.hotSizeToAdd = codeHeaderData.HotRegionSize;
-#endif            
+        g_gcEncodingInfo.hotSizeToAdd = codeHeaderData.HotRegionSize;
+
         g_targetMachine->Unassembly (
                 (DWORD_PTR) codeHeaderData.ColdRegionStart,
                 ((DWORD_PTR)codeHeaderData.ColdRegionStart) + codeHeaderData.ColdRegionSize,
                 dwStartAddr,
                 ((DWORD_PTR) MethodDescData.GCStressCodeCopy) + codeHeaderData.HotRegionSize,                
-#if !defined(FEATURE_PAL)
-                fWithGCInfo ? &gcEncodingInfo : 
-#endif            
-                NULL,
+                fWithGCInfo ? &g_gcEncodingInfo : NULL,
                 pInfo,
                 bSuppressLines,
                 bDisplayOffsets
@@ -9393,12 +9393,12 @@ DECLARE_API(u)
         delete pInfo;
         pInfo = NULL;
     }
-    
-#if !defined(FEATURE_PAL)
-    if (fWithGCInfo)
-        DeleteFiber(gcEncodingInfo.pvGCTableFiber);
-#endif
 
+    if (fWithGCInfo)
+    {
+        g_gcEncodingInfo.Deinitialize();
+    }
+    
     return Status;
 }