private static Assembly AssemblyResolve_ManagedProfilerDependencies(object sender, ResolveEventArgs args) { var assemblyName = new AssemblyName(args.Name); // On .NET Framework, having a non-US locale can cause mscorlib // to enter the AssemblyResolve event when searching for resources // in its satellite assemblies. This seems to have been fixed in // .NET Core in the 2.0 servicing branch, so we should not see this // occur, but guard against it anyways. If we do see it, exit early // so we don't cause infinite recursion. if (string.Equals(assemblyName.Name, "System.Private.CoreLib.resources", StringComparison.OrdinalIgnoreCase) || string.Equals(assemblyName.Name, "System.Net.Http", StringComparison.OrdinalIgnoreCase)) { return(null); } var path = Path.Combine(ManagedProfilerDirectory, $"{assemblyName.Name}.dll"); // Only load the main profiler into the default Assembly Load Context. // If Datadog.Trace or other libraries are provided by the NuGet package their loads are handled in the following two ways. // 1) The AssemblyVersion is greater than or equal to the version used by Datadog.Trace, the assembly // will load successfully and will not invoke this resolve event. // 2) The AssemblyVersion is lower than the version used by Datadog.Trace, the assembly will fail to load // and invoke this resolve event. It must be loaded in a separate AssemblyLoadContext since the application will only // load the originally referenced version if (File.Exists(path)) { StartupLogger.Debug("Loading {0} with DependencyLoadContext.LoadFromAssemblyPath", path); return(DependencyLoadContext.LoadFromAssemblyPath(path)); // Load unresolved framework and third-party dependencies into a custom Assembly Load Context } return(null); }
private static string ResolveManagedProfilerDirectory() { string tracerFrameworkDirectory = "netstandard2.0"; var version = Environment.Version; // Old versions of .net core have a major version of 4 if ((version.Major == 3 && version.Minor >= 1) || version.Major >= 5) { tracerFrameworkDirectory = "netcoreapp3.1"; } var tracerHomeDirectory = ReadEnvironmentVariable("DD_DOTNET_TRACER_HOME") ?? string.Empty; var fullPath = Path.Combine(tracerHomeDirectory, tracerFrameworkDirectory); // We use the List/Array approach due the number of files in the tracer home folder (7 in netstandard, 2 netcoreapp3.1) var assemblies = new List <CachedAssembly>(); foreach (var file in Directory.EnumerateFiles(fullPath, "*.dll", SearchOption.TopDirectoryOnly)) { assemblies.Add(new CachedAssembly(file, null)); } _assemblies = assemblies.ToArray(); StartupLogger.Debug("Total number of assemblies: {0}", _assemblies.Length); return(fullPath); }
private static Assembly ResolveAssembly(string name) { var assemblyName = new AssemblyName(name); StartupLogger.Debug("Assembly Resolve event received for: {0}", name); // On .NET Framework, having a non-US locale can cause mscorlib // to enter the AssemblyResolve event when searching for resources // in its satellite assemblies. Exit early so we don't cause // infinite recursion. if (string.Equals(assemblyName.Name, "mscorlib.resources", StringComparison.OrdinalIgnoreCase) || string.Equals(assemblyName.Name, "System.Net.Http", StringComparison.OrdinalIgnoreCase)) { return(null); } var path = Path.Combine(ManagedProfilerDirectory, $"{assemblyName.Name}.dll"); StartupLogger.Debug("Looking for: {0}", path); if (File.Exists(path)) { if (name.StartsWith("SignalFx.Tracing, Version=") && name != AssemblyName) { StartupLogger.Debug("Trying to load {0} which does not match the expected version ({1}). [Path={2}]", name, AssemblyName, path); return(null); } StartupLogger.Debug("Resolving {0}, loading {1}", name, path); return(Assembly.LoadFrom(path)); } StartupLogger.Debug("Assembly not found in path: {0}", path); return(null); }
private static Assembly AssemblyResolve_ManagedProfilerDependencies(object sender, ResolveEventArgs args) { var assemblyName = new AssemblyName(args.Name); // On .NET Framework, having a non-US locale can cause mscorlib // to enter the AssemblyResolve event when searching for resources // in its satellite assemblies. This seems to have been fixed in // .NET Core in the 2.0 servicing branch, so we should not see this // occur, but guard against it anyways. If we do see it, exit early // so we don't cause infinite recursion. if (string.Equals(assemblyName.Name, "System.Private.CoreLib.resources", StringComparison.OrdinalIgnoreCase) || string.Equals(assemblyName.Name, "System.Net.Http", StringComparison.OrdinalIgnoreCase)) { return(null); } var path = Path.Combine(ManagedProfilerDirectory, $"{assemblyName.Name}.dll"); if (assemblyName.Name.StartsWith("Datadog.Trace", StringComparison.OrdinalIgnoreCase) && assemblyName.FullName.IndexOf("PublicKeyToken=def86d061d0d2eeb", StringComparison.OrdinalIgnoreCase) >= 0 && File.Exists(path)) { StartupLogger.Debug("Loading {0} with Assembly.LoadFrom", path); return(Assembly.LoadFrom(path)); // Load the main profiler and tracer into the default Assembly Load Context } else if (File.Exists(path)) { StartupLogger.Debug("Loading {0} with DependencyLoadContext.LoadFromAssemblyPath", path); return(DependencyLoadContext.LoadFromAssemblyPath(path)); // Load unresolved framework and third-party dependencies into a custom Assembly Load Context } return(null); }
private static Assembly AssemblyResolve_ManagedProfilerDependencies(object sender, ResolveEventArgs args) { var assemblyName = new AssemblyName(args.Name).Name; // On .NET Framework, having a non-US locale can cause mscorlib // to enter the AssemblyResolve event when searching for resources // in its satellite assemblies. Exit early so we don't cause // infinite recursion. if (string.Equals(assemblyName, "mscorlib.resources", StringComparison.OrdinalIgnoreCase) || string.Equals(assemblyName, "System.Net.Http", StringComparison.OrdinalIgnoreCase)) { return(null); } var path = Path.Combine(ManagedProfilerDirectory, $"{assemblyName}.dll"); if (File.Exists(path)) { if (args.Name.StartsWith("Datadog.Trace, Version=") && args.Name != AssemblyName) { StartupLogger.Debug("Trying to load {0} which does not match the expected version ({1})", args.Name, AssemblyName); return(null); } StartupLogger.Debug("Resolving {0}, loading {1}", args.Name, path); return(Assembly.LoadFrom(path)); } return(null); }
private static string ReadEnvironmentVariable(string key) { try { return(Environment.GetEnvironmentVariable(key)); } catch (Exception ex) { StartupLogger.Log(ex, "Error while loading environment variable " + key); } return(null); }
/// <summary> /// Initializes static members of the <see cref="Startup"/> class. /// This method also attempts to load the Datadog.Trace.ClrProfiler.Managed .NET assembly. /// </summary> static Startup() { ManagedProfilerDirectory = ResolveManagedProfilerDirectory(); try { AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve_ManagedProfilerDependencies; } catch (Exception ex) { StartupLogger.Log(ex, "Unable to register a callback to the CurrentDomain.AssemblyResolve event."); } TryLoadManagedAssembly(); }
private static void TryInvokeManagedMethod(string typeName, string methodName) { try { var assembly = Assembly.Load(AssemblyName); if (assembly != null) { var type = assembly.GetType(typeName, throwOnError: false); var method = type?.GetRuntimeMethod(methodName, parameters: Type.EmptyTypes); method?.Invoke(obj: null, parameters: null); } } catch (Exception ex) { StartupLogger.Log(ex, "Error when invoking managed method: {0}.{1}", typeName, methodName); } }
private static void TryLoadManagedAssembly() { try { var assembly = Assembly.Load("Datadog.Trace.ClrProfiler.Managed, Version=1.26.3.0, Culture=neutral, PublicKeyToken=def86d061d0d2eeb"); if (assembly != null) { // call method Datadog.Trace.ClrProfiler.Instrumentation.Initialize() var type = assembly.GetType("Datadog.Trace.ClrProfiler.Instrumentation", throwOnError: false); var method = type?.GetRuntimeMethod("Initialize", parameters: new Type[0]); method?.Invoke(obj: null, parameters: null); } } catch (Exception ex) { StartupLogger.Log(ex, "Error when loading managed assemblies."); } }
private static Assembly LoadAssembly(string assemblyString) { try { return(Assembly.Load(assemblyString)); } catch (FileNotFoundException ex) { // In some IIS scenarios the `AssemblyResolve` event doesn't get triggered and we received this exception. // We will try to resolve it manually as a last chance. StartupLogger.Log(ex, "Error on assembly load: {0}, Trying to solve it manually...", assemblyString); var assembly = ResolveAssembly(assemblyString); if (assembly is not null) { StartupLogger.Log("Assembly resolved manually."); } return(assembly); } }
private static void TryInvokeManagedMethod(string typeName, string methodName) { try { var assembly = LoadAssembly(AssemblyName); if (assembly == null) { StartupLogger.Log("Assembly '{0}' cannot be loaded. The managed method ({1}.{2}) cannot be invoked", AssemblyName, typeName, methodName); return; } var type = assembly.GetType(typeName, throwOnError: false); var method = type?.GetRuntimeMethod(methodName, parameters: Type.EmptyTypes); method?.Invoke(obj: null, parameters: null); } catch (Exception ex) { StartupLogger.Log(ex, "Error when invoking managed method: {0}.{1}", typeName, methodName); } }
/// <summary> /// Initializes static members of the <see cref="Startup"/> class. /// This method also attempts to load the SignalFx.TracingTrace .NET assembly. /// </summary> static Startup() { ManagedProfilerDirectory = ResolveManagedProfilerDirectory(); StartupLogger.Debug("Resolving managed profiler directory to: {0}", ManagedProfilerDirectory); try { AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve_ManagedProfilerDependencies; } catch (Exception ex) { StartupLogger.Log(ex, "Unable to register a callback to the CurrentDomain.AssemblyResolve event."); } var runInAas = ReadBooleanEnvironmentVariable(AzureAppServicesKey, false); if (!runInAas) { TryInvokeManagedMethod("Datadog.Trace.ClrProfiler.Instrumentation", "Initialize"); return; } // In AAS, the loader can be used to load the tracer, the traceagent only (if only custom tracing is enabled), // dogstatsd or all of them. var customTracingEnabled = ReadBooleanEnvironmentVariable(AasCustomTracingKey, false); var needsDogStatsD = ReadBooleanEnvironmentVariable(AasCustomMetricsKey, false); var automaticTraceEnabled = ReadBooleanEnvironmentVariable(TraceEnabledKey, true); if (automaticTraceEnabled || customTracingEnabled || needsDogStatsD) { StartupLogger.Log("Invoking managed method to start external processes."); TryInvokeManagedMethod("Datadog.Trace.AgentProcessManager", "Initialize"); } if (automaticTraceEnabled) { StartupLogger.Log("Invoking managed tracer."); TryInvokeManagedMethod("Datadog.Trace.ClrProfiler.Instrumentation", "Initialize"); } }