[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;
assemblyidentitycache.cpp
coreclrbindercommon.cpp
fusionassemblyname.cpp
+ bindertracing.cpp
)
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
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
)
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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__
--- /dev/null
+// 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__
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" />
#include "stringarraylist.h"
+#include "../binder/inc/bindertracing.h"
#include "../binder/inc/clrprivbindercoreclr.h"
-
#include "clrprivtypecachewinrt.h"
// this file handles string conversion errors for itself
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
}
_ASSERTE((FAILED(hr) && !fThrowOnFileNotFound) || pAssembly != nullptr);
+ bindOperation.SetResult(pAssembly.GetValue());
return pAssembly.Extract();
}
else
result->AddRef();
}
+ bindOperation.SetResult(result.GetValue());
return result.Extract();
}
else
#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,
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();
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();
return metadata != nullptr;
}
+
+// static
+FCIMPL0(FC_BOOL_RET, AssemblyNative::IsTracingEnabled)
+{
+ FCALL_CONTRACT;
+
+ FC_RETURN_BOOL(BinderTracing::IsEnabled());
+}
+FCIMPLEND
CLR_BOOL fThrowOnFileNotFound,
AssemblyLoadContextBaseObject *assemblyLoadContextUNSAFE);
+ static FCDECL0(FC_BOOL_RET, IsTracingEnabled);
+
//
// instance FCALLs
//
// 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
{
void SetName(LPCSTR szName);
void SetName(SString const & ssName);
- LPCWSTR GetCodeBase();
+ LPCWSTR GetCodeBase() const;
void SetCodeBase(LPCWSTR szCodeBase);
VOID SetCulture(LPCSTR szCulture);
BOOL IsStrongNamed() const;
BOOL HasPublicKey() const;
BOOL HasPublicKeyToken() const;
- BOOL IsMscorlibSatellite();
+ BOOL IsMscorlibSatellite() const;
BOOL IsMscorlib();
//
m_wszCodeBase=szCodeBase;
}
-inline LPCWSTR BaseAssemblySpec::GetCodeBase()
+inline LPCWSTR BaseAssemblySpec::GetCodeBase() const
{
LIMITED_METHOD_CONTRACT;
return m_wszCodeBase;
QCFuncElement("InternalSetProfileRoot", MultiCoreJITNative::InternalSetProfileRoot)
QCFuncElement("InternalStartProfile", MultiCoreJITNative::InternalStartProfile)
#endif // defined(FEATURE_MULTICOREJIT)
+ FCFuncElement("IsTracingEnabled", AssemblyNative::IsTracingEnabled)
FCFuncEnd()
FCFuncStart(gAssemblyNameFuncs)
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))
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)
#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"
--- /dev/null
+// 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;
+ }
+ }
+ }
+}
--- /dev/null
+// 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;
+ }
+ }
+}
--- /dev/null
+<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>
// 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