Basic infrastructure for binder tracing (dotnet/coreclr#27383)
authorElinor Fung <47805090+elinor-fung@users.noreply.github.com>
Mon, 28 Oct 2019 02:22:53 +0000 (19:22 -0700)
committerGitHub <noreply@github.com>
Mon, 28 Oct 2019 02:22:53 +0000 (19:22 -0700)
Commit migrated from https://github.com/dotnet/coreclr/commit/f55bc3a97fc0dc0d17e1c05495c0f6e2d494af73

21 files changed:
src/coreclr/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs
src/coreclr/src/binder/CMakeLists.txt
src/coreclr/src/binder/activitytracker.cpp [new file with mode: 0644]
src/coreclr/src/binder/bindertracing.cpp [new file with mode: 0644]
src/coreclr/src/binder/inc/activitytracker.h [new file with mode: 0644]
src/coreclr/src/binder/inc/bindertracing.h [new file with mode: 0644]
src/coreclr/src/vm/ClrEtwAll.man
src/coreclr/src/vm/appdomain.cpp
src/coreclr/src/vm/assemblynative.cpp
src/coreclr/src/vm/assemblynative.hpp
src/coreclr/src/vm/baseassemblyspec.cpp
src/coreclr/src/vm/baseassemblyspec.h
src/coreclr/src/vm/baseassemblyspec.inl
src/coreclr/src/vm/ecalllist.h
src/coreclr/src/vm/metasig.h
src/coreclr/src/vm/mscorlib.h
src/coreclr/src/vm/namespace.h
src/coreclr/tests/src/Loader/binding/tracing/BinderEventListener.cs [new file with mode: 0644]
src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.cs [new file with mode: 0644]
src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.csproj [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs

index 1735a9d81556a360e62febefb380789e4858ee01..672702de4b2dfe48f3c561d996dc9f20275d3eb7 100644 (file)
@@ -31,6 +31,9 @@ namespace System.Runtime.Loader
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern Assembly[] GetLoadedAssemblies();
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern bool IsTracingEnabled();
+
         private Assembly InternalLoadFromPath(string? assemblyPath, string? nativeImagePath)
         {
             RuntimeAssembly? loadedAssembly = null;
index e17f5a707c73314d008be6f33f1005d6bcd6c6d9..a748565718b5cb4f0e454ded1d9d68b08fe4d1af 100644 (file)
@@ -21,6 +21,7 @@ set(BINDER_COMMON_SOURCES
     assemblyidentitycache.cpp
     coreclrbindercommon.cpp
     fusionassemblyname.cpp
+    bindertracing.cpp
 )
 
 set(BINDER_COMMON_HEADERS
@@ -38,6 +39,7 @@ set(BINDER_COMMON_HEADERS
     inc/assemblyversion.hpp
     inc/assemblyversion.inl
     inc/bindertypes.hpp
+    inc/bindertracing.h
     inc/bindinglog.hpp
     inc/bindinglog.inl
     inc/bindresult.hpp
@@ -64,11 +66,13 @@ set(BINDER_COMMON_HEADERS
 
 set(BINDER_SOURCES
     ${BINDER_COMMON_SOURCES}
+    activitytracker.cpp
     clrprivbinderassemblyloadcontext.cpp
 )
 
 set(BINDER_HEADERS
     ${BINDER_COMMON_HEADERS}
+    inc/activitytracker.h
     inc/clrprivbinderassemblyloadcontext.h
     inc/contextentry.hpp
 )
diff --git a/src/coreclr/src/binder/activitytracker.cpp b/src/coreclr/src/binder/activitytracker.cpp
new file mode 100644 (file)
index 0000000..d971d9c
--- /dev/null
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// ============================================================
+//
+// activitytracker.cpp
+//
+
+
+//
+// Helpers for interaction with the managed ActivityTracker
+//
+// ============================================================
+
+#include "common.h"
+#include "activitytracker.h"
+
+void ActivityTracker::Start(/*out*/ GUID *activityId, /*out*/ GUID *relatedActivityId)
+{
+    GCX_COOP();
+
+    PREPARE_NONVIRTUAL_CALLSITE(METHOD__ACTIVITY_TRACKER__START_ASSEMBLY_LOAD);
+    DECLARE_ARGHOLDER_ARRAY(args, 2);
+    args[ARGNUM_0] = PTR_TO_ARGHOLDER(activityId);
+    args[ARGNUM_1] = PTR_TO_ARGHOLDER(relatedActivityId);
+
+    CALL_MANAGED_METHOD_NORET(args)
+}
+
+void ActivityTracker::Stop(/*out*/ GUID *activityId)
+{
+    GCX_COOP();
+
+    PREPARE_NONVIRTUAL_CALLSITE(METHOD__ACTIVITY_TRACKER__STOP_ASSEMBLY_LOAD);
+    DECLARE_ARGHOLDER_ARRAY(args, 1);
+    args[ARGNUM_0] = PTR_TO_ARGHOLDER(activityId);
+
+    CALL_MANAGED_METHOD_NORET(args)
+}
\ No newline at end of file
diff --git a/src/coreclr/src/binder/bindertracing.cpp b/src/coreclr/src/binder/bindertracing.cpp
new file mode 100644 (file)
index 0000000..dc61777
--- /dev/null
@@ -0,0 +1,111 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// ============================================================
+//
+// bindertracing.cpp
+//
+
+
+//
+// Implements helpers for binder tracing
+//
+// ============================================================
+
+#include "common.h"
+#include "bindertracing.h"
+
+#include "activitytracker.h"
+
+#ifdef FEATURE_EVENT_TRACE
+#include "eventtracebase.h"
+#endif // FEATURE_EVENT_TRACE
+
+using namespace BINDER_SPACE;
+
+namespace
+{
+    void FireAssemblyLoadStart(const BinderTracing::AssemblyBindOperation::BindRequest &request)
+    {
+#ifdef FEATURE_EVENT_TRACE
+        if (!EventEnabledAssemblyLoadStart())
+            return;
+
+        GUID activityId = GUID_NULL;
+        GUID relatedActivityId = GUID_NULL;
+        ActivityTracker::Start(&activityId, &relatedActivityId);
+
+        FireEtwAssemblyLoadStart(
+            GetClrInstanceId(),
+            request.AssemblyName,
+            request.AssemblyPath,
+            request.RequestingAssembly,
+            request.AssemblyLoadContext,
+            &activityId,
+            &relatedActivityId);
+#endif // FEATURE_EVENT_TRACE
+    }
+
+    void FireAssemblyLoadStop(const BinderTracing::AssemblyBindOperation::BindRequest &request, bool success, const WCHAR *resultName, const WCHAR *resultPath, bool cached)
+    {
+#ifdef FEATURE_EVENT_TRACE
+        if (!EventEnabledAssemblyLoadStop())
+            return;
+
+        GUID activityId = GUID_NULL;
+        ActivityTracker::Stop(&activityId);
+        
+        FireEtwAssemblyLoadStop(
+            GetClrInstanceId(), 
+            request.AssemblyName,
+            request.AssemblyPath,
+            request.RequestingAssembly,
+            request.AssemblyLoadContext,
+            success,
+            resultName,
+            resultPath,
+            cached,
+            &activityId);
+#endif // FEATURE_EVENT_TRACE
+    }
+}
+
+bool BinderTracing::IsEnabled()
+{
+#ifdef FEATURE_EVENT_TRACE
+    // Just check for the AssemblyLoadStart event being enabled.
+    return EventEnabledAssemblyLoadStart();
+#endif // FEATURE_EVENT_TRACE
+    return false;
+}
+
+namespace BinderTracing
+{
+    AssemblyBindOperation::AssemblyBindOperation(AssemblySpec *assemblySpec)
+        : m_bindRequest { assemblySpec }
+        , m_success { false }
+        , m_cached { false }
+    {
+        _ASSERTE(assemblySpec != nullptr);
+
+        // ActivityTracker or EventSource may have triggered the system satellite load.
+        // Don't track system satellite binding to avoid potential infinite recursion.
+        m_trackingBind = BinderTracing::IsEnabled() && !m_bindRequest.AssemblySpec->IsMscorlibSatellite();
+        if (m_trackingBind)
+        {
+            m_bindRequest.AssemblySpec->GetFileOrDisplayName(ASM_DISPLAYF_VERSION | ASM_DISPLAYF_CULTURE | ASM_DISPLAYF_PUBLIC_KEY_TOKEN, m_bindRequest.AssemblyName);
+            FireAssemblyLoadStart(m_bindRequest);
+        }
+    }
+
+    AssemblyBindOperation::~AssemblyBindOperation()
+    {
+        if (m_trackingBind)
+            FireAssemblyLoadStop(m_bindRequest, m_success, m_resultName.GetUnicode(), m_resultPath.GetUnicode(), m_cached);
+    }
+
+    void AssemblyBindOperation::SetResult(PEAssembly *assembly)
+    {
+        m_success = assembly != nullptr;
+    }
+}
\ No newline at end of file
diff --git a/src/coreclr/src/binder/inc/activitytracker.h b/src/coreclr/src/binder/inc/activitytracker.h
new file mode 100644 (file)
index 0000000..128664c
--- /dev/null
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// activitytracker.h
+//
+
+#ifndef __ACTIVITY_TRACKER_H__
+#define __ACTIVITY_TRACKER_H__
+
+namespace ActivityTracker
+{
+    void Start(/*out*/ GUID *activityId, /*out*/ GUID *relatedActivityId);
+    void Stop(/*out*/ GUID *activityId);
+};
+
+#endif // __ACTIVITY_TRACKER_H__
diff --git a/src/coreclr/src/binder/inc/bindertracing.h b/src/coreclr/src/binder/inc/bindertracing.h
new file mode 100644 (file)
index 0000000..068d881
--- /dev/null
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// bindertracing.h
+//
+
+#ifndef __BINDER_TRACING_H__
+#define __BINDER_TRACING_H__
+
+class AssemblySpec;
+class PEAssembly;
+
+namespace BinderTracing
+{
+    bool IsEnabled();
+
+    // If tracing is enabled, this class fires an assembly bind start event on construction
+    // and the corresponding stop event on destruction
+    class AssemblyBindOperation
+    {
+    public:
+        // This class assumes the assembly spec will have a longer lifetime than itself
+        AssemblyBindOperation(AssemblySpec *assemblySpec);
+        ~AssemblyBindOperation();
+
+        void SetResult(PEAssembly *assembly);
+
+        struct BindRequest
+        {
+            AssemblySpec *AssemblySpec;
+            SString AssemblyName;
+            SString AssemblyPath;
+            SString RequestingAssembly;
+            SString AssemblyLoadContext;
+        };
+
+    private:
+        BindRequest m_bindRequest;
+
+        bool m_trackingBind;
+
+        bool m_success;
+        SString m_resultName;
+        SString m_resultPath;
+        bool m_cached;
+    };
+};
+
+#endif // __BINDER_TRACING_H__
index 3f5deeefd27ae599a5092459c1c45dab478463fe..5b5b4cae22b637c6d8e01dab4f31d06b2fe07abf 100644 (file)
@@ -17,8 +17,8 @@
                              message="$(string.RuntimePublisher.GCKeywordMessage)" symbol="CLR_GC_KEYWORD"/>
                     <keyword name="GCHandleKeyword" mask="0x2"
                              message="$(string.RuntimePublisher.GCHandleKeywordMessage)" symbol="CLR_GCHANDLE_KEYWORD"/>
-                    <keyword name="FusionKeyword" mask="0x4"
-                             message="$(string.RuntimePublisher.FusionKeywordMessage)" symbol="CLR_FUSION_KEYWORD"/>
+                    <keyword name="AssemblyLoaderKeyword" mask="0x4"
+                             message="$(string.RuntimePublisher.AssemblyLoaderKeywordMessage)" symbol="CLR_ASSEMBLY_LOADER_KEYWORD"/>
                     <keyword name="LoaderKeyword" mask="0x8"
                              message="$(string.RuntimePublisher.LoaderKeywordMessage)" symbol="CLR_LOADER_KEYWORD"/>
                     <keyword name="JitKeyword" mask="0x10"
                             <opcode name="Resume" message="$(string.RuntimePublisher.TieredCompilationResumeOpcodeMessage)" symbol="CLR_TIERED_COMPILATION_RESUME_OPCODE" value="13"/>
                         </opcodes>
                     </task>
-                <!--Next available ID is 32-->
+
+                    <task name="AssemblyLoader" symbol="CLR_ASSEMBLY_LOADER_TASK"
+                          value="32" eventGUID="{BCF2339E-B0A6-452D-966C-33AC9DD82573}"
+                          message="$(string.RuntimePublisher.AssemblyLoaderTaskMessage)">
+                        <opcodes>
+                        </opcodes>
+                    </task>
+                <!--Next available ID is 33-->
                 </tasks>
                 <!--Maps-->
                 <maps>
                         </UserData>
                     </template>
 
+                    <template tid="AssemblyLoadStart">
+                        <data name="ClrInstanceID" inType="win:UInt16" />
+                        <data name="AssemblyName" inType="win:UnicodeString" />
+                        <data name="AssemblyPath" inType="win:UnicodeString" />
+                        <data name="RequestingAssembly" inType="win:UnicodeString" />
+                        <data name="AssemblyLoadContext" inType="win:UnicodeString" />
+                        <UserData>
+                            <AssemblyLoadStart xmlns="myNs">
+                                <ClrInstanceID> %1 </ClrInstanceID>
+                                <AssemblyName> %2 </AssemblyName>
+                                <AssemblyPath> %3 </AssemblyPath>
+                                <RequestingAssembly> %4 </RequestingAssembly>
+                                <AssemblyLoadContext> %5 </AssemblyLoadContext>
+                            </AssemblyLoadStart>
+                        </UserData>
+                    </template>
+
+                    <template tid="AssemblyLoadStop">
+                        <data name="ClrInstanceID" inType="win:UInt16" />
+                        <data name="AssemblyName" inType="win:UnicodeString" />
+                        <data name="AssemblyPath" inType="win:UnicodeString" />
+                        <data name="RequestingAssembly" inType="win:UnicodeString" />
+                        <data name="AssemblyLoadContext" inType="win:UnicodeString" />
+                        <data name="Success" inType="win:Boolean" />
+                        <data name="ResultAssemblyName" inType="win:UnicodeString" />
+                        <data name="ResultAssemblyPath" inType="win:UnicodeString" />
+                        <data name="Cached" inType="win:Boolean" />
+                        <UserData>
+                            <AssemblyLoadStop xmlns="myNs">
+                                <ClrInstanceID> %1 </ClrInstanceID>
+                                <AssemblyName> %2 </AssemblyName>
+                                <AssemblyPath> %3 </AssemblyPath>
+                                <RequestingAssembly> %4 </RequestingAssembly>
+                                <AssemblyLoadContext> %5 </AssemblyLoadContext>
+                                <Success> %6 </Success>
+                                <ResultAssemblyName> %7 </ResultAssemblyName>
+                                <ResultAssemblyPath> %8 </ResultAssemblyPath>
+                                <Cached> %9 </Cached>
+                            </AssemblyLoadStop>
+                        </UserData>
+                    </template>
+
                     <template tid="MethodLoadUnload">
                         <data name="MethodID" inType="win:UInt64" outType="win:HexInt64" />
                         <data name="ModuleID" inType="win:UInt64" outType="win:HexInt64" />
                     <event value="284" version="0" level="win:Informational" template="TieredCompilationBackgroundJitStop"
                            keywords="CompilationKeyword" task="TieredCompilation" opcode="win:Stop"
                            symbol="TieredCompilationBackgroundJitStop" message="$(string.RuntimePublisher.TieredCompilationBackgroundJitStopEventMessage)"/>
+
+                    <!-- Assembly loader events 290-299 -->
+                    <event value="290" version="0" level="win:Informational"  template="AssemblyLoadStart"
+                           keywords ="AssemblyLoaderKeyword" opcode="win:Start"
+                           task="AssemblyLoader"
+                           symbol="AssemblyLoadStart" message="$(string.RuntimePublisher.AssemblyLoadStartEventMessage)"/>
+
+                    <event value="291" version="0" level="win:Informational"  template="AssemblyLoadStop"
+                           keywords ="AssemblyLoaderKeyword" opcode="win:Stop"
+                           task="AssemblyLoader"
+                           symbol="AssemblyLoadStop" message="$(string.RuntimePublisher.AssemblyLoadStopEventMessage)"/>
+
                 </events>
             </provider>
 
                 <string id="RuntimePublisher.AppDomainLoad_V1EventMessage" value="AppDomainID=%1;%nAppDomainFlags=%2;%nAppDomainName=%3;%nAppDomainIndex=%4;%nClrInstanceID=%5" />
                 <string id="RuntimePublisher.AppDomainUnloadEventMessage" value="AppDomainID=%1;%nAppDomainFlags=%2;%nAppDomainName=%3" />
                 <string id="RuntimePublisher.AppDomainUnload_V1EventMessage" value="AppDomainID=%1;%nAppDomainFlags=%2;%nAppDomainName=%3;%nAppDomainIndex=%4;%nClrInstanceID=%5" />
+                <string id="RuntimePublisher.AssemblyLoadStartEventMessage" value="ClrInstanceID=%1;%nAssemblyName=%2;%nAssemblyPath=%3;%nRequestingAssembly=%4;%nAssemblyLoadContext=%5" />
+                <string id="RuntimePublisher.AssemblyLoadStopEventMessage" value="ClrInstanceID=%1;%nAssemblyName=%2;%nAssemblyPath=%3;%nRequestingAssembly=%4;%nAssemblyLoadContext=%5;%nSuccess=%6;%nResultAssemblyName=%7;%nResultAssemblyPath=%8;%nCached=%9" />
                 <string id="RuntimePublisher.StackEventMessage" value="ClrInstanceID=%1;%nReserved1=%2;%nReserved2=%3;%nFrameCount=%4;%nStack=%5" />
                 <string id="RuntimePublisher.AppDomainMemAllocatedEventMessage" value="AppDomainID=%1;%nAllocated=%2;%nClrInstanceID=%3" />
                 <string id="RuntimePublisher.AppDomainMemSurvivedEventMessage" value="AppDomainID=%1;%nSurvived=%2;%nProcessSurvived=%3;%nClrInstanceID=%4" />
                 <string id="RuntimePublisher.DebugExceptionProcessingTaskMessage" value="DebugExceptionProcessing" />
                 <string id="RuntimePublisher.CodeSymbolsTaskMessage" value="CodeSymbols" />
                 <string id="RuntimePublisher.TieredCompilationTaskMessage" value="TieredCompilation" />
+                <string id="RuntimePublisher.AssemblyLoaderTaskMessage" value="AssemblyLoader" />
               
                 <string id="RundownPublisher.EEStartupTaskMessage" value="Runtime" />
                 <string id="RundownPublisher.MethodTaskMessage" value="Method" />
                 <!-- Keyword Messages -->
                 <string id="RuntimePublisher.GCKeywordMessage" value="GC" />
                 <string id="RuntimePublisher.ThreadingKeywordMessage" value="Threading" />
-                <string id="RuntimePublisher.FusionKeywordMessage" value="Binder" />
+                <string id="RuntimePublisher.AssemblyLoaderKeywordMessage" value="AssemblyLoader" />
                 <string id="RuntimePublisher.LoaderKeywordMessage" value="Loader" />
                 <string id="RuntimePublisher.JitKeywordMessage" value="Jit" />
                 <string id="RuntimePublisher.JittedMethodILToNativeMapKeywordMessage" value="JittedMethodILToNativeMap" />
index 98853c8311ceaa50abc29a155d1b51699371d02e..88d80793323b45298c637f8d6626cf68634617e9 100644 (file)
@@ -66,9 +66,9 @@
 
 #include "stringarraylist.h"
 
+#include "../binder/inc/bindertracing.h"
 #include "../binder/inc/clrprivbindercoreclr.h"
 
-
 #include "clrprivtypecachewinrt.h"
 
 // this file handles string conversion errors for itself
@@ -4915,6 +4915,8 @@ PEAssembly * AppDomain::BindAssemblySpec(
 
     BOOL fForceReThrow = FALSE;
 
+    BinderTracing::AssemblyBindOperation bindOperation(pSpec);
+
 #if defined(FEATURE_COMINTEROP)
     // Handle WinRT assemblies in the classic/hybrid scenario. If this is an AppX process,
     // then this case will be handled by the previous block as part of the full set of
@@ -4985,6 +4987,7 @@ EndTry2:;
         }
         _ASSERTE((FAILED(hr) && !fThrowOnFileNotFound) || pAssembly != nullptr);
 
+        bindOperation.SetResult(pAssembly.GetValue());
         return pAssembly.Extract();
     }
     else
@@ -5157,6 +5160,7 @@ EndTry2:;
                 result->AddRef();
         }
 
+        bindOperation.SetResult(result.GetValue());
         return result.Extract();
     }
     else
index 08ead4c983810a921ff20a44624b180004965dfc..c3711c3300c3f423b28a5e6b32635475403c14ef 100644 (file)
 #include "typeparse.h"
 
 #include "appdomainnative.hpp"
+#include "../binder/inc/bindertracing.h"
 #include "../binder/inc/clrprivbindercoreclr.h"
 
-
-
 FCIMPL6(Object*, AssemblyNative::Load, AssemblyNameBaseObject* assemblyNameUNSAFE,
         StringObject* codeBaseUNSAFE, 
         AssemblyBaseObject* requestingAssemblyUNSAFE,
@@ -190,6 +189,8 @@ Assembly* AssemblyNative::LoadFromPEImage(ICLRPrivBinder* pBinderContext, PEImag
     spec.InitializeSpec(TokenFromRid(1, mdtAssembly), pImage->GetMDImport(), pCallersAssembly);
     spec.SetBindingContext(pBinderContext);
     
+    BinderTracing::AssemblyBindOperation bindOperation(&spec);
+
     HRESULT hr = S_OK;
     PTR_AppDomain pCurDomain = GetAppDomain();
     CLRPrivBinderCoreCLR *pTPABinder = pCurDomain->GetTPABinderContext();
@@ -222,6 +223,7 @@ Assembly* AssemblyNative::LoadFromPEImage(ICLRPrivBinder* pBinderContext, PEImag
     assem = BINDER_SPACE::GetAssemblyFromPrivAssemblyFast(pAssembly);
     
     PEAssemblyHolder pPEAssembly(PEAssembly::Open(pParentAssembly, assem->GetPEImage(), assem->GetNativePEImage(), pAssembly));
+    bindOperation.SetResult(pPEAssembly.GetValue());
 
     DomainAssembly *pDomainAssembly = pCurDomain->LoadDomainAssembly(&spec, pPEAssembly, FILE_LOADED);
     RETURN pDomainAssembly->GetAssembly();
@@ -1416,3 +1418,12 @@ BOOL QCALLTYPE AssemblyNative::InternalTryGetRawMetadata(
 
     return metadata != nullptr;
 }
+
+// static
+FCIMPL0(FC_BOOL_RET, AssemblyNative::IsTracingEnabled)
+{
+    FCALL_CONTRACT;
+
+    FC_RETURN_BOOL(BinderTracing::IsEnabled());
+}
+FCIMPLEND
index 265f2f98581c40597462a2a3b63c3e89f9bdcabf..7ba4359f87c8b53cd6a9df0d8917d9d0957e830a 100644 (file)
@@ -39,6 +39,8 @@ public:
                                                                 CLR_BOOL fThrowOnFileNotFound,
                                                                 AssemblyLoadContextBaseObject *assemblyLoadContextUNSAFE);
 
+    static FCDECL0(FC_BOOL_RET, IsTracingEnabled);
+
     //
     // instance FCALLs
     //
index 4d6ba26e40b8748164dd8e8524a44d29edce90db..f6859d4ee05806271e57b3d1fe197eea39828d61 100644 (file)
@@ -155,7 +155,7 @@ BOOL BaseAssemblySpec::IsAssemblySpecForMscorlib()
 // mscorlib.debug.resources.dll and uses the same public key as mscorlib.
 // It does not necessarily have the same version, and the Culture will 
 // always be set to something like "jp-JP".
-BOOL BaseAssemblySpec::IsMscorlibSatellite()
+BOOL BaseAssemblySpec::IsMscorlibSatellite() const
 {
     CONTRACTL
     {
index 68172459aaa84e940e9ec66f9b0e538f0db1cb78..1621fa1dafdd0387627a65e23e93ecfd4de3f231 100644 (file)
@@ -93,7 +93,7 @@ public:
     void SetName(LPCSTR szName);
     void SetName(SString const & ssName);
 
-    LPCWSTR GetCodeBase();
+    LPCWSTR GetCodeBase() const;
     void SetCodeBase(LPCWSTR szCodeBase);
 
     VOID SetCulture(LPCSTR szCulture);
@@ -109,7 +109,7 @@ public:
     BOOL IsStrongNamed() const;
     BOOL HasPublicKey() const;
     BOOL HasPublicKeyToken() const;
-    BOOL IsMscorlibSatellite();
+    BOOL IsMscorlibSatellite() const;
     BOOL IsMscorlib();
 
     //
index 9a4f70e713dfab1cf889d2244e254f8b5e3824b1..2bd08de946f00ff4493a301b5e73b2db2c1697ca 100644 (file)
@@ -544,7 +544,7 @@ inline void BaseAssemblySpec::SetCodeBase(LPCWSTR szCodeBase)
     m_wszCodeBase=szCodeBase;
 }
 
-inline LPCWSTR BaseAssemblySpec::GetCodeBase()
+inline LPCWSTR BaseAssemblySpec::GetCodeBase() const
 {
     LIMITED_METHOD_CONTRACT;
     return m_wszCodeBase;
index 11dd8d82d1c438ce8686da864774133de1417acb..fa5d322b162138e9174472adb06ea7d5d9f9f9b4 100644 (file)
@@ -522,6 +522,7 @@ FCFuncStart(gAssemblyLoadContextFuncs)
     QCFuncElement("InternalSetProfileRoot", MultiCoreJITNative::InternalSetProfileRoot)
     QCFuncElement("InternalStartProfile",   MultiCoreJITNative::InternalStartProfile)
 #endif // defined(FEATURE_MULTICOREJIT)
+    FCFuncElement("IsTracingEnabled", AssemblyNative::IsTracingEnabled)
 FCFuncEnd()
 
 FCFuncStart(gAssemblyNameFuncs)
index e0141526202989c41d73bcd2239a53fbc1e2ffc8..886a7c046c2215692ba62e2372103a13235c2ae5 100644 (file)
@@ -560,6 +560,10 @@ DEFINE_METASIG_T(SM(SyncCtx_ArrIntPtr_Bool_Int_RetInt, C(SYNCHRONIZATION_CONTEXT
 DEFINE_METASIG_T(IM(RefGuid_OutIntPtr_RetCustomQueryInterfaceResult, r(g(GUID)) r(I), g(CUSTOMQUERYINTERFACERESULT)))
 #endif //FEATURE_COMINTEROP
 
+// Activity Tracker
+DEFINE_METASIG_T(SM(RefGuid_RefGuid_RetVoid, r(g(GUID)) r(g(GUID)) , v))
+DEFINE_METASIG_T(SM(RefGuid_RetVoid, r(g(GUID)), v))
+
 DEFINE_METASIG_T(SM(IntPtr_AssemblyName_RetAssemblyBase, I C(ASSEMBLY_NAME), C(ASSEMBLYBASE)))
 DEFINE_METASIG_T(SM(Str_AssemblyBase_IntPtr_RetIntPtr, s C(ASSEMBLYBASE) I, I))
 DEFINE_METASIG_T(SM(Str_AssemblyBase_Bool_UInt_RetIntPtr, s C(ASSEMBLYBASE) F K, I))
index aae072948da41458e71e9fde08703459835db220..c0f1ea0211ee83289f11a9a9e602fe4424af3198 100644 (file)
@@ -1378,6 +1378,10 @@ DEFINE_FIELD_U(_kind,               ContractExceptionObject,    _Kind)
 DEFINE_FIELD_U(_userMessage,        ContractExceptionObject,    _UserMessage)
 DEFINE_FIELD_U(_condition,          ContractExceptionObject,    _Condition)
 
+DEFINE_CLASS(ACTIVITY_TRACKER,      Tracing,                ActivityTracker)
+DEFINE_METHOD(ACTIVITY_TRACKER,     START_ASSEMBLY_LOAD,    StartAssemblyLoad,    SM_RefGuid_RefGuid_RetVoid)
+DEFINE_METHOD(ACTIVITY_TRACKER,     STOP_ASSEMBLY_LOAD,     StopAssemblyLoad,     SM_RefGuid_RetVoid)
+
 #ifdef FEATURE_COMINTEROP
 DEFINE_CLASS(CAUSALITY_TRACE_LEVEL, WindowsFoundationDiag,   CausalityTraceLevel)
 DEFINE_CLASS(ASYNC_TRACING_EVENT_ARGS,       WindowsFoundationDiag,         TracingStatusChangedEventArgs)
index ce40247f435a88282cee07e25b6c5ed9d585ebf3..d21df176df6b38dc6d2772675bdc2c86b4f22b41 100644 (file)
@@ -21,6 +21,7 @@
 #define g_ResourcesNS       g_SystemNS ".Resources"
 #define g_DiagnosticsNS     g_SystemNS ".Diagnostics"
 #define g_CodeContractsNS   g_DiagnosticsNS ".Contracts"
+#define g_TracingNS         g_DiagnosticsNS ".Tracing"
 #define g_AssembliesNS      g_SystemNS ".Configuration.Assemblies"
 #define g_GlobalizationNS   g_SystemNS ".Globalization"
 #define g_IsolatedStorageNS g_SystemNS ".IO.IsolatedStorage"
diff --git a/src/coreclr/tests/src/Loader/binding/tracing/BinderEventListener.cs b/src/coreclr/tests/src/Loader/binding/tracing/BinderEventListener.cs
new file mode 100644 (file)
index 0000000..e221a1e
--- /dev/null
@@ -0,0 +1,106 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Tracing;
+using System.Linq;
+using System.Threading;
+using System.Reflection;
+using Xunit;
+
+using Assert = Xunit.Assert;
+
+namespace BinderTracingTests
+{
+    internal class BindOperation
+    {
+        internal AssemblyName AssemblyName;
+        internal bool Success;
+
+        internal Guid ActivityId;
+        internal Guid ParentActivityId;
+
+        internal bool Completed;
+        internal bool Nested;
+    }
+
+    internal sealed class BinderEventListener : EventListener
+    {
+        private const EventKeywords TasksFlowActivityIds = (EventKeywords)0x80;
+        private const EventKeywords AssemblyLoaderKeyword = (EventKeywords)0x4;
+
+        private readonly object eventsLock = new object();
+        private readonly Dictionary<Guid, BindOperation> bindOperations = new Dictionary<Guid, BindOperation>();
+
+        public BindOperation[] WaitAndGetEventsForAssembly(string simpleName, int waitTimeoutInMs = 10000)
+        {
+            const int waitIntervalInMs = 50;
+            int timeWaitedInMs = 0;
+            do
+            {
+                lock (eventsLock)
+                {
+                    var events = bindOperations.Values.Where(e => e.Completed && e.AssemblyName.Name == simpleName && !e.Nested);
+                    if (events.Any())
+                    {
+                        return events.ToArray();
+                    }
+                }
+
+                Thread.Sleep(waitIntervalInMs);
+                timeWaitedInMs += waitIntervalInMs;
+            } while (timeWaitedInMs < waitTimeoutInMs);
+
+            throw new TimeoutException($"Timed out waiting for bind events for {simpleName}");
+        }
+
+        protected override void OnEventSourceCreated(EventSource eventSource)
+        {
+            if (eventSource.Name == "Microsoft-Windows-DotNETRuntime")
+            {
+                EnableEvents(eventSource, EventLevel.Verbose, AssemblyLoaderKeyword);
+            }
+            else if (eventSource.Name == "System.Threading.Tasks.TplEventSource")
+            {
+                EnableEvents(eventSource, EventLevel.Verbose, TasksFlowActivityIds);
+            }
+        }
+
+        protected override void OnEventWritten(EventWrittenEventArgs data)
+        {
+            if (data.EventSource.Name != "Microsoft-Windows-DotNETRuntime")
+                return;
+
+            object GetData(string name) => data.Payload[data.PayloadNames.IndexOf(name)];
+            string GetDataString(string name) => GetData(name).ToString();
+
+            switch (data.EventName)
+            {
+                case "AssemblyLoadStart":
+                    lock (eventsLock)
+                    {
+                        Assert.True(!bindOperations.ContainsKey(data.ActivityId), "AssemblyLoadStart should not exist for same activity ID ");
+                        var bindOperation = new BindOperation()
+                        {
+                            AssemblyName = new AssemblyName(GetDataString("AssemblyName")),
+                            ActivityId = data.ActivityId,
+                            ParentActivityId = data.RelatedActivityId,
+                            Nested = bindOperations.ContainsKey(data.RelatedActivityId)
+                        };
+                        bindOperations.Add(data.ActivityId, bindOperation);
+                    }
+                    break;
+                case "AssemblyLoadStop":
+                    lock (eventsLock)
+                    {
+                        Assert.True(bindOperations.ContainsKey(data.ActivityId), "AssemblyLoadStop should have a matching AssemblyLoadStart");
+                        bindOperations[data.ActivityId].Success = (bool)GetData("Success");
+                        bindOperations[data.ActivityId].Completed = true;
+                    }
+                    break;
+            }
+        }
+    }
+}
diff --git a/src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.cs b/src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.cs
new file mode 100644 (file)
index 0000000..a450cd0
--- /dev/null
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Loader;
+
+using Assert = Xunit.Assert;
+
+namespace BinderTracingTests
+{
+    class BinderTracingTest
+    {
+        public static void PlatformAssembly_DefaultALC()
+        {
+            Console.WriteLine($"Running {nameof(PlatformAssembly_DefaultALC)}...");
+            using (var listener = new BinderEventListener())
+            {
+                string assemblyName = "System.Xml";
+                Assembly asm = Assembly.Load(assemblyName);
+
+                BindOperation[] binds = listener.WaitAndGetEventsForAssembly(assemblyName);
+                Assert.True(binds.Length == 1, $"Bind count for {assemblyName} - expected: 1, actual: {binds.Length}");
+                BindOperation bind = binds[0];
+                Assert.True(bind.Success, $"Expected bind for {assemblyName} to succeed");
+            }
+        }
+
+        public static void NonExistentAssembly_DefaultALC()
+        {
+            Console.WriteLine($"Running {nameof(NonExistentAssembly_DefaultALC)}...");
+            using (var listener = new BinderEventListener())
+            {
+                string assemblyName = "DoesNotExist";
+                try
+                {
+                    Assembly.Load(assemblyName);
+                }
+                catch { }
+
+                BindOperation[] binds = listener.WaitAndGetEventsForAssembly(assemblyName);
+                Assert.True(binds.Length == 1, $"Bind event count for {assemblyName} - expected: 1, actual: {binds.Length}");
+                BindOperation bind = binds[0];
+                Assert.False(bind.Success, $"Expected bind for {assemblyName} to fail");
+            }
+        }
+
+        public static int Main(string[] unused)
+        {
+            try
+            {
+                PlatformAssembly_DefaultALC();
+                NonExistentAssembly_DefaultALC();
+            }
+            catch (Exception e)
+            {
+                Console.WriteLine($"Test Failure: {e}");
+                return 101;
+            }
+
+            return 100;
+        }
+    }
+}
diff --git a/src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.csproj b/src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.csproj
new file mode 100644 (file)
index 0000000..286fec5
--- /dev/null
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="BinderTracingTest.cs" />
+    <Compile Include="BinderEventListener.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="$(SourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+  </ItemGroup>
+</Project>
index 8ee2cc19fbc8f7ed2c7f9c32b25fcc8513d19b79..e55e81c4ce90abbf383df9317e9b89cc21925e43 100644 (file)
@@ -588,6 +588,25 @@ namespace System.Diagnostics.Tracing
             // currently we do nothing, as that seems better than setting to Guid.Emtpy.
         }
 
+        // Assembly load runtime activity name
+        private const string AssemblyLoadName = "AssemblyLoad";
+
+        /// <summary>
+        /// Called by the runtime to start an assembly load activity
+        /// </summary>
+        private static void StartAssemblyLoad(ref Guid activityId, ref Guid relatedActivityId)
+        {
+            Instance.OnStart(NativeRuntimeEventSource.Log.Name, AssemblyLoadName, 0, ref activityId, ref relatedActivityId, EventActivityOptions.Recursive);
+        }
+
+        /// <summary>
+        /// Called by the runtime to stop an assembly load activity
+        /// </summary>
+        private static void StopAssemblyLoad(ref Guid activityId)
+        {
+            Instance.OnStop(NativeRuntimeEventSource.Log.Name, AssemblyLoadName, 0, ref activityId);
+        }
+
         /// <summary>
         /// Async local variables have the property that the are automatically copied whenever a task is created and used
         /// while that task is running.   Thus m_current 'flows' to any task that is caused by the current thread that