Add tracing for Resolving/AssemblyResolve event handler invocation (dotnet/coreclr...
authorElinor Fung <47805090+elinor-fung@users.noreply.github.com>
Tue, 12 Nov 2019 03:55:30 +0000 (19:55 -0800)
committerGitHub <noreply@github.com>
Tue, 12 Nov 2019 03:55:30 +0000 (19:55 -0800)
Commit migrated from https://github.com/dotnet/coreclr/commit/ba0ab50cccb1e04f4e209cb426fcf05952c4fad0

src/coreclr/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs
src/coreclr/src/vm/ClrEtwAll.man
src/coreclr/src/vm/assemblynative.cpp
src/coreclr/src/vm/assemblynative.hpp
src/coreclr/src/vm/ecalllist.h
src/coreclr/tests/src/Loader/binding/tracing/AssemblyToLoad.csproj
src/coreclr/tests/src/Loader/binding/tracing/BinderEventListener.cs
src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.EventHandlers.cs [new file with mode: 0644]
src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.cs
src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.csproj
src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs

index c50cdc72f28b8b9eb29887c9dc2786ebc4442c03..0700ec302190589bcfe9005774cf5b9053f19f9f 100644 (file)
@@ -35,6 +35,12 @@ namespace System.Runtime.Loader
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern bool IsTracingEnabled();
 
+        [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
+        internal static extern bool TraceResolvingHandlerInvoked(string assemblyName, string handlerName, string? alcName, string? resultAssemblyName, string? resultAssemblyPath);
+
+        [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
+        internal static extern bool TraceAssemblyResolveHandlerInvoked(string assemblyName, string handlerName, string? resultAssemblyName, string? resultAssemblyPath);
+
         private Assembly InternalLoadFromPath(string? assemblyPath, string? nativeImagePath)
         {
             RuntimeAssembly? loadedAssembly = null;
index 286575dde9eebb2a972fe61cc88638efd19e7392..2ca30d119355a59bd8d1e3150a86dbb069ab8b61 100644 (file)
                           value="32" eventGUID="{BCF2339E-B0A6-452D-966C-33AC9DD82573}"
                           message="$(string.RuntimePublisher.AssemblyLoaderTaskMessage)">
                         <opcodes>
+                            <opcode name="AssemblyLoadContextResolvingHandlerInvoked" message="$(string.RuntimePublisher.AssemblyLoadContextResolvingHandlerInvokedOpcodeMessage)" symbol="CLR_ALC_RESOLVING_HANDLER_INVOKED_OPCODE" value="12"/>
+                            <opcode name="AppDomainAssemblyResolveHandlerInvoked" message="$(string.RuntimePublisher.AppDomainAssemblyResolveHandlerInvokedOpcodeMessage)" symbol="CLR_APPDOMAIN_ASSEMBLY_RESOLVE_HANDLER_INVOKED_OPCODE" value="13"/>
                         </opcodes>
                     </task>
                 <!--Next available ID is 33-->
                         </UserData>
                     </template>
 
+                    <template tid="AssemblyLoadContextResolvingHandlerInvoked">
+                        <data name="ClrInstanceID" inType="win:UInt16" />
+                        <data name="AssemblyName" inType="win:UnicodeString" />
+                        <data name="HandlerName" inType="win:UnicodeString" />
+                        <data name="AssemblyLoadContext" inType="win:UnicodeString" />
+                        <data name="ResultAssemblyName" inType="win:UnicodeString" />
+                        <data name="ResultAssemblyPath" inType="win:UnicodeString" />
+                        <UserData>
+                            <AssemblyLoadContextResolvingHandlerInvoked xmlns="myNs">
+                                <ClrInstanceID> %1 </ClrInstanceID>
+                                <AssemblyName> %2 </AssemblyName>
+                                <HandlerName> %3 </HandlerName>
+                                <AssemblyLoadContext> %4 </AssemblyLoadContext>
+                                <ResultAssemblyName> %5 </ResultAssemblyName>
+                                <ResultAssemblyPath> %6 </ResultAssemblyPath>
+                            </AssemblyLoadContextResolvingHandlerInvoked>
+                        </UserData>
+                    </template>
+
+                    <template tid="AppDomainAssemblyResolveHandlerInvoked">
+                        <data name="ClrInstanceID" inType="win:UInt16" />
+                        <data name="AssemblyName" inType="win:UnicodeString" />
+                        <data name="HandlerName" inType="win:UnicodeString" />
+                        <data name="ResultAssemblyName" inType="win:UnicodeString" />
+                        <data name="ResultAssemblyPath" inType="win:UnicodeString" />
+                        <UserData>
+                            <AppDomainAssemblyResolveHandlerInvoked xmlns="myNs">
+                                <ClrInstanceID> %1 </ClrInstanceID>
+                                <AssemblyName> %2 </AssemblyName>
+                                <HandlerName> %3 </HandlerName>
+                                <ResultAssemblyName> %4 </ResultAssemblyName>
+                                <ResultAssemblyPath> %5 </ResultAssemblyPath>
+                            </AppDomainAssemblyResolveHandlerInvoked>
+                        </UserData>
+                    </template>
+
                     <template tid="MethodLoadUnload">
                         <data name="MethodID" inType="win:UInt64" outType="win:HexInt64" />
                         <data name="ModuleID" inType="win:UInt64" outType="win:HexInt64" />
                            task="AssemblyLoader"
                            symbol="AssemblyLoadStop" message="$(string.RuntimePublisher.AssemblyLoadStopEventMessage)"/>
 
+                    <event value="293" version="0" level="win:Informational"  template="AssemblyLoadContextResolvingHandlerInvoked"
+                           keywords ="AssemblyLoaderKeyword" opcode="AssemblyLoadContextResolvingHandlerInvoked"
+                           task="AssemblyLoader"
+                           symbol="AssemblyLoadContextResolvingHandlerInvoked" message="$(string.RuntimePublisher.AssemblyLoadContextResolvingHandlerInvokedEventMessage)"/>
+
+                    <event value="294" version="0" level="win:Informational"  template="AppDomainAssemblyResolveHandlerInvoked"
+                           keywords ="AssemblyLoaderKeyword" opcode="AppDomainAssemblyResolveHandlerInvoked"
+                           task="AssemblyLoader"
+                           symbol="AppDomainAssemblyResolveHandlerInvoked" message="$(string.RuntimePublisher.AppDomainAssemblyResolveHandlerInvokedEventMessage)"/>
+
                 </events>
             </provider>
 
                 <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;%nRequestingAssemblyLoadContext=%6" />
                 <string id="RuntimePublisher.AssemblyLoadStopEventMessage" value="ClrInstanceID=%1;%nAssemblyName=%2;%nAssemblyPath=%3;%nRequestingAssembly=%4;%nAssemblyLoadContext=%5;%nRequestingAssemblyLoadContext=%6;%nSuccess=%7;%nResultAssemblyName=%8;%nResultAssemblyPath=%9;%nCached=%10" />
+                <string id="RuntimePublisher.AssemblyLoadContextResolvingHandlerInvokedEventMessage" value="ClrInstanceID=%1;%nAssemblyName=%2;%nHandlerName=%3;%nAssemblyLoadContext=%4;%nResultAssemblyName=%5;%nResultAssemblyPath=%6" />
+                <string id="RuntimePublisher.AppDomainAssemblyResolveHandlerInvokedEventMessage" value="ClrInstanceID=%1;%nAssemblyName=%2;%nHandlerName=%3;%nResultAssemblyName=%4;%nResultAssemblyPath=%5" />
                 <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.TieredCompilationPauseOpcodeMessage" value="Pause" />
                 <string id="RuntimePublisher.TieredCompilationResumeOpcodeMessage" value="Resume" />
 
+                <string id="RuntimePublisher.AssemblyLoadContextResolvingHandlerInvokedOpcodeMessage" value="AssemblyLoadContextResolvingHandlerInvoked" />
+                <string id="RuntimePublisher.AppDomainAssemblyResolveHandlerInvokedOpcodeMessage" value="AppDomainAssemblyResolveHandlerInvoked" />
+
                 <string id="RundownPublisher.MethodDCStartOpcodeMessage" value="DCStart" />
                 <string id="RundownPublisher.MethodDCEndOpcodeMessage" value="DCStop" />
                 <string id="RundownPublisher.MethodDCStartVerboseOpcodeMessage" value="DCStartVerbose" />
index 341de82c42856c2162fd0f36b6d7d16f78fda998..2b085685bdffec34b4cc8c1b924503c802b1ef1d 100644 (file)
@@ -1428,3 +1428,27 @@ FCIMPL0(FC_BOOL_RET, AssemblyNative::IsTracingEnabled)
     FC_RETURN_BOOL(BinderTracing::IsEnabled());
 }
 FCIMPLEND
+
+// static
+void QCALLTYPE AssemblyNative::TraceResolvingHandlerInvoked(LPCWSTR assemblyName, LPCWSTR handlerName, LPCWSTR alcName, LPCWSTR resultAssemblyName, LPCWSTR resultAssemblyPath)
+{
+    QCALL_CONTRACT;
+
+    BEGIN_QCALL;
+
+    FireEtwAssemblyLoadContextResolvingHandlerInvoked(GetClrInstanceId(), assemblyName, handlerName, alcName, resultAssemblyName, resultAssemblyPath);
+
+    END_QCALL;
+}
+
+// static
+void QCALLTYPE AssemblyNative::TraceAssemblyResolveHandlerInvoked(LPCWSTR assemblyName, LPCWSTR handlerName, LPCWSTR resultAssemblyName, LPCWSTR resultAssemblyPath)
+{
+    QCALL_CONTRACT;
+
+    BEGIN_QCALL;
+
+    FireEtwAppDomainAssemblyResolveHandlerInvoked(GetClrInstanceId(), assemblyName, handlerName, resultAssemblyName, resultAssemblyPath);
+
+    END_QCALL;
+}
\ No newline at end of file
index 8906813ac4fc9081223173b51418f54b7bae0edb..7f5466158de859d1ea65cdabe95124af011fc253 100644 (file)
@@ -133,6 +133,8 @@ public:
     static INT_PTR QCALLTYPE GetLoadContextForAssembly(QCall::AssemblyHandle pAssembly);
 
     static BOOL QCALLTYPE InternalTryGetRawMetadata(QCall::AssemblyHandle assembly, UINT8 **blobRef, INT32 *lengthRef);
+    static void QCALLTYPE TraceResolvingHandlerInvoked(LPCWSTR assemblyName, LPCWSTR handlerName, LPCWSTR alcName, LPCWSTR resultAssemblyName, LPCWSTR resultAssemblyPath);
+    static void QCALLTYPE TraceAssemblyResolveHandlerInvoked(LPCWSTR assemblyName, LPCWSTR handlerName, LPCWSTR resultAssemblyName, LPCWSTR resultAssemblyPath);
 };
 
 #endif
index 278e4290ad4e2e8dae3981a0ec821210391e0670..6fedfc8984ac21575026d4d4236efc71a81cb348 100644 (file)
@@ -513,6 +513,8 @@ FCFuncStart(gAssemblyLoadContextFuncs)
     QCFuncElement("InternalStartProfile",   MultiCoreJITNative::InternalStartProfile)
 #endif // defined(FEATURE_MULTICOREJIT)
     FCFuncElement("IsTracingEnabled", AssemblyNative::IsTracingEnabled)
+    QCFuncElement("TraceResolvingHandlerInvoked", AssemblyNative::TraceResolvingHandlerInvoked)
+    QCFuncElement("TraceAssemblyResolveHandlerInvoked", AssemblyNative::TraceAssemblyResolveHandlerInvoked)
 FCFuncEnd()
 
 FCFuncStart(gAssemblyNameFuncs)
index bf4c48ed617f6782436896f0c7c40cdc2f52eae9..278cdfe3337b54bad8297eb704733b495250ee5a 100644 (file)
@@ -2,6 +2,8 @@
   <PropertyGroup>
     <OutputType>Library</OutputType>
     <CLRTestKind>BuildOnly</CLRTestKind>
+    <AssemblyName Condition="'$(AssemblyNameSuffix)'!=''">$(AssemblyName)_$(AssemblyNameSuffix)</AssemblyName>
+    <CleanFile>$(AssemblyName).FileListAbsolute.txt</CleanFile>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="AssemblyToLoad.cs" />
index 5a8e173f5cae5bf66a3302bb225768dec559c60e..5d07fe77248819f6e9d11264f9dd0049330586af 100644 (file)
@@ -15,22 +15,65 @@ namespace BinderTracingTests
 {
     internal class BindOperation
     {
-        internal AssemblyName AssemblyName;
-        internal string AssemblyPath;
-        internal AssemblyName RequestingAssembly;
-        internal string AssemblyLoadContext;
-        internal string RequestingAssemblyLoadContext;
-
-        internal bool Success;
-        internal AssemblyName ResultAssemblyName;
-        internal string ResultAssemblyPath;
-        internal bool Cached;
-
-        internal Guid ActivityId;
-        internal Guid ParentActivityId;
-
-        internal bool Completed;
-        internal bool Nested;
+        public AssemblyName AssemblyName { get; internal set; }
+        public string AssemblyPath { get; internal set; }
+        public AssemblyName RequestingAssembly { get; internal set; }
+        public string AssemblyLoadContext { get; internal set; }
+        public string RequestingAssemblyLoadContext { get; internal set; }
+
+        public bool Success { get; internal set; }
+        public AssemblyName ResultAssemblyName { get; internal set; }
+        public string ResultAssemblyPath { get; internal set; }
+        public bool Cached { get; internal set; }
+
+        public Guid ActivityId { get; internal set; }
+        public Guid ParentActivityId { get; internal set; }
+
+        public bool Completed { get; internal set; }
+        public bool Nested { get; internal set; }
+
+        public List<HandlerInvocation> AssemblyLoadContextResolvingHandlers { get; internal set; }
+        public List<HandlerInvocation> AppDomainAssemblyResolveHandlers { get; internal set; }
+
+        public List<BindOperation> NestedBinds { get; internal set; }
+
+        public BindOperation()
+        {
+            AssemblyLoadContextResolvingHandlers = new List<HandlerInvocation>();
+            AppDomainAssemblyResolveHandlers = new List<HandlerInvocation>();
+            NestedBinds = new List<BindOperation>();
+        }
+
+        public override string ToString()
+        {
+            var sb = new System.Text.StringBuilder();
+            sb.Append(AssemblyName);
+            sb.Append($" - Request: Path={AssemblyPath}, ALC={AssemblyLoadContext}, RequestingAssembly={RequestingAssembly}, RequestingALC={RequestingAssemblyLoadContext}");
+            sb.Append($" - Result: Success={Success}, Name={ResultAssemblyName}, Path={ResultAssemblyPath}, Cached={Cached}");
+            return sb.ToString();
+        }
+    }
+
+    internal class HandlerInvocation
+    {
+        public AssemblyName AssemblyName { get; internal set; }
+        public string HandlerName { get; internal set; }
+        public string AssemblyLoadContext { get; internal set; }
+
+        public AssemblyName ResultAssemblyName { get; internal set; }
+        public string ResultAssemblyPath { get; internal set; }
+
+        public override string ToString()
+        {
+            var sb = new System.Text.StringBuilder();
+            sb.Append($"{HandlerName} - ");
+            sb.Append($"Request: Name={AssemblyName.FullName}");
+            if (!string.IsNullOrEmpty(AssemblyLoadContext))
+                sb.Append($", ALC={AssemblyLoadContext}");
+
+            sb.Append($" - Result: Name={ResultAssemblyName?.FullName}, Path={ResultAssemblyPath}");
+            return sb.ToString();
+        }
     }
 
     internal sealed class BinderEventListener : EventListener
@@ -80,34 +123,32 @@ namespace BinderTracingTests
             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();
+            object GetData(string name)
+            {
+                int index = data.PayloadNames.IndexOf(name);
+                return index >= 0 ? data.Payload[index] : null;
+            };
+            string GetDataString(string name) => GetData(name) as string;
 
             switch (data.EventName)
             {
                 case "AssemblyLoadStart":
+                {
+                    BindOperation bindOperation = ParseAssemblyLoadStartEvent(data, GetDataString);
                     lock (eventsLock)
                     {
                         Assert.IsTrue(!bindOperations.ContainsKey(data.ActivityId), "AssemblyLoadStart should not exist for same activity ID ");
-                        var bindOperation = new BindOperation()
-                        {
-                            AssemblyName = new AssemblyName(GetDataString("AssemblyName")),
-                            AssemblyPath = GetDataString("AssemblyPath"),
-                            AssemblyLoadContext = GetDataString("AssemblyLoadContext"),
-                            RequestingAssemblyLoadContext = GetDataString("RequestingAssemblyLoadContext"),
-                            ActivityId = data.ActivityId,
-                            ParentActivityId = data.RelatedActivityId,
-                            Nested = bindOperations.ContainsKey(data.RelatedActivityId)
-                        };
-                        string requestingAssembly = GetDataString("RequestingAssembly");
-                        if (!string.IsNullOrEmpty(requestingAssembly))
+                        bindOperation.Nested = bindOperations.ContainsKey(data.RelatedActivityId);
+                        bindOperations.Add(data.ActivityId, bindOperation);
+                        if (bindOperation.Nested)
                         {
-                            bindOperation.RequestingAssembly = new AssemblyName(requestingAssembly);
+                            bindOperations[data.RelatedActivityId].NestedBinds.Add(bindOperation);
                         }
-                        bindOperations.Add(data.ActivityId, bindOperation);
                     }
                     break;
+                }
                 case "AssemblyLoadStop":
+                {
                     lock (eventsLock)
                     {
                         Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), "AssemblyLoadStop should have a matching AssemblyBindStart");
@@ -123,7 +164,68 @@ namespace BinderTracingTests
                         bind.Completed = true;
                     }
                     break;
+                }
+                case "AssemblyLoadContextResolvingHandlerInvoked":
+                {
+                    HandlerInvocation handlerInvocation = ParseHandlerInvokedEvent(GetDataString);
+                    lock (eventsLock)
+                    {
+                        Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), "AssemblyLoadContextResolvingHandlerInvoked should have a matching AssemblyBindStart");
+                        BindOperation bind = bindOperations[data.ActivityId];
+                        bind.AssemblyLoadContextResolvingHandlers.Add(handlerInvocation);
+                    }
+                    break;
+                }
+                case "AppDomainAssemblyResolveHandlerInvoked":
+                {
+                    HandlerInvocation handlerInvocation = ParseHandlerInvokedEvent(GetDataString);
+                    lock (eventsLock)
+                    {
+                        Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), "AppDomainAssemblyResolveHandlerInvoked should have a matching AssemblyBindStart");
+                        BindOperation bind = bindOperations[data.ActivityId];
+                        bind.AppDomainAssemblyResolveHandlers.Add(handlerInvocation);
+                    }
+                    break;
+                }
             }
         }
+
+        private BindOperation ParseAssemblyLoadStartEvent(EventWrittenEventArgs data, Func<string, string> getDataString)
+        {
+            var bindOperation = new BindOperation()
+            {
+                AssemblyName = new AssemblyName(getDataString("AssemblyName")),
+                AssemblyPath = getDataString("AssemblyPath"),
+                AssemblyLoadContext = getDataString("AssemblyLoadContext"),
+                RequestingAssemblyLoadContext = getDataString("RequestingAssemblyLoadContext"),
+                ActivityId = data.ActivityId,
+                ParentActivityId = data.RelatedActivityId,
+            };
+            string requestingAssembly = getDataString("RequestingAssembly");
+            if (!string.IsNullOrEmpty(requestingAssembly))
+            {
+                bindOperation.RequestingAssembly = new AssemblyName(requestingAssembly);
+            }
+
+            return bindOperation;
+        }
+
+        private HandlerInvocation ParseHandlerInvokedEvent(Func<string, string> getDataString)
+        {
+            var handlerInvocation = new HandlerInvocation()
+            {
+                AssemblyName = new AssemblyName(getDataString("AssemblyName")),
+                HandlerName = getDataString("HandlerName"),
+                AssemblyLoadContext = getDataString("AssemblyLoadContext"),
+                ResultAssemblyPath = getDataString("ResultAssemblyPath")
+            };
+            string resultName = getDataString("ResultAssemblyName");
+            if (!string.IsNullOrEmpty(resultName))
+            {
+                handlerInvocation.ResultAssemblyName = new AssemblyName(resultName);
+            }
+
+            return handlerInvocation;
+        }
     }
 }
diff --git a/src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.EventHandlers.cs b/src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.EventHandlers.cs
new file mode 100644 (file)
index 0000000..bf97a43
--- /dev/null
@@ -0,0 +1,334 @@
+// 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;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Loader;
+
+using TestLibrary;
+
+namespace BinderTracingTests
+{
+    partial class BinderTracingTest
+    {
+        [BinderTest]
+        public static BindOperation AssemblyLoadContextResolving_ReturnNull()
+        {
+            var assemblyName = new AssemblyName(SubdirectoryAssemblyName);
+            using (var handlers = new Handlers(HandlerReturn.Null, AssemblyLoadContext.Default))
+            {
+                try
+                {
+                    Assembly.Load(assemblyName);
+                }
+                catch { }
+
+                Assert.AreEqual(1, handlers.Invocations.Count);
+                Assert.AreEqual(0, handlers.Binds.Count);
+                return new BindOperation()
+                {
+                    AssemblyName = assemblyName,
+                    AssemblyLoadContext = DefaultALC,
+                    RequestingAssembly = Assembly.GetExecutingAssembly().GetName(),
+                    RequestingAssemblyLoadContext = DefaultALC,
+                    Success = false,
+                    Cached = false,
+                    AssemblyLoadContextResolvingHandlers = handlers.Invocations,
+                    NestedBinds = handlers.Binds
+                };
+            }
+        }
+
+        [BinderTest]
+        public static BindOperation AssemblyLoadContextResolving_LoadAssembly()
+        {
+            var assemblyName = new AssemblyName(SubdirectoryAssemblyName);
+            CustomALC alc = new CustomALC(nameof(AssemblyLoadContextResolving_LoadAssembly));
+            using (var handlers = new Handlers(HandlerReturn.RequestedAssembly, alc))
+            {
+                Assembly asm = alc.LoadFromAssemblyName(assemblyName);
+
+                Assert.AreEqual(1, handlers.Invocations.Count);
+                Assert.AreEqual(1, handlers.Binds.Count);
+                return new BindOperation()
+                {
+                    AssemblyName = assemblyName,
+                    AssemblyLoadContext = alc.ToString(),
+                    Success = true,
+                    ResultAssemblyName = asm.GetName(),
+                    ResultAssemblyPath = asm.Location,
+                    Cached = false,
+                    AssemblyLoadContextResolvingHandlers = handlers.Invocations,
+                    NestedBinds = handlers.Binds
+                };
+            }
+        }
+
+        [BinderTest]
+        public static BindOperation AssemblyLoadContextResolving_NameMismatch()
+        {
+            var assemblyName = new AssemblyName(SubdirectoryAssemblyName);
+            CustomALC alc = new CustomALC(nameof(AssemblyLoadContextResolving_NameMismatch));
+            using (var handlers = new Handlers(HandlerReturn.NameMismatch, alc))
+            {
+                Assert.Throws<FileLoadException>(() => alc.LoadFromAssemblyName(assemblyName));
+
+                Assert.AreEqual(1, handlers.Invocations.Count);
+                Assert.AreEqual(1, handlers.Binds.Count);
+                return new BindOperation()
+                {
+                    AssemblyName = assemblyName,
+                    AssemblyLoadContext = alc.ToString(),
+                    Success = false,
+                    Cached = false,
+                    AssemblyLoadContextResolvingHandlers = handlers.Invocations,
+                    NestedBinds = handlers.Binds
+                };
+            }
+        }
+
+        [BinderTest]
+        public static BindOperation AssemblyLoadContextResolving_MultipleHandlers()
+        {
+            var assemblyName = new AssemblyName(SubdirectoryAssemblyName);
+            CustomALC alc = new CustomALC(nameof(AssemblyLoadContextResolving_NameMismatch));
+            using (var handlerNull = new Handlers(HandlerReturn.Null, alc))
+            using (var handlerLoad = new Handlers(HandlerReturn.RequestedAssembly, alc))
+            {
+                Assembly asm = alc.LoadFromAssemblyName(assemblyName);
+
+                Assert.AreEqual(1, handlerNull.Invocations.Count);
+                Assert.AreEqual(0, handlerNull.Binds.Count);
+                Assert.AreEqual(1, handlerLoad.Invocations.Count);
+                Assert.AreEqual(1, handlerLoad.Binds.Count);
+                return new BindOperation()
+                {
+                    AssemblyName = assemblyName,
+                    AssemblyLoadContext = alc.ToString(),
+                    Success = true,
+                    ResultAssemblyName = asm.GetName(),
+                    ResultAssemblyPath = asm.Location,
+                    Cached = false,
+                    AssemblyLoadContextResolvingHandlers = handlerNull.Invocations.Concat(handlerLoad.Invocations).ToList(),
+                    NestedBinds = handlerNull.Binds.Concat(handlerLoad.Binds).ToList()
+                };
+            }
+        }
+
+        [BinderTest]
+        public static BindOperation AppDomainAssemblyResolve_ReturnNull()
+        {
+            var assemblyName = new AssemblyName(SubdirectoryAssemblyName);
+            using (var handlers = new Handlers(HandlerReturn.Null))
+            {
+                try
+                {
+                    Assembly.Load(assemblyName);
+                }
+                catch { }
+
+                Assert.AreEqual(1, handlers.Invocations.Count);
+                Assert.AreEqual(0, handlers.Binds.Count);
+                return new BindOperation()
+                {
+                    AssemblyName = assemblyName,
+                    AssemblyLoadContext = DefaultALC,
+                    RequestingAssembly = Assembly.GetExecutingAssembly().GetName(),
+                    RequestingAssemblyLoadContext = DefaultALC,
+                    Success = false,
+                    Cached = false,
+                    AppDomainAssemblyResolveHandlers = handlers.Invocations,
+                    NestedBinds = handlers.Binds
+                };
+            }
+        }
+
+        [BinderTest]
+        public static BindOperation AppDomainAssemblyResolve_LoadAssembly()
+        {
+            var assemblyName = new AssemblyName(SubdirectoryAssemblyName);
+            CustomALC alc = new CustomALC(nameof(AppDomainAssemblyResolve_LoadAssembly));
+            using (var handlers = new Handlers(HandlerReturn.RequestedAssembly))
+            {
+                Assembly asm = alc.LoadFromAssemblyName(assemblyName);
+
+                Assert.AreEqual(1, handlers.Invocations.Count);
+                Assert.AreEqual(1, handlers.Binds.Count);
+                return new BindOperation()
+                {
+                    AssemblyName = assemblyName,
+                    AssemblyLoadContext = alc.ToString(),
+                    Success = true,
+                    ResultAssemblyName = asm.GetName(),
+                    ResultAssemblyPath = asm.Location,
+                    Cached = false,
+                    AppDomainAssemblyResolveHandlers = handlers.Invocations,
+                    NestedBinds = handlers.Binds
+                };
+            }
+        }
+
+        [BinderTest]
+        public static BindOperation AppDomainAssemblyResolve_NameMismatch()
+        {
+            var assemblyName = new AssemblyName(SubdirectoryAssemblyName);
+            CustomALC alc = new CustomALC(nameof(AppDomainAssemblyResolve_NameMismatch));
+            using (var handlers = new Handlers(HandlerReturn.NameMismatch))
+            {
+                // Result of AssemblyResolve event does not get checked for name mismatch
+                Assembly asm = alc.LoadFromAssemblyName(assemblyName);
+
+                Assert.AreEqual(1, handlers.Invocations.Count);
+                Assert.AreEqual(1, handlers.Binds.Count);
+                return new BindOperation()
+                {
+                    AssemblyName = assemblyName,
+                    AssemblyLoadContext = alc.ToString(),
+                    Success = true,
+                    ResultAssemblyName = asm.GetName(),
+                    ResultAssemblyPath = asm.Location,
+                    Cached = false,
+                    AppDomainAssemblyResolveHandlers = handlers.Invocations,
+                    NestedBinds = handlers.Binds
+                };
+            }
+        }
+
+        [BinderTest]
+        public static BindOperation AppDomainAssemblyResolve_MultipleHandlers()
+        {
+            var assemblyName = new AssemblyName(SubdirectoryAssemblyName);
+            CustomALC alc = new CustomALC(nameof(AppDomainAssemblyResolve_LoadAssembly));
+            using (var handlerNull = new Handlers(HandlerReturn.Null))
+            using (var handlerLoad = new Handlers(HandlerReturn.RequestedAssembly))
+            {
+                Assembly asm = alc.LoadFromAssemblyName(assemblyName);
+
+                Assert.AreEqual(1, handlerNull.Invocations.Count);
+                Assert.AreEqual(0, handlerNull.Binds.Count);
+                Assert.AreEqual(1, handlerLoad.Invocations.Count);
+                Assert.AreEqual(1, handlerLoad.Binds.Count);
+                return new BindOperation()
+                {
+                    AssemblyName = assemblyName,
+                    AssemblyLoadContext = alc.ToString(),
+                    Success = true,
+                    ResultAssemblyName = asm.GetName(),
+                    ResultAssemblyPath = asm.Location,
+                    Cached = false,
+                    AppDomainAssemblyResolveHandlers = handlerNull.Invocations.Concat(handlerLoad.Invocations).ToList(),
+                    NestedBinds = handlerNull.Binds.Concat(handlerLoad.Binds).ToList()
+                };
+            }
+        }
+
+        private enum HandlerReturn
+        {
+            Null,
+            RequestedAssembly,
+            NameMismatch
+        }
+
+        private class Handlers : IDisposable
+        {
+            private HandlerReturn handlerReturn;
+            private AssemblyLoadContext alc;
+
+            internal readonly List<HandlerInvocation> Invocations = new List<HandlerInvocation>();
+            internal readonly List<BindOperation> Binds = new List<BindOperation>();
+
+            public Handlers(HandlerReturn handlerReturn)
+            {
+                this.handlerReturn = handlerReturn;
+                AppDomain.CurrentDomain.AssemblyResolve += OnAppDomainAssemblyResolve;
+            }
+
+            public Handlers(HandlerReturn handlerReturn, AssemblyLoadContext alc)
+            {
+                this.handlerReturn = handlerReturn;
+                this.alc = alc;
+                this.alc.Resolving += OnAssemblyLoadContextResolving;
+            }
+
+            public void Dispose()
+            {
+                AppDomain.CurrentDomain.AssemblyResolve -= OnAppDomainAssemblyResolve;
+                if (alc != null)
+                    alc.Resolving -= OnAssemblyLoadContextResolving;
+            }
+
+            private Assembly OnAssemblyLoadContextResolving(AssemblyLoadContext context, AssemblyName assemblyName)
+            {
+                Assembly asm = ResolveAssembly(context, assemblyName);
+                var invocation = new HandlerInvocation()
+                {
+                    AssemblyName = assemblyName,
+                    HandlerName = nameof(OnAssemblyLoadContextResolving),
+                    AssemblyLoadContext = context == AssemblyLoadContext.Default ? context.Name : context.ToString(),
+                };
+                if (asm != null)
+                {
+                    invocation.ResultAssemblyName = asm.GetName();
+                    invocation.ResultAssemblyPath = asm.Location;
+                }
+
+                Invocations.Add(invocation);
+                return asm;
+            }
+
+            private Assembly OnAppDomainAssemblyResolve(object sender, ResolveEventArgs args)
+            {
+                var assemblyName = new AssemblyName(args.Name);
+                var customContext = new CustomALC(nameof(OnAppDomainAssemblyResolve));
+                Assembly asm = ResolveAssembly(customContext, assemblyName);
+                var invocation = new HandlerInvocation()
+                {
+                    AssemblyName = assemblyName,
+                    HandlerName = nameof(OnAppDomainAssemblyResolve),
+                };
+                if (asm != null)
+                {
+                    invocation.ResultAssemblyName = asm.GetName();
+                    invocation.ResultAssemblyPath = asm.Location;
+                }
+
+                Invocations.Add(invocation);
+                return asm;
+            }
+
+            private Assembly ResolveAssembly(AssemblyLoadContext context, AssemblyName assemblyName)
+            {
+                if (handlerReturn == HandlerReturn.Null)
+                    return null;
+
+                string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+                string fileName = handlerReturn == HandlerReturn.RequestedAssembly ? $"{assemblyName.Name}.dll" : $"{assemblyName.Name}Mismatch.dll";
+                string assemblyPath = Path.Combine(appPath, "DependentAssemblies", fileName);
+
+                if (!File.Exists(assemblyPath))
+                    return null;
+
+                Assembly asm = context.LoadFromAssemblyPath(assemblyPath);
+                var bind = new BindOperation()
+                {
+                    AssemblyName = asm.GetName(),
+                    AssemblyPath = assemblyPath,
+                    AssemblyLoadContext = context == AssemblyLoadContext.Default ? context.Name : context.ToString(),
+                    RequestingAssembly = CoreLibName,
+                    RequestingAssemblyLoadContext = DefaultALC,
+                    Success = true,
+                    ResultAssemblyName = asm.GetName(),
+                    ResultAssemblyPath = asm.Location,
+                    Cached = false
+                };
+                Binds.Add(bind);
+                return asm;
+            }
+        }
+    }
+}
index 9aee2c4bd90582226592dcf7a13c9574360f50d3..43733810ad5574556fb4bfdd1a6ebc9d0088c94a 100644 (file)
@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using System;
+using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
@@ -25,7 +26,7 @@ namespace BinderTracingTests
         }
     }
 
-    class BinderTracingTest
+    partial class BinderTracingTest
     {
         public class CustomALC : AssemblyLoadContext
         {
@@ -35,6 +36,9 @@ namespace BinderTracingTests
 
         private const string DefaultALC = "Default";
         private const string DependentAssemblyName = "AssemblyToLoad";
+        private const string SubdirectoryAssemblyName = "AssemblyToLoad_Subdirectory";
+
+        private static readonly AssemblyName CoreLibName = typeof(object).Assembly.GetName();
 
         [BinderTest]
         public static BindOperation LoadFile()
@@ -47,6 +51,7 @@ namespace BinderTracingTests
                 AssemblyName = executingAssembly.GetName(),
                 AssemblyPath = executingAssembly.Location,
                 AssemblyLoadContext = AssemblyLoadContext.GetLoadContext(asm).ToString(),
+                RequestingAssembly = CoreLibName,
                 RequestingAssemblyLoadContext = DefaultALC,
                 Success = true,
                 ResultAssemblyName = asm.GetName(),
@@ -66,6 +71,7 @@ namespace BinderTracingTests
             {
                 AssemblyName = executingAssembly.GetName(),
                 AssemblyLoadContext = AssemblyLoadContext.GetLoadContext(asm).ToString(),
+                RequestingAssembly = CoreLibName,
                 RequestingAssemblyLoadContext = DefaultALC,
                 Success = true,
                 ResultAssemblyName = asm.GetName(),
@@ -86,6 +92,7 @@ namespace BinderTracingTests
             {
                 AssemblyName = executingAssembly.GetName(),
                 AssemblyLoadContext = alc.ToString(),
+                RequestingAssembly = CoreLibName,
                 RequestingAssemblyLoadContext = DefaultALC,
                 Success = true,
                 ResultAssemblyName = asm.GetName(),
@@ -106,6 +113,7 @@ namespace BinderTracingTests
                 AssemblyName = executingAssembly.GetName(),
                 AssemblyPath = executingAssembly.Location,
                 AssemblyLoadContext = alc.ToString(),
+                RequestingAssembly = CoreLibName,
                 RequestingAssemblyLoadContext = DefaultALC,
                 Success = true,
                 ResultAssemblyName = asm.GetName(),
@@ -143,6 +151,7 @@ namespace BinderTracingTests
                 AssemblyName = executingAssembly.GetName(),
                 AssemblyPath = executingAssembly.Location,
                 AssemblyLoadContext = DefaultALC,
+                RequestingAssembly = CoreLibName,
                 RequestingAssemblyLoadContext = DefaultALC,
                 Success = true,
                 ResultAssemblyName = asm.GetName(),
@@ -154,13 +163,14 @@ namespace BinderTracingTests
         [BinderTest(isolate: true)]
         public static BindOperation PlatformAssembly()
         {
-            string assemblyName = "System.Xml";
+            var assemblyName = new AssemblyName("System.Xml");
             Assembly asm = Assembly.Load(assemblyName);
 
             return new BindOperation()
             {
-                AssemblyName = new AssemblyName(assemblyName),
+                AssemblyName = assemblyName,
                 AssemblyLoadContext = DefaultALC,
+                RequestingAssembly = Assembly.GetExecutingAssembly().GetName(),
                 RequestingAssemblyLoadContext = DefaultALC,
                 Success = true,
                 ResultAssemblyName = asm.GetName(),
@@ -191,6 +201,7 @@ namespace BinderTracingTests
             {
                 AssemblyName = new AssemblyName(assemblyName),
                 AssemblyLoadContext = DefaultALC,
+                RequestingAssembly = Assembly.GetExecutingAssembly().GetName(),
                 RequestingAssemblyLoadContext = DefaultALC,
                 Success = false,
                 Cached = false
@@ -206,8 +217,8 @@ namespace BinderTracingTests
             {
                 AssemblyName = new AssemblyName(DependentAssemblyName),
                 AssemblyLoadContext = DefaultALC,
-                RequestingAssemblyLoadContext = DefaultALC,
                 RequestingAssembly = Assembly.GetExecutingAssembly().GetName(),
+                RequestingAssemblyLoadContext = DefaultALC,
                 Success = true,
                 ResultAssemblyName = t.Assembly.GetName(),
                 ResultAssemblyPath = t.Assembly.Location,
@@ -368,7 +379,7 @@ namespace BinderTracingTests
                     // Run specific test - first argument should be the test method name
                     MethodInfo method = typeof(BinderTracingTest)
                         .GetMethod(args[0], BindingFlags.Public | BindingFlags.Static);
-                    Assert.IsTrue(method != null && method.GetCustomAttribute<BinderTestAttribute>() != null && method.ReturnType == typeof(BindOperation));
+                    Assert.IsTrue(method != null && method.GetCustomAttribute<BinderTestAttribute>() != null && method.ReturnType == typeof(BindOperation), "Invalid test muthod specified");
                     success = RunSingleTest(method);
                 }
             }
@@ -459,32 +470,79 @@ namespace BinderTracingTests
             Assert.IsTrue(binds.Length == 1, $"Bind event count for {assemblyName} - expected: 1, actual: {binds.Length}");
             BindOperation actual = binds[0];
 
+            ValidateBindOperation(expected, actual);
+        }
+
+        private static void ValidateBindOperation(BindOperation expected, BindOperation actual)
+        {
             ValidateAssemblyName(expected.AssemblyName, actual.AssemblyName, nameof(BindOperation.AssemblyName));
             Assert.AreEqual(expected.AssemblyPath ?? string.Empty, actual.AssemblyPath, $"Unexpected value for {nameof(BindOperation.AssemblyPath)} on event");
             Assert.AreEqual(expected.AssemblyLoadContext, actual.AssemblyLoadContext, $"Unexpected value for {nameof(BindOperation.AssemblyLoadContext)} on event");
-            ValidateAssemblyName(expected.RequestingAssembly, actual.RequestingAssembly, nameof(BindOperation.RequestingAssembly));
             Assert.AreEqual(expected.RequestingAssemblyLoadContext ?? string.Empty, actual.RequestingAssemblyLoadContext, $"Unexpected value for {nameof(BindOperation.RequestingAssemblyLoadContext)} on event");
+            ValidateAssemblyName(expected.RequestingAssembly, actual.RequestingAssembly, nameof(BindOperation.RequestingAssembly));
 
             Assert.AreEqual(expected.Success, actual.Success, $"Unexpected value for {nameof(BindOperation.Success)} on event");
             Assert.AreEqual(expected.ResultAssemblyPath ?? string.Empty, actual.ResultAssemblyPath, $"Unexpected value for {nameof(BindOperation.ResultAssemblyPath)} on event");
             Assert.AreEqual(expected.Cached, actual.Cached, $"Unexpected value for {nameof(BindOperation.Cached)} on event");
             ValidateAssemblyName(expected.ResultAssemblyName, actual.ResultAssemblyName, nameof(BindOperation.ResultAssemblyName));
+
+            ValidateHandlerInvocations(expected.AssemblyLoadContextResolvingHandlers, actual.AssemblyLoadContextResolvingHandlers, "AssemblyLoadContextResolving");
+            ValidateHandlerInvocations(expected.AppDomainAssemblyResolveHandlers, actual.AppDomainAssemblyResolveHandlers, "AppDomainAssemblyResolve");
+
+            ValidateNestedBinds(expected.NestedBinds, actual.NestedBinds);
+        }
+
+        private static bool AssemblyNamesMatch(AssemblyName name1, AssemblyName name2)
+        {
+            if (name1 == null || name2 == null)
+                return name1 == null && name2 == null;
+
+            return name1.Name == name2.Name
+                && ((name1.Version == null && name2.Version == null) || name1.Version == name2.Version)
+                && ((string.IsNullOrEmpty(name1.CultureName) && string.IsNullOrEmpty(name1.CultureName)) || name1.CultureName == name2.CultureName);
         }
 
         private static void ValidateAssemblyName(AssemblyName expected, AssemblyName actual, string propertyName)
         {
-            if (expected == null)
+            Assert.IsTrue(AssemblyNamesMatch(expected, actual), $"Unexpected value for {propertyName} on event - expected: {expected}, actual: {actual}");
+        }
+
+        private static void ValidateHandlerInvocations(List<HandlerInvocation> expected, List<HandlerInvocation> actual, string eventName)
+        {
+            Assert.AreEqual(expected.Count, actual.Count, $"Unexpected handler invocation count for {eventName}");
+
+            foreach (var match in expected)
             {
-                return;
+                Predicate<HandlerInvocation> pred = h =>
+                    AssemblyNamesMatch(h.AssemblyName, match.AssemblyName)
+                        && h.HandlerName == match.HandlerName
+                        && h.AssemblyLoadContext == match.AssemblyLoadContext
+                        && AssemblyNamesMatch(h.ResultAssemblyName, match.ResultAssemblyName)
+                        && h.ResultAssemblyPath == match.ResultAssemblyPath;
+                Assert.IsTrue(actual.Exists(pred), $"Handler invocation not found: {match.ToString()}");
             }
+        }
 
-            if (expected.Version != null)
+        private static bool BindOperationsMatch(BindOperation bind1, BindOperation bind2)
+        {
+            try
             {
-                Assert.AreEqual(expected.FullName, actual.FullName, $"Unexpected value for {propertyName} on event");
+                ValidateBindOperation(bind1, bind2);
             }
-            else
+            catch (AssertTestException e)
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        private static void ValidateNestedBinds(List<BindOperation> expected, List<BindOperation> actual)
+        {
+            foreach (var match in expected)
             {
-                Assert.AreEqual(expected.Name, actual.Name, $"Unexpected value for {propertyName} on event");
+                Predicate<BindOperation> pred = b => BindOperationsMatch(match, b);
+                Assert.IsTrue(actual.Exists(pred), $"Nested bind operation not found: {match.ToString()}");
             }
         }
     }
index f581bf126a0c29910e744beac2819abfd5174b81..0df6d4f8c76de8f35eb972a682badccf022ae005 100644 (file)
@@ -4,10 +4,34 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="BinderTracingTest.cs" />
+    <Compile Include="BinderTracingTest.EventHandlers.cs" />
     <Compile Include="BinderEventListener.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="$(SourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
     <ProjectReference Include="AssemblyToLoad.csproj" />
   </ItemGroup>
+  <ItemGroup>
+    <!-- Build the same AssemblyToLoad project with a different assembly name. These will be moved to a subdirectory
+         by the MoveDependentAssembliesToSubdirectory target, such that they will not be on the test's app path -->
+    <ProjectReference Include="AssemblyToLoad.csproj">
+      <Properties>AssemblyNameSuffix=Subdirectory</Properties>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+      <OutputItemType>Content</OutputItemType>
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </ProjectReference>
+    <ProjectReference Include="AssemblyToLoad.csproj">
+      <Properties>AssemblyNameSuffix=SubdirectoryMismatch</Properties>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+      <OutputItemType>Content</OutputItemType>
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </ProjectReference>
+  </ItemGroup>
+
+  <Target Name="MoveDependentAssembliesToSubdirectory" AfterTargets="CopyFilesToOutputDirectory">
+    <ItemGroup>
+      <AssembliesToMove Include="$(OutDir)/AssemblyToLoad_*.*" />
+    </ItemGroup>
+    <Move SourceFiles="@(AssembliesToMove)" DestinationFolder="$(OutDir)/DependentAssemblies" />
+  </Target>
 </Project>
index ef546319a6139c46d65d059b79864f09b64077db..5ae3f8d06f307fe659deb7245857a7ac79e3e838 100644 (file)
@@ -582,18 +582,29 @@ namespace System.Runtime.Loader
             return context.ResolveSatelliteAssembly(assemblyName);
         }
 
-        private Assembly? GetFirstResolvedAssembly(AssemblyName assemblyName)
+        private Assembly? GetFirstResolvedAssemblyFromResolvingEvent(AssemblyName assemblyName)
         {
             Assembly? resolvedAssembly = null;
 
-            Func<AssemblyLoadContext, AssemblyName, Assembly>? assemblyResolveHandler = _resolving;
+            Func<AssemblyLoadContext, AssemblyName, Assembly>? resolvingHandler = _resolving;
 
-            if (assemblyResolveHandler != null)
+            if (resolvingHandler != null)
             {
                 // Loop through the event subscribers and return the first non-null Assembly instance
-                foreach (Func<AssemblyLoadContext, AssemblyName, Assembly> handler in assemblyResolveHandler.GetInvocationList())
+                foreach (Func<AssemblyLoadContext, AssemblyName, Assembly> handler in resolvingHandler.GetInvocationList())
                 {
                     resolvedAssembly = handler(this, assemblyName);
+#if CORECLR
+                    if (AssemblyLoadContext.IsTracingEnabled())
+                    {
+                        AssemblyLoadContext.TraceResolvingHandlerInvoked(
+                            assemblyName.FullName,
+                            handler.Method.Name,
+                            this != AssemblyLoadContext.Default ? ToString() : Name,
+                            resolvedAssembly?.FullName,
+                            resolvedAssembly != null && !resolvedAssembly.IsDynamic ? resolvedAssembly.Location : null);
+                    }
+#endif // CORECLR
                     if (resolvedAssembly != null)
                     {
                         return resolvedAssembly;
@@ -606,6 +617,11 @@ namespace System.Runtime.Loader
 
         private static Assembly ValidateAssemblyNameWithSimpleName(Assembly assembly, string? requestedSimpleName)
         {
+            if (string.IsNullOrEmpty(requestedSimpleName))
+            {
+                throw new ArgumentException(SR.ArgumentNull_AssemblyNameName);
+            }
+
             // Get the name of the loaded assembly
             string? loadedSimpleName = null;
 
@@ -619,10 +635,6 @@ namespace System.Runtime.Loader
             }
 
             // The simple names should match at the very least
-            if (string.IsNullOrEmpty(requestedSimpleName))
-            {
-                throw new ArgumentException(SR.ArgumentNull_AssemblyNameName);
-            }
             if (string.IsNullOrEmpty(loadedSimpleName) || !requestedSimpleName.Equals(loadedSimpleName, StringComparison.InvariantCultureIgnoreCase))
             {
                 throw new InvalidOperationException(SR.Argument_CustomAssemblyLoadContextRequestedNameMismatch);
@@ -648,8 +660,8 @@ namespace System.Runtime.Loader
         {
             string? simpleName = assemblyName.Name;
 
-            // Invoke the AssemblyResolve event callbacks if wired up
-            Assembly? assembly = GetFirstResolvedAssembly(assemblyName);
+            // Invoke the Resolving event callbacks if wired up
+            Assembly? assembly = GetFirstResolvedAssemblyFromResolvingEvent(assemblyName);
             if (assembly != null)
             {
                 assembly = ValidateAssemblyNameWithSimpleName(assembly, simpleName);
@@ -692,6 +704,16 @@ namespace System.Runtime.Loader
             foreach (ResolveEventHandler handler in eventHandler.GetInvocationList())
             {
                 Assembly? asm = handler(AppDomain.CurrentDomain, args);
+#if CORECLR
+                if (eventHandler == AssemblyResolve && AssemblyLoadContext.IsTracingEnabled())
+                {
+                    AssemblyLoadContext.TraceAssemblyResolveHandlerInvoked(
+                        name,
+                        handler.Method.Name,
+                        asm?.FullName,
+                        asm != null && !asm.IsDynamic ? asm.Location : null);
+                }
+#endif // CORECLR
                 RuntimeAssembly? ret = GetRuntimeAssembly(asm);
                 if (ret != null)
                     return ret;