Work for IBC profiling with ReadyToRun images
authorBrian Sullivan <briansul@microsoft.com>
Wed, 8 Mar 2017 02:58:11 +0000 (18:58 -0800)
committerBrian Sullivan <briansul@microsoft.com>
Mon, 13 Mar 2017 19:45:14 +0000 (12:45 -0700)
Incremented ReadyToRun version to 2.2
Implemented caching for IsInstrumented using IS_INSTRUMENTED_UNSET
Added method Module::InitializeForProfiling()
Added full support for method profile counts in ReadyToRun image

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

17 files changed:
src/coreclr/src/inc/readytorun.h
src/coreclr/src/inc/readytorunhelpers.h
src/coreclr/src/jit/flowgraph.cpp
src/coreclr/src/tools/crossgen/crossgen.cpp
src/coreclr/src/utilcode/pedecoder.cpp
src/coreclr/src/vm/assembly.cpp
src/coreclr/src/vm/assembly.hpp
src/coreclr/src/vm/ceeload.cpp
src/coreclr/src/vm/ceeload.h
src/coreclr/src/vm/pefile.h
src/coreclr/src/vm/pefile.inl
src/coreclr/src/vm/readytoruninfo.cpp
src/coreclr/src/zap/zapheaders.cpp
src/coreclr/src/zap/zapimage.cpp
src/coreclr/src/zap/zapimage.h
src/coreclr/src/zap/zapper.cpp
src/coreclr/src/zap/zapreadytorun.cpp

index 9084b92..0f5183f 100644 (file)
@@ -16,8 +16,9 @@
 #define READYTORUN_SIGNATURE 0x00525452 // 'RTR'
 
 #define READYTORUN_MAJOR_VERSION 0x0002
-#define READYTORUN_MINOR_VERSION 0x0001
+#define READYTORUN_MINOR_VERSION 0x0002
 // R2R Version 2.1 adds the READYTORUN_SECTION_INLINING_INFO section
+// R2R Version 2.2 adds the READYTORUN_SECTION_PROFILEDATA_INFO section
 
 struct READYTORUN_HEADER
 {
@@ -58,7 +59,8 @@ enum ReadyToRunSectionType
     // 107 used by an older format of READYTORUN_SECTION_AVAILABLE_TYPES
     READYTORUN_SECTION_AVAILABLE_TYPES              = 108,
     READYTORUN_SECTION_INSTANCE_METHOD_ENTRYPOINTS  = 109,
-    READYTORUN_SECTION_INLINING_INFO                = 110  // Added in V2.1
+    READYTORUN_SECTION_INLINING_INFO                = 110, // Added in V2.1
+    READYTORUN_SECTION_PROFILEDATA_INFO             = 111  // Added in V2.2
 
        // If you add a new section consider whether it is a breaking or non-breaking change.
        // Usually it is non-breaking, but if it is preferable to have older runtimes fail
@@ -228,6 +230,9 @@ enum ReadyToRunHelper
     // Get string handle lazily
     READYTORUN_HELPER_GetString                 = 0x50,
 
+    // Used by /Tuning for Profile optimizations
+    READYTORUN_HELPER_LogMethodEnter            = 0x51,
+
     // Reflection helpers
     READYTORUN_HELPER_GetRuntimeTypeHandle      = 0x54,
     READYTORUN_HELPER_GetRuntimeMethodHandle    = 0x55,
index 4524e1a..9baf0e4 100644 (file)
@@ -32,6 +32,8 @@ HELPER(READYTORUN_HELPER_Ldelema_Ref,               CORINFO_HELP_LDELEMA_REF,
 HELPER(READYTORUN_HELPER_MemSet,                    CORINFO_HELP_MEMSET,                            )
 HELPER(READYTORUN_HELPER_MemCpy,                    CORINFO_HELP_MEMCPY,                            )
 
+HELPER(READYTORUN_HELPER_LogMethodEnter,            CORINFO_HELP_BBT_FCN_ENTER,                     )
+
 HELPER(READYTORUN_HELPER_GetRuntimeTypeHandle,      CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE,         )
 HELPER(READYTORUN_HELPER_GetRuntimeMethodHandle,    CORINFO_HELP_METHODDESC_TO_STUBRUNTIMEMETHOD,   )
 HELPER(READYTORUN_HELPER_GetRuntimeFieldHandle,     CORINFO_HELP_FIELDDESC_TO_STUBRUNTIMEFIELD,     )
index 3d6dba9..1c77572 100644 (file)
@@ -330,14 +330,37 @@ void Compiler::fgInstrumentMethod()
 
         // Add the method entry callback node
 
-        GenTreeArgList* args = gtNewArgList(gtNewIconEmbMethHndNode(info.compMethodHnd));
+        GenTreePtr arg;
+
+#ifdef FEATURE_READYTORUN_COMPILER
+        if (opts.IsReadyToRun())
+        {
+            mdMethodDef currentMethodToken = info.compCompHnd->getMethodDefFromMethod(info.compMethodHnd);
+
+            CORINFO_RESOLVED_TOKEN resolvedToken;
+            resolvedToken.tokenContext = MAKE_METHODCONTEXT(info.compMethodHnd);
+            resolvedToken.tokenScope   = info.compScopeHnd;
+            resolvedToken.token        = currentMethodToken;
+            resolvedToken.tokenType    = CORINFO_TOKENKIND_Method;
+
+            info.compCompHnd->resolveToken(&resolvedToken);
+
+            arg = impTokenToHandle(&resolvedToken);
+        }
+        else
+#endif
+        {
+            arg = gtNewIconEmbMethHndNode(info.compMethodHnd);
+        }
+
+        GenTreeArgList* args = gtNewArgList(arg);
         GenTreePtr      call = gtNewHelperCallNode(CORINFO_HELP_BBT_FCN_ENTER, TYP_VOID, 0, args);
 
         GenTreePtr handle =
             gtNewIconEmbHndNode((void*)&bbProfileBufferStart->ExecutionCount, nullptr, GTF_ICON_BBC_PTR);
         GenTreePtr value = gtNewOperNode(GT_IND, TYP_INT, handle);
         GenTreePtr relop = gtNewOperNode(GT_NE, TYP_INT, value, gtNewIconNode(0, TYP_INT));
-        relop->gtFlags |= GTF_RELOP_QMARK;
+        relop->gtFlags |= GTF_RELOP_QMARK; // TODO-Cleanup: [Simple]  Move this to gtNewQmarkNode
         GenTreePtr colon = new (this, GT_COLON) GenTreeColon(TYP_VOID, gtNewNothingNode(), call);
         GenTreePtr cond  = gtNewQmarkNode(TYP_VOID, relop, colon);
         stmt             = gtNewStmt(cond);
index 6257896..df16e94 100644 (file)
@@ -834,14 +834,6 @@ int _cdecl wmain(int argc, __in_ecount(argc) WCHAR **argv)
             exit(FAILURE_RESULT);
         }
     }
-
-#ifdef FEATURE_READYTORUN_COMPILER
-    if (((dwFlags & NGENWORKER_FLAGS_TUNING) != 0) && ((dwFlags & NGENWORKER_FLAGS_READYTORUN) != 0))
-    {
-        Output(W("The /Tuning switch cannot be used with /ReadyToRun switch.\n"));
-        exit(FAILURE_RESULT);
-    }
-#endif
     
     // All argument processing has happened by now. The only messages that should appear before here are errors
     // related to argument parsing, such as the Usage message. Afterwards, other messages can appear.
index e4fc465..9b7a058 100644 (file)
@@ -1075,8 +1075,8 @@ CHECK PEDecoder::CheckCorHeader() const
     if (IsStrongNameSigned())
         CHECK(HasStrongNameSignature());
 
-    // IL library files (really a misnomer - these are native images) only
-    // may have a native image header
+    // IL library files (really a misnomer - these are native images or ReadyToRun images)
+    // only they can have a native image header
     if ((pCor->Flags&VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) == 0)
     {
         CHECK(VAL32(pCor->ManagedNativeHeader.Size) == 0);
@@ -1828,7 +1828,7 @@ BOOL PEDecoder::HasNativeHeader() const
     CONTRACT_END;
 
 #ifdef FEATURE_PREJIT
-    // Pretend that ready-to-run images do not have native header
+    // Pretend that ready-to-run images do not have native header
     RETURN (((GetCorHeader()->Flags & VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) != 0) && !HasReadyToRunHeader());
 #else
     RETURN FALSE;
index 5f0e75d..d95eec0 100644 (file)
@@ -141,6 +141,9 @@ Assembly::Assembly(BaseDomain *pDomain, PEAssembly* pFile, DebuggerAssemblyContr
 #ifdef FEATURE_COMINTEROP
     , m_InteropAttributeStatus(INTEROP_ATTRIBUTE_UNSET)
 #endif
+#ifdef FEATURE_PREJIT
+    , m_isInstrumentedStatus(IS_INSTRUMENTED_UNSET)
+#endif
 {
     STANDARD_VM_CONTRACT;
 }
@@ -2329,21 +2332,37 @@ BOOL Assembly::IsInstrumented()
     STATIC_CONTRACT_GC_TRIGGERS;
     STATIC_CONTRACT_FAULT;
 
-    BOOL isInstrumented = false;
-
-    EX_TRY
+    // This will set the value of m_isInstrumentedStatus by calling IsInstrumentedHelper()
+    // that method performs string pattern matching using the Config value of ZapBBInstr
+    // We cache the value returned from that method in m_isInstrumentedStatus
+    //
+    if (m_isInstrumentedStatus == IS_INSTRUMENTED_UNSET)
     {
-        FAULT_NOT_FATAL();
+        EX_TRY
+        {
+            FAULT_NOT_FATAL();
 
-        isInstrumented = IsInstrumentedHelper();
-    }
-    EX_CATCH
-    {
-        isInstrumented = false;
+            if (IsInstrumentedHelper())
+            {
+                m_isInstrumentedStatus = IS_INSTRUMENTED_TRUE;
+            }
+            else
+            {
+                m_isInstrumentedStatus = IS_INSTRUMENTED_FALSE;
+            }
+        }
+
+        EX_CATCH
+        {
+            m_isInstrumentedStatus = IS_INSTRUMENTED_FALSE;
+        }
+        EX_END_CATCH(RethrowTerminalExceptions);
     }
-    EX_END_CATCH(RethrowTerminalExceptions);
 
-    return isInstrumented;
+    // At this point m_isInstrumentedStatus can't have the value of IS_INSTRUMENTED_UNSET
+    _ASSERTE(m_isInstrumentedStatus != IS_INSTRUMENTED_UNSET);
+
+    return (m_isInstrumentedStatus == IS_INSTRUMENTED_TRUE);
 }
 
 BOOL Assembly::IsInstrumentedHelper()
@@ -2357,7 +2376,7 @@ BOOL Assembly::IsInstrumentedHelper()
         return false;
 
     // We must have a native image in order to perform IBC instrumentation
-    if (!GetManifestFile()->HasNativeImage())
+    if (!GetManifestFile()->HasNativeOrReadyToRunImage())
         return false;
     
     // @Consider using the full name instead of the short form
index 22b031b..0fdb9a2 100644 (file)
@@ -551,7 +551,7 @@ public:
 
     BOOL IsSIMDVectorAssembly() { LIMITED_METHOD_DAC_CONTRACT; return m_fIsSIMDVectorAssembly; }
 
-#ifdef FEATURE_PREJIT    
+#ifdef FEATURE_PREJIT
     BOOL IsInstrumented();
     BOOL IsInstrumentedHelper();
 #endif // FEATURE_PREJIT
@@ -704,7 +704,6 @@ protected:
     // Keep track of the vars that need to be freed.
     short int m_FreeFlag;
 
-
 private:
 
     //****************************************************************************************
@@ -775,6 +774,15 @@ private:
 
     DWORD                 m_dwReliabilityContract;
 
+#ifdef FEATURE_PREJIT
+    enum IsInstrumentedStatus {
+        IS_INSTRUMENTED_UNSET = 0,
+        IS_INSTRUMENTED_FALSE = 1,
+        IS_INSTRUMENTED_TRUE = 2,
+    };
+    IsInstrumentedStatus    m_isInstrumentedStatus;
+#endif // FEATURE_PREJIT
+
 };
 
 typedef Assembly::ModuleIterator ModuleIterator;
index 61d8ae2..1979d69 100644 (file)
@@ -437,6 +437,55 @@ Module::Module(Assembly *pAssembly, mdFile moduleRef, PEFile *file)
     file->AddRef();
 }
 
+void Module::InitializeForProfiling()
+{
+    CONTRACTL
+    {
+        INSTANCE_CHECK;
+        THROWS;
+        GC_TRIGGERS;
+        MODE_PREEMPTIVE;
+        PRECONDITION(HasNativeOrReadyToRunImage());
+    }
+    CONTRACTL_END;
+
+    COUNT_T  cbProfileList = 0;
+
+    m_nativeImageProfiling = FALSE;
+
+    if (HasNativeImage())
+    {
+        PEImageLayout * pNativeImage = GetNativeImage();
+        CORCOMPILE_VERSION_INFO * pNativeVersionInfo = pNativeImage->GetNativeVersionInfoMaybeNull();
+        if ((pNativeVersionInfo != NULL) && (pNativeVersionInfo->wConfigFlags & CORCOMPILE_CONFIG_INSTRUMENTATION))
+        {
+            m_nativeImageProfiling = GetAssembly()->IsInstrumented();
+        }
+
+        // Link the module to the profile data list if available.
+        m_methodProfileList = pNativeImage->GetNativeProfileDataList(&cbProfileList);
+    }
+    else // ReadyToRun image
+    {
+        // We already setup the m_methodProfileList in the ReadyToRunInfo constructor
+        if (m_methodProfileList != nullptr)
+        {
+            ReadyToRunInfo * pInfo = GetReadyToRunInfo();
+            PEImageLayout *  pImage = pInfo->GetImage();
+
+            // Enable profiling if the ZapBBInstr value says to
+            m_nativeImageProfiling = GetAssembly()->IsInstrumented();
+        }
+    }
+
+#ifdef FEATURE_LAZY_COW_PAGES
+    // When running a IBC tuning image to gather profile data 
+    // we increment the block counts contained in this area.
+    //
+    if (cbProfileList)
+        EnsureWritablePages(m_methodProfileList, cbProfileList);
+#endif
+}
 
 #ifdef FEATURE_PREJIT 
 
@@ -461,20 +510,6 @@ void Module::InitializeNativeImage(AllocMemTracker* pamTracker)
 
     ExecutionManager::AddNativeImageRange(dac_cast<TADDR>(pNativeImage->GetBase()), pNativeImage->GetVirtualSize(), this);
 
-    CORCOMPILE_VERSION_INFO * pNativeVersionInfo = pNativeImage->GetNativeVersionInfoMaybeNull();
-    if ((pNativeVersionInfo != NULL) && (pNativeVersionInfo->wConfigFlags & CORCOMPILE_CONFIG_INSTRUMENTATION))
-    {
-        m_nativeImageProfiling = GetAssembly()->IsInstrumented();
-    }
-
-    // Link the module to the profile data list if available.
-    COUNT_T cbProfileList;
-    m_methodProfileList = pNativeImage->GetNativeProfileDataList(&cbProfileList);
-#ifdef FEATURE_LAZY_COW_PAGES
-    if (cbProfileList)
-        EnsureWritablePages(m_methodProfileList, cbProfileList);
-#endif
-
 #ifndef CROSSGEN_COMPILE
     LoadTokenTables();
     LoadHelperTable();
@@ -652,9 +687,15 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName)
 #ifdef FEATURE_PREJIT 
     // Set up native image
     if (HasNativeImage())
+    {
         InitializeNativeImage(pamTracker);
+    }
 #endif // FEATURE_PREJIT
 
+    if (HasNativeOrReadyToRunImage())
+    {
+        InitializeForProfiling();
+    }
 
 #ifdef FEATURE_NATIVE_IMAGE_GENERATION
     if (g_CorCompileVerboseLevel)
@@ -4279,6 +4320,16 @@ BOOL Module::IsVisibleToDebugger()
     return TRUE;
 }
 
+BOOL            Module::HasNativeOrReadyToRunImage()
+{
+#ifdef FEATURE_READYTORUN
+    if (IsReadyToRun())
+        return TRUE;
+#endif
+
+    return HasNativeImage();
+}
+
 PEImageLayout * Module::GetNativeOrReadyToRunImage()
 {
     LIMITED_METHOD_CONTRACT;
index ba95b2d..2f3fe90 100644 (file)
@@ -1790,6 +1790,7 @@ protected:
 protected:
 #ifndef DACCESS_COMPILE
     virtual void Initialize(AllocMemTracker *pamTracker, LPCWSTR szName = NULL);
+    void InitializeForProfiling();
 #ifdef FEATURE_PREJIT 
     void InitializeNativeImage(AllocMemTracker* pamTracker);
 #endif
@@ -2734,6 +2735,8 @@ public:
     }
 #endif // FEATURE_PREJIT
 
+
+    BOOL            HasNativeOrReadyToRunImage();
     PEImageLayout * GetNativeOrReadyToRunImage();
     PTR_CORCOMPILE_IMPORT_SECTION GetImportSections(COUNT_T *pCount);
     PTR_CORCOMPILE_IMPORT_SECTION GetImportSectionFromIndex(COUNT_T index);
@@ -3066,6 +3069,10 @@ public:
     static void ProfileDataAllocateTokenLists(ProfileEmitter * pEmitter, TokenProfileData* pTokenProfileData);
     HRESULT WriteMethodProfileDataLogFile(bool cleanup);
     static void WriteAllModuleProfileData(bool cleanup);
+    void SetMethodProfileList(CORCOMPILE_METHOD_PROFILE_LIST * value)
+    {
+        m_methodProfileList = value;
+    }
 
     void CreateProfilingData();
     void DeleteProfilingData();
index d4f3755..b7c7bd3 100644 (file)
@@ -378,6 +378,7 @@ public:
     PEImage *GetNativeImageWithRef();
     PEImage *GetPersistentNativeImage();
 #endif
+    BOOL HasNativeOrReadyToRunImage();
     BOOL HasNativeImage();
     PTR_PEImageLayout GetLoaded();
     PTR_PEImageLayout GetLoadedNative();
index 9bd478e..44192ae 100644 (file)
@@ -1338,6 +1338,23 @@ inline BOOL PEFile::HasNativeImage()
 #endif
 }
 
+inline BOOL PEFile::HasNativeOrReadyToRunImage()
+{
+    CONTRACTL
+    {
+        INSTANCE_CHECK;
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY;
+        SO_TOLERANT;
+        CANNOT_TAKE_LOCK;
+        SUPPORTS_DAC;
+    }
+    CONTRACTL_END;
+
+    return (HasNativeImage() || IsILImageReadyToRun());
+}
+
 inline PTR_PEImageLayout PEFile::GetLoadedIL() 
 {
     LIMITED_METHOD_CONTRACT;
index 6a47aa8..623556d 100644 (file)
@@ -590,17 +590,29 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYT
         m_entryPointToMethodDescMap.Init(TRUE, &lock);
     }
 
-       //In format version >= 2.1 there is an optional inlining table 
-       if (IsImageVersionAtLeast(2, 1))
-       {
-               IMAGE_DATA_DIRECTORY * pInlineTrackingInfoDir = FindSection(READYTORUN_SECTION_INLINING_INFO);
-               if (pInlineTrackingInfoDir != NULL)
-               {
-                       const BYTE* pInlineTrackingMapData = (const BYTE*)GetImage()->GetDirectoryData(pInlineTrackingInfoDir);
-                       PersistentInlineTrackingMapR2R::TryLoad(pModule, pInlineTrackingMapData, pInlineTrackingInfoDir->Size,
-                               pamTracker, &m_pPersistentInlineTrackingMap);
-               }
-       }
+    // For format version 2.1 and later, there is an optional inlining table 
+    if (IsImageVersionAtLeast(2, 1))
+    {
+        IMAGE_DATA_DIRECTORY * pInlineTrackingInfoDir = FindSection(READYTORUN_SECTION_INLINING_INFO);
+        if (pInlineTrackingInfoDir != NULL)
+        {
+            const BYTE* pInlineTrackingMapData = (const BYTE*)GetImage()->GetDirectoryData(pInlineTrackingInfoDir);
+            PersistentInlineTrackingMapR2R::TryLoad(pModule, pInlineTrackingMapData, pInlineTrackingInfoDir->Size,
+                                                    pamTracker, &m_pPersistentInlineTrackingMap);
+        }
+    }
+    // Fpr format version 2.2 and later, there is an optional profile-data section
+    if (IsImageVersionAtLeast(2, 2))
+    {
+        IMAGE_DATA_DIRECTORY * pProfileDataInfoDir = FindSection(READYTORUN_SECTION_PROFILEDATA_INFO);
+        if (pProfileDataInfoDir != NULL)
+        {
+            CORCOMPILE_METHOD_PROFILE_LIST * pMethodProfileList;
+            pMethodProfileList = (CORCOMPILE_METHOD_PROFILE_LIST *)GetImage()->GetDirectoryData(pProfileDataInfoDir);
+
+            pModule->SetMethodProfileList(pMethodProfileList);  
+        }
+    }
 }
 
 static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, Module * pModule)
index ecb32c0..acec36b 100644 (file)
@@ -95,7 +95,7 @@ void ZapImage::SaveNativeHeader()
         // Pretend that ready-to-run images are IL-only
         nativeHeader.COR20Flags |= COMIMAGE_FLAGS_ILONLY;
 
-        // Pretend that ready-to-run images do not have native header
+        // Pretend that ready-to-run images do not have native header
         nativeHeader.COR20Flags &= ~COMIMAGE_FLAGS_IL_LIBRARY;
 
         // Remember whether the source IL image had ReadyToRun header
index 0267829..457b82e 100644 (file)
@@ -1753,6 +1753,7 @@ void ZapImage::Compile()
         OutputDebugInfoForReadyToRun();
         OutputTypesTableForReadyToRun(m_pMDImport);
         OutputInliningTableForReadyToRun();
+        OutputProfileDataForReadyToRun();
     }
     else
 #endif
index 65347ff..f014fd2 100644 (file)
@@ -558,6 +558,7 @@ private:
     void OutputDebugInfoForReadyToRun();
     void OutputTypesTableForReadyToRun(IMDInternalImport * pMDImport);
     void OutputInliningTableForReadyToRun();
+    void OutputProfileDataForReadyToRun();
 
     void CopyDebugDirEntry();
     void CopyWin32VersionResource();
index 904b9d1..50e86db 100644 (file)
@@ -1657,19 +1657,16 @@ ZapImage * Zapper::CompileModule(CORINFO_MODULE_HANDLE hModule,
 
     module->Compile();
 
-    if (IsReadyToRunCompilation())
+    if (!IsReadyToRunCompilation())
     {
-        return module.Extract();
-    }
-
-    //
-    // Link preloaded module.
-    //
-
-    Info(W("Linking preloaded input file %s\n"), module->m_pModuleFileName);
+        //
+        // Link preloaded module.
+        //
 
-    module->LinkPreload();
+        Info(W("Linking preloaded input file %s\n"), module->m_pModuleFileName);
 
+        module->LinkPreload();
+    }
     return module.Extract();
 }
 
index 8ed3f55..e67f9d3 100644 (file)
@@ -389,6 +389,14 @@ void ZapImage::OutputInliningTableForReadyToRun()
     GetReadyToRunHeader()->RegisterSection(READYTORUN_SECTION_INLINING_INFO, pBlob);
 }
 
+void ZapImage::OutputProfileDataForReadyToRun()
+{
+    if (m_pInstrumentSection != nullptr)
+    {
+        GetReadyToRunHeader()->RegisterSection(READYTORUN_SECTION_PROFILEDATA_INFO, m_pInstrumentSection);
+    }
+}
+
 void ZapImage::OutputTypesTableForReadyToRun(IMDInternalImport * pMDImport)
 {
     NativeWriter writer;