Add tracing for handling of dependencies for Assembly.LoadFrom (dotnet/coreclr#27833)
authorElinor Fung <47805090+elinor-fung@users.noreply.github.com>
Wed, 13 Nov 2019 09:11:41 +0000 (01:11 -0800)
committerVitek Karas <vitek.karas@microsoft.com>
Wed, 13 Nov 2019 09:11:41 +0000 (01:11 -0800)
Commit migrated from https://github.com/dotnet/coreclr/commit/80557919596be103e60ce1c90ed4860e0708ff56

14 files changed:
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.cs
src/coreclr/tests/src/Loader/binding/tracing/AssemblyToLoad.csproj
src/coreclr/tests/src/Loader/binding/tracing/AssemblyToLoadDependency.cs [new file with mode: 0644]
src/coreclr/tests/src/Loader/binding/tracing/AssemblyToLoadDependency.csproj [new file with mode: 0644]
src/coreclr/tests/src/Loader/binding/tracing/BinderEventListener.cs
src/coreclr/tests/src/Loader/binding/tracing/BinderTracingTest.EventHandlers.cs
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/Reflection/Assembly.cs

index 0700ec3..84070a6 100644 (file)
@@ -41,6 +41,9 @@ namespace System.Runtime.Loader
         [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
         internal static extern bool TraceAssemblyResolveHandlerInvoked(string assemblyName, string handlerName, string? resultAssemblyName, string? resultAssemblyPath);
 
+        [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
+        internal static extern bool TraceAssemblyLoadFromResolveHandlerInvoked(string assemblyName, bool isTrackedAssembly, string requestingAssemblyPath, string? requestedAssemblyPath);
+
         private Assembly InternalLoadFromPath(string? assemblyPath, string? nativeImagePath)
         {
             RuntimeAssembly? loadedAssembly = null;
index 2ca30d1..91bec23 100644 (file)
                         <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"/>
+                            <opcode name="AssemblyLoadFromResolveHandlerInvoked" message="$(string.RuntimePublisher.AssemblyLoadFromResolveHandlerInvokedOpcodeMessage)" symbol="CLR_ASSEMBLY_LOAD_FROM_RESOLVE_HANDLER_INVOKED_OPCODE" value="14"/>
                         </opcodes>
                     </task>
                 <!--Next available ID is 33-->
                         </UserData>
                     </template>
 
+                    <template tid="AssemblyLoadFromResolveHandlerInvoked">
+                        <data name="ClrInstanceID" inType="win:UInt16" />
+                        <data name="AssemblyName" inType="win:UnicodeString" />
+                        <data name="IsTrackedLoad" inType="win:Boolean" />
+                        <data name="RequestingAssemblyPath" inType="win:UnicodeString" />
+                        <data name="ComputedRequestedAssemblyPath" inType="win:UnicodeString" />
+                        <UserData>
+                            <AssemblyLoadFromResolveHandlerInvoked xmlns="myNs">
+                                <ClrInstanceID> %1 </ClrInstanceID>
+                                <AssemblyName> %2 </AssemblyName>
+                                <IsTrackedAssembly> %3 </IsTrackedAssembly>
+                                <RequestingAssemblyName> %4 </RequestingAssemblyName>
+                                <ComputedRequestedAssemblyPath> %5 </ComputedRequestedAssemblyPath>
+                            </AssemblyLoadFromResolveHandlerInvoked>
+                        </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="AppDomainAssemblyResolveHandlerInvoked" message="$(string.RuntimePublisher.AppDomainAssemblyResolveHandlerInvokedEventMessage)"/>
 
+                    <event value="295" version="0" level="win:Informational"  template="AssemblyLoadFromResolveHandlerInvoked"
+                           keywords ="AssemblyLoaderKeyword" opcode="AssemblyLoadFromResolveHandlerInvoked"
+                           task="AssemblyLoader"
+                           symbol="AssemblyLoadFromResolveHandlerInvoked" message="$(string.RuntimePublisher.AssemblyLoadFromResolveHandlerInvokedEventMessage)"/>
+
                 </events>
             </provider>
 
                 <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.AssemblyLoadFromResolveHandlerInvokedEventMessage" value="ClrInstanceID=%1;%nAssemblyName=%2;%nIsTrackedLoad=%3;%nRequestingAssemblyPath=%4;%nComputedRequestedAssemblyPath=%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.AssemblyLoadContextResolvingHandlerInvokedOpcodeMessage" value="AssemblyLoadContextResolvingHandlerInvoked" />
                 <string id="RuntimePublisher.AppDomainAssemblyResolveHandlerInvokedOpcodeMessage" value="AppDomainAssemblyResolveHandlerInvoked" />
+                <string id="RuntimePublisher.AssemblyLoadFromResolveHandlerInvokedOpcodeMessage" value="AssemblyLoadFromResolveHandlerInvoked" />
 
                 <string id="RundownPublisher.MethodDCStartOpcodeMessage" value="DCStart" />
                 <string id="RundownPublisher.MethodDCEndOpcodeMessage" value="DCStop" />
index 2b08568..4d27100 100644 (file)
@@ -1451,4 +1451,16 @@ void QCALLTYPE AssemblyNative::TraceAssemblyResolveHandlerInvoked(LPCWSTR assemb
     FireEtwAppDomainAssemblyResolveHandlerInvoked(GetClrInstanceId(), assemblyName, handlerName, resultAssemblyName, resultAssemblyPath);
 
     END_QCALL;
+}
+
+// static
+void QCALLTYPE AssemblyNative::TraceAssemblyLoadFromResolveHandlerInvoked(LPCWSTR assemblyName, bool isTrackedAssembly, LPCWSTR requestingAssemblyPath, LPCWSTR requestedAssemblyPath)
+{
+    QCALL_CONTRACT;
+
+    BEGIN_QCALL;
+
+    FireEtwAssemblyLoadFromResolveHandlerInvoked(GetClrInstanceId(), assemblyName, isTrackedAssembly, requestingAssemblyPath, requestedAssemblyPath);
+
+    END_QCALL;
 }
\ No newline at end of file
index 7f54661..1d0aa0b 100644 (file)
@@ -135,6 +135,7 @@ public:
     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);
+    static void QCALLTYPE TraceAssemblyLoadFromResolveHandlerInvoked(LPCWSTR assemblyName, bool isTrackedAssembly, LPCWSTR requestingAssemblyPath, LPCWSTR requestedAssemblyPath);
 };
 
 #endif
index 6fedfc8..b591f5c 100644 (file)
@@ -515,6 +515,7 @@ FCFuncStart(gAssemblyLoadContextFuncs)
     FCFuncElement("IsTracingEnabled", AssemblyNative::IsTracingEnabled)
     QCFuncElement("TraceResolvingHandlerInvoked", AssemblyNative::TraceResolvingHandlerInvoked)
     QCFuncElement("TraceAssemblyResolveHandlerInvoked", AssemblyNative::TraceAssemblyResolveHandlerInvoked)
+    QCFuncElement("TraceAssemblyLoadFromResolveHandlerInvoked", AssemblyNative::TraceAssemblyLoadFromResolveHandlerInvoked)
 FCFuncEnd()
 
 FCFuncStart(gAssemblyNameFuncs)
index 6d26ac7..eb97acd 100644 (file)
@@ -5,5 +5,11 @@
 namespace AssemblyToLoad
 {
     public class Program
-    { }
+    {
+        public static System.Reflection.Assembly UseDependentAssembly()
+        {
+            var p = new AssemblyToLoadDependency.Program();
+            return System.Reflection.Assembly.GetAssembly(p.GetType());
+        }
+    }
 }
index 278cdfe..c7ec026 100644 (file)
@@ -8,4 +8,7 @@
   <ItemGroup>
     <Compile Include="AssemblyToLoad.cs" />
   </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="AssemblyToLoadDependency.csproj" />
+  </ItemGroup>
 </Project>
diff --git a/src/coreclr/tests/src/Loader/binding/tracing/AssemblyToLoadDependency.cs b/src/coreclr/tests/src/Loader/binding/tracing/AssemblyToLoadDependency.cs
new file mode 100644 (file)
index 0000000..5dda46b
--- /dev/null
@@ -0,0 +1,9 @@
+// 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.
+
+namespace AssemblyToLoadDependency
+{
+    public class Program
+    { }
+}
diff --git a/src/coreclr/tests/src/Loader/binding/tracing/AssemblyToLoadDependency.csproj b/src/coreclr/tests/src/Loader/binding/tracing/AssemblyToLoadDependency.csproj
new file mode 100644 (file)
index 0000000..34d4271
--- /dev/null
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Library</OutputType>
+    <CLRTestKind>BuildOnly</CLRTestKind>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="AssemblyToLoadDependency.cs" />
+  </ItemGroup>
+</Project>
index 5d07fe7..5d77446 100644 (file)
@@ -34,6 +34,7 @@ namespace BinderTracingTests
 
         public List<HandlerInvocation> AssemblyLoadContextResolvingHandlers { get; internal set; }
         public List<HandlerInvocation> AppDomainAssemblyResolveHandlers { get; internal set; }
+        public LoadFromHandlerInvocation AssemblyLoadFromHandler { get; internal set; }
 
         public List<BindOperation> NestedBinds { get; internal set; }
 
@@ -76,6 +77,14 @@ namespace BinderTracingTests
         }
     }
 
+    internal class LoadFromHandlerInvocation
+    {
+        public AssemblyName AssemblyName { get; internal set; }
+        public bool IsTrackedLoad { get; internal set; }
+        public string RequestingAssemblyPath { get; internal set; }
+        public string ComputedRequestedAssemblyPath { get; internal set; }
+    }
+
     internal sealed class BinderEventListener : EventListener
     {
         private const EventKeywords TasksFlowActivityIds = (EventKeywords)0x80;
@@ -151,7 +160,7 @@ namespace BinderTracingTests
                 {
                     lock (eventsLock)
                     {
-                        Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), "AssemblyLoadStop should have a matching AssemblyBindStart");
+                        Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), $"{data.EventName} should have a matching AssemblyBindStart");
                         BindOperation bind = bindOperations[data.ActivityId];
                         bind.Success = (bool)GetData("Success");
                         string resultName = GetDataString("ResultAssemblyName");
@@ -170,7 +179,7 @@ namespace BinderTracingTests
                     HandlerInvocation handlerInvocation = ParseHandlerInvokedEvent(GetDataString);
                     lock (eventsLock)
                     {
-                        Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), "AssemblyLoadContextResolvingHandlerInvoked should have a matching AssemblyBindStart");
+                        Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), $"{data.EventName} should have a matching AssemblyBindStart");
                         BindOperation bind = bindOperations[data.ActivityId];
                         bind.AssemblyLoadContextResolvingHandlers.Add(handlerInvocation);
                     }
@@ -181,12 +190,23 @@ namespace BinderTracingTests
                     HandlerInvocation handlerInvocation = ParseHandlerInvokedEvent(GetDataString);
                     lock (eventsLock)
                     {
-                        Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), "AppDomainAssemblyResolveHandlerInvoked should have a matching AssemblyBindStart");
+                        Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), $"{data.EventName} should have a matching AssemblyBindStart");
                         BindOperation bind = bindOperations[data.ActivityId];
                         bind.AppDomainAssemblyResolveHandlers.Add(handlerInvocation);
                     }
                     break;
                 }
+                case "AssemblyLoadFromResolveHandlerInvoked":
+                {
+                    LoadFromHandlerInvocation loadFrom = ParseLoadFromHandlerInvokedEvent(GetData, GetDataString);
+                    lock (eventsLock)
+                    {
+                        Assert.IsTrue(bindOperations.ContainsKey(data.ActivityId), $"{data.EventName} should have a matching AssemblyBindStart");
+                        BindOperation bind = bindOperations[data.ActivityId];
+                        bind.AssemblyLoadFromHandler = loadFrom;
+                    }
+                    break;
+                }
             }
         }
 
@@ -227,5 +247,17 @@ namespace BinderTracingTests
 
             return handlerInvocation;
         }
+
+        private LoadFromHandlerInvocation ParseLoadFromHandlerInvokedEvent(Func<string, object> getData, Func<string, string> getDataString)
+        {
+            var loadFrom = new LoadFromHandlerInvocation()
+            {
+                AssemblyName = new AssemblyName(getDataString("AssemblyName")),
+                IsTrackedLoad = (bool)getData("IsTrackedLoad"),
+                RequestingAssemblyPath = getDataString("RequestingAssemblyPath"),
+                ComputedRequestedAssemblyPath = getDataString("ComputedRequestedAssemblyPath"),
+            };
+            return loadFrom;
+        }
     }
 }
index bf97a43..0eaefef 100644 (file)
@@ -16,6 +16,8 @@ namespace BinderTracingTests
 {
     partial class BinderTracingTest
     {
+        private const string AssemblyLoadFromHandlerName = "LoadFromResolveHandler";
+
         [BinderTest]
         public static BindOperation AssemblyLoadContextResolving_ReturnNull()
         {
@@ -227,6 +229,126 @@ namespace BinderTracingTests
             }
         }
 
+        [BinderTest(isolate: true)]
+        public static BindOperation AssemblyLoadFromResolveHandler_LoadDependency()
+        {
+            string assemblyPath = GetAssemblyInSubdirectoryPath(SubdirectoryAssemblyName);
+            Assembly asm = Assembly.LoadFrom(assemblyPath);
+            Type t = asm.GetType(DependentAssemblyTypeName);
+            MethodInfo method = t.GetMethod("UseDependentAssembly", BindingFlags.Public | BindingFlags.Static);
+            Assembly asmDependency = (Assembly)method.Invoke(null, new object[0]);
+
+            return new BindOperation()
+            {
+                AssemblyName = asmDependency.GetName(),
+                AssemblyLoadContext = DefaultALC,
+                RequestingAssembly = asm.GetName(),
+                RequestingAssemblyLoadContext = DefaultALC,
+                Success = true,
+                ResultAssemblyName = asmDependency.GetName(),
+                ResultAssemblyPath = asmDependency.Location,
+                Cached = false,
+                AppDomainAssemblyResolveHandlers = new List<HandlerInvocation>()
+                {
+                    new HandlerInvocation()
+                    {
+                        AssemblyName = asmDependency.GetName(),
+                        HandlerName = AssemblyLoadFromHandlerName,
+                        ResultAssemblyName = asmDependency.GetName(),
+                        ResultAssemblyPath = asmDependency.Location
+                    }
+                },
+                AssemblyLoadFromHandler = new LoadFromHandlerInvocation()
+                {
+                    AssemblyName = asmDependency.GetName(),
+                    IsTrackedLoad = true,
+                    RequestingAssemblyPath = asm.Location,
+                    ComputedRequestedAssemblyPath = asmDependency.Location
+                }
+            };
+        }
+
+        [BinderTest(isolate: true)]
+        public static BindOperation AssemblyLoadFromResolveHandler_MissingDependency()
+        {
+            string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+            string assemblyPath = Path.Combine(appPath, $"{DependentAssemblyName}.dll");
+            Assembly asm = Assembly.LoadFrom(assemblyPath);
+            Type t = asm.GetType(DependentAssemblyTypeName);
+            MethodInfo method = t.GetMethod("UseDependentAssembly", BindingFlags.Public | BindingFlags.Static);
+            Assert.Throws<TargetInvocationException, FileNotFoundException>(() => method.Invoke(null, new object[0]));
+
+            var assemblyName = new AssemblyName(asm.FullName);
+            assemblyName.Name = "AssemblyToLoadDependency";
+            var expectedPath = Path.Combine(appPath, $"{assemblyName.Name}.dll");
+            return new BindOperation()
+            {
+                AssemblyName = assemblyName,
+                AssemblyLoadContext = DefaultALC,
+                RequestingAssembly = asm.GetName(),
+                RequestingAssemblyLoadContext = DefaultALC,
+                Success = false,
+                Cached = false,
+                AppDomainAssemblyResolveHandlers = new List<HandlerInvocation>()
+                {
+                    new HandlerInvocation()
+                    {
+                        AssemblyName = assemblyName,
+                        HandlerName = AssemblyLoadFromHandlerName,
+                    }
+                },
+                AssemblyLoadFromHandler = new LoadFromHandlerInvocation()
+                {
+                    AssemblyName = assemblyName,
+                    IsTrackedLoad = true,
+                    RequestingAssemblyPath = asm.Location,
+                    ComputedRequestedAssemblyPath = expectedPath
+                }
+            };
+        }
+
+        [BinderTest(isolate: true)]
+        public static BindOperation AssemblyLoadFromResolveHandler_NotTracked()
+        {
+            string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+            string assemblyPath = Path.Combine(appPath, $"{DependentAssemblyName}.dll");
+            Assembly.LoadFrom(assemblyPath);
+
+            var assemblyName = new AssemblyName(SubdirectoryAssemblyName);
+            Assert.Throws<FileNotFoundException>(() => Assembly.Load(SubdirectoryAssemblyName));
+
+            Assembly executingAssembly = Assembly.GetExecutingAssembly();
+            return new BindOperation()
+            {
+                AssemblyName = assemblyName,
+                AssemblyLoadContext = DefaultALC,
+                RequestingAssembly = executingAssembly.GetName(),
+                RequestingAssemblyLoadContext = DefaultALC,
+                Success = false,
+                Cached = false,
+                AppDomainAssemblyResolveHandlers = new List<HandlerInvocation>()
+                {
+                    new HandlerInvocation()
+                    {
+                        AssemblyName = assemblyName,
+                        HandlerName = AssemblyLoadFromHandlerName,
+                    }
+                },
+                AssemblyLoadFromHandler = new LoadFromHandlerInvocation()
+                {
+                    AssemblyName = assemblyName,
+                    IsTrackedLoad = false,
+                    RequestingAssemblyPath = executingAssembly.Location
+                }
+            };
+        }
+
+        private static string GetAssemblyInSubdirectoryPath(string assemblyName)
+        {
+            string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+            return Path.Combine(appPath, "DependentAssemblies", $"{assemblyName}.dll");
+        }
+
         private enum HandlerReturn
         {
             Null,
@@ -306,9 +428,8 @@ namespace BinderTracingTests
                 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);
+                string name = handlerReturn == HandlerReturn.RequestedAssembly ? assemblyName.Name : $"{assemblyName.Name}Mismatch";
+                string assemblyPath = GetAssemblyInSubdirectoryPath(name);
 
                 if (!File.Exists(assemblyPath))
                     return null;
index 4373381..1af3333 100644 (file)
@@ -36,6 +36,7 @@ namespace BinderTracingTests
 
         private const string DefaultALC = "Default";
         private const string DependentAssemblyName = "AssemblyToLoad";
+        private const string DependentAssemblyTypeName = "AssemblyToLoad.Program";
         private const string SubdirectoryAssemblyName = "AssemblyToLoad_Subdirectory";
 
         private static readonly AssemblyName CoreLibName = typeof(object).Assembly.GetName();
@@ -400,7 +401,7 @@ namespace BinderTracingTests
 
         private static Type GetDependentAssemblyType()
         {
-            return Type.GetType($"AssemblyToLoad.Program, {DependentAssemblyName}");
+            return Type.GetType($"{DependentAssemblyTypeName}, {DependentAssemblyName}");
         }
 
         private static Type LoadTestClassInALC(AssemblyLoadContext alc)
@@ -488,6 +489,7 @@ namespace BinderTracingTests
 
             ValidateHandlerInvocations(expected.AssemblyLoadContextResolvingHandlers, actual.AssemblyLoadContextResolvingHandlers, "AssemblyLoadContextResolving");
             ValidateHandlerInvocations(expected.AppDomainAssemblyResolveHandlers, actual.AppDomainAssemblyResolveHandlers, "AppDomainAssemblyResolve");
+            ValidateLoadFromHandlerInvocation(expected.AssemblyLoadFromHandler, actual.AssemblyLoadFromHandler);
 
             ValidateNestedBinds(expected.NestedBinds, actual.NestedBinds);
         }
@@ -523,6 +525,21 @@ namespace BinderTracingTests
             }
         }
 
+        private static void ValidateLoadFromHandlerInvocation(LoadFromHandlerInvocation expected, LoadFromHandlerInvocation actual)
+        {
+            if (expected == null || actual == null)
+            {
+                Assert.IsNull(expected);
+                Assert.IsNull(actual);
+                return;
+            }
+
+            ValidateAssemblyName(expected.AssemblyName, actual.AssemblyName, nameof(LoadFromHandlerInvocation.AssemblyName));
+            Assert.AreEqual(expected.IsTrackedLoad, actual.IsTrackedLoad, $"Unexpected value for {nameof(LoadFromHandlerInvocation.IsTrackedLoad)} on event");
+            Assert.AreEqual(expected.RequestingAssemblyPath, actual.RequestingAssemblyPath, $"Unexpected value for {nameof(LoadFromHandlerInvocation.RequestingAssemblyPath)} on event");
+            Assert.AreEqual(expected.ComputedRequestedAssemblyPath, actual.ComputedRequestedAssemblyPath, $"Unexpected value for {nameof(LoadFromHandlerInvocation.ComputedRequestedAssemblyPath)} on event");
+        }
+
         private static bool BindOperationsMatch(BindOperation bind1, BindOperation bind2)
         {
             try
index 0df6d4f..ef59ad4 100644 (file)
@@ -31,6 +31,7 @@
   <Target Name="MoveDependentAssembliesToSubdirectory" AfterTargets="CopyFilesToOutputDirectory">
     <ItemGroup>
       <AssembliesToMove Include="$(OutDir)/AssemblyToLoad_*.*" />
+      <AssembliesToMove Include="$(OutDir)/AssemblyToLoadDependency.*" />
     </ItemGroup>
     <Move SourceFiles="@(AssembliesToMove)" DestinationFolder="$(OutDir)/DependentAssemblies" />
   </Target>
index db7d10a..27c8186 100644 (file)
@@ -258,7 +258,15 @@ namespace System.Reflection
             {
                 // If the requestor assembly was not loaded using LoadFrom, exit.
                 if (!s_loadFromAssemblyList.Contains(requestorPath))
+                {
+#if CORECLR
+                    if (AssemblyLoadContext.IsTracingEnabled())
+                    {
+                        AssemblyLoadContext.TraceAssemblyLoadFromResolveHandlerInvoked(args.Name, false, requestorPath, null);
+                    }
+#endif // CORECLR
                     return null;
+                }
             }
 
             // Requestor assembly was loaded using loadFrom, so look for its dependencies
@@ -266,7 +274,12 @@ namespace System.Reflection
             // Form the name of the assembly using the path of the assembly that requested its load.
             AssemblyName requestedAssemblyName = new AssemblyName(args.Name!);
             string requestedAssemblyPath = Path.Combine(Path.GetDirectoryName(requestorPath)!, requestedAssemblyName.Name + ".dll");
-
+#if CORECLR
+            if (AssemblyLoadContext.IsTracingEnabled())
+            {
+                AssemblyLoadContext.TraceAssemblyLoadFromResolveHandlerInvoked(args.Name, true, requestorPath, requestedAssemblyPath);
+            }
+#endif // CORECLR
             try
             {
                 // Load the dependency via LoadFrom so that it goes through the same path of being in the LoadFrom list.