Implement /disassemble nidump option for readytorun images
authorJan Kotas <jkotas@microsoft.com>
Mon, 10 Aug 2015 17:02:28 +0000 (10:02 -0700)
committerJan Kotas <jkotas@microsoft.com>
Mon, 10 Aug 2015 17:02:28 +0000 (10:02 -0700)
[tfs-changeset: 1512604]

src/debug/daccess/nidump.cpp
src/debug/daccess/nidump.h

index c370bd5..44569d9 100644 (file)
@@ -3440,6 +3440,11 @@ size_t NativeImageDumper::TranslateSymbol(IXCLRDisassemblySupport *dis,
                                           CLRDATA_ADDRESS addr, __out_ecount(nameSize) WCHAR *name,
                                           SIZE_T nameSize, DWORDLONG *offset)
 {
+#ifdef FEATURE_READYTORUN
+    if (m_pReadyToRunHeader != NULL)
+        return 0;
+#endif
+
     if (isInRange((TADDR)addr))
     {
         COUNT_T rva = (COUNT_T)(addr - PTR_TO_TADDR(m_decoder.GetBase()));
@@ -3768,7 +3773,6 @@ void NativeImageDumper::DumpModule( PTR_Module module )
     _ASSERTE(file == NULL); 
     DisplayWriteFieldPointer( m_file, DPtrToPreferredAddr(file), Module,
                               MODULE );
-    //DumpPEFile( file, "PEFile" );
 
     PTR_MethodDesc dllMain( TO_TADDR(module->m_pDllMain) );
     WriteFieldMethodDesc( m_pDllMain, dllMain, Module,
@@ -9279,11 +9283,45 @@ StandardEntryDisplay:
 }
 
 #ifdef FEATURE_READYTORUN
+IMAGE_DATA_DIRECTORY * NativeImageDumper::FindReadyToRunSection(DWORD type)
+{
+    PTR_READYTORUN_SECTION pSections = dac_cast<PTR_READYTORUN_SECTION>(dac_cast<TADDR>(m_pReadyToRunHeader) + sizeof(READYTORUN_HEADER));
+    for (DWORD i = 0; i < m_pReadyToRunHeader->NumberOfSections; i++)
+    {
+        // Verify that section types are sorted
+        _ASSERTE(i == 0 || (pSections[i - 1].Type < pSections[i].Type));
+
+        READYTORUN_SECTION * pSection = pSections + i;
+        if (pSection->Type == type)
+            return &pSection->Section;
+    }
+    return NULL;
+}
+
 //
 // Ready to Run specific dumping methods
 //
 void NativeImageDumper::DumpReadyToRun()
 {
+    m_pReadyToRunHeader = m_decoder.GetReadyToRunHeader();
+
+    m_nativeReader = NativeFormat::NativeReader(dac_cast<PTR_BYTE>(m_decoder.GetBase()), m_decoder.GetVirtualSize());
+
+    IMAGE_DATA_DIRECTORY * pRuntimeFunctionsDir = FindReadyToRunSection(READYTORUN_SECTION_RUNTIME_FUNCTIONS);
+    if (pRuntimeFunctionsDir != NULL)
+    {
+        m_pRuntimeFunctions = dac_cast<PTR_RUNTIME_FUNCTION>(m_decoder.GetDirectoryData(pRuntimeFunctionsDir));
+        m_nRuntimeFunctions = pRuntimeFunctionsDir->Size / sizeof(RUNTIME_FUNCTION);
+    }
+    else
+    {
+        m_nRuntimeFunctions = 0;
+    }
+
+    IMAGE_DATA_DIRECTORY * pEntryPointsDir = FindReadyToRunSection(READYTORUN_SECTION_METHODDEF_ENTRYPOINTS);
+    if (pEntryPointsDir != NULL)
+        m_methodDefEntryPoints = NativeFormat::NativeArray(&m_nativeReader, pEntryPointsDir->VirtualAddress);
+
     DisplayStartCategory("NativeInfo", NATIVE_INFO);
 
     IF_OPT(NATIVE_INFO)
@@ -9291,6 +9329,9 @@ void NativeImageDumper::DumpReadyToRun()
 
     DisplayEndCategory(NATIVE_INFO); //NativeInfo
 
+    IF_OPT_OR3(METHODS, GC_INFO, DISASSEMBLE_CODE)
+        DumpReadyToRunMethods();
+
     IF_OPT(RELOCATIONS)
         DumpBaseRelocs();
 }
@@ -9304,25 +9345,163 @@ const NativeImageDumper::EnumMnemonics s_ReadyToRunFlags[] =
 
 void NativeImageDumper::DumpReadyToRunHeader()
 {
-    PTR_READYTORUN_HEADER nativeHeader(m_decoder.GetReadyToRunHeader());
-
     IF_OPT(NATIVE_INFO)
     {
         m_display->StartStructure( "READYTORUN_HEADER",
-                                   DPtrToPreferredAddr(nativeHeader),
-                                   sizeof(*nativeHeader) );
+                                   DPtrToPreferredAddr(dac_cast<PTR_READYTORUN_HEADER>(m_pReadyToRunHeader)),
+                                   sizeof(*m_pReadyToRunHeader) );
 
-        DisplayWriteFieldUInt( Signature, nativeHeader->Signature, READYTORUN_HEADER, ALWAYS );
-        DisplayWriteFieldUInt( MajorVersion, nativeHeader->MajorVersion, READYTORUN_HEADER, ALWAYS );
-        DisplayWriteFieldUInt( MinorVersion, nativeHeader->MinorVersion, READYTORUN_HEADER, ALWAYS );
+        DisplayWriteFieldUInt( Signature, m_pReadyToRunHeader->Signature, READYTORUN_HEADER, ALWAYS );
+        DisplayWriteFieldUInt( MajorVersion, m_pReadyToRunHeader->MajorVersion, READYTORUN_HEADER, ALWAYS );
+        DisplayWriteFieldUInt( MinorVersion, m_pReadyToRunHeader->MinorVersion, READYTORUN_HEADER, ALWAYS );
 
-        DisplayWriteFieldEnumerated( Flags, nativeHeader->Flags,
+        DisplayWriteFieldEnumerated( Flags, m_pReadyToRunHeader->Flags,
                                      READYTORUN_HEADER, s_ReadyToRunFlags, W(", "),
                                      NATIVE_INFO );
 
         m_display->EndStructure(); //READYTORUN_HEADER
     }
 }
+
+void NativeImageDumper::DumpReadyToRunMethods()
+{
+    DisplayStartArray("Methods", NULL, METHODS);
+
+    for (uint rid = 1; rid <= m_methodDefEntryPoints.GetCount(); rid++)
+    {
+        uint offset;
+        if (!m_methodDefEntryPoints.TryGetAt(rid - 1, &offset))
+            continue;
+
+        uint id;
+        offset = m_nativeReader.DecodeUnsigned(offset, &id);
+
+        if (id & 1)
+        {
+            if (id & 2)
+            {
+                uint val;
+                m_nativeReader.DecodeUnsigned(offset, &val);
+                offset -= val;
+            }
+
+            // TODO: Dump fixups from dac_cast<TADDR>(m_pLayout->GetBase()) + offset
+
+            id >>= 2;
+        }
+        else
+        {
+            id >>= 1;
+        }
+
+        _ASSERTE(id < m_nRuntimeFunctions);
+        PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctions + id;
+        PCODE pEntryPoint = dac_cast<TADDR>(m_decoder.GetBase()) + pRuntimeFunction->BeginAddress;
+
+        SString buf;
+        AppendTokenName(TokenFromRid(rid, mdtMethodDef), buf, m_import);
+
+        DumpReadyToRunMethod(pEntryPoint, pRuntimeFunction, buf);
+    }
+
+    DisplayEndArray("Total Methods", METHODS); //Methods
+}
+
+extern PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFunction, /* out */ SIZE_T * pSize);
+
+void NativeImageDumper::DumpReadyToRunMethod(PCODE pEntryPoint, PTR_RUNTIME_FUNCTION pRuntimeFunction, SString& name)
+{
+    //Read the GCInfo to get the total method size.
+    unsigned methodSize = 0;
+    unsigned gcInfoSize = UINT_MAX;
+
+    SIZE_T nUnwindDataSize;
+    PTR_VOID pUnwindData = GetUnwindDataBlob(dac_cast<TADDR>(m_decoder.GetBase()), pRuntimeFunction, &nUnwindDataSize);
+
+    // GCInfo immediatelly follows unwind data
+    PTR_CBYTE gcInfo = dac_cast<PTR_CBYTE>(pUnwindData) + nUnwindDataSize;
+
+    void(*stringOutFn)(const char *, ...);
+    IF_OPT(GC_INFO)
+    {
+        stringOutFn = stringOut;
+    }
+    else
+    {
+        stringOutFn = nullStringOut;
+    }
+    if (gcInfo != NULL)
+    {
+        PTR_CBYTE curGCInfoPtr = gcInfo;
+        g_holdStringOutData.Clear();
+        GCDump gcDump;
+        gcDump.gcPrintf = stringOutFn;
+#if !defined(_TARGET_X86_) && defined(USE_GC_INFO_DECODER)
+        GcInfoDecoder gcInfoDecoder(curGCInfoPtr, DECODE_CODE_LENGTH, 0);
+        methodSize = gcInfoDecoder.GetCodeLength();
+#endif
+
+        //dump the data to a string first so we can get the gcinfo size.
+#ifdef _TARGET_X86_
+        InfoHdr hdr;
+        stringOutFn("method info Block:\n");
+        curGCInfoPtr += gcDump.DumpInfoHdr(curGCInfoPtr, &hdr, &methodSize, 0);
+        stringOutFn("\n");
+#endif
+
+        IF_OPT(METHODS)
+        {
+#ifdef _TARGET_X86_
+            stringOutFn("PointerTable:\n");
+            curGCInfoPtr += gcDump.DumpGCTable(curGCInfoPtr,
+                hdr,
+                methodSize, 0);
+            gcInfoSize = curGCInfoPtr - gcInfo;
+#elif defined(USE_GC_INFO_DECODER)
+            stringOutFn("PointerTable:\n");
+            curGCInfoPtr += gcDump.DumpGCTable(curGCInfoPtr,
+                methodSize, 0);
+            gcInfoSize = (unsigned)(curGCInfoPtr - gcInfo);
+#endif
+        }
+
+        //data is output below.
+    }
+
+    DisplayStartElement("Method", METHODS);
+    DisplayWriteElementStringW("Name", (const WCHAR *)name, METHODS);
+
+    DisplayStartStructure("GCInfo",
+        DPtrToPreferredAddr(gcInfo),
+        gcInfoSize,
+        METHODS);
+
+    DisplayStartTextElement("Contents", GC_INFO);
+    DisplayWriteXmlTextBlock(("%S", (const WCHAR *)g_holdStringOutData), GC_INFO);
+    DisplayEndTextElement(GC_INFO); //Contents
+
+    DisplayEndStructure(METHODS); //GCInfo
+
+    DisplayStartStructure("Code", DataPtrToDisplay(pEntryPoint), methodSize,
+        METHODS);
+
+    IF_OPT(DISASSEMBLE_CODE)
+    {
+        // Disassemble hot code.  Read the code into the host process.
+        /* REVISIT_TODO Mon 10/24/2005
+        * Is this align up right?
+        */
+        BYTE * codeStartHost =
+            reinterpret_cast<BYTE*>(PTR_READ(pEntryPoint,
+                (ULONG32)ALIGN_UP(methodSize,
+                    CODE_SIZE_ALIGN)));
+        DisassembleMethod(codeStartHost, methodSize);
+    }
+
+    DisplayEndStructure(METHODS); //Code 
+
+    DisplayEndElement(METHODS); //Method
+}
 #endif // FEATURE_READYTORUN
 
 #if 0
@@ -9338,36 +9517,6 @@ mdTypeRef NativeImageDumper::FindTypeRefForMT( PTR_MethodTable mt )
 #endif
 
 
-#if 0
-void NativeImageDumper::DumpPEFile( PTR_PEFile file, const char * name )
-{
-    IF_OPT(PE_INFO)
-    {
-        m_display->StartStructure( name, DPtrToPreferredAddr(file),
-                                   sizeof(*file) );
-    }
-    _ASSERTE(file->m_identity);
-    _ASSERTE(file->m_ILimage);
-    _ASSERTE(file->m_nativeImage);
-    DumpPEImage( file->m_identity, "ThisImage" );
-    DumpPEImage( file->m_ILimage, "ILImage" );
-    DumpPEImage( file->m_nativeImage, "NativeImage" );
-
-    IF_OPT(PE_INFO)
-    {
-        m_display->WriteElementFlag( "CanUseNativeImage",
-                                     file->m_fCanUseNativeImage );
-        m_display->WriteElementFlag( "HasPersistentMDImport",
-                                     file->m_bHasPersistentMDImport );
-    }
-#ifdef _DEBUG
-    _ASSERTE( !"Look at m_pDebugName and m_debugName.  Also look at IMDstuff" );
-#endif
-    IF_OPT(PE_INFO)
-        m_display->EndStructure(); //name
-}
-#endif
-
 /* REVISIT_TODO Mon 10/10/2005
  * Here is where it gets bad.  There is no DAC build of gcdump, so instead
  * build it directly into the the dac.  That's what all these ugly defines
index ff97b1f..4efec37 100644 (file)
@@ -243,18 +243,24 @@ public:
     void FieldDescToString( PTR_FieldDesc fd, mdFieldDef tok, SString& buf );
 
 #ifdef FEATURE_READYTORUN
-    void DumpReadyToRun();
-    void DumpReadyToRunHeader();
-#endif
+private:
+    READYTORUN_HEADER *                        m_pReadyToRunHeader;
 
-#if 0
-    void DumpPEFile( PTR_PEFile file, const char * name );
-    void DumpPEImage( PTR_PEImage image, const char * name );
-#endif
+    PTR_RUNTIME_FUNCTION        m_pRuntimeFunctions;
+    DWORD                       m_nRuntimeFunctions;
 
+    NativeFormat::NativeReader  m_nativeReader;
+    NativeFormat::NativeArray   m_methodDefEntryPoints;
+
+    IMAGE_DATA_DIRECTORY * FindReadyToRunSection(DWORD type);
+
+public:
+    void DumpReadyToRun();
+    void DumpReadyToRunHeader();
+    void DumpReadyToRunMethods();
+    void DumpReadyToRunMethod(PCODE pEntryPoint, PTR_RUNTIME_FUNCTION pRuntimeFunction, SString& name);
+#endif // FEATURE_READYTORUN
 
-    inline PTR_VOID BaseAddress() const { return m_baseAddress; }
-    
 private:
     PEDecoder m_decoder;
     const WCHAR * const m_name;