dotnet / orleans

Cloud Native application framework for .NET
https://docs.microsoft.com/dotnet/orleans
MIT License
10.07k stars 2.03k forks source link

Initializing Orleans client in an Asp.net RC2 Core (.Net Framework) project logs the following exception on startup #1840

Closed globeport closed 6 years ago

globeport commented 8 years ago

I'm getting the following output running Orleans from an asp.net core app... (.Net Framwork 4.6.1) The application runs successfully beyond this.

[2016-06-14 13:25:23.041 GMT 6 ERROR 101716 AssemblyLoader.Client ] !!!!!!!!!! An unexpected exception occurred while attempting to load an assembly.
Exc level 0: System.BadImageFormatException: Could not load file or assembly 'file:///C:\Tentacle\Applications\Test\WebApi\0.1.20160614.2_1\libuv.dll' or one of its dependencies. The module was expected to contain an assembly manifest. at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) at System.Reflection.RuntimeAssembly.InternalLoadFrom(String assemblyFile, Evidence securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm, Boolean forIntrospection, Boolean suppressSecurityChecks, StackCrawlMark& stackMark) at System.Reflection.Assembly.ReflectionOnlyLoadFrom(String assemblyFile) at Orleans.Runtime.AssemblyLoader.ReflectionOnlyLoadAssembly(String pathName, Assembly& assembly, String[]& complaints) A ReflectionTypeLoadException has been thrown. The original exception and the contents of the LoaderExceptions property have been aggregated for your convenience. Exception = System.AggregateException: A ReflectionTypeLoadException has been thrown. The original exception and the contents of the LoaderExceptions property have been aggregated for your convenience. ---> System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module) at System.Reflection.RuntimeAssembly.get_DefinedTypes() at Orleans.Runtime.AssemblyLoaderReflectionCriterion.<>cDisplayClass3_0.b0(Assembly assembly, IEnumerable`1& assemblyComplaints) --- End of inner exception stack trace --- at Orleans.Runtime.Utils.Flatten(ReflectionTypeLoadException rtle) at Orleans.Runtime.AssemblyLoaderReflectionCriterion.<>cDisplayClass3_0.b0(Assembly assembly, IEnumerable1& assemblyComplaints) at Orleans.Runtime.AssemblyLoaderCriterion.EvaluateCandidate(Object input, IEnumerable1& complaints) at Orleans.Runtime.AssemblyLoader.ShouldLoadAssembly(String pathName) ---> (Inner Exception #0) System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information. at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module) at System.Reflection.RuntimeAssembly.get_DefinedTypes() at Orleans.Runtime.AssemblyLoaderReflectionCriterion.<>c__DisplayClass3_0.b__0(Assembly assembly, IEnumerable`1& assemblyComplaints)<--- ... ... ---> (Inner Exception #1) System.TypeLoadException: Could not load type 'System.Object' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' because the parent does not exist.<--- ---> (Inner Exception #2) System.TypeLoadException: Could not load type 'System.Object' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' because the parent does not exist.<--- ... ...

Hosting environment: Production Content root path: C:\Tentacle\Applications\Test\WebApi\0.1.20160614.2_1 Now listening on: http://localhost:4306/ Application started. Press Ctrl+C to shut down.

davidfowl commented 8 years ago

Not sure what's going on but it looks like some component is trying to load libuv.dll (a native component) via reflection only load. I assume this is just a first chance exception?

globeport commented 8 years ago

Yes, the Orleans client functions correctly following initialization despite these errors.

shayhatsor commented 8 years ago

On startup, the Orleans client searches for grain interfaces using reflection. That's normal behavior. Sometimes a DLL load may fail, in these cases the error is logged.

sergeybykov commented 8 years ago

We are looking to add an option to avoid blind scanning of binaries, but this is the current 'by design' behavior.

globeport commented 8 years ago

Ok thanks. It would definitely be nice to have some hooks to control the assembly scan.

sergeybykov commented 8 years ago

@globeport Feel free to suggest what the hooks should look like.

lucasgodshalk commented 8 years ago

With the code-based configuration, could it just be a delegate? Say, Func<IEnumerable<string>> where every entry is the full path of the assembly to scan? That would allow for both static assembly lists and finding them at runtime.

var config = new ClusterConfiguration();
config.Defaults.AssemblyListProvider = () => {
    return new List<string>() 
    {
        "C:/Foo/bar.dll"
    };
};

Not sure how this works out with xml-based configurations...

attilah commented 8 years ago

I think we can solve this by smartening the loader by checking the PE Header in the binaries using Kirill Osenkov's answer here at SO: http://stackoverflow.com/questions/367761/how-to-determine-whether-a-dll-is-a-managed-assembly-or-native-prevent-loading

Which points to: http://apichange.codeplex.com/SourceControl/changeset/view/76c98b8c7311#ApiChange.Api/src/Introspection/CorFlagsReader.cs

What do you think?

sergeybykov commented 8 years ago

@TrexinanF14 I'm concerned full paths may be a problem, especially in case Azure Cloud Services. Why not just assembly names?

@attilah It would really help indeed to auto-skip non-managed dlls.

lucasgodshalk commented 8 years ago

@sergeybykov Yeah, scratch the full path requirement, although it seems that accepting relative paths would be a good idea (relative based on the location of the executing assembly?). Or some other method to look outside the default directories.

Should this conversation hop over to #1852? It seems like the solution provided by @attilah would solve this issue.

attilah commented 8 years ago

Based on the CorFlagsReader above I wrote this snippet and tested on major windows directories on my system, seems to work ok. What was got to assembly loading that was loaded properly!

try
{
    var flags = default(CorFlagsReader);

    // Use stream to have least access required for Assembly loading
    using (var stream = File.Open(filename, FileMode.Open, FileAccess.Read))
    {
        flags = CorFlagsReader.ReadAssemblyMetadata(stream);
    }

    if (flags != null)
    {
        var isManaged = flags.IsPureIL || flags.ProcessorArchitecture == ProcessorArchitecture.MSIL;

        var isProcessorArchitectureMatching = 
            (Environment.Is64BitProcess && flags.ProcessorArchitecture == ProcessorArchitecture.Amd64) ||
            (!Environment.Is64BitProcess && flags.ProcessorArchitecture == ProcessorArchitecture.X86);

        if (isManaged && isProcessorArchitectureMatching)
        {
            // At this point the file is "safe" to load

            // user specific predicate can be called here if we'd like to support blacklisting too.
        }
    }
}
catch (IOException)
{
}
catch (UnauthorizedAccessException)
{
}

BadImageFormatException is not needed to check here, since no assembly loading happens here, but access errors can happen.

What is the current behavior for the exceptions above?

globeport commented 8 years ago

If be happy if there was a switch in the configuration to enable/disable automatic scanning of assemblies. If enabled, I'd like to be able to exclude specific assemblies by name. If disabled, I'd like to be able to include specific assemblies by name. It would also be nice to be able to override this behaviour in code and supply an array of assemblies that can be located via reflection. I'm unclear why there would be a requirement to include assemblies by their path (relative or absolute)?

shayhatsor commented 8 years ago

@sergeybykov, it might seem like a basic question, well... I guess it is: does the Orleans client have to to scan the assemblies on initialization? Is there a technical limitation that prevents it from applying the Orleans magic dynamically to assemblies as they're loaded ?

sergeybykov commented 8 years ago

@globeport Couldn't we achieve both goals by turning off scanning when an explicit list of application assemblies is provided? Otherwise, scan like we do today. We'd still need to load Orleans assemblies regardless, of course.

@shayhatsor The reason Orleans client scans assemblies IIRC is for stream providers. We might be able to do away with that because providers are explicitly defined in config.

globeport commented 8 years ago

Sure, sorry I was thinking that it would be nice to exclude problem assemblies, but if the scan is updated to exclude native assemblies then its probably not required. Providing a single include list to override automatic scanning (via the configuration file or via code) would be perfect.

sergeybykov commented 6 years ago

Closing this, as we don't scan all assemblies by default in 2.0.0.